diff --git a/data/actions/player.json b/data/actions/player.json index d8cc5521..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, diff --git a/src/common/Common.h b/src/common/Common.h index 6cc4c791..b2f94ab0 100644 --- a/src/common/Common.h +++ b/src/common/Common.h @@ -1053,7 +1053,7 @@ namespace Sapphire::Common CritDirectHitDamage = 3 }; - enum class ActionEffectResultFlag : uint8_t + enum class ActionResultFlag : uint8_t { None = 0, Absorbed = 0x04, diff --git a/src/scripts/action/common/ActionSprint3.cpp b/src/scripts/action/common/ActionSprint3.cpp index 82bf80fb..2f8d9942 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()->applyStatusEffect( sourceChara, 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 afd5e726..98fcf0d0 100644 --- a/src/scripts/action/war/ActionInnerBeast.cpp +++ b/src/scripts/action/war/ActionInnerBeast.cpp @@ -30,13 +30,11 @@ public: 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.applyStatusEffectSelf( InnerBeast, 15000, false, { StatusModifier{ Common::ParamModifier::DamageTakenPercent, -20 } } ); if( !pPlayer->hasStatusEffect( Unchained ) ) { diff --git a/src/scripts/action/war/ActionUnchained.cpp b/src/scripts/action/war/ActionUnchained.cpp index 3025ce20..c358b489 100644 --- a/src/scripts/action/war/ActionUnchained.cpp +++ b/src/scripts/action/war/ActionUnchained.cpp @@ -25,8 +25,7 @@ public: if( auto status = pPlayer->getStatusEffectById( Defiance ); status ) status->setModifier( Common::ParamModifier::DamageDealtPercent, 0 ); - action.applyStatusEffectSelf( Unchained ); - pPlayer->addStatusEffectByIdIfNotExist( Unchained, 20000, *pPlayer->getAsChara() ); + action.applyStatusEffectSelf( Unchained, 20000, false ); } }; 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/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 2f8b6bc5..f9c87c05 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,11 @@ uint32_t Action::Action::getId() const return m_id; } +uint32_t Action::Action::getResultId() const +{ + return m_resultId; +} + bool Action::Action::init() { if( !m_actionData ) @@ -80,8 +86,11 @@ 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 ); @@ -108,9 +117,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; @@ -244,34 +253,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 ) ) @@ -284,7 +293,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 ); @@ -318,20 +327,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(); @@ -365,7 +374,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() ) { @@ -405,25 +413,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() ) @@ -433,13 +436,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.) @@ -447,13 +446,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 ); - } } } @@ -469,13 +464,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 ); @@ -492,27 +487,34 @@ 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 ) +void Action::Action::applyStatusEffectSelf( StatusEntry& statusEntry, bool shouldOverride, uint8_t param ) { if( m_hitActors.size() > 0 ) - getEffectbuilder()->applyStatusEffect( m_hitActors[ 0 ], statusId, param, true ); + getActionResultBuilder()->applyStatusEffect( m_hitActors[ 0 ], statusEntry.id, statusEntry.duration, param, + statusEntry.modifiers, statusEntry.flag, shouldOverride, true ); else - getEffectbuilder()->applyStatusEffect( m_pSource, statusId, param ); + getActionResultBuilder()->applyStatusEffect( m_pSource, statusEntry.id, statusEntry.duration, param, + statusEntry.modifiers, statusEntry.flag, shouldOverride ); } -void Action::Action::handleAction() +void Action::Action::applyStatusEffectSelf( uint16_t statusId, int32_t duration, bool shouldOverride, + std::vector< StatusModifier > modifiers, uint8_t param ) +{ + if( m_hitActors.size() > 0 ) + getActionResultBuilder()->applyStatusEffect( m_hitActors[ 0 ], statusId, duration, param, modifiers, shouldOverride, true ); + else + getActionResultBuilder()->applyStatusEffect( m_pSource, statusId, duration, param, modifiers, shouldOverride ); +} + +void Action::Action::buildActionResults() { snapshotAffectedActors( m_hitActors ); @@ -523,22 +525,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; } @@ -559,14 +558,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; } @@ -575,35 +574,33 @@ 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; } } @@ -619,7 +616,7 @@ void Action::Action::handleAction() if( m_lutEntry.statuses.caster.size() > 0 || m_lutEntry.statuses.target.size() > 0 ) handleStatusEffects(); - m_effectBuilder->buildAndSendPackets( m_hitActors ); + 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 @@ -636,8 +633,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 ); + applyStatusEffectSelf( status, true ); } } @@ -648,8 +644,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 ); + getActionResultBuilder()->applyStatusEffect( actor, status.id, status.duration, 0, status.modifiers, status.flag, true ); } if( actor->getStatusEffectMap().size() > 0 ) @@ -748,9 +743,7 @@ bool Action::Action::isCorrectCombo() const auto lastActionId = m_pSource->getLastComboActionId(); if( lastActionId == 0 ) - { return false; - } return m_actionData->data().ComboParent == lastActionId; } @@ -934,10 +927,7 @@ std::vector< Entity::CharaPtr >& Action::Action::getHitCharas() Entity::CharaPtr Action::Action::getHitChara() { if( !m_hitActors.empty() ) - { return m_hitActors.at( 0 ); - } - return nullptr; } @@ -948,9 +938,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 0e00a6a1..2ccc3668 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 ); @@ -105,11 +107,14 @@ 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 applyStatusEffectSelf( StatusEntry& statusEntry, bool shouldOverride, uint8_t param = 0 ); - void handleAction(); + void applyStatusEffectSelf( uint16_t statusId, int32_t duration, bool shouldOverride, + std::vector< StatusModifier > modifiers = {}, uint8_t param = 0 ); + + void buildActionResults(); void handleStatusEffects(); @@ -180,6 +185,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{}; @@ -218,7 +224,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/ActionResult.cpp b/src/world/Action/ActionResult.cpp new file mode 100644 index 00000000..996b5945 --- /dev/null +++ b/src/world/Action/ActionResult.cpp @@ -0,0 +1,165 @@ +#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, bool forSelf ) +{ + m_result.Value = static_cast< int16_t >( id ); + m_result.Arg2 = param; + if( forSelf ) + m_result.Flag = static_cast< uint8_t >( Common::ActionResultFlag::EffectOnSource ); + m_result.Type = forSelf ? Common::ActionEffectType::CALC_RESULT_TYPE_SET_STATUS_ME : 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, bool forSelf ) +{ + m_result.Value = static_cast< int16_t >( id ); + m_result.Arg2 = param; + if( forSelf ) + m_result.Flag = static_cast< uint8_t >( Common::ActionResultFlag::EffectOnSource ); + m_result.Type = forSelf ? Common::ActionEffectType::CALC_RESULT_TYPE_SET_STATUS_ME : 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::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->getId(), m_pStatus->getDuration(), *m_pStatus->getSrcActor(), + m_pStatus->getStatusModifiers(), m_pStatus->getFlag(), m_pStatus->getParam() ); + else + m_target->addStatusEffectById( m_pStatus->getId(), m_pStatus->getDuration(), *m_pStatus->getSrcActor(), + m_pStatus->getStatusModifiers(), m_pStatus->getFlag(), m_pStatus->getParam() ); + 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..f60e94cb --- /dev/null +++ b/src/world/Action/ActionResult.h @@ -0,0 +1,50 @@ +#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, bool forSelf ); + 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, bool forSelf ); + 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..c430854a --- /dev/null +++ b/src/world/Action/ActionResultBuilder.cpp @@ -0,0 +1,188 @@ +#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, bool forSelf ) +{ + ActionResultPtr nextResult = make_ActionResult( target, 0 ); + nextResult->applyStatusEffect( statusId, duration, *m_sourceChara, param, shouldOverride, forSelf ); + 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, bool forSelf ) +{ + ActionResultPtr nextResult = make_ActionResult( target, 0 ); + nextResult->applyStatusEffect( statusId, duration, *m_sourceChara, param, modifiers, flag, shouldOverride, forSelf ); + addResultToActor( target, 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 ) + 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..940ab977 --- /dev/null +++ b/src/world/Action/ActionResultBuilder.h @@ -0,0 +1,52 @@ +#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, bool forSelf = 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, bool forSelf = 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 7f4cbb1a..4b09a79d 100644 --- a/src/world/Action/Job/Warrior.cpp +++ b/src/world/Action/Job/Warrior.cpp @@ -60,7 +60,6 @@ void Warrior::handleWrath( Entity::Player& player, Action& action ) if( !player.hasStatusEffect( Infuriated ) ) { - action.applyStatusEffectSelf( effectToApply ); - player.addStatusEffectByIdIfNotExist( effectToApply, 30000, *asChara, { StatusModifier{ Common::ParamModifier::ParryPercent, parry } } ); + action.applyStatusEffectSelf( effectToApply, 30000, false, { StatusModifier{ Common::ParamModifier::ParryPercent, parry } } ); } } \ 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..2224e089 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