diff --git a/src/world/Action/Action.cpp b/src/world/Action/Action.cpp index 4663fadc..ea8d5718 100644 --- a/src/world/Action/Action.cpp +++ b/src/world/Action/Action.cpp @@ -28,10 +28,11 @@ Sapphire::Action::Action::Action( Entity::CharaPtr caster, uint32_t actionId, m_pFw( std::move( fw ) ), m_id( actionId ), m_startTime( 0 ), - m_interruptType( Common::ActionInterruptType::None ) + m_interruptType( Common::ActionInterruptType::None ), + m_hasResidentTarget( false ) { m_castTime = static_cast< uint32_t >( action->cast100ms * 100 ); - m_cooldownTime = static_cast< uint16_t >( action->recast100ms * 100 ); + m_recastTime = static_cast< uint16_t >( action->recast100ms * 100 ); m_cooldownGroup = action->cooldownGroup; m_actionCost.fill( { Common::ActionCostType::None, 0 } ); @@ -67,6 +68,17 @@ void Sapphire::Action::Action::setTargetChara( Sapphire::Entity::CharaPtr chara m_targetId = chara->getId(); } +void Sapphire::Action::Action::setResidentTargetId( uint64_t targetId ) +{ + m_targetId = targetId; + m_hasResidentTarget = true; +} + +bool Sapphire::Action::Action::hasResidentTarget() const +{ + return m_hasResidentTarget; +} + Sapphire::Entity::CharaPtr Sapphire::Action::Action::getTargetChara() const { return m_pTarget; @@ -121,6 +133,11 @@ bool Sapphire::Action::Action::update() return true; } + if( !hasResidentTarget() ) + { + // todo: check if the target is still in range + } + uint64_t currTime = Util::getTimeMs(); if( !isCastedAction() || std::difftime( currTime, m_startTime ) > m_castTime ) @@ -136,6 +153,12 @@ void Sapphire::Action::Action::onStart() { assert( m_pSource ); + auto player = m_pSource->getAsPlayer(); + if( player ) + { + player->sendDebug( "onStart()" ); + } + if( isCastedAction() ) { auto castPacket = makeZonePacket< Server::FFXIVIpcActorCast >( getId() ); @@ -149,10 +172,9 @@ void Sapphire::Action::Action::onStart() m_pSource->sendToInRangeSet( castPacket, true ); - if( auto player = m_pSource->getAsPlayer() ) + if( player ) { player->setStateFlag( PlayerStateFlag::Casting ); - player->sendDebug( "onStart()" ); } } } @@ -208,8 +230,16 @@ void Sapphire::Action::Action::onFinish() 0x219, m_id, m_id, m_id, m_id ); m_pSource->sendToInRangeSet( control, true );*/ + } + + if( !hasResidentTarget() ) + { pScriptMgr->onCastFinish( *pPlayer, m_pTarget, m_id ); } + else + { + pScriptMgr->onEObjHit( *pPlayer, m_targetId ); + } } void Sapphire::Action::Action::buildEffectPacket() diff --git a/src/world/Action/Action.h b/src/world/Action/Action.h index 42ff64b3..874088ac 100644 --- a/src/world/Action/Action.h +++ b/src/world/Action/Action.h @@ -37,6 +37,7 @@ namespace Sapphire::Action Common::FFXIVARR_POSITION3 getPos() const; void setTargetChara( Entity::CharaPtr chara ); + void setResidentTargetId( uint64_t targetId ); Entity::CharaPtr getTargetChara() const; Entity::CharaPtr getActionSource() const; @@ -46,6 +47,14 @@ namespace Sapphire::Action uint32_t getCastTime() const; void setCastTime( uint32_t castTime ); + /*! + * @brief Checks if the action *may* target a resident instead of an actor + * This checks if m_pTarget is nullptr but m_targetId is set + * + * @return true if the target *may* be a resident and not an actor, otherwise false. + */ + bool hasResidentTarget() const; + const ActionCostArray& getCostArray() const; /*! @@ -113,12 +122,13 @@ namespace Sapphire::Action uint64_t m_startTime; uint32_t m_castTime; - uint16_t m_cooldownTime; + uint16_t m_recastTime; uint8_t m_cooldownGroup; Entity::CharaPtr m_pSource; Entity::CharaPtr m_pTarget; uint64_t m_targetId; + bool m_hasResidentTarget; Common::ActionInterruptType m_interruptType; diff --git a/src/world/Manager/ActionMgr.cpp b/src/world/Manager/ActionMgr.cpp index 38936cb4..49a84947 100644 --- a/src/world/Manager/ActionMgr.cpp +++ b/src/world/Manager/ActionMgr.cpp @@ -38,12 +38,16 @@ void World::Manager::ActionMgr::handleTargetedPlayerAction( Entity::Player& play if( targetId != player.getId() ) { auto target = player.lookupTargetById( targetId ); - if( auto chara = target->getAsChara() ) + if( !target ) + { + // an eobj? + player.sendDebug( "Unable to find actor for targetId#{0}, passing through to event scripts...", targetId ); + action->setResidentTargetId( targetId ); + } + else if( auto chara = target->getAsChara() ) + { action->setTargetChara( chara ); - } - else - { - // maybe an eobj? wat do? + } } bootstrapAction( player, action, *actionData ); diff --git a/src/world/Script/NativeScriptApi.cpp b/src/world/Script/NativeScriptApi.cpp index 06e04cc2..66679465 100644 --- a/src/world/Script/NativeScriptApi.cpp +++ b/src/world/Script/NativeScriptApi.cpp @@ -141,6 +141,10 @@ namespace Sapphire::ScriptAPI { } + void EventScript::onEObjHit( Sapphire::Entity::Player& player, uint64_t actorId ) + { + } + /////////////////////////////////////////////////////////////////// EventObjectScript::EventObjectScript( uint32_t eobjId ) : diff --git a/src/world/Script/NativeScriptApi.h b/src/world/Script/NativeScriptApi.h index a86a8c63..24f8851e 100644 --- a/src/world/Script/NativeScriptApi.h +++ b/src/world/Script/NativeScriptApi.h @@ -180,6 +180,8 @@ namespace Sapphire::ScriptAPI virtual void onEventHandlerTradeReturn( Sapphire::Entity::Player& player, uint32_t eventId, uint16_t subEvent, uint16_t param, uint32_t catalogId ); + + virtual void onEObjHit( Sapphire::Entity::Player& player, uint64_t actorId ); }; /*! diff --git a/src/world/Script/ScriptMgr.cpp b/src/world/Script/ScriptMgr.cpp index e5c59236..69cd1543 100644 --- a/src/world/Script/ScriptMgr.cpp +++ b/src/world/Script/ScriptMgr.cpp @@ -299,6 +299,32 @@ bool Sapphire::Scripting::ScriptMgr::onBNpcKill( Entity::Player& player, uint16_ return true; } +bool Sapphire::Scripting::ScriptMgr::onEObjHit( Sapphire::Entity::Player& player, uint64_t actorId ) +{ + auto pEventMgr = framework()->get< World::Manager::EventMgr >(); + + for( size_t i = 0; i < 30; i++ ) + { + auto activeQuests = player.getQuestActive( static_cast< uint16_t >( i ) ); + if( !activeQuests ) + continue; + + uint32_t questId = activeQuests->c.questId | Event::EventHandler::EventHandlerType::Quest << 16; + + auto script = m_nativeScriptMgr->getScript< Sapphire::ScriptAPI::EventScript >( questId ); + if( script ) + { + std::string objName = pEventMgr->getEventName( questId ); + + player.sendDebug( "Calling: {0}.onEObjHit actorId#{1}", objName, actorId ); + + script->onEObjHit( player, actorId ); + } + } + + return true; +} + bool Sapphire::Scripting::ScriptMgr::onCastFinish( Entity::Player& player, Entity::CharaPtr pTarget, uint32_t actionId ) { auto script = m_nativeScriptMgr->getScript< Sapphire::ScriptAPI::ActionScript >( actionId ); diff --git a/src/world/Script/ScriptMgr.h b/src/world/Script/ScriptMgr.h index b2423ccf..1217caa4 100644 --- a/src/world/Script/ScriptMgr.h +++ b/src/world/Script/ScriptMgr.h @@ -70,6 +70,8 @@ namespace Sapphire::Scripting bool onBNpcKill( Entity::Player& player, uint16_t nameId ); + bool onEObjHit( Entity::Player& player, uint64_t actorId ); + bool onCastFinish( Entity::Player& pPlayer, Entity::CharaPtr pTarget, uint32_t actionId ); bool onStatusReceive( Entity::CharaPtr pActor, uint32_t effectId );