diff --git a/src/scripts/action/common/ActionSprint3.cpp b/src/scripts/action/common/ActionSprint3.cpp index 317bf661..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.getActionResultBuilder()->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/world/Action/ActionResult.cpp b/src/world/Action/ActionResult.cpp index d12b1945..588d77ab 100644 --- a/src/world/Action/ActionResult.cpp +++ b/src/world/Action/ActionResult.cpp @@ -7,6 +7,7 @@ #include "Actor/Chara.h" #include "Actor/Player.h" +#include "StatusEffect/StatusEffect.h" using namespace Sapphire; using namespace Sapphire::World::Action; @@ -70,11 +71,15 @@ void ActionResult::comboSucceed() m_result.Type = Common::ActionEffectType::CALC_RESULT_TYPE_COMBO_HIT; } -void ActionResult::applyStatusEffect( uint16_t statusId, uint8_t param ) +void ActionResult::applyStatusEffect( uint32_t id, int32_t duration, Entity::Chara& source, uint8_t param, bool shouldOverride ) { - m_result.Value = static_cast< int16_t >( statusId ); + 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 = StatusEffect::make_StatusEffect( id, source.getAsChara(), m_target, duration, 3000 ); + m_pStatus->setParam( param ); } void ActionResult::mount( uint16_t mountId ) @@ -89,6 +94,11 @@ const Common::CalcResultParam& ActionResult::getCalcResultParam() const return m_result; } +const StatusEffect::StatusEffectPtr ActionResult::getStatusEffect() const +{ + return m_pStatus; +} + void ActionResult::execute() { if( !m_target ) @@ -114,6 +124,15 @@ void ActionResult::execute() break; } + case Common::ActionEffectType::CALC_RESULT_TYPE_SET_STATUS: + { + if( !m_bOverrideStatus ) + m_target->addStatusEffectByIdIfNotExist( m_pStatus->getId(), m_pStatus->getDuration(), *m_pStatus->getSrcActor(), m_pStatus->getParam() ); + else + m_target->addStatusEffectById( m_pStatus->getId(), m_pStatus->getDuration(), *m_pStatus->getSrcActor(), m_pStatus->getParam() ); + break; + } + case Common::ActionEffectType::CALC_RESULT_TYPE_MOUNT: { auto pPlayer = m_target->getAsPlayer(); diff --git a/src/world/Action/ActionResult.h b/src/world/Action/ActionResult.h index fd41403a..933db5fa 100644 --- a/src/world/Action/ActionResult.h +++ b/src/world/Action/ActionResult.h @@ -19,7 +19,7 @@ namespace Sapphire::World::Action void restoreMP( uint32_t amount, Common::ActionResultFlag flag = Common::ActionResultFlag::None ); void startCombo( uint16_t actionId ); void comboSucceed(); - void applyStatusEffect( uint16_t statusId, uint8_t param ); + void applyStatusEffect( uint32_t id, int32_t duration, Entity::Chara& source, uint8_t param, bool shouldOverride ); void mount( uint16_t mountId ); Entity::CharaPtr getTarget() const; @@ -27,6 +27,7 @@ namespace Sapphire::World::Action uint64_t getDelay(); const Common::CalcResultParam& getCalcResultParam() const; + const Sapphire::StatusEffect::StatusEffectPtr getStatusEffect() const; void execute(); @@ -37,6 +38,9 @@ namespace Sapphire::World::Action Common::CalcResultParam m_result; + bool m_bOverrideStatus { false }; + Sapphire::StatusEffect::StatusEffectPtr m_pStatus; + }; using ActionResultList = std::vector< ActionResultPtr >; diff --git a/src/world/Action/ActionResultBuilder.cpp b/src/world/Action/ActionResultBuilder.cpp index 2cb5d6c0..c3f382b2 100644 --- a/src/world/Action/ActionResultBuilder.cpp +++ b/src/world/Action/ActionResultBuilder.cpp @@ -81,10 +81,10 @@ void ActionResultBuilder::comboSucceed( Entity::CharaPtr& target ) addResultToActor( target, nextResult ); } -void ActionResultBuilder::applyStatusEffect( Entity::CharaPtr& target, uint16_t statusId, uint8_t param ) +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, param ); + nextResult->applyStatusEffect( statusId, duration, *m_sourceChara, param, shouldOverride ); addResultToActor( target, nextResult ); } @@ -122,6 +122,8 @@ std::shared_ptr< FFXIVPacketBase > ActionResultBuilder::createActionResultPacket actionResult->setRequestId( m_requestId ); actionResult->setResultId( m_resultId ); actionResult->setEffectFlags( Common::ActionEffectDisplayType::HideActionName ); + if( !m_actorResultsMap.empty() ) + taskMgr.queueTask( World::makeActionIntegrityTask( m_resultId, m_sourceChara, m_actorResultsMap.begin()->second, 300 ) ); m_actorResultsMap.clear(); return actionResult; } diff --git a/src/world/Action/ActionResultBuilder.h b/src/world/Action/ActionResultBuilder.h index a258d3d1..aca5b967 100644 --- a/src/world/Action/ActionResultBuilder.h +++ b/src/world/Action/ActionResultBuilder.h @@ -25,7 +25,7 @@ namespace Sapphire::World::Action void comboSucceed( Entity::CharaPtr& target ); - void applyStatusEffect( Entity::CharaPtr& target, uint16_t statusId, uint8_t param ); + void applyStatusEffect( Entity::CharaPtr& target, uint16_t statusId, uint32_t duration, uint8_t param, bool shouldOverride ); void mount( Entity::CharaPtr& target, uint16_t mountId ); diff --git a/src/world/Actor/Chara.cpp b/src/world/Actor/Chara.cpp index 183e2718..035bb36c 100644 --- a/src/world/Actor/Chara.cpp +++ b/src/world/Actor/Chara.cpp @@ -500,37 +500,8 @@ void Chara::addStatusEffect( StatusEffect::StatusEffectPtr pEffect ) return; pEffect->applyStatus(); + pEffect->setSlot( nextSlot ); m_statusEffectMap[ nextSlot ] = pEffect; - - auto statusEffectAdd = makeZonePacket< FFXIVIpcActionIntegrity >( getId() ); - - statusEffectAdd->data().ResultId = pZone->getNextActionResultId(); - statusEffectAdd->data().Target = pEffect->getTargetActorId(); - statusEffectAdd->data().Hp = getHp(); - statusEffectAdd->data().Mp = static_cast< uint16_t >( getMp() ); - statusEffectAdd->data().Tp = static_cast< uint16_t >( getTp() ); - statusEffectAdd->data().HpMax = getMaxHp(); - statusEffectAdd->data().MpMax = static_cast< uint16_t >( getMaxMp() ); - statusEffectAdd->data().ClassJob = static_cast< uint8_t >( getClass() ); - statusEffectAdd->data().StatusCount = 1; - statusEffectAdd->data().unknown_E0 = 0xE0; - - // set all status sources to u32 invalid game obj - // todo: chara status effect map should be filled instead, since hudparam also uses invalid gameobj - for( int i = 0; i < 4; ++i ) - { - statusEffectAdd->data().Status[ i ].Source = INVALID_GAME_OBJECT_ID; - } - - auto& status = statusEffectAdd->data().Status[ 0 ]; - - status.Source = pEffect->getSrcActorId(); - status.Time = static_cast< float >( pEffect->getDuration() ) / 1000; - status.Id = static_cast< uint16_t >( pEffect->getId() ); - status.Slot = static_cast< uint8_t >( nextSlot ); - status.SystemParam = static_cast< int16_t >( pEffect->getParam() ); - - server().queueForPlayers( getInRangePlayerIds( isPlayer() ), statusEffectAdd ); } /*! \param StatusEffectPtr to be applied to the actor */ diff --git a/src/world/StatusEffect/StatusEffect.cpp b/src/world/StatusEffect/StatusEffect.cpp index 1b371938..e0f6e14e 100644 --- a/src/world/StatusEffect/StatusEffect.cpp +++ b/src/world/StatusEffect/StatusEffect.cpp @@ -72,6 +72,11 @@ uint32_t Sapphire::StatusEffect::StatusEffect::getSrcActorId() const return m_sourceActor->getId(); } +Sapphire::Entity::CharaPtr Sapphire::StatusEffect::StatusEffect::getSrcActor() const +{ + return m_sourceActor; +} + uint32_t Sapphire::StatusEffect::StatusEffect::getTargetActorId() const { return m_targetActor->getId(); @@ -86,25 +91,6 @@ void Sapphire::StatusEffect::StatusEffect::applyStatus() { m_startTime = Util::getTimeMs(); auto& scriptMgr = Common::Service< Scripting::ScriptMgr >::ref(); - - // this is only right when an action is being used by the player - // else you probably need to use an actorcontrol - - //GamePacketNew< FFXIVIpcEffect > effectPacket( m_sourceActor->getId() ); - //effectPacket.data().targetId = m_sourceActor->getId(); - //effectPacket.data().actionAnimationId = 3; - //effectPacket.data().unknown_3 = 1; - //effectPacket.data().actionTextId = 3; - //effectPacket.data().unknown_5 = 1; - //effectPacket.data().unknown_6 = 321; - //effectPacket.data().rotation = ( uint16_t ) ( 0x8000 * ( ( m_sourceActor->getPos().getR() + 3.1415926 ) ) / 3.1415926 ); - //effectPacket.data().effectTargetId = m_sourceActor->getId(); - //effectPacket.data().effects[4].unknown_1 = 17; - //effectPacket.data().effects[4].bonusPercent = 30; - //effectPacket.data().effects[4].param1 = m_id; - //effectPacket.data().effects[4].unknown_5 = 0x80; - //m_sourceActor->sendToInRangeSet( effectPacket, true ); - scriptMgr.onStatusReceive( m_targetActor, m_id ); } @@ -153,3 +139,13 @@ const std::string& Sapphire::StatusEffect::StatusEffect::getName() const { return m_name; } + +uint8_t Sapphire::StatusEffect::StatusEffect::getSlot() const +{ + return m_slot; +} + +void Sapphire::StatusEffect::StatusEffect::setSlot( uint8_t slot ) +{ + m_slot = slot; +} diff --git a/src/world/StatusEffect/StatusEffect.h b/src/world/StatusEffect/StatusEffect.h index e6bb1c8e..ff0e21e2 100644 --- a/src/world/StatusEffect/StatusEffect.h +++ b/src/world/StatusEffect/StatusEffect.h @@ -28,6 +28,7 @@ public: uint32_t getTickRate() const; uint32_t getSrcActorId() const; + Entity::CharaPtr getSrcActor() const; uint32_t getTargetActorId() const; @@ -47,6 +48,9 @@ public: const std::string& getName() const; + uint8_t getSlot() const; + void setSlot( uint8_t slot ); + private: uint32_t m_id; Entity::CharaPtr m_sourceActor; @@ -57,6 +61,7 @@ private: uint64_t m_lastTick; uint16_t m_param; std::string m_name; + uint8_t m_slot; std::pair< uint8_t, uint32_t > m_currTickEffect; }; diff --git a/src/world/Task/ActionIntegrityTask.cpp b/src/world/Task/ActionIntegrityTask.cpp index ce3c8241..6306f81b 100644 --- a/src/world/Task/ActionIntegrityTask.cpp +++ b/src/world/Task/ActionIntegrityTask.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include @@ -39,15 +40,31 @@ void ActionIntegrityTask::execute() if( inRangePlayers.empty() ) return; + auto integrityPacket = makeZonePacket< FFXIVIpcActionIntegrity >( 0 ); + auto& data = integrityPacket->data(); + integrityPacket->setSourceActor( m_pTarget->getId() ); + + for( int i = 0; i < 4; ++i ) + data.Status[ i ].Source = Common::INVALID_GAME_OBJECT_ID; + + int statusIdx = 0; for( auto& actionResult : m_results ) { if( actionResult && actionResult->getTarget() && actionResult->getTarget()->isAlive() ) actionResult->execute(); + + if( actionResult->getCalcResultParam().Type == Common::CALC_RESULT_TYPE_SET_STATUS ) + { + auto& status = data.Status[ statusIdx++ ]; + auto pEffect = actionResult->getStatusEffect(); + status.Source = pEffect->getSrcActorId(); + status.Time = static_cast< float >( pEffect->getDuration() ) / 1000; + status.Id = static_cast< uint16_t >( pEffect->getId() ); + status.Slot = static_cast< uint8_t >( pEffect->getSlot() ); + status.SystemParam = static_cast< int16_t >( pEffect->getParam() ); + } } - auto integrityPacket = makeZonePacket< FFXIVIpcActionIntegrity >( 0 ); - auto& data = integrityPacket->data(); - integrityPacket->setSourceActor( m_pTarget->getId() ); data.Hp = m_pTarget->getHp(); data.HpMax = m_pTarget->getMaxHp(); data.Mp = m_pTarget->getMp(); @@ -55,6 +72,9 @@ void ActionIntegrityTask::execute() data.Tp = m_pTarget->getTp(); data.ResultId = m_resultId; data.Target = m_pTarget->getId(); + data.StatusCount = statusIdx; + data.ClassJob = static_cast< uint8_t >( m_pTarget->getClass() ); + data.unknown_E0 = 0xE0; server.queueForPlayers( inRangePlayers, integrityPacket );