From cf5fbc2620264d25d8526e378d7eb2dc28b52049 Mon Sep 17 00:00:00 2001 From: collett Date: Thu, 8 May 2025 01:51:43 +0900 Subject: [PATCH] Update action target filtering and add more calls to action script at different stages during execution. --- src/world/Action/Action.cpp | 62 ++++++++++---------- src/world/Actor/Chara.cpp | 13 ++++ src/world/Actor/Chara.h | 4 ++ src/world/Manager/ActionMgr.cpp | 11 ++-- src/world/Network/Handlers/ActionHandler.cpp | 3 - src/world/Script/NativeScriptApi.cpp | 8 +++ src/world/Script/NativeScriptApi.h | 4 ++ src/world/Script/ScriptMgr.cpp | 25 ++++++++ src/world/Script/ScriptMgr.h | 4 ++ 9 files changed, 97 insertions(+), 37 deletions(-) diff --git a/src/world/Action/Action.cpp b/src/world/Action/Action.cpp index aefb889f..2eae0ed3 100644 --- a/src/world/Action/Action.cpp +++ b/src/world/Action/Action.cpp @@ -350,7 +350,7 @@ void Action::Action::start() onStart(); // instantly finish cast if there's no cast time - if( !hasCastTime() ) + if( !hasCastTime() && !isInterrupted() ) execute(); } @@ -379,6 +379,8 @@ void Action::Action::onStart() void Action::Action::interrupt() { assert( m_pSource ); + if( m_interruptType == ActionInterruptType::None ) + m_interruptType = ActionInterruptType::RegularInterrupt; // things that aren't players don't care about cooldowns and state flags if( m_pSource->isPlayer() ) { @@ -522,6 +524,7 @@ void Action::Action::buildActionResults() if( !m_enableGenericHandler || !hasLutEntry || m_hitActors.empty() ) { + scriptMgr.onAfterBuildEffect( *this ); // send any effect packet added by script or an empty one just to play animation for other players m_actionResultBuilder->sendActionResults( {} ); return; @@ -592,7 +595,7 @@ void Action::Action::buildActionResults() } // If we hit an enemy - if( !m_hitActors.empty() && getHitChara()->getObjKind() != m_pSource->getObjKind() ) + if( !m_hitActors.empty() && getHitChara()->isHostile( *m_pSource ) ) { m_pSource->removeStatusEffectByFlag( Common::StatusEffectFlag::RemoveOnSuccessfulHit ); } @@ -600,6 +603,8 @@ void Action::Action::buildActionResults() handleJobAction(); handleStatusEffects(); + scriptMgr.onAfterBuildEffect( *this ); + m_actionResultBuilder->sendActionResults( m_hitActors ); // TODO: disabled, reset kills our queued actions @@ -667,7 +672,6 @@ bool Action::Action::preCheck() bool Action::Action::playerPreCheck( Entity::Player& player ) { - // lol if( !player.isAlive() ) return false; @@ -675,28 +679,28 @@ bool Action::Action::playerPreCheck( Entity::Player& player ) //if( m_actionData->data().UseClassJob == -1 /* dunno what this is in old data && !m_actionData->data().isRoleAction*/ ) // return false; - if( player.getLevel() < m_actionData->data().UseClassJob ) - return false; + //if( player.getLevel() < m_actionData->data().UseClassJob ) + // return false; - auto currentClass = player.getClass(); - auto actionClass = static_cast< Common::ClassJob >( m_actionData->data().UseClassJob ); + //auto currentClass = player.getClass(); + //auto actionClass = static_cast< Common::ClassJob >( m_actionData->data().UseClassJob ); - if( actionClass != Common::ClassJob::Adventurer && currentClass != actionClass /* dunno what this is in old data && !m_actionData->data().isRoleAction*/ ) - { + //if( actionClass != Common::ClassJob::Adventurer && currentClass != actionClass /* dunno what this is in old data && !m_actionData->data().isRoleAction*/ ) + //{ // check if not a base class action - auto& exdData = Common::Service< Data::ExdData >::ref(); + //auto& exdData = Common::Service< Data::ExdData >::ref(); - auto classJob = exdData.getRow< Excel::ClassJob >( static_cast< uint8_t >( currentClass ) ); - if( !classJob ) - return false; + //auto classJob = exdData.getRow< Excel::ClassJob >( static_cast< uint8_t >( currentClass ) ); + //if( !classJob ) + // return false; - if( classJob->data().MainClass != m_actionData->data().UseClassJob ) - return false; - } - - if( !m_actionData->data().SelectMyself && getTargetId() == m_pSource->getId() ) - return false; + //if( classJob->data().MainClass != m_actionData->data().UseClassJob ) + // return false; + //} + //if( !m_actionData->data().SelectMyself && getTargetId() == m_pSource->getId() ) + // return false; + // todo: does this need to check for party/alliance stuff or it's just same type? // todo: m_pTarget doesn't exist at this stage because we only fill it when we snapshot targets // if( !m_actionData->canTargetFriendly && m_pSource->getObjKind() == m_pTarget->getObjKind() ) @@ -885,6 +889,9 @@ void Action::Action::addDefaultActorFilters() bool Action::Action::preFilterActor( Entity::GameObject& actor ) const { + if( m_castType == Common::CastType::SingleTarget ) // client filters any single target action by itself + return true; + auto kind = actor.getObjKind(); auto chara = actor.getAsChara(); @@ -892,20 +899,15 @@ bool Action::Action::preFilterActor( Entity::GameObject& actor ) const if( kind != ObjKind::BattleNpc && kind != ObjKind::Player ) return false; - // todo: evaluate other actions that can hit condition (eg. sprint) - /* if( !m_canTargetSelf && chara->getId() == m_pSource->getId() ) - return false;*/ + // for any non-targeted aoe action m_canTargetSelf is always true and no other info is available - if( ( m_lutEntry.potency > 0 || m_lutEntry.curePotency > 0 ) && !chara->isAlive() ) // !m_canTargetDead not working for aoe - return false; + if( chara->isAlive() && ( m_lutEntry.curePotency > 0 || m_canTargetFriendly ) && m_pSource->isFriendly( *chara ) ) + return true; - if( m_lutEntry.potency > 0 && m_pSource->getObjKind() == chara->getObjKind() ) // !m_canTargetFriendly not working for aoe - return false; + if( chara->isAlive() && ( m_lutEntry.potency > 0 || m_canTargetHostile ) > 0 && m_pSource->isHostile( *chara ) ) + return true; - if( ( m_lutEntry.potency == 0 && m_lutEntry.curePotency > 0 ) && m_pSource->getObjKind() != chara->getObjKind() ) // !m_canTargetHostile not working for aoe - return false; - - return true; + return false; } std::vector< Entity::CharaPtr >& Action::Action::getHitCharas() diff --git a/src/world/Actor/Chara.cpp b/src/world/Actor/Chara.cpp index 9593b045..96260cda 100644 --- a/src/world/Actor/Chara.cpp +++ b/src/world/Actor/Chara.cpp @@ -833,8 +833,21 @@ bool Chara::isFacingTarget( const Chara& other, float threshold ) return dot >= threshold; } +bool Sapphire::Entity::Chara::isHostile( const Chara& chara ) +{ + return m_objKind != chara.getObjKind(); +} + +bool Sapphire::Entity::Chara::isFriendly( const Chara& chara ) +{ + return m_objKind == chara.getObjKind(); +} + void Chara::onTick() { + if ( !isAlive() ) + return; + uint32_t thisTickDmg = 0; uint32_t thisTickHeal = 0; diff --git a/src/world/Actor/Chara.h b/src/world/Actor/Chara.h index 212ea1de..3d4ad653 100644 --- a/src/world/Actor/Chara.h +++ b/src/world/Actor/Chara.h @@ -229,8 +229,12 @@ namespace Sapphire::Entity virtual void onDamageTaken( Chara& pSource ) {}; + virtual bool isHostile( const Chara& chara ); + virtual void onActionHostile( CharaPtr pSource ) {}; + virtual bool isFriendly( const Chara& chara ); + virtual void onActionFriendly( Chara& pSource ) {}; virtual void onTick(); diff --git a/src/world/Manager/ActionMgr.cpp b/src/world/Manager/ActionMgr.cpp index 46cb7b4d..9cd67e33 100644 --- a/src/world/Manager/ActionMgr.cpp +++ b/src/world/Manager/ActionMgr.cpp @@ -121,15 +121,18 @@ void ActionMgr::handleMountAction( Entity::Player& player, uint16_t mountId, void ActionMgr::bootstrapAction( Entity::Chara& src, Action::ActionPtr currentAction, Excel::ExcelStructPtr< Excel::Action > actionData ) { - /* - //TODO: need to be fixed + auto& scriptMgr = Common::Service< Scripting::ScriptMgr >::ref(); + scriptMgr.onBeforeBootstrap( *currentAction ); + if( currentAction->isInterrupted() ) + return; + + // re-enable this call but disable most of the old unfixed checks as scripts can override it with special checks. if( !currentAction->preCheck() ) { - // forcefully interrupt the action and reset the cooldown currentAction->interrupt(); return; } - */ + if( src.getCurrentAction() ) { diff --git a/src/world/Network/Handlers/ActionHandler.cpp b/src/world/Network/Handlers/ActionHandler.cpp index 4465f64d..6829f5c6 100644 --- a/src/world/Network/Handlers/ActionHandler.cpp +++ b/src/world/Network/Handlers/ActionHandler.cpp @@ -90,9 +90,6 @@ void Sapphire::Network::GameConnection::actionRequest( const Packets::FFXIVARR_P break; } } - - - } void Sapphire::Network::GameConnection::selectGroundActionRequest( const Packets::FFXIVARR_PACKET_RAW& inPacket, Entity::Player& player ) diff --git a/src/world/Script/NativeScriptApi.cpp b/src/world/Script/NativeScriptApi.cpp index 87997387..3705b654 100644 --- a/src/world/Script/NativeScriptApi.cpp +++ b/src/world/Script/NativeScriptApi.cpp @@ -77,6 +77,10 @@ namespace Sapphire::ScriptAPI { } + void ActionScript::onBeforeBootstrap( Sapphire::World::Action::Action& action ) + { + } + void ActionScript::onStart( Sapphire::World::Action::Action& action ) { } @@ -89,6 +93,10 @@ namespace Sapphire::ScriptAPI { } + void ActionScript::onAfterBuildEffect( Sapphire::World::Action::Action& action ) + { + } + /////////////////////////////////////////////////////////////////// EventScript::EventScript( uint32_t eventId ) : ScriptObject( eventId, typeid( EventScript ).hash_code() ) diff --git a/src/world/Script/NativeScriptApi.h b/src/world/Script/NativeScriptApi.h index 531a9b40..8fff1ef2 100644 --- a/src/world/Script/NativeScriptApi.h +++ b/src/world/Script/NativeScriptApi.h @@ -133,12 +133,16 @@ namespace Sapphire::ScriptAPI public: explicit ActionScript( uint32_t actionId ); + virtual void onBeforeBootstrap( Sapphire::World::Action::Action& action ); + virtual void onStart( Sapphire::World::Action::Action& action ); virtual void onExecute( Sapphire::World::Action::Action& action ); virtual void onInterrupt( Sapphire::World::Action::Action& action ); + virtual void onAfterBuildEffect( Sapphire::World::Action::Action& action ); + World::Manager::WarpMgr& warpMgr() { return Common::Service< World::Manager::WarpMgr >::ref(); diff --git a/src/world/Script/ScriptMgr.cpp b/src/world/Script/ScriptMgr.cpp index e6d9c1c1..7095b84b 100644 --- a/src/world/Script/ScriptMgr.cpp +++ b/src/world/Script/ScriptMgr.cpp @@ -598,6 +598,18 @@ bool Sapphire::Scripting::ScriptMgr::onExecute( World::Action::Action& action ) return false; } +bool Sapphire::Scripting::ScriptMgr::onAfterBuildEffect( World::Action::Action& action ) +{ + auto script = m_nativeScriptMgr->getScript< Sapphire::ScriptAPI::ActionScript >( action.getId() ); + + if( script ) + { + script->onAfterBuildEffect( action ); + return true; + } + return false; +} + bool Sapphire::Scripting::ScriptMgr::onInterrupt( World::Action::Action& action ) { auto script = m_nativeScriptMgr->getScript< Sapphire::ScriptAPI::ActionScript >( action.getId() ); @@ -610,6 +622,19 @@ bool Sapphire::Scripting::ScriptMgr::onInterrupt( World::Action::Action& action return false; } +bool Sapphire::Scripting::ScriptMgr::onBeforeBootstrap( World::Action::Action& action ) +{ + auto script = m_nativeScriptMgr->getScript< Sapphire::ScriptAPI::ActionScript >( action.getId() ); + + if( script ) + { + script->onBeforeBootstrap( action ); + return true; + } + + return false; +} + bool Sapphire::Scripting::ScriptMgr::onStart( World::Action::Action& action ) { auto script = m_nativeScriptMgr->getScript< Sapphire::ScriptAPI::ActionScript >( action.getId() ); diff --git a/src/world/Script/ScriptMgr.h b/src/world/Script/ScriptMgr.h index 6e2a85f1..1429b432 100644 --- a/src/world/Script/ScriptMgr.h +++ b/src/world/Script/ScriptMgr.h @@ -78,12 +78,16 @@ namespace Sapphire::Scripting bool onEObjHit( Entity::Player& player, uint64_t actorId, uint32_t actionId ); + bool onBeforeBootstrap( World::Action::Action& action ); + bool onStart( World::Action::Action& action ); bool onInterrupt( World::Action::Action& action ); bool onExecute( World::Action::Action& action ); + bool onAfterBuildEffect( World::Action::Action& action ); + bool onStatusReceive( Entity::CharaPtr pActor, uint32_t effectId ); bool onStatusTick( Entity::CharaPtr pActor, Sapphire::StatusEffect::StatusEffect& effect );