diff --git a/data/actions/player.json b/data/actions/player.json index 8771bae7..c266e805 100644 --- a/data/actions/player.json +++ b/data/actions/player.json @@ -1,7 +1,7 @@ { "7": { "name": "Attack", - "potency": 0, + "potency": 110, "comboPotency": 0, "flankPotency": 0, "frontPotency": 0, @@ -16,7 +16,7 @@ }, "8": { "name": "Shot", - "potency": 0, + "potency": 100, "comboPotency": 0, "flankPotency": 0, "frontPotency": 0, @@ -629,7 +629,7 @@ }, "44": { "name": "Vengeance", - "potency": 50, + "potency": 0, "comboPotency": 0, "flankPotency": 0, "frontPotency": 0, @@ -638,7 +638,18 @@ "restorePercentage": 0, "nextCombo": [], "statuses": { - "caster": [], + "caster": [ + { + "id": 89, + "duration": 20000, + "modifiers": [ + { + "modifier": "ReflectPhysical", + "value": 50 + } + ] + } + ], "target": [] } }, @@ -1263,7 +1274,19 @@ "restorePercentage": 0, "nextCombo": [], "statuses": { - "caster": [], + "caster": [ + { + "id": 116, + "duration": 10000, + "flag": 4096, + "modifiers": [ + { + "modifier": "CriticalHitPercent", + "value": 100 + } + ] + } + ], "target": [] } }, diff --git a/deps/datReader/Exd/Structs.h b/deps/datReader/Exd/Structs.h index 078d26fa..1d1de7b0 100644 --- a/deps/datReader/Exd/Structs.h +++ b/deps/datReader/Exd/Structs.h @@ -391,13 +391,14 @@ namespace Excel uint8_t EffectWidth; uint8_t CostType; uint8_t Cond; - uint8_t RecastGroup; uint8_t Element; uint8_t ProcStatus; - uint8_t UseClassJob; + uint8_t ClassJobCategory; + uint8_t RecastGroup; uint8_t Init; uint8_t Omen; - int8_t Learn; + uint8_t Learn; + int8_t UseClassJob; int8_t SelectRange; int8_t SelectCorpse; int8_t AttackType; @@ -429,7 +430,7 @@ namespace Excel uint8_t HideCastBar : 1; uint8_t IsTargetLine : 1; - int8_t padding0; + int8_t unknown : 8; }; /* 75653 */ @@ -2053,7 +2054,8 @@ namespace Excel uint8_t NotControl : 1; uint8_t NotAction : 1; uint8_t NotMove : 1; - uint8_t padding0 : 6; + uint8_t padding0 : 5; + uint8_t CanOff : 1; uint8_t SemiTransparent : 1; uint8_t FcAction : 1; int8_t padding1[2]; diff --git a/sql/migrations/20230309164293_AddBorrowAction.sql b/sql/migrations/20230309164293_AddBorrowAction.sql new file mode 100644 index 00000000..1cdc3f22 --- /dev/null +++ b/sql/migrations/20230309164293_AddBorrowAction.sql @@ -0,0 +1 @@ +ALTER TABLE `characlass` ADD `BorrowAction` binary(40) DEFAULT NULL NULL AFTER `Lvl`; \ No newline at end of file diff --git a/src/api/PlayerMinimal.cpp b/src/api/PlayerMinimal.cpp index c761ac04..f383a96d 100644 --- a/src/api/PlayerMinimal.cpp +++ b/src/api/PlayerMinimal.cpp @@ -256,12 +256,14 @@ void PlayerMinimal::saveAsNew() break; } - // CharacterId, ClassIdx, Exp, Lvl + // CharacterId, ClassIdx, Exp, Lvl, BorrowAction auto stmtClass = g_charaDb.getPreparedStatement( Db::ZoneDbStatements::CHARA_CLASS_INS ); stmtClass->setUInt64( 1, m_characterId ); stmtClass->setInt( 2, g_exdData.getRow< Excel::ClassJob >( m_class )->data().WorkIndex ); stmtClass->setInt( 3, 0 ); stmtClass->setInt( 4, 1 ); + std::vector< uint8_t > borrowActionVec( Common::ARRSIZE_BORROWACTION * 4 ); + stmtClass->setBinary( 5, borrowActionVec ); g_charaDb.directExecute( stmtClass ); auto stmtSearchInfo = g_charaDb.getPreparedStatement( Db::ZoneDbStatements::CHARA_SEARCHINFO_INS ); diff --git a/src/common/Common.h b/src/common/Common.h index b8b8220c..b2f94ab0 100644 --- a/src/common/Common.h +++ b/src/common/Common.h @@ -37,6 +37,7 @@ namespace Sapphire::Common const uint16_t ARRSIZE_UNLOCKS = 64u; const uint16_t ARRSIZE_ORCHESTRION = 40u; const uint16_t ARRSIZE_MONSTERNOTE = 12u; + const uint16_t ARRSIZE_BORROWACTION = 10u; const uint8_t TOWN_COUNT = 6; @@ -917,7 +918,25 @@ namespace Sapphire::Common SlashingResistancePercent = 1027, PiercingResistancePercent = 1028, BluntResistancePercent = 1029, - ProjectileResistancePercent = 1030 + ProjectileResistancePercent = 1030, + ParryPercent = 1031 + }; + + enum class StatusEffectFlag : uint32_t + { + BuffCategory = 1, + DebuffCategory = 2, + Permanent = 4, + IsGaze = 8, + Transfiguration = 16, + CanDispel = 32, + LockActions = 64, + LockControl = 128, + LockMovement = 256, + Invisibilty = 512, + CanStatusOff = 1024, + FcBuff = 2048, + RemoveOnSuccessfulHit = 4096 }; enum struct ActionAspect : uint8_t @@ -1034,7 +1053,7 @@ namespace Sapphire::Common CritDirectHitDamage = 3 }; - enum class ActionEffectResultFlag : uint8_t + enum class ActionResultFlag : uint8_t { None = 0, Absorbed = 0x04, @@ -1853,8 +1872,8 @@ namespace Sapphire::Common { SingleTarget = 1, CircularAOE = 2, - Type3 = 3, // another single target? no idea how to call it - RectangularAOE = 4, + RectangularAOE = 3, + ConeAOE = 4, CircularAoEPlaced = 7 }; diff --git a/src/common/Database/ZoneDbConnection.cpp b/src/common/Database/ZoneDbConnection.cpp index 10f9f854..6a0f79a0 100644 --- a/src/common/Database/ZoneDbConnection.cpp +++ b/src/common/Database/ZoneDbConnection.cpp @@ -161,11 +161,11 @@ void Sapphire::Db::ZoneDbConnection::doPrepareStatements() prepareStatement( CHARA_SEL_QUEST, "SELECT * FROM charaquest WHERE CharacterId = ?;", CONNECTION_SYNC ); /// CLASS INFO - prepareStatement( CHARA_CLASS_SEL, "SELECT ClassIdx, Exp, Lvl FROM characlass WHERE CharacterId = ?;", + prepareStatement( CHARA_CLASS_SEL, "SELECT ClassIdx, Exp, Lvl, BorrowAction FROM characlass WHERE CharacterId = ?;", CONNECTION_SYNC ); - prepareStatement( CHARA_CLASS_INS, "INSERT INTO characlass ( CharacterId, ClassIdx, Exp, Lvl ) VALUES( ?,?,?,? );", + prepareStatement( CHARA_CLASS_INS, "INSERT INTO characlass ( CharacterId, ClassIdx, Exp, Lvl, BorrowAction ) VALUES( ?,?,?,?,? );", CONNECTION_BOTH ); - prepareStatement( CHARA_CLASS_UP, "UPDATE characlass SET Exp = ?, Lvl = ? WHERE CharacterId = ? AND ClassIdx = ?;", + prepareStatement( CHARA_CLASS_UP, "UPDATE characlass SET Exp = ?, Lvl = ?, BorrowAction = ? WHERE CharacterId = ? AND ClassIdx = ?;", CONNECTION_ASYNC ); prepareStatement( CHARA_CLASS_DEL, "DELETE FROM characlass WHERE CharacterId = ?;", CONNECTION_ASYNC ); diff --git a/src/scripts/action/common/ActionSprint3.cpp b/src/scripts/action/common/ActionSprint3.cpp index 82bf80fb..929487f9 100644 --- a/src/scripts/action/common/ActionSprint3.cpp +++ b/src/scripts/action/common/ActionSprint3.cpp @@ -17,10 +17,7 @@ public: return; uint32_t duration = ( sourceChara->getAsPlayer()->getTp() / 50 ) * 1000; - - action.getEffectbuilder()->applyStatusEffect( sourceChara, 50, 30 ); - - sourceChara->getAsPlayer()->addStatusEffectByIdIfNotExist( 50, duration, *sourceChara, 30 ); + action.getActionResultBuilder()->applyStatusEffectSelf( 50, duration, 30, false ); sourceChara->getAsPlayer()->setTp( 0 ); } }; diff --git a/src/scripts/action/war/ActionInnerBeast.cpp b/src/scripts/action/war/ActionInnerBeast.cpp index f53ba491..14a39dbe 100644 --- a/src/scripts/action/war/ActionInnerBeast.cpp +++ b/src/scripts/action/war/ActionInnerBeast.cpp @@ -3,6 +3,7 @@ #include #include #include +#include using namespace Sapphire; using namespace Sapphire::World::Action; @@ -25,20 +26,21 @@ public: if( !pPlayer ) return; - if( !pPlayer->hasStatusEffect( Unchained ) ) - pPlayer->delModifier( Common::ParamModifier::DamageDealtPercent, -25 ); + if( auto status = pPlayer->getStatusEffectById( Defiance ); status ) + status->setModifier( Common::ParamModifier::DamageDealtPercent, 0 ); auto dmg = action.calcDamage( Potency ); - action.getEffectbuilder()->damage( pSource, pTarget, dmg.first, dmg.second ); - action.getEffectbuilder()->heal( pTarget, pSource, dmg.first, Common::ActionHitSeverityType::NormalHeal, - Common::ActionEffectResultFlag::EffectOnSource ); + action.getActionResultBuilder()->damage( pSource, pTarget, dmg.first, dmg.second ); + action.getActionResultBuilder()->heal( pTarget, pSource, dmg.first, Common::ActionHitSeverityType::NormalHeal, + Common::ActionResultFlag::EffectOnSource ); - action.applyStatusEffectSelf( InnerBeast ); - pPlayer->addStatusEffectByIdIfNotExist( InnerBeast, 15000, *pSource, - { StatusModifier{ Common::ParamModifier::DamageTakenPercent, -20 } } ); + action.getActionResultBuilder()->applyStatusEffectSelf( InnerBeast, 15000, 0, { StatusModifier{ Common::ParamModifier::DamageTakenPercent, -20 } } ); if( !pPlayer->hasStatusEffect( Unchained ) ) - pPlayer->addModifier( Common::ParamModifier::DamageDealtPercent, -25 ); + { + if( auto status = pPlayer->getStatusEffectById( Defiance ); status ) + status->setModifier( Common::ParamModifier::DamageDealtPercent, -25 ); + } } }; diff --git a/src/scripts/action/war/ActionUnchained.cpp b/src/scripts/action/war/ActionUnchained.cpp index 482e9249..e201fa3d 100644 --- a/src/scripts/action/war/ActionUnchained.cpp +++ b/src/scripts/action/war/ActionUnchained.cpp @@ -3,6 +3,7 @@ #include #include #include +#include using namespace Sapphire; using namespace Sapphire::World::Action; @@ -21,10 +22,10 @@ public: if( !pPlayer ) return; - pPlayer->delModifier( Common::ParamModifier::DamageDealtPercent, -25 ); + if( auto status = pPlayer->getStatusEffectById( Defiance ); status ) + status->setModifier( Common::ParamModifier::DamageDealtPercent, 0 ); - action.applyStatusEffectSelf( Unchained ); - pPlayer->addStatusEffectByIdIfNotExist( Unchained, 20000, *pPlayer->getAsChara() ); + action.getActionResultBuilder()->applyStatusEffectSelf( Unchained, 20000, 0 ); } }; diff --git a/src/scripts/quest/festivalquest/FesEst101.cpp b/src/scripts/quest/festivalquest/FesEst101.cpp index e72447b7..32b88a78 100644 --- a/src/scripts/quest/festivalquest/FesEst101.cpp +++ b/src/scripts/quest/festivalquest/FesEst101.cpp @@ -179,7 +179,8 @@ private: void Scene00002Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result ) { - player.addStatusEffectById( Status0, 0, player, Transformation0 ); + // todo - fix status effect without action? + //player.addStatusEffectById( Status0, 0, player, Transformation0 ); eventMgr().sendEventNotice( player, getId(), 0, 0 ); quest.setSeq( Seq2 ); } @@ -294,7 +295,7 @@ private: void Scene00010Return( World::Quest& quest, Entity::Player& player, const Event::SceneResult& result ) { - player.addStatusEffectById( Status0, 0, player, Transformation0 ); + // player.addStatusEffectById( Status0, 0, player, Transformation0 ); } ////////////////////////////////////////////////////////////////////// diff --git a/src/scripts/statuseffect/StatusEffectDefiance.cpp b/src/scripts/statuseffect/StatusEffectDefiance.cpp index 25fe7c6d..9f26421a 100644 --- a/src/scripts/statuseffect/StatusEffectDefiance.cpp +++ b/src/scripts/statuseffect/StatusEffectDefiance.cpp @@ -16,6 +16,11 @@ public: void onExpire( Entity::Chara& actor ) override { actor.removeSingleStatusEffectById( Unchained ); + actor.removeSingleStatusEffectById( Wrath ); + actor.removeSingleStatusEffectById( WrathII ); + actor.removeSingleStatusEffectById( WrathIII ); + actor.removeSingleStatusEffectById( WrathIV ); + actor.removeSingleStatusEffectById( Infuriated ); } }; diff --git a/src/scripts/statuseffect/StatusEffectUnchained.cpp b/src/scripts/statuseffect/StatusEffectUnchained.cpp index 19aecdef..3a5f991e 100644 --- a/src/scripts/statuseffect/StatusEffectUnchained.cpp +++ b/src/scripts/statuseffect/StatusEffectUnchained.cpp @@ -2,6 +2,7 @@ #include #include #include +#include using namespace Sapphire; using namespace Sapphire::World::Action; @@ -15,8 +16,8 @@ public: void onExpire( Entity::Chara& actor ) override { - if( actor.hasStatusEffect( Defiance ) ) - actor.addModifier( Common::ParamModifier::DamageDealtPercent, -25 ); + if( auto status = actor.getStatusEffectById( Defiance ); status ) + status->setModifier( Common::ParamModifier::DamageDealtPercent, -25 ); } }; diff --git a/src/tools/action_parse/actions/player.json b/src/tools/action_parse/actions/player.json index 149d05aa..e6c9cb3e 100644 --- a/src/tools/action_parse/actions/player.json +++ b/src/tools/action_parse/actions/player.json @@ -1,7 +1,7 @@ { "7": { "name": "Attack", - "potency": 0, + "potency": 110, "comboPotency": 0, "flankPotency": 0, "frontPotency": 0, @@ -16,7 +16,7 @@ }, "8": { "name": "Shot", - "potency": 0, + "potency": 100, "comboPotency": 0, "flankPotency": 0, "frontPotency": 0, diff --git a/src/tools/action_parse/main.cpp b/src/tools/action_parse/main.cpp index dee585cc..a5af43f9 100644 --- a/src/tools/action_parse/main.cpp +++ b/src/tools/action_parse/main.cpp @@ -41,6 +41,7 @@ struct StatusEntry { uint16_t id; int32_t duration; + uint32_t flag; std::vector< StatusModifier > modifiers; }; @@ -78,6 +79,7 @@ void to_json( nlohmann::ordered_json& j, const StatusEntry& statusEntry ) j = nlohmann::ordered_json{ { "id", statusEntry.id }, { "duration", statusEntry.duration }, + { "flag", statusEntry.flag }, { "modifiers", statusEntry.modifiers } }; } diff --git a/src/world/AI/GambitRule.cpp b/src/world/AI/GambitRule.cpp new file mode 100644 index 00000000..9fb40e63 --- /dev/null +++ b/src/world/AI/GambitRule.cpp @@ -0,0 +1,52 @@ +#include +#include +#include "GambitTargetCondition.h" +#include "GambitRule.h" + +using namespace Sapphire; +using namespace Sapphire::World; + +AI::GambitRule::GambitRule( const GambitTargetConditionPtr targetCondition, Action::ActionPtr action, uint32_t coolDown ) : + m_targetCondition( targetCondition ), + m_pAction( std::move( action ) ), + m_lastExecutionMs( 0 ), + m_coolDownMs( coolDown ), + m_isEnabled( true ) +{ + +} + +void AI::GambitRule::toggleEnabled() +{ + m_isEnabled = !m_isEnabled; +} + +bool AI::GambitRule::isEnabled() const +{ + return m_isEnabled; +} + +uint64_t AI::GambitRule::getLastExecutionMs() const +{ + return m_lastExecutionMs; +} + +void AI::GambitRule::setLastExecutionMs( uint64_t lastExecution ) +{ + m_lastExecutionMs = lastExecution; +} + +uint32_t AI::GambitRule::getCoolDown() const +{ + return m_coolDownMs; +} + +AI::GambitTargetConditionPtr AI::GambitRule::getGambitTargetCondition() +{ + return m_targetCondition; +} + +Action::ActionPtr AI::GambitRule::getActionPtr() +{ + return m_pAction; +} \ No newline at end of file diff --git a/src/world/AI/GambitRule.h b/src/world/AI/GambitRule.h new file mode 100644 index 00000000..c8a43648 --- /dev/null +++ b/src/world/AI/GambitRule.h @@ -0,0 +1,33 @@ +#include +#include +#include "GambitTargetCondition.h" + +#pragma once + +namespace Sapphire::World::AI +{ + class GambitRule + { + public: + GambitRule( GambitTargetConditionPtr targetCondition, Action::ActionPtr action, uint32_t coolDown ); + ~GambitRule() = default; + + bool isEnabled() const; + void toggleEnabled(); + + uint64_t getLastExecutionMs() const; + void setLastExecutionMs( uint64_t lastExecution ); + + uint32_t getCoolDown() const; + + GambitTargetConditionPtr getGambitTargetCondition(); + + Action::ActionPtr getActionPtr(); + private: + GambitTargetConditionPtr m_targetCondition; + Action::ActionPtr m_pAction; + uint32_t m_coolDownMs; + uint64_t m_lastExecutionMs; + bool m_isEnabled; + }; +} \ No newline at end of file diff --git a/src/world/AI/GambitTargetCondition.h b/src/world/AI/GambitTargetCondition.h new file mode 100644 index 00000000..ec0cd7ae --- /dev/null +++ b/src/world/AI/GambitTargetCondition.h @@ -0,0 +1,66 @@ +#include +#include +#include + +#pragma once + +namespace Sapphire::World::AI +{ + enum GambitTargetType : uint8_t + { + Self, + Player, + PlayerAndAlly, + Ally, + BNpc + }; + + class GambitTargetCondition + { + public: + GambitTargetCondition( GambitTargetType targetType ) : m_targetType( targetType ) {}; + virtual ~GambitTargetCondition() = default; + + virtual bool isConditionMet( Sapphire::Entity::BNpc& src ) { return false; }; + Sapphire::Entity::CharaPtr getTarget() const { return m_pTarget; }; + protected: + GambitTargetType m_targetType; + Sapphire::Entity::CharaPtr m_pTarget; + }; + + class TopHateTargetCondition : public GambitTargetCondition + { + public: + TopHateTargetCondition() : GambitTargetCondition( PlayerAndAlly ) {}; + + bool isConditionMet( Sapphire::Entity::BNpc& src ) override + { + auto foundChara = src.hateListGetHighest(); + if( foundChara ) + { + m_pTarget = foundChara; + return true; + } + return false; + }; + }; + + class HPSelfPctLessThan : public GambitTargetCondition + { + public: + HPSelfPctLessThan( uint8_t pct ) : GambitTargetCondition( Self ), m_HpPct( pct ) {}; + + virtual bool isConditionMet( Sapphire::Entity::BNpc& src ) + { + if( src.getHpPercent() < m_HpPct ) + { + m_pTarget = src.getAsBNpc(); + return true; + } + return false; + }; + private: + uint8_t m_HpPct; + }; + +} \ No newline at end of file diff --git a/src/world/Action/Action.cpp b/src/world/Action/Action.cpp index 190965eb..44b6cc9f 100644 --- a/src/world/Action/Action.cpp +++ b/src/world/Action/Action.cpp @@ -16,6 +16,7 @@ #include "Manager/PlayerMgr.h" #include "Manager/MgrUtil.h" +#include "Manager/TerritoryMgr.h" #include "Session.h" #include "Network/GameConnection.h" @@ -23,7 +24,7 @@ #include "Network/PacketWrappers/ActorControlPacket.h" #include "Network/PacketWrappers/ActorControlSelfPacket.h" #include "Network/PacketWrappers/ActorControlTargetPacket.h" -#include "Network/Util/PlayerUtil.h" +#include "Network/Util/PacketUtil.h" #include @@ -67,6 +68,16 @@ uint32_t Action::Action::getId() const return m_id; } +uint32_t Action::Action::getResultId() const +{ + return m_resultId; +} + +std::shared_ptr< Excel::ExcelStruct< Excel::Action > > Action::Action::getActionData() const +{ + return m_actionData; +} + bool Action::Action::init() { if( !m_actionData ) @@ -80,14 +91,18 @@ bool Action::Action::init() m_actionData = actionData; } + auto teriMgr = Common::Service< Manager::TerritoryMgr >::ref(); + auto zone = teriMgr.getTerritoryByGuId( m_pSource->getTerritoryId() ); + m_resultId = zone->getNextActionResultId(); - m_effectBuilder = make_EffectBuilder( m_pSource, getId(), m_requestId ); + m_actionResultBuilder = make_ActionResultBuilder( m_pSource, getId(), m_resultId, m_requestId ); m_castTimeMs = static_cast< uint32_t >( m_actionData->data().CastTime * 100 ); m_recastTimeMs = static_cast< uint32_t >( m_actionData->data().RecastTime * 100 ); m_cooldownGroup = m_actionData->data().RecastGroup; m_range = m_actionData->data().SelectRange; m_effectRange = m_actionData->data().EffectRange; + m_effectWidth = m_actionData->data().EffectWidth; m_category = static_cast< Common::ActionCategory >( m_actionData->data().Category ); m_castType = static_cast< Common::CastType >( m_actionData->data().EffectType ); m_aspect = static_cast< Common::ActionAspect >( m_actionData->data().AttackType ); @@ -107,9 +122,9 @@ bool Action::Action::init() { case Common::ClassJob::Bard: case Common::ClassJob::Archer: + case Common::ClassJob::Machinist: m_range = 25; break; - // anything that isnt ranged default: m_range = 3; break; @@ -119,7 +134,7 @@ bool Action::Action::init() m_primaryCostType = static_cast< Common::ActionPrimaryCostType >( m_actionData->data().CostType ); m_primaryCost = m_actionData->data().CostValue; - /*if( !m_actionData->targetArea ) + if( !m_actionData->data().SelectGround ) { // override pos to target position // todo: this is kinda dirty @@ -131,7 +146,7 @@ bool Action::Action::init() break; } } - }*/ + } // todo: add missing rows for secondaryCostType/secondaryCostType and rename the current rows to primaryCostX @@ -243,34 +258,34 @@ bool Action::Action::update() // todo: check if the target is still in range } - uint64_t tickCount = Common::Util::getTimeMs(); - uint32_t castTime = m_castTimeMs; + auto tickCount = static_cast< time_t >( Common::Util::getTimeMs() ); + auto startTime = static_cast< time_t >( m_startTime ); + uint64_t castTime = m_castTimeMs; if( auto player = m_pSource->getAsPlayer() ) { - uint64_t lastActionTick = player->getLastActionTick(); + auto lastActionTick = static_cast< time_t >( player->getLastActionTick() ); uint32_t lastTickMs = 0; if( lastActionTick > 0 ) { - lastTickMs = static_cast< uint32_t >( std::difftime( static_cast< time_t >( tickCount ), static_cast< time_t >( lastActionTick ) ) ); + lastTickMs = static_cast< uint32_t >( std::difftime( tickCount, lastActionTick ) ); if( lastTickMs > 100 ) //max 100ms lastTickMs = 100; } player->setLastActionTick( tickCount ); - uint32_t delayMs = 100 - lastTickMs; + uint64_t delayMs = 100 - lastTickMs; castTime = ( m_castTimeMs + delayMs ); - m_castTimeRestMs = static_cast< uint64_t >( m_castTimeMs ) - - static_cast< uint64_t >( std::difftime( static_cast< time_t >( tickCount ), static_cast< time_t >( m_startTime ) ) ); + m_castTimeRestMs = static_cast< uint64_t >( m_castTimeMs ) - static_cast< uint64_t >( std::difftime( tickCount, startTime ) ); } - if( !hasCastTime() || std::difftime( static_cast< time_t >( tickCount ), static_cast< time_t >( m_startTime ) ) > castTime ) + if( !hasCastTime() || std::difftime( tickCount, startTime ) > castTime ) { execute(); return true; } - if( m_pTarget == nullptr && m_targetId != 0 ) + if( !m_pTarget && m_targetId != 0 ) { // try to search for the target actor for( const auto& actor : m_pSource->getInRangeActors( true ) ) @@ -283,7 +298,7 @@ bool Action::Action::update() } } - if( m_pTarget != nullptr && !m_pTarget->isAlive() ) + if( m_pTarget && !m_pTarget->isAlive() ) { // interrupt the cast if target died setInterrupted( Common::ActionInterruptType::RegularInterrupt ); @@ -317,20 +332,20 @@ void Action::Action::start() data.TargetPos[ 2 ] = Common::Util::floatToUInt16( m_pSource->getPos().z ); data.Dir = m_pSource->getRot(); - server().queueForPlayers( m_pSource->getInRangePlayerIds( true ), castPacket ); + server().queueForPlayers( m_pSource->getInRangePlayerIds( m_pSource->isPlayer() ), castPacket ); if( player ) - { player->setCondition( PlayerCondition::Casting ); - } } // todo: m_recastTimeMs needs to be adjusted for player sks/sps auto actionStartPkt = makeActorControlSelf( m_pSource->getId(), ActorControlType::ActionStart, m_cooldownGroup, getId(), m_recastTimeMs / 10 ); - player->setRecastGroup( m_cooldownGroup, static_cast< float >( m_castTimeMs ) / 1000.f ); - - server().queueForPlayer( player->getCharacterId(), actionStartPkt ); + if( player ) + { + player->setRecastGroup( m_cooldownGroup, static_cast< float >( m_castTimeMs ) / 1000.f ); + server().queueForPlayer( player->getCharacterId(), actionStartPkt ); + } onStart(); @@ -364,7 +379,6 @@ void Action::Action::onStart() void Action::Action::interrupt() { assert( m_pSource ); - // things that aren't players don't care about cooldowns and state flags if( m_pSource->isPlayer() ) { @@ -404,25 +418,20 @@ void Action::Action::onInterrupt() void Action::Action::execute() { assert( m_pSource ); - // subtract costs first, if somehow the caster stops meeting those requirements cancel the cast - if( !consumeResources() ) { interrupt(); return; } - auto& scriptMgr = Common::Service< Scripting::ScriptMgr >::ref(); - if( hasCastTime() ) + if( hasCastTime() && m_pSource->isPlayer() ) { - if( auto pPlayer = m_pSource->getAsPlayer(); pPlayer ) - { - pPlayer->setLastActionTick( 0 ); - pPlayer->removeCondition( PlayerCondition::Casting ); - } + auto pPlayer = m_pSource->getAsPlayer(); + pPlayer->setLastActionTick( 0 ); + pPlayer->removeCondition( PlayerCondition::Casting ); } if( isCorrectCombo() ) @@ -432,13 +441,9 @@ void Action::Action::execute() } if( !hasClientsideTarget() ) - { - handleAction(); - } + buildActionResults(); else if( auto player = m_pSource->getAsPlayer() ) - { scriptMgr.onEObjHit( *player, m_targetId, getId() ); - } // set currently casted action as the combo action if it interrupts a combo // ignore it otherwise (ogcds, etc.) @@ -446,13 +451,9 @@ void Action::Action::execute() { // potential combo starter or correct combo from last action, must hit something to progress combo if( !m_hitActors.empty() && ( !isComboAction() || isCorrectCombo() ) ) - { m_pSource->setLastComboActionId( getId() ); - } else // clear last combo action if the combo breaks - { m_pSource->setLastComboActionId( 0 ); - } } } @@ -468,13 +469,13 @@ std::pair< uint32_t, Common::ActionHitSeverityType > Action::Action::calcDamage( auto role = player->getRole(); if( role == Common::Role::RangedMagical || role == Common::Role::Healer ) - { wepDmg = item->getMagicalDmg(); - } else - { wepDmg = item->getPhysicalDmg(); - } + + // is auto attack + if( getId() == 7 || getId() == 8 ) + return Math::CalcStats::calcAutoAttackDamage( *m_pSource->getAsPlayer() ); } return Math::CalcStats::calcActionDamage( *m_pSource, potency, wepDmg ); @@ -491,27 +492,15 @@ std::pair< uint32_t, Common::ActionHitSeverityType > Action::Action::calcHealing auto role = player->getRole(); if( role == Common::Role::RangedMagical || role == Common::Role::Healer ) - { wepDmg = item->getMagicalDmg(); - } else - { wepDmg = item->getPhysicalDmg(); - } } return Math::CalcStats::calcActionHealing( *m_pSource, potency, wepDmg ); } -void Action::Action::applyStatusEffectSelf( uint16_t statusId, uint8_t param ) -{ - if( m_hitActors.size() > 0 ) - getEffectbuilder()->applyStatusEffect( m_hitActors[ 0 ], statusId, param, true ); - else - getEffectbuilder()->applyStatusEffect( m_pSource, statusId, param ); -} - -void Action::Action::handleAction() +void Action::Action::buildActionResults() { snapshotAffectedActors( m_hitActors ); @@ -522,22 +511,19 @@ void Action::Action::handleAction() if( !hasScript && !hasLutEntry ) { if( auto player = m_pSource->getAsPlayer() ) - { Manager::PlayerMgr::sendUrgent( *player, "missing lut entry for action#{}", getId() ); - } - return; } if( !hasScript ) m_enableGenericHandler = true; - Network::Util::Player::sendHudParam( *m_pSource->getAsPlayer() ); + Network::Util::Packet::sendHudParam( *m_pSource ); if( !m_enableGenericHandler || !hasLutEntry || m_hitActors.empty() ) { // send any effect packet added by script or an empty one just to play animation for other players - m_effectBuilder->buildAndSendPackets( m_hitActors ); + m_actionResultBuilder->sendActionResults( {} ); return; } @@ -558,14 +544,14 @@ void Action::Action::handleAction() if( m_lutEntry.potency > 0 ) { auto dmg = calcDamage( isCorrectCombo() ? m_lutEntry.comboPotency : m_lutEntry.potency ); - m_effectBuilder->damage( m_pSource, actor, dmg.first, dmg.second ); + m_actionResultBuilder->damage( m_pSource, actor, dmg.first, dmg.second ); if( dmg.first > 0 ) actor->onActionHostile( m_pSource ); if( isCorrectCombo() && shouldApplyComboSucceedEffect ) { - m_effectBuilder->comboSucceed( m_pSource ); + m_actionResultBuilder->comboSucceed( m_pSource ); shouldApplyComboSucceedEffect = false; } @@ -574,45 +560,49 @@ void Action::Action::handleAction() if( m_lutEntry.curePotency > 0 ) // actions with self heal { auto heal = calcHealing( m_lutEntry.curePotency ); - m_effectBuilder->heal( actor, m_pSource, heal.first, heal.second, Common::ActionEffectResultFlag::EffectOnSource ); + m_actionResultBuilder->heal( actor, m_pSource, heal.first, heal.second, Common::ActionResultFlag::EffectOnSource ); } if( m_lutEntry.restoreMPPercentage > 0 && shouldRestoreMP ) { - m_effectBuilder->restoreMP( actor, m_pSource, m_pSource->getMaxMp() * m_lutEntry.restoreMPPercentage / 100, Common::ActionEffectResultFlag::EffectOnSource ); + m_actionResultBuilder->restoreMP( actor, m_pSource, m_pSource->getMaxMp() * m_lutEntry.restoreMPPercentage / 100, Common::ActionResultFlag::EffectOnSource ); shouldRestoreMP = false; } if( !m_lutEntry.nextCombo.empty() ) // if we have a combo action followup - { - m_effectBuilder->startCombo( m_pSource, getId() ); // this is on all targets hit - } + m_actionResultBuilder->startCombo( m_pSource, getId() ); // this is on all targets hit } } else if( m_lutEntry.curePotency > 0 ) { auto heal = calcHealing( m_lutEntry.curePotency ); - m_effectBuilder->heal( actor, actor, heal.first, heal.second ); + m_actionResultBuilder->heal( actor, actor, heal.first, heal.second ); if( m_lutEntry.restoreMPPercentage > 0 && shouldRestoreMP ) { - m_effectBuilder->restoreMP( actor, m_pSource, m_pSource->getMaxMp() * m_lutEntry.restoreMPPercentage / 100, Common::ActionEffectResultFlag::EffectOnSource ); + m_actionResultBuilder->restoreMP( actor, m_pSource, m_pSource->getMaxMp() * m_lutEntry.restoreMPPercentage / 100, Common::ActionResultFlag::EffectOnSource ); shouldRestoreMP = false; } } else if( m_lutEntry.restoreMPPercentage > 0 && shouldRestoreMP ) { - m_effectBuilder->restoreMP( actor, m_pSource, m_pSource->getMaxMp() * m_lutEntry.restoreMPPercentage / 100, Common::ActionEffectResultFlag::EffectOnSource ); + m_actionResultBuilder->restoreMP( actor, m_pSource, m_pSource->getMaxMp() * m_lutEntry.restoreMPPercentage / 100, Common::ActionResultFlag::EffectOnSource ); shouldRestoreMP = false; } } - if( m_lutEntry.statuses.caster.size() > 0 || m_lutEntry.statuses.target.size() > 0 ) - handleStatusEffects(); + // If we hit an enemy + if( m_hitActors.size() > 0 && getHitChara()->getObjKind() != m_pSource->getObjKind() ) + { + m_pSource->removeStatusEffectByFlag( Common::StatusEffectFlag::RemoveOnSuccessfulHit ); + } handleJobAction(); - m_effectBuilder->buildAndSendPackets( m_hitActors ); + if( m_lutEntry.statuses.caster.size() > 0 || m_lutEntry.statuses.target.size() > 0 ) + handleStatusEffects(); + + m_actionResultBuilder->sendActionResults( m_hitActors ); // TODO: disabled, reset kills our queued actions // at this point we're done with it and no longer need it @@ -629,8 +619,7 @@ void Action::Action::handleStatusEffects() { for( auto& status : m_lutEntry.statuses.caster ) { - applyStatusEffectSelf( status.id ); - m_pSource->addStatusEffectByIdIfNotExist( status.id, status.duration, *m_pSource, status.modifiers ); + getActionResultBuilder()->applyStatusEffectSelf( status.id, status.duration, 0, status.modifiers, status.flag, true ); } } @@ -641,8 +630,7 @@ void Action::Action::handleStatusEffects() { for( auto& status : m_lutEntry.statuses.target ) { - getEffectbuilder()->applyStatusEffect( actor, status.id, 0 ); - actor->addStatusEffectByIdIfNotExist( status.id, status.duration, *m_pSource, status.modifiers ); + getActionResultBuilder()->applyStatusEffect( actor, status.id, status.duration, 0, status.modifiers, status.flag, true ); } if( actor->getStatusEffectMap().size() > 0 ) @@ -741,9 +729,7 @@ bool Action::Action::isCorrectCombo() const auto lastActionId = m_pSource->getLastComboActionId(); if( lastActionId == 0 ) - { return false; - } return m_actionData->data().ComboParent == lastActionId; } @@ -866,7 +852,6 @@ void Action::Action::addDefaultActorFilters() switch( m_castType ) { case Common::CastType::SingleTarget: - case Common::CastType::Type3: { auto filter = std::make_shared< World::Util::ActorFilterSingleTarget >( static_cast< uint32_t >( m_targetId ) ); addActorFilter( filter ); @@ -928,10 +913,7 @@ std::vector< Entity::CharaPtr >& Action::Action::getHitCharas() Entity::CharaPtr Action::Action::getHitChara() { if( !m_hitActors.empty() ) - { return m_hitActors.at( 0 ); - } - return nullptr; } @@ -942,9 +924,9 @@ bool Action::Action::hasValidLutEntry() const m_lutEntry.statuses.caster.size() > 0 || m_lutEntry.statuses.target.size() > 0; } -Action::EffectBuilderPtr Action::Action::getEffectbuilder() +Action::ActionResultBuilderPtr Action::Action::getActionResultBuilder() { - return m_effectBuilder; + return m_actionResultBuilder; } uint8_t Action::Action::getActionKind() const diff --git a/src/world/Action/Action.h b/src/world/Action/Action.h index 66a680f9..6f2ed0b6 100644 --- a/src/world/Action/Action.h +++ b/src/world/Action/Action.h @@ -4,7 +4,7 @@ #include "ActionLut.h" #include "Util/ActorFilter.h" #include "ForwardsZone.h" -#include "EffectBuilder.h" +#include "ActionResultBuilder.h" #include "Exd/Structs.h" namespace Sapphire::World::Action @@ -23,6 +23,8 @@ namespace Sapphire::World::Action uint32_t getId() const; + uint32_t getResultId() const; + bool init(); void setPos( const Common::FFXIVARR_POSITION3& pos ); @@ -54,6 +56,8 @@ namespace Sapphire::World::Action uint64_t getCastTimeRest() const; void enableGenericHandler(); + + std::shared_ptr< Excel::ExcelStruct< Excel::Action > > getActionData() const; /*! * @brief Checks if a chara has enough resources available to cast the action (tp/mp/etc) @@ -105,11 +109,9 @@ namespace Sapphire::World::Action */ bool snapshotAffectedActors( std::vector< Entity::CharaPtr >& actors ); - EffectBuilderPtr getEffectbuilder(); + ActionResultBuilderPtr getActionResultBuilder(); - void applyStatusEffectSelf( uint16_t statusId, uint8_t param = 0 ); - - void handleAction(); + void buildActionResults(); void handleStatusEffects(); @@ -180,6 +182,7 @@ namespace Sapphire::World::Action uint8_t m_actionKind{}; uint16_t m_requestId{}; + uint32_t m_resultId{}; Common::ActionPrimaryCostType m_primaryCostType; uint16_t m_primaryCost{}; @@ -193,6 +196,7 @@ namespace Sapphire::World::Action uint8_t m_cooldownGroup{}; int8_t m_range{}; uint8_t m_effectRange{}; + uint8_t m_effectWidth{}; uint8_t m_xAxisModifier{}; Common::ActionAspect m_aspect; Common::CastType m_castType; @@ -217,7 +221,7 @@ namespace Sapphire::World::Action Common::FFXIVARR_POSITION3 m_pos{}; - EffectBuilderPtr m_effectBuilder; + ActionResultBuilderPtr m_actionResultBuilder; std::vector< World::Util::ActorFilterPtr > m_actorFilters; std::vector< Entity::CharaPtr > m_hitActors; diff --git a/src/world/Action/ActionLut.h b/src/world/Action/ActionLut.h index bd0fbe3f..25865854 100644 --- a/src/world/Action/ActionLut.h +++ b/src/world/Action/ActionLut.h @@ -18,6 +18,7 @@ namespace Sapphire::World::Action { uint16_t id; int32_t duration; + uint32_t flag; std::vector< StatusModifier > modifiers; }; diff --git a/src/world/Action/ActionLutData.cpp b/src/world/Action/ActionLutData.cpp index 4a86ebfb..d8a74a4d 100644 --- a/src/world/Action/ActionLutData.cpp +++ b/src/world/Action/ActionLutData.cpp @@ -116,6 +116,7 @@ std::unordered_map< std::string, Common::ParamModifier > ActionLutData::m_modifi { "PiercingResistancePercent", Common::ParamModifier::PiercingResistancePercent }, { "BluntResistancePercent", Common::ParamModifier::BluntResistancePercent }, { "ProjectileResistancePercent", Common::ParamModifier::ProjectileResistancePercent }, + { "ParryPercent", Common::ParamModifier::ParryPercent } }; bool ActionLutData::cacheActions() diff --git a/src/world/Action/ActionLutData.h b/src/world/Action/ActionLutData.h index 99e78f03..22cf62bf 100644 --- a/src/world/Action/ActionLutData.h +++ b/src/world/Action/ActionLutData.h @@ -33,6 +33,8 @@ namespace Sapphire::World::Action { j.at( "id" ).get_to( statusEntry.id ); j.at( "duration" ).get_to( statusEntry.duration ); + if( j.contains( "flag" ) ) + j.at( "flag" ).get_to( statusEntry.flag ); if( j.contains( "modifiers" ) ) j.at( "modifiers" ).get_to( statusEntry.modifiers ); } diff --git a/src/world/Action/ActionResult.cpp b/src/world/Action/ActionResult.cpp new file mode 100644 index 00000000..0a0e8d68 --- /dev/null +++ b/src/world/Action/ActionResult.cpp @@ -0,0 +1,184 @@ +#include "ActionResult.h" + +#include + +#include +#include + +#include "Actor/Chara.h" +#include "Actor/Player.h" +#include "StatusEffect/StatusEffect.h" + +using namespace Sapphire; +using namespace Sapphire::World::Action; + + +ActionResult::ActionResult( Entity::CharaPtr target, uint64_t runAfter ) : + m_target( std::move( target ) ), + m_delayMs( runAfter ) +{ + m_result.Arg0 = 0; + m_result.Arg1 = 0; + m_result.Arg2 = 0; + m_result.Value = 0; + m_result.Flag = static_cast< uint8_t >( Common::ActionResultFlag::None ); + m_result.Type = Common::ActionEffectType::CALC_RESULT_TYPE_NONE; +} + +Entity::CharaPtr ActionResult::getTarget() const +{ + return m_target; +} + +uint64_t ActionResult::getDelay() +{ + return m_delayMs; +} + +void ActionResult::damage( uint32_t amount, Common::ActionHitSeverityType severity, Common::ActionResultFlag flag ) +{ + m_result.Arg0 = static_cast< uint8_t >( severity ); + m_result.Value = static_cast< int16_t >( amount ); + m_result.Flag = static_cast< uint8_t >( flag ); + m_result.Type = Common::ActionEffectType::CALC_RESULT_TYPE_DAMAGE_HP; +} + +void ActionResult::heal( uint32_t amount, Common::ActionHitSeverityType severity, Common::ActionResultFlag flag ) +{ + m_result.Arg1 = static_cast< uint8_t >( severity ); + m_result.Value = static_cast< int16_t >( amount ); + m_result.Flag = static_cast< uint8_t >( flag ); + m_result.Type = Common::ActionEffectType::CALC_RESULT_TYPE_RECOVER_HP; +} + +void ActionResult::restoreMP( uint32_t amount, Common::ActionResultFlag flag ) +{ + m_result.Value = static_cast< int16_t >( amount ); + m_result.Flag = static_cast< uint8_t >( flag ); + m_result.Type = Common::ActionEffectType::CALC_RESULT_TYPE_RECOVER_MP; +} + +void ActionResult::startCombo( uint16_t actionId ) +{ + m_result.Value = static_cast< int16_t >( actionId ); + m_result.Flag = static_cast< uint8_t >( Common::ActionResultFlag::EffectOnSource ); + m_result.Type = Common::ActionEffectType::CALC_RESULT_TYPE_COMBO; +} + +void ActionResult::comboSucceed() +{ + // no EffectOnSource flag on this + m_result.Type = Common::ActionEffectType::CALC_RESULT_TYPE_COMBO_HIT; +} + +void ActionResult::applyStatusEffect( uint32_t id, int32_t duration, Entity::Chara& source, uint8_t param, bool shouldOverride ) +{ + m_result.Value = static_cast< int16_t >( id ); + m_result.Arg2 = param; + m_result.Type = Common::ActionEffectType::CALC_RESULT_TYPE_SET_STATUS; + + m_bOverrideStatus = shouldOverride; + m_pStatus = Sapphire::StatusEffect::make_StatusEffect( id, source.getAsChara(), m_target, duration, 3000 ); + m_pStatus->setParam( param ); +} + +void ActionResult::applyStatusEffect( uint32_t id, int32_t duration, Entity::Chara& source, uint8_t param, + std::vector< StatusModifier > modifiers, uint32_t flag, bool shouldOverride ) +{ + m_result.Value = static_cast< int16_t >( id ); + m_result.Arg2 = param; + m_result.Type = Common::ActionEffectType::CALC_RESULT_TYPE_SET_STATUS; + + m_bOverrideStatus = shouldOverride; + m_pStatus = Sapphire::StatusEffect::make_StatusEffect( id, source.getAsChara(), m_target, duration, modifiers, flag, 3000 ); + m_pStatus->setParam( param ); +} + +void ActionResult::applyStatusEffectSelf( uint32_t id, int32_t duration, uint8_t param, bool shouldOverride ) +{ + m_result.Value = static_cast< int16_t >( id ); + m_result.Arg2 = param; + m_result.Type = Common::ActionEffectType::CALC_RESULT_TYPE_SET_STATUS_ME; + m_result.Flag = static_cast< uint8_t >( Common::ActionResultFlag::EffectOnSource ); + + m_bOverrideStatus = shouldOverride; + m_pStatus = Sapphire::StatusEffect::make_StatusEffect( id, m_target, m_target, duration, 3000 ); + m_pStatus->setParam( param ); +} + +void ActionResult::applyStatusEffectSelf( uint32_t id, int32_t duration, uint8_t param, std::vector< World::Action::StatusModifier > modifiers, + uint32_t flag, bool shouldOverride ) +{ + m_result.Value = static_cast< int16_t >( id ); + m_result.Arg2 = param; + m_result.Type = Common::ActionEffectType::CALC_RESULT_TYPE_SET_STATUS_ME; + m_result.Flag = static_cast< uint8_t >( Common::ActionResultFlag::EffectOnSource ); + + m_bOverrideStatus = shouldOverride; + m_pStatus = Sapphire::StatusEffect::make_StatusEffect( id, m_target, m_target, duration, modifiers, flag, 3000 ); + m_pStatus->setParam( param ); +} + +void ActionResult::mount( uint16_t mountId ) +{ + m_result.Value = static_cast< int16_t >( mountId ); + m_result.Arg0 = 1; + m_result.Type = Common::ActionEffectType::CALC_RESULT_TYPE_MOUNT; +} + +const Common::CalcResultParam& ActionResult::getCalcResultParam() const +{ + return m_result; +} + +const Sapphire::StatusEffect::StatusEffectPtr ActionResult::getStatusEffect() const +{ + return m_pStatus; +} + +void ActionResult::execute() +{ + if( !m_target ) + return; + + switch( m_result.Type ) + { + case Common::ActionEffectType::CALC_RESULT_TYPE_DAMAGE_HP: + { + m_target->takeDamage( m_result.Value ); + break; + } + + case Common::ActionEffectType::CALC_RESULT_TYPE_RECOVER_HP: + { + m_target->heal( m_result.Value ); + break; + } + + case Common::ActionEffectType::CALC_RESULT_TYPE_RECOVER_MP: + { + m_target->restoreMP( m_result.Value ); + break; + } + + case Common::ActionEffectType::CALC_RESULT_TYPE_SET_STATUS: + case Common::ActionEffectType::CALC_RESULT_TYPE_SET_STATUS_ME: + { + if( !m_bOverrideStatus ) + m_target->addStatusEffectByIdIfNotExist( m_pStatus ); + else + m_target->addStatusEffectById( m_pStatus ); + break; + } + + case Common::ActionEffectType::CALC_RESULT_TYPE_MOUNT: + { + auto pPlayer = m_target->getAsPlayer(); + pPlayer->setMount( m_result.Value ); + break; + } + + default: + break; + } +} \ No newline at end of file diff --git a/src/world/Action/ActionResult.h b/src/world/Action/ActionResult.h new file mode 100644 index 00000000..69dfa3c3 --- /dev/null +++ b/src/world/Action/ActionResult.h @@ -0,0 +1,53 @@ +#pragma once + +#include +#include +#include "ActionLut.h" + +namespace Sapphire::World::Action +{ + /*! + * @brief A container for the computed result of an effect on a single actor. Used to apply damage/healing dealt + * at a later point in time. + */ + class ActionResult + { + public: + explicit ActionResult( Entity::CharaPtr target, uint64_t delayMs ); + + void damage( uint32_t amount, Common::ActionHitSeverityType severity, Common::ActionResultFlag flag = Common::ActionResultFlag::None ); + void heal( uint32_t amount, Common::ActionHitSeverityType severity, Common::ActionResultFlag flag = Common::ActionResultFlag::None ); + void restoreMP( uint32_t amount, Common::ActionResultFlag flag = Common::ActionResultFlag::None ); + void startCombo( uint16_t actionId ); + void comboSucceed(); + void applyStatusEffect( uint32_t id, int32_t duration, Entity::Chara& source, uint8_t param, bool shouldOverride ); + void applyStatusEffect( uint32_t id, int32_t duration, Entity::Chara& source, uint8_t param, + std::vector< World::Action::StatusModifier > modifiers, uint32_t flag, bool shouldOverride ); + void applyStatusEffectSelf( uint32_t id, int32_t duration, uint8_t param, bool shouldOverride ); + void applyStatusEffectSelf( uint32_t id, int32_t duration, uint8_t param, std::vector< World::Action::StatusModifier > modifiers, + uint32_t flag, bool shouldOverride ); + void mount( uint16_t mountId ); + + Entity::CharaPtr getTarget() const; + + uint64_t getDelay(); + + const Common::CalcResultParam& getCalcResultParam() const; + const Sapphire::StatusEffect::StatusEffectPtr getStatusEffect() const; + + void execute(); + + private: + uint64_t m_delayMs; + + Entity::CharaPtr m_target; + + Common::CalcResultParam m_result; + + bool m_bOverrideStatus { false }; + Sapphire::StatusEffect::StatusEffectPtr m_pStatus; + + }; + + using ActionResultList = std::vector< ActionResultPtr >; +} \ No newline at end of file diff --git a/src/world/Action/ActionResultBuilder.cpp b/src/world/Action/ActionResultBuilder.cpp new file mode 100644 index 00000000..bedd3a2a --- /dev/null +++ b/src/world/Action/ActionResultBuilder.cpp @@ -0,0 +1,202 @@ + #include "ActionResultBuilder.h" +#include "ActionResult.h" + +#include + +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include + +using namespace Sapphire; +using namespace Sapphire::World::Action; +using namespace Sapphire::World::Manager; +using namespace Sapphire::Network::Packets; +using namespace Sapphire::Network::Packets::WorldPackets::Server; + +ActionResultBuilder::ActionResultBuilder( Entity::CharaPtr source, uint32_t actionId, uint32_t resultId, uint16_t requestId ) : + m_sourceChara( std::move( source ) ), + m_actionId( actionId ), + m_resultId( resultId ), + m_requestId( requestId ) +{ + +} + +void ActionResultBuilder::addResultToActor( Entity::CharaPtr& chara, ActionResultPtr result ) +{ + auto it = m_actorResultsMap.find( chara ); + if( it == m_actorResultsMap.end() ) + { + m_actorResultsMap[ chara ].push_back( std::move( result ) ); + return; + } + + it->second.push_back( std::move( result ) ); +} + +void ActionResultBuilder::heal( Entity::CharaPtr& effectTarget, Entity::CharaPtr& healingTarget, uint32_t amount, Common::ActionHitSeverityType severity, Common::ActionResultFlag flag ) +{ + ActionResultPtr nextResult = make_ActionResult( healingTarget, 0 ); + nextResult->heal( amount, severity, flag ); + addResultToActor( effectTarget, nextResult ); +} + +void ActionResultBuilder::restoreMP( Entity::CharaPtr& target, Entity::CharaPtr& restoringTarget, uint32_t amount, Common::ActionResultFlag flag ) +{ + ActionResultPtr nextResult = make_ActionResult( restoringTarget, 0 ); // restore mp source actor + nextResult->restoreMP( amount, flag ); + addResultToActor( target, nextResult ); +} + +void ActionResultBuilder::damage( Entity::CharaPtr& effectTarget, Entity::CharaPtr& damagingTarget, uint32_t amount, Common::ActionHitSeverityType severity, Common::ActionResultFlag flag ) +{ + ActionResultPtr nextResult = make_ActionResult( damagingTarget, 0 ); + nextResult->damage( amount, severity, flag ); + addResultToActor( damagingTarget, nextResult ); +} + +void ActionResultBuilder::startCombo( Entity::CharaPtr& target, uint16_t actionId ) +{ + ActionResultPtr nextResult = make_ActionResult( target, 0 ); + nextResult->startCombo( actionId ); + addResultToActor( target, nextResult ); +} + +void ActionResultBuilder::comboSucceed( Entity::CharaPtr& target ) +{ + ActionResultPtr nextResult = make_ActionResult( target, 0 ); + nextResult->comboSucceed(); + addResultToActor( target, nextResult ); +} + +void ActionResultBuilder::applyStatusEffect( Entity::CharaPtr& target, uint16_t statusId, uint32_t duration, uint8_t param, bool shouldOverride ) +{ + ActionResultPtr nextResult = make_ActionResult( target, 0 ); + nextResult->applyStatusEffect( statusId, duration, *m_sourceChara, param, shouldOverride ); + addResultToActor( target, nextResult ); +} + +void ActionResultBuilder::applyStatusEffect( Entity::CharaPtr& target, uint16_t statusId, uint32_t duration, uint8_t param, + std::vector< World::Action::StatusModifier > modifiers, uint32_t flag, bool shouldOverride ) +{ + ActionResultPtr nextResult = make_ActionResult( target, 0 ); + nextResult->applyStatusEffect( statusId, duration, *m_sourceChara, param, modifiers, flag, shouldOverride ); + addResultToActor( target, nextResult ); +} + +void ActionResultBuilder::applyStatusEffectSelf( uint16_t statusId, uint32_t duration, uint8_t param, bool shouldOverride ) +{ + ActionResultPtr nextResult = make_ActionResult( m_sourceChara, 0 ); + nextResult->applyStatusEffectSelf( statusId, duration, param, shouldOverride ); + addResultToActor( m_sourceChara, nextResult ); +} + +void ActionResultBuilder::applyStatusEffectSelf( uint16_t statusId, uint32_t duration, uint8_t param, std::vector< World::Action::StatusModifier > modifiers, + uint32_t flag, bool shouldOverride ) +{ + ActionResultPtr nextResult = make_ActionResult( m_sourceChara, 0 ); + nextResult->applyStatusEffectSelf( statusId, duration, param, modifiers, flag, shouldOverride ); + addResultToActor( m_sourceChara, nextResult ); +} + +void ActionResultBuilder::mount( Entity::CharaPtr& target, uint16_t mountId ) +{ + ActionResultPtr nextResult = make_ActionResult( target, 0 ); + nextResult->mount( mountId ); + addResultToActor( target, nextResult ); +} + +void ActionResultBuilder::sendActionResults( const std::vector< Entity::CharaPtr >& targetList ) +{ + Logger::debug( "EffectBuilder result: " ); + Logger::debug( "Targets afflicted: {}", targetList.size() ); + + do // we want to send at least one packet even nothing is hit so other players can see + { + auto packet = createActionResultPacket( targetList ); + server().queueForPlayers( m_sourceChara->getInRangePlayerIds( true ), packet ); + } + while( !m_actorResultsMap.empty() ); +} + +std::shared_ptr< FFXIVPacketBase > ActionResultBuilder::createActionResultPacket( const std::vector< Entity::CharaPtr >& targetList ) +{ + auto targetCount = targetList.size(); + auto& taskMgr = Common::Service< World::Manager::TaskMgr >::ref(); + auto& teriMgr = Common::Service< Sapphire::World::Manager::TerritoryMgr >::ref(); + auto zone = teriMgr.getTerritoryByGuId( m_sourceChara->getTerritoryId() ); + + if( targetCount > 1 ) // use AoeEffect packets + { + auto actionResult = std::make_shared< EffectPacket >( m_sourceChara->getId(), targetList[ 0 ]->getId(), m_actionId ); + actionResult->setRotation( Common::Util::floatToUInt16Rot( m_sourceChara->getRot() ) ); + actionResult->setRequestId( m_requestId ); + actionResult->setResultId( m_resultId ); + + uint8_t targetIndex = 0; + for( auto it = m_actorResultsMap.begin(); it != m_actorResultsMap.end(); ++it ) + { + // get all effect results for an actor + auto actorResultList = it->second; + + if( it->first ) + taskMgr.queueTask( World::makeActionIntegrityTask( m_resultId, it->first, actorResultList, 300 ) ); + + for( auto& result : actorResultList ) + { + auto effect = result->getCalcResultParam(); + if( result->getTarget() == m_sourceChara ) + actionResult->addSourceEffect( effect ); + else + actionResult->addTargetEffect( effect, result->getTarget()->getId() ); + } + targetIndex++; + + if( targetIndex == 15 ) + break; + } + + return actionResult; + } + else // use Effect for single target + { + uint32_t mainTargetId = targetList.empty() ? m_sourceChara->getId() : targetList[ 0 ]->getId(); + auto actionResult = std::make_shared< EffectPacket1 >( m_sourceChara->getId(), mainTargetId, m_actionId ); + actionResult->setRotation( Common::Util::floatToUInt16Rot( m_sourceChara->getRot() ) ); + actionResult->setRequestId( m_requestId ); + actionResult->setResultId( m_resultId ); + + for( auto it = m_actorResultsMap.begin(); it != m_actorResultsMap.end(); ++it ) + { + // get all effect results for an actor + auto actorResultList = it->second; + + if( it->first ) + taskMgr.queueTask( World::makeActionIntegrityTask( m_resultId, it->first, actorResultList, 300 ) ); + + for( auto& result : actorResultList ) + { + auto effect = result->getCalcResultParam(); + if( result->getTarget() == m_sourceChara && result->getCalcResultParam().Type != Common::ActionEffectType::CALC_RESULT_TYPE_SET_STATUS_ME ) + actionResult->addSourceEffect( effect ); + else + actionResult->addTargetEffect( effect ); + } + } + + m_actorResultsMap.clear(); + return actionResult; + } +} \ No newline at end of file diff --git a/src/world/Action/ActionResultBuilder.h b/src/world/Action/ActionResultBuilder.h new file mode 100644 index 00000000..408c8ef9 --- /dev/null +++ b/src/world/Action/ActionResultBuilder.h @@ -0,0 +1,54 @@ +#pragma once + +#include +#include +#include "ActionLut.h" + +namespace Sapphire::World::Action +{ + class ActionResultBuilder + { + public: + ActionResultBuilder( Entity::CharaPtr source, uint32_t actionId, uint32_t resultId, uint16_t requestId ); + + void heal( Entity::CharaPtr& effectTarget, Entity::CharaPtr& healingTarget, uint32_t amount, + Common::ActionHitSeverityType severity = Common::ActionHitSeverityType::NormalHeal, + Common::ActionResultFlag flag = Common::ActionResultFlag::None ); + + void restoreMP( Entity::CharaPtr& effectTarget, Entity::CharaPtr& restoringTarget, uint32_t amount, + Common::ActionResultFlag flag = Common::ActionResultFlag::None ); + + void damage( Entity::CharaPtr& effectTarget, Entity::CharaPtr& damagingTarget, uint32_t amount, + Common::ActionHitSeverityType severity = Common::ActionHitSeverityType::NormalDamage, + Common::ActionResultFlag flag = Common::ActionResultFlag::None ); + + void startCombo( Entity::CharaPtr& target, uint16_t actionId ); + + void comboSucceed( Entity::CharaPtr& target ); + + void applyStatusEffect( Entity::CharaPtr& target, uint16_t statusId, uint32_t duration, uint8_t param, bool shouldOverride = false ); + void applyStatusEffect( Entity::CharaPtr& target, uint16_t statusId, uint32_t duration, uint8_t param, + std::vector< World::Action::StatusModifier > modifiers, uint32_t flag = 0, bool shouldOverride = false ); + void applyStatusEffectSelf( uint16_t statusId, uint32_t duration, uint8_t param, bool shouldOverride = false ); + void applyStatusEffectSelf( uint16_t statusId, uint32_t duration, uint8_t param, std::vector< World::Action::StatusModifier > modifiers, + uint32_t flag = 0, bool shouldOverride = false ); + + void mount( Entity::CharaPtr& target, uint16_t mountId ); + + void sendActionResults( const std::vector< Entity::CharaPtr >& targetList ); + + private: + void addResultToActor( Entity::CharaPtr& chara, ActionResultPtr result ); + + Network::Packets::FFXIVPacketBasePtr createActionResultPacket( const std::vector< Entity::CharaPtr >& targetList ); + + private: + uint32_t m_actionId; + uint16_t m_requestId; + uint32_t m_resultId; + + Entity::CharaPtr m_sourceChara; + std::unordered_map< Entity::CharaPtr, std::vector< ActionResultPtr > > m_actorResultsMap; + }; + +} \ No newline at end of file diff --git a/src/world/Action/EffectBuilder.cpp b/src/world/Action/EffectBuilder.cpp deleted file mode 100644 index dd5bae9e..00000000 --- a/src/world/Action/EffectBuilder.cpp +++ /dev/null @@ -1,236 +0,0 @@ -#include "EffectBuilder.h" -#include "EffectResult.h" - -#include - -#include -#include - -#include - -#include -#include - -#include -#include -#include -#include - -#include -#include - -using namespace Sapphire; -using namespace Sapphire::World::Action; -using namespace Sapphire::World::Manager; -using namespace Sapphire::Network::Packets; -using namespace Sapphire::Network::Packets::WorldPackets::Server; - -EffectBuilder::EffectBuilder( Entity::CharaPtr source, uint32_t actionId, uint16_t requestId ) : - m_sourceChara( std::move( source ) ), - m_actionId( actionId ), - m_requestId( requestId ) -{ - -} - -uint64_t EffectBuilder::getResultDelayMs() -{ - // todo: actually figure this retarded shit out - - return Common::Util::getTimeMs() + 850; -} - -void EffectBuilder::addResultToActor( Entity::CharaPtr& chara, EffectResultPtr result ) -{ - auto it = m_actorEffectsMap.find( chara->getId() ); - if( it == m_actorEffectsMap.end() ) - { - // create a new one - auto resultList = std::vector< EffectResultPtr >(); - - resultList.push_back( std::move( result ) ); - - m_actorEffectsMap[ chara->getId() ] = resultList; - - return; - } - - it->second.push_back( std::move( result ) ); -} - -void EffectBuilder::heal( Entity::CharaPtr& effectTarget, Entity::CharaPtr& healingTarget, uint32_t amount, Common::ActionHitSeverityType severity, Common::ActionEffectResultFlag flag ) -{ - EffectResultPtr nextResult = make_EffectResult( healingTarget, getResultDelayMs() ); - nextResult->heal( amount, severity, flag ); - addResultToActor( effectTarget, nextResult ); -} - -void EffectBuilder::restoreMP( Entity::CharaPtr& target, Entity::CharaPtr& restoringTarget, uint32_t amount, Common::ActionEffectResultFlag flag ) -{ - EffectResultPtr nextResult = make_EffectResult( restoringTarget, getResultDelayMs() ); // restore mp source actor - nextResult->restoreMP( amount, flag ); - addResultToActor( target, nextResult ); -} - -void EffectBuilder::damage( Entity::CharaPtr& effectTarget, Entity::CharaPtr& damagingTarget, uint32_t amount, Common::ActionHitSeverityType severity, Common::ActionEffectResultFlag flag ) -{ - EffectResultPtr nextResult = make_EffectResult( damagingTarget, getResultDelayMs() ); - nextResult->damage( amount, severity, flag ); - addResultToActor( damagingTarget, nextResult ); -} - -void EffectBuilder::startCombo( Entity::CharaPtr& target, uint16_t actionId ) -{ - EffectResultPtr nextResult = make_EffectResult( target, 0 ); - nextResult->startCombo( actionId ); - addResultToActor( target, nextResult ); -} - -void EffectBuilder::comboSucceed( Entity::CharaPtr& target ) -{ - EffectResultPtr nextResult = make_EffectResult( target, 0 ); - nextResult->comboSucceed(); - addResultToActor( target, nextResult ); -} - -void EffectBuilder::applyStatusEffect( Entity::CharaPtr& target, uint16_t statusId, uint8_t param, bool forSelf ) -{ - EffectResultPtr nextResult = make_EffectResult( target, 0 ); - nextResult->applyStatusEffect( statusId, param, forSelf ); - addResultToActor( target, nextResult ); -} - -void EffectBuilder::mount( Entity::CharaPtr& target, uint16_t mountId ) -{ - EffectResultPtr nextResult = make_EffectResult( target, getResultDelayMs() ); - nextResult->mount( mountId ); - addResultToActor( target, nextResult ); -} - -void EffectBuilder::buildAndSendPackets( const std::vector< Entity::CharaPtr >& targetList ) -{ - Logger::debug( "EffectBuilder result: " ); - Logger::debug( "Targets afflicted: {}", targetList.size() ); - - do // we want to send at least one packet even nothing is hit so other players can see - { - auto packet = buildNextEffectPacket( targetList ); - server().queueForPlayers( m_sourceChara->getInRangePlayerIds( true ), packet ); - } - while( !m_actorEffectsMap.empty() ); -} - -std::shared_ptr< FFXIVPacketBase > EffectBuilder::buildNextEffectPacket( const std::vector< Entity::CharaPtr >& targetList ) -{ - auto remainingTargetCount = targetList.size(); - auto& teriMgr = Common::Service< Sapphire::World::Manager::TerritoryMgr >::ref(); - auto zone = teriMgr.getTerritoryByGuId( m_sourceChara->getTerritoryId() ); - auto resultId = zone->getNextEffectResultId(); - - if( remainingTargetCount > 1 ) // use AoeEffect packets - { - auto effectPacket = std::make_shared< EffectPacket >( m_sourceChara->getId(), m_actionId ); - effectPacket->setRotation( Common::Util::floatToUInt16Rot( m_sourceChara->getRot() ) ); - effectPacket->setRequestId( m_requestId ); - effectPacket->setResultId( resultId ); - effectPacket->setTargetActor( targetList[ 0 ]->getId() ); - - uint8_t targetIndex = 0; - for( auto it = m_actorEffectsMap.begin(); it != m_actorEffectsMap.end(); ) - { - // get all effect results for an actor - auto actorResultList = it->second; - - for( auto i = 0; i < actorResultList.size(); ++i ) - { - auto result = actorResultList.data()[ i ]; - auto effect = result->getCalcResultParam(); - - // if effect result is a source/caster effect - if( result->getTarget() == m_sourceChara ) - { - effectPacket->addSourceEffect( effect ); - } - else - { - effectPacket->addTargetEffect( effect, result->getTarget()->getId() ); - auto& taskMgr = Common::Service< World::Manager::TaskMgr >::ref(); - taskMgr.queueTask( Sapphire::World::makeActionIntegrityTask( resultId, result->getTarget(), 1000 ) ); - } - - zone->addEffectResult( std::move( result ) ); - - } - - actorResultList.clear(); - it = m_actorEffectsMap.erase( it ); - targetIndex++; - - if( targetIndex == 15 ) - break; - } - - return effectPacket; - } - else if( remainingTargetCount == 1 ) // use Effect for single target - { - - Logger::debug( " - id: {}", targetList[0]->getId() ); - Logger::debug( "------------------------------------------" ); - - auto effectPacket = std::make_shared< EffectPacket1 >( m_sourceChara->getId(), targetList[ 0 ]->getId(), m_actionId ); - effectPacket->setRotation( Common::Util::floatToUInt16Rot( m_sourceChara->getRot() ) ); - effectPacket->setRequestId( m_requestId ); - effectPacket->setResultId( resultId ); - - for( auto it = m_actorEffectsMap.begin(); it != m_actorEffectsMap.end(); ) - { - // get all effect results for an actor - auto actorResultList = it->second; - - for( auto i = 0; i < actorResultList.size(); ++i ) - { - auto result = actorResultList.data()[ i ]; - auto effect = result->getCalcResultParam(); - - // if effect result is a source/caster effect - if( result->getTarget() == m_sourceChara ) - { - effectPacket->addSourceEffect( effect ); - } - else - { - effectPacket->addTargetEffect( effect ); - auto& taskMgr = Common::Service< World::Manager::TaskMgr >::ref(); - taskMgr.queueTask( Sapphire::World::makeActionIntegrityTask( resultId, result->getTarget(), 1000 ) ); - } - - zone->addEffectResult( std::move( result ) ); - } - - actorResultList.clear(); - it = m_actorEffectsMap.erase( it ); - } - - m_actorEffectsMap.clear(); - - return effectPacket; - } - else // nothing is hit, this only happens when using aoe and AoeEffect8 is used on retail - { - auto effectPacket = makeZonePacket< FFXIVIpcActionResult1 >( m_sourceChara->getId() ); - - effectPacket->data().ActionKey = m_actionId; - effectPacket->data().Action = static_cast< uint16_t >( m_actionId ); - effectPacket->data().Target = m_sourceChara->getId(); - effectPacket->data().MainTarget = static_cast< uint64_t >( m_sourceChara->getId() ); - effectPacket->data().DirTarget = Common::Util::floatToUInt16Rot( m_sourceChara->getRot() ); - effectPacket->data().Flag = Common::ActionEffectDisplayType::HideActionName; - effectPacket->data().RequestId = m_requestId; - effectPacket->data().ResultId = resultId; - - m_actorEffectsMap.clear(); - - return effectPacket; - } -} \ No newline at end of file diff --git a/src/world/Action/EffectBuilder.h b/src/world/Action/EffectBuilder.h deleted file mode 100644 index c0711cf2..00000000 --- a/src/world/Action/EffectBuilder.h +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once - -#include -#include - -namespace Sapphire::World::Action -{ - class EffectBuilder - { - public: - EffectBuilder( Entity::CharaPtr source, uint32_t actionId, uint16_t requestId ); - - void heal( Entity::CharaPtr& effectTarget, Entity::CharaPtr& healingTarget, uint32_t amount, - Common::ActionHitSeverityType severity = Common::ActionHitSeverityType::NormalHeal, - Common::ActionEffectResultFlag flag = Common::ActionEffectResultFlag::None ); - - void restoreMP( Entity::CharaPtr& effectTarget, Entity::CharaPtr& restoringTarget, uint32_t amount, - Common::ActionEffectResultFlag flag = Common::ActionEffectResultFlag::None ); - - void damage( Entity::CharaPtr& effectTarget, Entity::CharaPtr& damagingTarget, uint32_t amount, - Common::ActionHitSeverityType severity = Common::ActionHitSeverityType::NormalDamage, - Common::ActionEffectResultFlag flag = Common::ActionEffectResultFlag::None ); - - void startCombo( Entity::CharaPtr& target, uint16_t actionId ); - - void comboSucceed( Entity::CharaPtr& target ); - - void applyStatusEffect( Entity::CharaPtr& target, uint16_t statusId, uint8_t param, bool forSelf = false ); - - void mount( Entity::CharaPtr& target, uint16_t mountId ); - - void buildAndSendPackets( const std::vector< Entity::CharaPtr >& targetList ); - - private: - void addResultToActor( Entity::CharaPtr& chara, EffectResultPtr result ); - - uint64_t getResultDelayMs(); - - Network::Packets::FFXIVPacketBasePtr buildNextEffectPacket( const std::vector< Entity::CharaPtr >& targetList ); - - private: - uint32_t m_actionId; - uint16_t m_requestId; - - Entity::CharaPtr m_sourceChara; - std::unordered_map< uint32_t, std::vector< EffectResultPtr > > m_actorEffectsMap; - }; - -} \ No newline at end of file diff --git a/src/world/Action/EffectResult.cpp b/src/world/Action/EffectResult.cpp deleted file mode 100644 index 9de97df3..00000000 --- a/src/world/Action/EffectResult.cpp +++ /dev/null @@ -1,126 +0,0 @@ -#include "EffectResult.h" - -#include - -#include -#include - -#include "Actor/Chara.h" -#include "Actor/Player.h" - -using namespace Sapphire; -using namespace Sapphire::World::Action; - - -EffectResult::EffectResult( Entity::CharaPtr target, uint64_t runAfter ) : - m_target( std::move( target ) ), - m_delayMs( runAfter ) -{ - m_result.Arg0 = 0; - m_result.Arg1 = 0; - m_result.Arg2 = 0; - m_result.Value = 0; - m_result.Flag = static_cast< uint8_t >( Common::ActionEffectResultFlag::None ); - m_result.Type = Common::ActionEffectType::CALC_RESULT_TYPE_NONE; -} - -Entity::CharaPtr EffectResult::getTarget() const -{ - return m_target; -} - -uint64_t EffectResult::getDelay() -{ - return m_delayMs; -} - -void EffectResult::damage( uint32_t amount, Common::ActionHitSeverityType severity, Common::ActionEffectResultFlag flag ) -{ - m_result.Arg0 = static_cast< uint8_t >( severity ); - m_result.Value = static_cast< int16_t >( amount ); - m_result.Flag = static_cast< uint8_t >( flag ); - m_result.Type = Common::ActionEffectType::CALC_RESULT_TYPE_DAMAGE_HP; -} - -void EffectResult::heal( uint32_t amount, Common::ActionHitSeverityType severity, Common::ActionEffectResultFlag flag ) -{ - m_result.Arg1 = static_cast< uint8_t >( severity ); - m_result.Value = static_cast< int16_t >( amount ); - m_result.Flag = static_cast< uint8_t >( flag ); - m_result.Type = Common::ActionEffectType::CALC_RESULT_TYPE_RECOVER_HP; -} - -void EffectResult::restoreMP( uint32_t amount, Common::ActionEffectResultFlag flag ) -{ - m_result.Value = static_cast< int16_t >( amount ); - m_result.Flag = static_cast< uint8_t >( flag ); - m_result.Type = Common::ActionEffectType::CALC_RESULT_TYPE_RECOVER_MP; -} - -void EffectResult::startCombo( uint16_t actionId ) -{ - m_result.Value = static_cast< int16_t >( actionId ); - m_result.Flag = static_cast< uint8_t >( Common::ActionEffectResultFlag::EffectOnSource ); - m_result.Type = Common::ActionEffectType::CALC_RESULT_TYPE_COMBO; -} - -void EffectResult::comboSucceed() -{ - // no EffectOnSource flag on this - m_result.Type = Common::ActionEffectType::CALC_RESULT_TYPE_COMBO_HIT; -} - -void EffectResult::applyStatusEffect( uint16_t statusId, uint8_t param, bool forSelf ) -{ - m_result.Value = static_cast< int16_t >( statusId ); - m_result.Arg2 = param; - if( forSelf ) - m_result.Flag = static_cast< uint8_t >( Common::ActionEffectResultFlag::EffectOnSource ); - m_result.Type = forSelf ? Common::ActionEffectType::CALC_RESULT_TYPE_SET_STATUS_ME : Common::ActionEffectType::CALC_RESULT_TYPE_SET_STATUS; -} - -void EffectResult::mount( uint16_t mountId ) -{ - m_result.Value = static_cast< int16_t >( mountId ); - m_result.Arg0 = 1; - m_result.Type = Common::ActionEffectType::CALC_RESULT_TYPE_MOUNT; -} - -const Common::CalcResultParam& EffectResult::getCalcResultParam() const -{ - return m_result; -} - -void EffectResult::execute() -{ - switch( m_result.Type ) - { - case Common::ActionEffectType::CALC_RESULT_TYPE_DAMAGE_HP: - { - m_target->takeDamage( m_result.Value ); - break; - } - - case Common::ActionEffectType::CALC_RESULT_TYPE_RECOVER_HP: - { - m_target->heal( m_result.Value ); - break; - } - - case Common::ActionEffectType::CALC_RESULT_TYPE_RECOVER_MP: - { - m_target->restoreMP( m_result.Value ); - break; - } - - case Common::ActionEffectType::CALC_RESULT_TYPE_MOUNT: - { - auto pPlayer = m_target->getAsPlayer(); - pPlayer->setMount( m_result.Value ); - break; - } - - default: - break; - } -} \ No newline at end of file diff --git a/src/world/Action/EffectResult.h b/src/world/Action/EffectResult.h deleted file mode 100644 index 48d43c71..00000000 --- a/src/world/Action/EffectResult.h +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once - -#include -#include - -namespace Sapphire::World::Action -{ - /*! - * @brief A container for the computed result of an effect on a single actor. Used to apply damage/healing dealt - * at a later point in time. - */ - class EffectResult - { - public: - explicit EffectResult( Entity::CharaPtr target, uint64_t delayMs ); - - void damage( uint32_t amount, Common::ActionHitSeverityType severity, Common::ActionEffectResultFlag flag = Common::ActionEffectResultFlag::None ); - void heal( uint32_t amount, Common::ActionHitSeverityType severity, Common::ActionEffectResultFlag flag = Common::ActionEffectResultFlag::None ); - void restoreMP( uint32_t amount, Common::ActionEffectResultFlag flag = Common::ActionEffectResultFlag::None ); - void startCombo( uint16_t actionId ); - void comboSucceed(); - void applyStatusEffect( uint16_t statusId, uint8_t param, bool forSelf ); - void mount( uint16_t mountId ); - - Entity::CharaPtr getTarget() const; - - uint64_t getDelay(); - - const Common::CalcResultParam& getCalcResultParam() const; - - void execute(); - - private: - uint64_t m_delayMs; - - Entity::CharaPtr m_target; - - Common::CalcResultParam m_result; - - }; -} \ No newline at end of file diff --git a/src/world/Action/ItemAction.cpp b/src/world/Action/ItemAction.cpp index 03139e01..fdb09aeb 100644 --- a/src/world/Action/ItemAction.cpp +++ b/src/world/Action/ItemAction.cpp @@ -81,9 +81,8 @@ void ItemAction::handleVFXItem() effect.Type = Common::ActionEffectType::CALC_RESULT_TYPE_CHECK_BARRIER; effect.Value = m_itemAction->data().Calcu0Arg[ 0 ]; - auto effectPacket = std::make_shared< EffectPacket >( getSourceChara()->getId(), getId() ); - effectPacket->setTargetActor( getSourceChara()->getId() ); - effectPacket->setAnimationId( Common::ItemActionType::ItemActionVFX ); + auto effectPacket = std::make_shared< EffectPacket >( getSourceChara()->getId(), getSourceChara()->getId(), getId() ); + effectPacket->setActionId( Common::ItemActionType::ItemActionVFX ); effectPacket->setDisplayType( Common::ActionEffectDisplayType::ShowItemName ); effectPacket->addTargetEffect( effect, static_cast< uint64_t >( getSourceChara()->getId() ) ); server().queueForPlayers( m_pSource->getInRangePlayerIds( true ), effectPacket ); diff --git a/src/world/Action/ItemManipulationAction.cpp b/src/world/Action/ItemManipulationAction.cpp index dc6a95c6..816d691f 100644 --- a/src/world/Action/ItemManipulationAction.cpp +++ b/src/world/Action/ItemManipulationAction.cpp @@ -40,7 +40,7 @@ void ItemManipulationAction::execute() { assert( m_pSource ); - m_effectBuilder->buildAndSendPackets( m_hitActors ); + m_actionResultBuilder->sendActionResults( m_hitActors ); } void ItemManipulationAction::onFinish() diff --git a/src/world/Action/Job/Warrior.cpp b/src/world/Action/Job/Warrior.cpp index 0e8434ad..d6c341b2 100644 --- a/src/world/Action/Job/Warrior.cpp +++ b/src/world/Action/Job/Warrior.cpp @@ -30,32 +30,36 @@ void Warrior::onAction( Entity::Player& player, Action& action ) void Warrior::handleWrath( Entity::Player& player, Action& action ) { auto effectToApply = Wrath; + auto parry = 2; auto asChara = player.getAsChara(); if( player.hasStatusEffect( Wrath ) ) { player.replaceSingleStatusEffectById( Wrath ); effectToApply = WrathII; + parry += 2; } else if( player.hasStatusEffect( WrathII ) ) { player.replaceSingleStatusEffectById( WrathII ); effectToApply = WrathIII; + parry += 2; } else if( player.hasStatusEffect( WrathIII ) ) { player.replaceSingleStatusEffectById( WrathIII ); effectToApply = WrathIV; + parry += 2; } else if( player.hasStatusEffect( WrathIV ) ) { player.replaceSingleStatusEffectById( WrathIV ); effectToApply = Infuriated; + parry += 2; } if( !player.hasStatusEffect( Infuriated ) ) { - action.applyStatusEffectSelf( effectToApply ); - player.addStatusEffectByIdIfNotExist( effectToApply, 30000, *asChara ); + action.getActionResultBuilder()->applyStatusEffectSelf( effectToApply, 30000, 0, { StatusModifier{ Common::ParamModifier::ParryPercent, parry } }, 0, false ); } } \ No newline at end of file diff --git a/src/world/Action/MountAction.cpp b/src/world/Action/MountAction.cpp index f8150a27..0879c651 100644 --- a/src/world/Action/MountAction.cpp +++ b/src/world/Action/MountAction.cpp @@ -69,6 +69,6 @@ void MountAction::execute() assert( m_pSource ); m_pSource->getAsPlayer()->removeCondition( Common::PlayerCondition::Casting ); - m_effectBuilder->mount( m_pSource, m_mountId ); - m_effectBuilder->buildAndSendPackets( { m_pSource } ); + m_actionResultBuilder->mount( m_pSource, m_mountId ); + m_actionResultBuilder->sendActionResults( { m_pSource } ); } \ No newline at end of file diff --git a/src/world/Actor/BNpc.cpp b/src/world/Actor/BNpc.cpp index 106c5002..4a9c95e6 100644 --- a/src/world/Actor/BNpc.cpp +++ b/src/world/Actor/BNpc.cpp @@ -18,7 +18,7 @@ #include "Network/PacketWrappers/ActorControlTargetPacket.h" #include "Network/PacketWrappers/NpcSpawnPacket.h" #include "Network/PacketWrappers/MoveActorPacket.h" -#include "Network/Util/PlayerUtil.h" +#include "Network/Util/PacketUtil.h" #include "Navi/NaviProvider.h" @@ -36,6 +36,7 @@ #include #include #include +#include #include