diff --git a/scripts/chai/skill/cnj/skillDef_124.chai b/scripts/chai/skill/cnj/skillDef_124.chai new file mode 100644 index 00000000..325dd3d9 --- /dev/null +++ b/scripts/chai/skill/cnj/skillDef_124.chai @@ -0,0 +1,18 @@ +// Skill Name: Medica +// Skill ID: 124 + +class skillDef_124Def +{ + def skillDef_124Def() + { + + } + + def onFinish( player, target ) + { + player.handleScriptSkill( STD_HEAL, 124, 300, 0, player ); + } + +}; + +GLOBAL skillDef_124 = skillDef_124Def(); diff --git a/scripts/chai/skill/cnj/skillDef_133.chai b/scripts/chai/skill/cnj/skillDef_133.chai new file mode 100644 index 00000000..9a279eaa --- /dev/null +++ b/scripts/chai/skill/cnj/skillDef_133.chai @@ -0,0 +1,19 @@ +// Skill Name: Medica II +// Skill ID: 133 + +class skillDef_133Def +{ + def skillDef_133Def() + { + + } + + def onFinish( player, target ) + { + player.handleScriptSkill( STD_HEAL, 133, 200, 0, player ); + target.addStatusEffectByIdIfNotExist( 150, 30000, player, 50 ); + } + +}; + +GLOBAL skillDef_133 = skillDef_133Def(); diff --git a/src/servers/Server_Common/Common.h b/src/servers/Server_Common/Common.h index 966596bc..a99fb8d9 100644 --- a/src/servers/Server_Common/Common.h +++ b/src/servers/Server_Common/Common.h @@ -571,6 +571,35 @@ namespace Core { LimitBreak = 8, }; + enum ActionEffectType : uint8_t + { + Nothing = 0, + Miss = 1, + FullResist = 2, + Damage = 3, + Heal = 4, + BlockedDamage = 5, + ParriedDamage = 6, + Invulnerable = 7, + NoEffectText = 8, + Unknown_0 = 9, + MpLoss = 10, + MpGain = 11, + TpLoss = 12, + TpGain = 13, + GpGain = 14 + }; + + enum ActionHitSeverityType : uint8_t + { + NormalDamage = 0, + CritHeal = 0, + CritDamage = 1, + NormalHeal = 1, + DirectHitDamage = 2, + CritDirectHitDamage = 3 + }; + enum HandleActionType : uint8_t { Event, diff --git a/src/servers/Server_Common/Exd/ExdData.cpp b/src/servers/Server_Common/Exd/ExdData.cpp index 5f9f22c5..0c5fd98f 100644 --- a/src/servers/Server_Common/Exd/ExdData.cpp +++ b/src/servers/Server_Common/Exd/ExdData.cpp @@ -315,8 +315,7 @@ bool Core::Data::ExdData::loadActionInfo() for( auto row : rows ) { auto& fields = row.second; - - ActionInfo info{ 0 }; + auto info = boost::make_shared< ActionInfo >(); uint32_t id = row.first; if( id == 0 ) @@ -350,7 +349,7 @@ bool Core::Data::ExdData::loadActionInfo() uint16_t recast_time = getField< uint16_t >( fields, 37 ); // 37 int8_t model = getField< int8_t >( fields, 39 ); // 39: Action model - uint8_t aspect = getField< uint8_t >( fields, 40 ); // 40: Action aspect + uint8_t aspect = getField< uint8_t >( fields, 40 ); // 40: Action aspect uint8_t typeshift = 0x6; uint8_t mask = 1 << typeshift; @@ -360,36 +359,36 @@ bool Core::Data::ExdData::loadActionInfo() - info.id = id; - info.name = name; - info.category = category; + info->id = id; + info->name = name; + info->category = category; - info.class_job = class_job; - info.unlock_level = unlock_level; - info.range = range; - info.can_target_self = can_target_self; - info.can_target_party = can_target_party; - info.can_target_friendly = can_target_friendly; - info.can_target_enemy = can_target_enemy; + info->class_job = class_job; + info->unlock_level = unlock_level; + info->range = range; + info->can_target_self = can_target_self; + info->can_target_party = can_target_party; + info->can_target_friendly = can_target_friendly; + info->can_target_enemy = can_target_enemy; - info.can_target_ko = can_target_ko; + info->can_target_ko = can_target_ko; - info.is_aoe = is_aoe; + info->is_aoe = is_aoe; - info.aoe_type = aoe_type; - info.radius = radius; + info->aoe_type = aoe_type; + info->radius = radius; - info.points_type = points_type; - info.points_cost = points_cost; + info->points_type = points_type; + info->points_cost = points_cost; - info.is_instant = is_instant; - info.cast_time = cast_time * 100; - info.recast_time = recast_time * 100; + info->is_instant = is_instant; + info->cast_time = cast_time * 100; + info->recast_time = recast_time * 100; - info.model = model; - info.aspect = aspect; + info->model = model; + info->aspect = aspect; - m_actionInfoMap[id] = info; + m_actionInfoMap.emplace( std::make_pair( info->id, info ) ); } @@ -464,6 +463,22 @@ boost::shared_ptr< Core::Data::AetheryteInfo > } +boost::shared_ptr< Core::Data::ActionInfo > +Core::Data::ExdData::getActionInfo( uint32_t actionId ) +{ + try + { + return m_actionInfoMap[actionId]; + } + catch ( ... ) + { + return nullptr; + } + + return nullptr; + +} + boost::shared_ptr< Core::Data::CustomTalkInfo > Core::Data::ExdData::getCustomTalkInfo( uint32_t customTalkId ) { diff --git a/src/servers/Server_Common/Exd/ExdData.h b/src/servers/Server_Common/Exd/ExdData.h index f0c5793f..a6376550 100644 --- a/src/servers/Server_Common/Exd/ExdData.h +++ b/src/servers/Server_Common/Exd/ExdData.h @@ -299,7 +299,7 @@ namespace Core { std::map m_classJobInfoMap; std::map m_paramGrowthInfoMap; std::map m_EventActionInfoMap; - std::map m_actionInfoMap; + std::map > m_actionInfoMap; std::map m_statusEffectInfoMap; std::map > m_aetheryteInfoMap; std::map m_tribeInfoMap; @@ -317,6 +317,7 @@ namespace Core { boost::shared_ptr< OpeningInfo > getOpeningInfo( uint32_t openingId ); boost::shared_ptr< CustomTalkInfo > getCustomTalkInfo( uint32_t customTalkId ); boost::shared_ptr< AetheryteInfo > getAetheryteInfo( uint32_t aetheryteId ); + boost::shared_ptr< ActionInfo > getActionInfo( uint32_t actionId ); boost::shared_ptr< PlaceNameInfo > getPlaceNameInfo( uint32_t placeNameId ); boost::shared_ptr< ItemInfo > getItemInfo( uint32_t catalogId ); boost::shared_ptr< RaceInfo > getRaceInfo( uint32_t raceId ); diff --git a/src/servers/Server_Common/Network/PacketDef/Zone/ServerZoneDef.h b/src/servers/Server_Common/Network/PacketDef/Zone/ServerZoneDef.h index 6bf6759b..c1c2388f 100644 --- a/src/servers/Server_Common/Network/PacketDef/Zone/ServerZoneDef.h +++ b/src/servers/Server_Common/Network/PacketDef/Zone/ServerZoneDef.h @@ -301,12 +301,12 @@ struct FFXIVIpcUpdateHpMpTp : FFXIVIpcBasePacket */ struct effectEntry { - uint8_t unknown_1; - uint8_t unknown_2; + Common::ActionEffectType effectType; + Common::ActionHitSeverityType hitSeverity; uint8_t unknown_3; int8_t bonusPercent; - int16_t param1; - uint8_t unknown_5; + int16_t value; + uint8_t valueMultiplier; // This multiplies whatever value is in the 'value' param by 10. Possibly a workaround for big numbers uint8_t unknown_6; }; diff --git a/src/servers/Server_Zone/Action/ActionCast.cpp b/src/servers/Server_Zone/Action/ActionCast.cpp index ef138cdd..16d07d8b 100644 --- a/src/servers/Server_Zone/Action/ActionCast.cpp +++ b/src/servers/Server_Zone/Action/ActionCast.cpp @@ -31,7 +31,7 @@ Core::Action::ActionCast::ActionCast( Entity::ActorPtr pActor, Entity::ActorPtr m_startTime = 0; m_id = actionId; m_handleActionType = HandleActionType::Spell; - m_castTime = g_exdData.m_actionInfoMap[actionId].cast_time; // TODO: Add security checks. + m_castTime = g_exdData.getActionInfo( actionId )->cast_time; // TODO: Add security checks. m_pSource = pActor; m_pTarget = pTarget; m_bInterrupt = false; diff --git a/src/servers/Server_Zone/Action/ActionTeleport.cpp b/src/servers/Server_Zone/Action/ActionTeleport.cpp index 06207db0..22705d71 100644 --- a/src/servers/Server_Zone/Action/ActionTeleport.cpp +++ b/src/servers/Server_Zone/Action/ActionTeleport.cpp @@ -26,7 +26,7 @@ Core::Action::ActionTeleport::ActionTeleport( Entity::ActorPtr pActor, uint16_t m_startTime = 0; m_id = 5; m_handleActionType = HandleActionType::Teleport; - m_castTime = g_exdData.m_actionInfoMap[5].cast_time; // TODO: Add security checks. + m_castTime = g_exdData.getActionInfo(5)->cast_time; // TODO: Add security checks. m_pSource = pActor; m_bInterrupt = false; m_targetAetheryte = targetZone; diff --git a/src/servers/Server_Zone/Actor/Actor.cpp b/src/servers/Server_Zone/Actor/Actor.cpp index e5ba2213..1b4e3c38 100644 --- a/src/servers/Server_Zone/Actor/Actor.cpp +++ b/src/servers/Server_Zone/Actor/Actor.cpp @@ -595,7 +595,7 @@ void Core::Entity::Actor::autoAttack( ActorPtr pTarget ) srand( static_cast< uint32_t >( tick ) ); uint32_t damage = 10 + rand() % 12; - uint32_t variation = 0 + rand() % 3; + uint32_t variation = 0 + rand() % 4; GamePacketNew< FFXIVIpcEffect, ServerZoneIpcType > effectPacket( getId() ); effectPacket.data().targetId = pTarget->getId(); @@ -606,9 +606,9 @@ void Core::Entity::Actor::autoAttack( ActorPtr pTarget ) effectPacket.data().numEffects = 1; effectPacket.data().rotation = Math::Util::floatToUInt16Rot( getRotation() ); effectPacket.data().effectTarget = pTarget->getId(); - effectPacket.data().effects[0].param1 = damage; - effectPacket.data().effects[0].unknown_1 = 3; - effectPacket.data().effects[0].unknown_2 = 1; + effectPacket.data().effects[0].value = damage; + effectPacket.data().effects[0].effectType = ActionEffectType::Damage; + effectPacket.data().effects[0].hitSeverity = static_cast< ActionHitSeverityType >( variation ); effectPacket.data().effects[0].unknown_3 = 7; sendToInRangeSet( effectPacket ); diff --git a/src/servers/Server_Zone/Actor/Player.cpp b/src/servers/Server_Zone/Actor/Player.cpp index dc5bbf41..ed6af547 100644 --- a/src/servers/Server_Zone/Actor/Player.cpp +++ b/src/servers/Server_Zone/Actor/Player.cpp @@ -980,10 +980,11 @@ const uint8_t * Core::Entity::Player::getStateFlags() const bool Core::Entity::Player::actionHasCastTime( uint32_t actionId ) //TODO: Add logic for special cases { - if( g_exdData.m_actionInfoMap[actionId].is_instant ) + auto actionInfoPtr = g_exdData.getActionInfo( actionId ); + if( actionInfoPtr->is_instant ) return false; - if( g_exdData.m_actionInfoMap[actionId].cast_time == 0 ) + if( actionInfoPtr->cast_time == 0 ) return false; return true; @@ -1481,9 +1482,9 @@ void Core::Entity::Player::autoAttack( ActorPtr pTarget ) effectPacket.data().rotation = Math::Util::floatToUInt16Rot(getRotation()); effectPacket.data().effectTargetId = pTarget->getId(); effectPacket.data().effectTarget = pTarget->getId(); - effectPacket.data().effects[0].param1 = damage; - effectPacket.data().effects[0].unknown_1 = 3; - effectPacket.data().effects[0].unknown_2 = 1; + effectPacket.data().effects[0].value = damage; + effectPacket.data().effects[0].effectType = Common::ActionEffectType::Damage; + effectPacket.data().effects[0].hitSeverity = Common::ActionHitSeverityType::NormalDamage; effectPacket.data().effects[0].unknown_3 = 7; sendToInRangeSet(effectPacket, true); @@ -1501,9 +1502,9 @@ void Core::Entity::Player::autoAttack( ActorPtr pTarget ) effectPacket.data().actionTextId = 7; effectPacket.data().rotation = Math::Util::floatToUInt16Rot(getRotation()); effectPacket.data().effectTarget = pTarget->getId(); - effectPacket.data().effects[0].param1 = damage; - effectPacket.data().effects[0].unknown_1 = 3; - effectPacket.data().effects[0].unknown_2 = 2; + effectPacket.data().effects[0].value = damage; + effectPacket.data().effects[0].effectType = Common::ActionEffectType::Damage; + effectPacket.data().effects[0].hitSeverity = Common::ActionHitSeverityType::NormalDamage; effectPacket.data().effects[0].unknown_3 = 71; sendToInRangeSet(effectPacket, true); @@ -1517,6 +1518,9 @@ void Core::Entity::Player::handleScriptSkill( uint32_t type, uint32_t actionId, { sendDebug( std::to_string( pTarget.getId() ) ); sendDebug( "Handle script skill type: " + std::to_string( type ) ); + + auto actionInfoPtr = g_exdData.getActionInfo( actionId ); + switch( type ) { @@ -1534,9 +1538,9 @@ void Core::Entity::Player::handleScriptSkill( uint32_t type, uint32_t actionId, effectPacket.data().numEffects = 1; effectPacket.data().rotation = Math::Util::floatToUInt16Rot( getRotation() ); effectPacket.data().effectTarget = pTarget.getId(); - effectPacket.data().effects[0].param1 = static_cast< int16_t >( param1 ); - effectPacket.data().effects[0].unknown_1 = 3; - effectPacket.data().effects[0].unknown_2 = 1; + effectPacket.data().effects[0].value = static_cast< int16_t >( param1 ); + effectPacket.data().effects[0].effectType = ActionEffectType::Damage; + effectPacket.data().effects[0].hitSeverity = ActionHitSeverityType::NormalDamage; effectPacket.data().effects[0].unknown_3 = 7; sendToInRangeSet( effectPacket, true ); @@ -1564,9 +1568,9 @@ void Core::Entity::Player::handleScriptSkill( uint32_t type, uint32_t actionId, effectPacket.data().numEffects = 1; effectPacket.data().rotation = Math::Util::floatToUInt16Rot( getRotation() ); effectPacket.data().effectTarget = pTarget.getId(); - effectPacket.data().effects[0].param1 = calculatedHeal; - effectPacket.data().effects[0].unknown_1 = 4; - effectPacket.data().effects[0].unknown_2 = 1; + effectPacket.data().effects[0].value = calculatedHeal; + effectPacket.data().effects[0].effectType = ActionEffectType::Heal; + effectPacket.data().effects[0].hitSeverity = ActionHitSeverityType::NormalHeal; effectPacket.data().effects[0].unknown_3 = 7; sendToInRangeSet( effectPacket, true ); @@ -1574,6 +1578,41 @@ void Core::Entity::Player::handleScriptSkill( uint32_t type, uint32_t actionId, if ( !pTarget.isAlive() ) break; + // todo: get proper packets: the following was just kind of thrown together from what we know + // also toss AoE to another spot and make it generic + + if ( actionInfoPtr->is_aoe ) + { + for ( auto pCurAct : m_inRangePlayers ) + { + assert( pCurAct ); + if ( !pCurAct->isAlive() ) + break; + + if ( Math::Util::distance( pTarget.getPos().x, pTarget.getPos().y, pTarget.getPos().z, pCurAct->getPos().x, pCurAct->getPos().y, pCurAct->getPos().z ) <= actionInfoPtr->radius ) + { + GamePacketNew< FFXIVIpcEffect, ServerZoneIpcType > effectPacket( pCurAct->getId() ); + effectPacket.data().targetId = pCurAct->getId(); + effectPacket.data().unknown_1 = 1; // the magic trick for getting it to work + effectPacket.data().unknown_2 = 1; + effectPacket.data().unknown_8 = 1; + effectPacket.data().unknown_5 = 1; + effectPacket.data().actionAnimationId = actionId; + effectPacket.data().actionTextId = 0; + effectPacket.data().numEffects = 1; + effectPacket.data().effectTarget = pCurAct->getId(); + effectPacket.data().effects[0].value = calculatedHeal; + effectPacket.data().effects[0].effectType = ActionEffectType::Heal; + effectPacket.data().effects[0].hitSeverity = ActionHitSeverityType::NormalHeal; + effectPacket.data().effects[0].unknown_3 = 7; + + pCurAct->sendToInRangeSet( effectPacket, true ); + pCurAct->heal( calculatedHeal ); + sendDebug( "AoE hit actor " + pCurAct->getName() ); + } + } + } + pTarget.heal( calculatedHeal ); break; } diff --git a/src/servers/Server_Zone/Network/Handlers/SkillHandler.cpp b/src/servers/Server_Zone/Network/Handlers/SkillHandler.cpp index d08b6db0..457aa855 100644 --- a/src/servers/Server_Zone/Network/Handlers/SkillHandler.cpp +++ b/src/servers/Server_Zone/Network/Handlers/SkillHandler.cpp @@ -52,7 +52,7 @@ void Core::Network::GameConnection::skillHandler( const Packets::GamePacket& inP std::string actionIdStr = boost::str( boost::format( "%|04X|" ) % action ); pPlayer->sendDebug( "---------------------------------------" ); pPlayer->sendDebug( "ActionHandler ( " + actionIdStr + " | " + - g_exdData.m_actionInfoMap[action].name + + g_exdData.getActionInfo( action )->name + " | " + std::to_string( targetId ) + " )" ); pPlayer->queuePacket( ActorControlPacket142( pPlayer->getId(), ActorControlType::ActionStart, 0x01, action ) );