From 542f37fb3d0d1113213eb28706101dbee37cd2e6 Mon Sep 17 00:00:00 2001 From: collett Date: Sun, 12 Mar 2023 00:39:46 +0900 Subject: [PATCH] allow bnpc to perform actions --- src/world/Action/Action.cpp | 22 ++--- src/world/Actor/BNpc.cpp | 98 +++++++++++--------- src/world/Actor/BNpc.h | 1 + src/world/Manager/ActionMgr.cpp | 47 ++++++---- src/world/Manager/ActionMgr.h | 6 +- src/world/Network/Handlers/ActionHandler.cpp | 4 +- 6 files changed, 97 insertions(+), 81 deletions(-) diff --git a/src/world/Action/Action.cpp b/src/world/Action/Action.cpp index 5b068d83..a6e87202 100644 --- a/src/world/Action/Action.cpp +++ b/src/world/Action/Action.cpp @@ -401,7 +401,7 @@ void Action::Action::execute() assert( m_pSource ); // subtract costs first, if somehow the caster stops meeting those requirements cancel the cast - if( !consumeResources() ) + if( m_pSource->getAsPlayer() && !consumeResources() ) { interrupt(); return; @@ -422,13 +422,6 @@ void Action::Action::execute() } } - if( isCorrectCombo() ) - { - auto player = m_pSource->getAsPlayer(); - - player->sendDebug( "action combo success from action#{0}", player->getLastComboActionId() ); - } - if( !hasClientsideTarget() ) { buildEffects(); @@ -638,14 +631,11 @@ void Action::Action::buildEffects() if( isCorrectCombo() ) m_effectBuilder->comboSucceed( actor ); - if( m_isAutoAttack && m_pSource->isPlayer() ) + if( m_isAutoAttack && player ) { - if( auto player = m_pSource->getAsPlayer() ) + if( player->getClass() == Common::ClassJob::Paladin ) { - if( player->getClass() == Common::ClassJob::Paladin ) - { - player->gaugePldSetOath( std::min( 100, player->gaugePldGetOath() + 5 ) ); - } + player->gaugePldSetOath( std::min( 100, player->gaugePldGetOath() + 5 ) ); } } @@ -655,7 +645,7 @@ void Action::Action::buildEffects() m_effectBuilder->restoreMP( actor, m_pSource, m_pSource->getMaxMp() * m_lutEntry.getMPGainPercentage() / 100, Common::ActionEffectResultFlag::EffectOnSource ); } - if( m_lutEntry.bonusEffect & Common::ActionBonusEffect::GainJobResource ) + if( ( m_lutEntry.bonusEffect & Common::ActionBonusEffect::GainJobResource ) && player ) { if( checkActionBonusRequirement() ) { @@ -686,7 +676,7 @@ void Action::Action::buildEffects() } } - if( m_lutEntry.bonusEffect & Common::ActionBonusEffect::GainJobTimer ) + if( ( m_lutEntry.bonusEffect & Common::ActionBonusEffect::GainJobTimer ) && player ) { if( checkActionBonusRequirement() ) { diff --git a/src/world/Actor/BNpc.cpp b/src/world/Actor/BNpc.cpp index 18f44bd9..713c3472 100644 --- a/src/world/Actor/BNpc.cpp +++ b/src/world/Actor/BNpc.cpp @@ -40,6 +40,7 @@ #include #include #include +#include #include using namespace Sapphire::Common; @@ -407,6 +408,13 @@ void Sapphire::Entity::BNpc::update( uint64_t tickCount ) const uint8_t maxDistanceToOrigin = 40; const uint32_t roamTick = 20; + if( getCurrentAction() && getCurrentAction()->hasCastTime() ) + { + if( m_pCurrentAction->update() ) + m_pCurrentAction = nullptr; + return; + } + auto pNaviProvider = m_pCurrentTerritory->getNaviProvider(); if( !pNaviProvider ) @@ -485,66 +493,63 @@ void Sapphire::Entity::BNpc::update( uint64_t tickCount ) case BNpcState::Combat: { - auto pHatedActor = hateListGetHighest(); - if( !pHatedActor ) - return; - pNaviProvider->updateAgentParameters( *this ); auto distanceOrig = Util::distance( getPos().x, getPos().y, getPos().z, m_spawnPos.x, m_spawnPos.y, m_spawnPos.z ); - if( pHatedActor && !pHatedActor->isAlive() ) + auto pHatedActor = hateListGetHighest(); + + while( pHatedActor && !pHatedActor->isAlive() ) { hateListRemove( pHatedActor ); pHatedActor = hateListGetHighest(); } + if( !pHatedActor ) + { + hateListClear(); + changeTarget( INVALID_GAME_OBJECT_ID64 ); + setStance( Stance::Passive ); + setOwner( nullptr ); + m_state = BNpcState::Retreat; + return; + } + if( pNaviProvider->syncPosToChara( *this ) ) sendPositionUpdate(); - if( pHatedActor ) - { - auto distance = Util::distance( getPos().x, getPos().y, getPos().z, - pHatedActor->getPos().x, pHatedActor->getPos().y, pHatedActor->getPos().z ); - - if( !hasFlag( NoDeaggro ) && ( distanceOrig > maxDistanceToOrigin ) ) - { - hateListClear(); - changeTarget( INVALID_GAME_OBJECT_ID64 ); - setStance( Stance::Passive ); - setOwner( nullptr ); - m_state = BNpcState::Retreat; - break; - } - - if( distance > ( getRadius() + pHatedActor->getRadius() ) ) - { - if( hasFlag( Immobile ) ) - break; - - if( pNaviProvider ) - pNaviProvider->setMoveTarget( *this, pHatedActor->getPos() ); - - moveTo( *pHatedActor ); - } - - if( distance < ( getRadius() + pHatedActor->getRadius() + 3.f ) ) - { - if( !hasFlag( TurningDisabled ) && face( pHatedActor->getPos() ) ) - sendPositionUpdate(); - - // in combat range. ATTACK! - autoAttack( pHatedActor ); - } - } - else + auto distance = Util::distance( getPos().x, getPos().y, getPos().z, + pHatedActor->getPos().x, pHatedActor->getPos().y, pHatedActor->getPos().z ); + + if( !hasFlag( NoDeaggro ) && ( distanceOrig > maxDistanceToOrigin ) ) { + hateListClear(); changeTarget( INVALID_GAME_OBJECT_ID64 ); setStance( Stance::Passive ); - //setOwner( nullptr ); + setOwner( nullptr ); m_state = BNpcState::Retreat; - pNaviProvider->updateAgentParameters( *this ); + break; + } + + if( distance > ( getRadius() + pHatedActor->getRadius() ) ) + { + if( hasFlag( Immobile ) ) + break; + + if( pNaviProvider ) + pNaviProvider->setMoveTarget( *this, pHatedActor->getPos() ); + + moveTo( *pHatedActor ); + } + + if( distance < ( getRadius() + pHatedActor->getRadius() + 3.f ) ) + { + if( !hasFlag( TurningDisabled ) && face( pHatedActor->getPos() ) ) + sendPositionUpdate(); + + // in combat range. ATTACK! + autoAttack( pHatedActor ); } } } @@ -653,7 +658,7 @@ void Sapphire::Entity::BNpc::setOwner( Sapphire::Entity::CharaPtr m_pChara ) else { auto setOwnerPacket = makeZonePacket< FFXIVIpcActorOwner >( getId() ); - setOwnerPacket->data().type = 0x01; + setOwnerPacket->data().type = 0x00; setOwnerPacket->data().actorId = static_cast< uint32_t >( INVALID_GAME_OBJECT_ID ); sendToInRangeSet( setOwnerPacket ); } @@ -679,6 +684,11 @@ void Sapphire::Entity::BNpc::setFlag( uint32_t flag ) m_flags |= flag; } +void Sapphire::Entity::BNpc::unsetFlag( uint32_t flag ) +{ + m_flags = m_flags & ( 0xFFFFFFFF - flag ); +} + /*! TODO: this only solves attacks from melee classes. Will have to be extended for ranged attacks. diff --git a/src/world/Actor/BNpc.h b/src/world/Actor/BNpc.h index aea42d48..12e084a9 100644 --- a/src/world/Actor/BNpc.h +++ b/src/world/Actor/BNpc.h @@ -117,6 +117,7 @@ namespace Sapphire::Entity bool hasFlag( uint32_t flag ) const; void setFlag( uint32_t flags ); + void unsetFlag( uint32_t flag ); void calculateStats() override; diff --git a/src/world/Manager/ActionMgr.cpp b/src/world/Manager/ActionMgr.cpp index 7a4c770b..240d81e4 100644 --- a/src/world/Manager/ActionMgr.cpp +++ b/src/world/Manager/ActionMgr.cpp @@ -6,7 +6,7 @@ #include "Action/MountAction.h" #include "Script/ScriptMgr.h" #include "Actor/Player.h" - +#include "Service.h" #include "StatusEffect/StatusEffect.h" #include @@ -15,14 +15,15 @@ using namespace Sapphire; -void World::Manager::ActionMgr::handlePlacedPlayerAction( Entity::Player& player, uint32_t actionId, +void World::Manager::ActionMgr::handlePlacedAction( Entity::Chara& chara, uint32_t actionId, Data::ActionPtr actionData, Common::FFXIVARR_POSITION3 pos, uint16_t sequence ) { - player.sendDebug( "got aoe act: {0}", actionData->name ); + if( auto player = chara.getAsPlayer() ) + player->sendDebug( "got aoe act: {0}", actionData->name ); - auto action = Action::make_Action( player.getAsPlayer(), actionId, sequence, actionData ); + auto action = Action::make_Action( chara.getAsChara(), actionId, sequence, actionData ); action->setPos( pos ); @@ -36,18 +37,30 @@ void World::Manager::ActionMgr::handlePlacedPlayerAction( Entity::Player& player return; } - bootstrapAction( player, action, *actionData ); + bootstrapAction( chara, action, *actionData ); } -void World::Manager::ActionMgr::handleTargetedPlayerAction( Entity::Player& player, uint32_t actionId, +void World::Manager::ActionMgr::handleTargetedAction( Entity::Chara& chara, uint32_t actionId, Data::ActionPtr actionData, uint64_t targetId, uint16_t sequence ) { - auto action = Action::make_Action( player.getAsPlayer(), actionId, sequence, actionData ); + if( !actionData ) + { + actionData = Common::Service< Data::ExdDataGenerated >::ref().get< Data::Action >( actionId ); + } + + if( !actionData ) + { + if( auto player = chara.getAsPlayer() ) + player->sendUrgent( "Cannot find action {}.", actionId ); + return; + } + + auto action = Action::make_Action( chara.getAsChara(), actionId, sequence, actionData ); action->setTargetId( targetId ); - action->setPos( player.getPos() ); + action->setPos( chara.getPos() ); if( !action->init() ) return; @@ -59,7 +72,7 @@ void World::Manager::ActionMgr::handleTargetedPlayerAction( Entity::Player& play return; } - bootstrapAction( player, action, *actionData ); + bootstrapAction( chara, action, *actionData ); } void World::Manager::ActionMgr::handleItemAction( Sapphire::Entity::Player& player, uint32_t itemId, @@ -105,11 +118,11 @@ void World::Manager::ActionMgr::handleMountAction( Entity::Player& player, uint1 bootstrapAction( player, action, *actionData ); } -void World::Manager::ActionMgr::bootstrapAction( Entity::Player& player, +void World::Manager::ActionMgr::bootstrapAction( Entity::Chara& chara, Action::ActionPtr currentAction, Data::Action& actionData ) { - for( const auto& statusIt : player.getStatusEffectMap() ) + for( const auto& statusIt : chara.getStatusEffectMap() ) { statusIt.second->onBeforeActionStart( currentAction.get() ); } @@ -119,23 +132,25 @@ void World::Manager::ActionMgr::bootstrapAction( Entity::Player& player, if( !currentAction->preCheck() ) { - player.sendDebug( "preCheck failed" ); // forcefully interrupt the action and reset the cooldown currentAction->interrupt(); return; } - if( player.getCurrentAction() ) + if( chara.getCurrentAction() ) { - player.sendDebug( "Skill queued: {0}", currentAction->getId() ); - player.setQueuedAction( currentAction ); + if( auto player = chara.getAsPlayer() ) + { + player->sendDebug( "Skill queued: {0}", currentAction->getId() ); + player->setQueuedAction( currentAction ); + } } else { // if we have a cast time we want to associate the action with the player so update is called if( currentAction->hasCastTime() ) { - player.setCurrentAction( currentAction ); + chara.setCurrentAction( currentAction ); } // todo: what do in cases of swiftcast/etc? script callback? diff --git a/src/world/Manager/ActionMgr.h b/src/world/Manager/ActionMgr.h index 5dc0a7bc..9a651056 100644 --- a/src/world/Manager/ActionMgr.h +++ b/src/world/Manager/ActionMgr.h @@ -23,9 +23,9 @@ namespace Sapphire::World::Manager ActionMgr() = default; ~ActionMgr() = default; - void handleTargetedPlayerAction( Entity::Player& player, uint32_t actionId, + void handleTargetedAction( Entity::Chara& chara, uint32_t actionId, Data::ActionPtr actionData, uint64_t targetId, uint16_t sequence ); - void handlePlacedPlayerAction( Entity::Player& player, uint32_t actionId, + void handlePlacedAction( Entity::Chara& chara, uint32_t actionId, Data::ActionPtr actionData, Common::FFXIVARR_POSITION3 pos, uint16_t sequence ); void handleItemAction( Entity::Player& player, uint32_t itemId, Data::ItemActionPtr itemActionData, @@ -37,7 +37,7 @@ namespace Sapphire::World::Manager Data::ActionPtr actionData, uint64_t targetId, uint16_t sequence ); private: - void bootstrapAction( Entity::Player& player, Action::ActionPtr currentAction, Data::Action& actionData ); + void bootstrapAction( Entity::Chara& chara, Action::ActionPtr currentAction, Data::Action& actionData ); // item action handlers void handleItemActionVFX( Entity::Player& player, uint32_t itemId, uint16_t vfxId ); diff --git a/src/world/Network/Handlers/ActionHandler.cpp b/src/world/Network/Handlers/ActionHandler.cpp index 4576a0a8..29a0a02d 100644 --- a/src/world/Network/Handlers/ActionHandler.cpp +++ b/src/world/Network/Handlers/ActionHandler.cpp @@ -45,7 +45,7 @@ void Sapphire::Network::GameConnection::actionHandler( const Packets::FFXIVARR_P if( !action ) return; - actionMgr.handleTargetedPlayerAction( player, actionId, action, targetId, sequence ); + actionMgr.handleTargetedAction( player, actionId, action, targetId, sequence ); break; } @@ -117,5 +117,5 @@ void Sapphire::Network::GameConnection::placedActionHandler( const Packets::FFXI return; auto& actionMgr = Common::Service< World::Manager::ActionMgr >::ref(); - actionMgr.handlePlacedPlayerAction( player, actionId, action, pos, sequence ); + actionMgr.handlePlacedAction( player, actionId, action, pos, sequence ); }