diff --git a/src/scripts/instances/questbattles/ChasingShadows.cpp b/src/scripts/instances/questbattles/ChasingShadows.cpp index 8963ff11..8fb7740f 100644 --- a/src/scripts/instances/questbattles/ChasingShadows.cpp +++ b/src/scripts/instances/questbattles/ChasingShadows.cpp @@ -84,10 +84,10 @@ public: a2->setFlag( Entity::NoDeaggro ); a3->setFlag( Entity::NoDeaggro ); - a2->hateListAdd( ida, 10000 ); - a3->hateListAdd( ida, 10000 ); - a2->hateListAdd( papa, 10000 ); - a3->hateListAdd( papa, 10000 ); + a2->hateListAddOrUpdate( ida, 10000 ); + a3->hateListAddOrUpdate( ida, 10000 ); + a2->hateListAddOrUpdate( papa, 10000 ); + a3->hateListAddOrUpdate( papa, 10000 ); auto a4 = instance.createBNpcFromLevelEntry( INIT_POP_ENEMY_A_01, 5, 0, 300, 937, @@ -98,8 +98,8 @@ public: a5->setFlag( Entity::NoDeaggro ); auto pPlayer = instance.getPlayerPtr(); - a4->hateListAdd( pPlayer, 1 ); - a5->hateListAdd( pPlayer, 1 ); + a4->hateListAddOrUpdate( pPlayer, 1 ); + a5->hateListAddOrUpdate( pPlayer, 1 ); } if( pair2Spawnd == 0 && bossHpPercent <= 50 ) @@ -112,10 +112,10 @@ public: a2->setFlag( Entity::NoDeaggro ); a3->setFlag( Entity::NoDeaggro ); - a2->hateListAdd( ida, 10000 ); - a3->hateListAdd( ida, 10000 ); - a2->hateListAdd( papa, 10000 ); - a3->hateListAdd( papa, 10000 ); + a2->hateListAddOrUpdate( ida, 10000 ); + a3->hateListAddOrUpdate( ida, 10000 ); + a2->hateListAddOrUpdate( papa, 10000 ); + a3->hateListAddOrUpdate( papa, 10000 ); auto a4 = instance.createBNpcFromLevelEntry( INIT_POP_ENEMY_A_03, 5, 0, 300, 937, instance.getDirectorId(), Common::BNpcType::Enemy ); @@ -125,8 +125,8 @@ public: a5->setFlag( Entity::NoDeaggro ); auto pPlayer = instance.getPlayerPtr(); - a4->hateListAdd( pPlayer, 1 ); - a5->hateListAdd( pPlayer, 1 ); + a4->hateListAddOrUpdate( pPlayer, 1 ); + a5->hateListAddOrUpdate( pPlayer, 1 ); } @@ -142,8 +142,8 @@ public: a5->setFlag( Entity::NoDeaggro ); auto pPlayer = instance.getPlayerPtr(); - a4->hateListAdd( pPlayer, 1 ); - a5->hateListAdd( pPlayer, 1 ); + a4->hateListAddOrUpdate( pPlayer, 1 ); + a5->hateListAddOrUpdate( pPlayer, 1 ); } if( instance.getCountEnemyBNpc() == 0 && successCalled == 0 ) @@ -189,24 +189,24 @@ public: a3->setFlag( Entity::NoDeaggro ); a4->setFlag( Entity::NoDeaggro ); a5->setFlag( Entity::NoDeaggro ); - a1->hateListAdd( a4, 10000 ); - a1->hateListAdd( a5, 10000 ); + a1->hateListAddOrUpdate( a4, 10000 ); + a1->hateListAddOrUpdate( a5, 10000 ); - a2->hateListAdd( player.getAsPlayer(), 1 ); - a2->hateListAdd( a4, 10000 ); - a2->hateListAdd( a5, 10000 ); + a2->hateListAddOrUpdate( player.getAsPlayer(), 1 ); + a2->hateListAddOrUpdate( a4, 10000 ); + a2->hateListAddOrUpdate( a5, 10000 ); - a3->hateListAdd( player.getAsPlayer(), 1 ); - a3->hateListAdd( a4, 10000 ); - a3->hateListAdd( a5, 10000 ); + a3->hateListAddOrUpdate( player.getAsPlayer(), 1 ); + a3->hateListAddOrUpdate( a4, 10000 ); + a3->hateListAddOrUpdate( a5, 10000 ); - a4->hateListAdd( a1, 10000 ); - a4->hateListAdd( a2, 9999 ); - a4->hateListAdd( a3, 9999 ); + a4->hateListAddOrUpdate( a1, 10000 ); + a4->hateListAddOrUpdate( a2, 9999 ); + a4->hateListAddOrUpdate( a3, 9999 ); - a5->hateListAdd( a1, 10000 ); - a5->hateListAdd( a2, 9999 ); - a5->hateListAdd( a3, 9999 ); + a5->hateListAddOrUpdate( a1, 10000 ); + a5->hateListAddOrUpdate( a2, 9999 ); + a5->hateListAddOrUpdate( a3, 9999 ); } }; 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/Action/EffectResult.cpp b/src/world/Action/EffectResult.cpp index a031a5fd..2147633e 100644 --- a/src/world/Action/EffectResult.cpp +++ b/src/world/Action/EffectResult.cpp @@ -45,6 +45,11 @@ uint32_t EffectResult::getValue() const return m_value; } +Common::ActionEffectType Sapphire::World::Action::EffectResult::getType() const +{ + return m_type; +} + uint64_t EffectResult::getDelay() { return m_delayMs; diff --git a/src/world/Action/EffectResult.h b/src/world/Action/EffectResult.h index 33160e6d..2ab53c69 100644 --- a/src/world/Action/EffectResult.h +++ b/src/world/Action/EffectResult.h @@ -37,7 +37,7 @@ namespace Sapphire::World::Action Entity::CharaPtr getTarget() const; uint32_t getValue() const; - + Common::ActionEffectType getType() const; uint64_t getDelay(); Common::EffectEntry buildEffectEntry() const; diff --git a/src/world/Actor/BNpc.cpp b/src/world/Actor/BNpc.cpp index 18f44bd9..a9602057 100644 --- a/src/world/Actor/BNpc.cpp +++ b/src/world/Actor/BNpc.cpp @@ -33,6 +33,7 @@ #include "Player.h" #include "BNpc.h" #include "BNpcTemplate.h" +#include "Script/ScriptMgr.h" #include "Common.h" @@ -40,6 +41,7 @@ #include #include #include +#include #include using namespace Sapphire::Common; @@ -119,6 +121,8 @@ Sapphire::Entity::BNpc::BNpc( uint32_t id, BNpcTemplatePtr pTemplate, float posX m_naviTargetReachedDistance = 4.f; calculateStats(); + auto& scriptMgr = Common::Service< Sapphire::Scripting::ScriptMgr >::ref(); + scriptMgr.onBNpcInit( *this ); } Sapphire::Entity::BNpc::~BNpc() = default; @@ -254,6 +258,15 @@ bool Sapphire::Entity::BNpc::moveTo( const Entity::Chara& targetChara ) return false; } +void Sapphire::Entity::BNpc::stopMoving() +{ + auto pNaviProvider = m_pCurrentTerritory->getNaviProvider(); + if( !pNaviProvider ) + return; + sendPositionUpdate(); + pNaviProvider->updateAgentPosition( *this ); +} + void Sapphire::Entity::BNpc::sendPositionUpdate() { uint8_t unk1 = 0x3a; @@ -268,13 +281,14 @@ void Sapphire::Entity::BNpc::sendPositionUpdate() void Sapphire::Entity::BNpc::hateListClear() { - auto it = m_hateList.begin(); - for( auto& listEntry : m_hateList ) + if( m_hateList.empty() ) + return; + + auto hateListCopy = m_hateList; + for( auto& listEntry : hateListCopy ) { - if( isInRangeSet( listEntry->m_pChara ) ) - deaggro( listEntry->m_pChara ); + deaggro( listEntry->m_pChara ); } - m_hateList.clear(); } Sapphire::Entity::CharaPtr Sapphire::Entity::BNpc::hateListGetHighest() @@ -297,25 +311,11 @@ Sapphire::Entity::CharaPtr Sapphire::Entity::BNpc::hateListGetHighest() return nullptr; } -void Sapphire::Entity::BNpc::hateListAdd( Sapphire::Entity::CharaPtr pChara, int32_t hateAmount ) -{ - auto hateEntry = std::make_shared< HateListEntry >(); - hateEntry->m_hateAmount = static_cast< uint32_t >( hateAmount ); - hateEntry->m_pChara = pChara; - - m_hateList.insert( hateEntry ); - if( pChara->isPlayer() ) - { - auto pPlayer = pChara->getAsPlayer(); - pPlayer->hateListAdd( getAsBNpc() ); - } -} - -void Sapphire::Entity::BNpc::hateListUpdate( Sapphire::Entity::CharaPtr pChara, int32_t hateAmount ) +void Sapphire::Entity::BNpc::hateListAddOrUpdate( Sapphire::Entity::CharaPtr pChara, int32_t hateAmount ) { for( auto listEntry : m_hateList ) { - if( listEntry->m_pChara == pChara ) + if( listEntry->m_pChara->getId() == pChara->getId() ) { listEntry->m_hateAmount += static_cast< uint32_t >( hateAmount ); return; @@ -326,21 +326,29 @@ void Sapphire::Entity::BNpc::hateListUpdate( Sapphire::Entity::CharaPtr pChara, hateEntry->m_hateAmount = static_cast< uint32_t >( hateAmount ); hateEntry->m_pChara = pChara; m_hateList.insert( hateEntry ); + if( pChara->isPlayer() ) + { + auto pPlayer = pChara->getAsPlayer(); + pPlayer->hateListAdd( getAsBNpc() ); + } + auto& scriptMgr = Common::Service< Sapphire::Scripting::ScriptMgr >::ref(); + scriptMgr.onBNpcHateListAdd( *this, *pChara ); } void Sapphire::Entity::BNpc::hateListRemove( Sapphire::Entity::CharaPtr pChara ) { for( auto listEntry : m_hateList ) { - if( listEntry->m_pChara == pChara ) + if( listEntry->m_pChara->getId() == pChara->getId() ) { - m_hateList.erase( listEntry ); if( pChara->isPlayer() ) { PlayerPtr tmpPlayer = pChara->getAsPlayer(); tmpPlayer->onMobDeaggro( getAsBNpc() ); } + auto& scriptMgr = Common::Service< Sapphire::Scripting::ScriptMgr >::ref(); + scriptMgr.onBNpcHateListRemove( *this, *pChara ); return; } } @@ -362,34 +370,28 @@ void Sapphire::Entity::BNpc::aggro( Sapphire::Entity::CharaPtr pChara ) auto variation = static_cast< uint32_t >( pRNGMgr.getRandGenerator< float >( 500, 1000 ).next() ); m_lastAttack = Util::getTimeMs() + variation; - hateListUpdate( pChara, 1 ); changeTarget( pChara->getId() ); setStance( Stance::Active ); m_state = BNpcState::Combat; - sendToInRangeSet( makeActorControl( getId(), ActorControlType::ToggleWeapon, 1, 1, 0 ) ); - sendToInRangeSet( makeActorControl( getId(), ActorControlType::ToggleAggro, 1, 0, 0 ) ); - - if( pChara->isPlayer() ) + if( m_hateList.empty() ) { - PlayerPtr tmpPlayer = pChara->getAsPlayer(); - tmpPlayer->onMobAggro( getAsBNpc() ); + sendToInRangeSet( makeActorControl( getId(), ActorControlType::ToggleWeapon, 1, 1, 0 ) ); + sendToInRangeSet( makeActorControl( getId(), ActorControlType::ToggleAggro, 1, 0, 0 ) ); } + hateListAddOrUpdate( pChara, 1 ); } void Sapphire::Entity::BNpc::deaggro( Sapphire::Entity::CharaPtr pChara ) { - if( !hateListHasActor( pChara ) ) - hateListRemove( pChara ); + hateListRemove( pChara ); - if( pChara->isPlayer() ) + if( m_hateList.empty() ) { - PlayerPtr tmpPlayer = pChara->getAsPlayer(); sendToInRangeSet( makeActorControl( getId(), ActorControlType::ToggleWeapon, 0, 1, 1 ) ); sendToInRangeSet( makeActorControl( getId(), ActorControlType::ToggleAggro, 0, 0, 0 ) ); - tmpPlayer->onMobDeaggro( getAsBNpc() ); } } @@ -403,6 +405,22 @@ void Sapphire::Entity::BNpc::onTick() } void Sapphire::Entity::BNpc::update( uint64_t tickCount ) +{ + if( getCurrentAction() && getCurrentAction()->hasCastTime() ) + { + if( m_pCurrentAction->update() ) + m_pCurrentAction = nullptr; + return; + } + + auto& scriptMgr = Common::Service< Sapphire::Scripting::ScriptMgr >::ref(); + if( !scriptMgr.onBNpcUpdate( *this, tickCount ) ) + doDefaultBNpcUpdate( tickCount ); + + Chara::update( tickCount ); +} + +void Sapphire::Entity::BNpc::doDefaultBNpcUpdate( uint64_t tickCount ) { const uint8_t maxDistanceToOrigin = 40; const uint32_t roamTick = 20; @@ -482,75 +500,70 @@ void Sapphire::Entity::BNpc::update( uint64_t tickCount ) checkAggro(); } + break; 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 ); + 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 ); } } } - - - Chara::update( tickCount ); } void Sapphire::Entity::BNpc::regainHp() @@ -585,6 +598,9 @@ void Sapphire::Entity::BNpc::onDeath() m_timeOfDeath = Util::getTimeSeconds(); setOwner( nullptr ); + auto& scriptMgr = Common::Service< Sapphire::Scripting::ScriptMgr >::ref(); + scriptMgr.onBNpcDeath( *this ); + for( auto& pHateEntry : m_hateList ) { // TODO: handle drops @@ -642,6 +658,8 @@ void Sapphire::Entity::BNpc::checkAggro() void Sapphire::Entity::BNpc::setOwner( Sapphire::Entity::CharaPtr m_pChara ) { + if( ( !m_pOwner && !m_pChara ) || ( m_pOwner && m_pChara && m_pOwner->getId() == m_pChara->getId() ) ) + return; m_pOwner = m_pChara; if( m_pChara != nullptr ) { @@ -653,7 +671,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 +697,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. @@ -744,4 +767,17 @@ void Sapphire::Entity::BNpc::calculateStats() m_baseStats.attack = m_baseStats.str; m_baseStats.attackPotMagic = m_baseStats.inte; m_baseStats.healingPotMagic = m_baseStats.mnd; -} \ No newline at end of file +} + +void Sapphire::Entity::BNpc::setCustomVar( uint32_t varId, uint64_t value ) +{ + m_customVarMap[ varId ] = value; +} + +uint64_t Sapphire::Entity::BNpc::getCustomVar( uint32_t varId ) +{ + auto it = m_customVarMap.find( varId ); + if( it != m_customVarMap.end() ) + return it->second; + return 0; +} diff --git a/src/world/Actor/BNpc.h b/src/world/Actor/BNpc.h index aea42d48..7e693a1c 100644 --- a/src/world/Actor/BNpc.h +++ b/src/world/Actor/BNpc.h @@ -79,6 +79,8 @@ namespace Sapphire::Entity bool moveTo( const Entity::Chara& targetChara ); + void stopMoving(); + void sendPositionUpdate(); BNpcState getState() const; @@ -86,8 +88,7 @@ namespace Sapphire::Entity void hateListClear(); CharaPtr hateListGetHighest(); - void hateListAdd( CharaPtr pChara, int32_t hateAmount ); - void hateListUpdate( CharaPtr pChara, int32_t hateAmount ); + void hateListAddOrUpdate( CharaPtr pChara, int32_t hateAmount ); void hateListRemove( CharaPtr pChara ); bool hateListHasActor( CharaPtr pChara ); @@ -95,6 +96,7 @@ namespace Sapphire::Entity void deaggro( CharaPtr pChara ); void update( uint64_t tickCount ) override; + void doDefaultBNpcUpdate( uint64_t tickCount ); void onTick() override; void onActionHostile( CharaPtr pSource ) override; @@ -117,9 +119,13 @@ namespace Sapphire::Entity bool hasFlag( uint32_t flag ) const; void setFlag( uint32_t flags ); + void unsetFlag( uint32_t flag ); void calculateStats() override; + void setCustomVar( uint32_t varId, uint64_t value ); + uint64_t getCustomVar( uint32_t varId ); + private: uint32_t m_bNpcBaseId; uint32_t m_bNpcNameId; @@ -154,6 +160,7 @@ namespace Sapphire::Entity CharaPtr m_pOwner; + std::unordered_map< uint32_t, uint64_t > m_customVarMap; }; } diff --git a/src/world/Actor/Chara.cpp b/src/world/Actor/Chara.cpp index cdc7c414..61dcac63 100644 --- a/src/world/Actor/Chara.cpp +++ b/src/world/Actor/Chara.cpp @@ -374,6 +374,8 @@ Change the current target and propagate to in range players */ void Sapphire::Entity::Chara::changeTarget( uint64_t targetId ) { + if( m_targetId == targetId ) + return; setTargetId( targetId ); FFXIVPacketBasePtr packet = makeActorControlTarget( m_id, SetTarget, 0, 0, 0, 0, targetId ); sendToInRangeSet( packet ); diff --git a/src/world/Actor/Player.cpp b/src/world/Actor/Player.cpp index b619c62b..ccb77f32 100644 --- a/src/world/Actor/Player.cpp +++ b/src/world/Actor/Player.cpp @@ -1035,14 +1035,16 @@ void Sapphire::Entity::Player::despawn( Entity::PlayerPtr pTarget ) Sapphire::Entity::ActorPtr Sapphire::Entity::Player::lookupTargetById( uint64_t targetId ) { - ActorPtr targetActor; + if( targetId == 0 || targetId == INVALID_GAME_OBJECT_ID64 ) + return nullptr; + auto inRange = getInRangeActors( true ); for( auto actor : inRange ) { if( actor->getId() == targetId ) - targetActor = actor; + return actor; } - return targetActor; + return nullptr; } void Sapphire::Entity::Player::setLastPing( uint32_t ping ) diff --git a/src/world/Manager/ActionMgr.cpp b/src/world/Manager/ActionMgr.cpp index 7a4c770b..07aecd30 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,19 @@ 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 ); + if( !actionData ) + { + actionData = Common::Service< Data::ExdDataGenerated >::ref().get< Data::Action >( actionId ); + } - 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 +41,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 +76,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 +122,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 +136,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/Manager/DebugCommandMgr.cpp b/src/world/Manager/DebugCommandMgr.cpp index 044bc7f2..3550b4e8 100644 --- a/src/world/Manager/DebugCommandMgr.cpp +++ b/src/world/Manager/DebugCommandMgr.cpp @@ -28,6 +28,7 @@ #include "Script/ScriptMgr.h" #include "Script/NativeScriptMgr.h" +#include "Actor/Actor.h" #include "Actor/EventObject.h" #include "Actor/BNpc.h" @@ -38,6 +39,7 @@ #include "Territory/PublicContent.h" #include "Territory/InstanceObjectCache.h" #include "Manager/TerritoryMgr.h" +#include "Manager/ActionMgr.h" #include "Event/EventDefs.h" #include "ServerMgr.h" @@ -466,6 +468,28 @@ void Sapphire::World::Manager::DebugCommandMgr::set( char* data, Entity::Player& } player.setVisualEffect( id ); } + else if( subCommand == "bnpccast" || subCommand == "bcast" ) + { + if( auto target = player.lookupTargetById( player.getTargetId() ) ) + { + if( auto bnpc = target->getAsBNpc() ) + { + int32_t id; + sscanf( params.c_str(), "%d", &id ); + auto actionData = Common::Service< Data::ExdDataGenerated >::ref().get< Data::Action >( id ); + if ( !actionData ) + return; + if( !actionData->targetArea && bnpc->getTargetId() != 0 && bnpc->getTargetId() != INVALID_GAME_OBJECT_ID64 ) + { + Service< World::Manager::ActionMgr >::ref().handleTargetedAction( *bnpc, id, actionData, bnpc->getTargetId(), 0 ); + } + else if ( actionData->targetArea ) + { + Service< World::Manager::ActionMgr >::ref().handlePlacedAction( *bnpc, id, actionData, player.getPos(), 0 ); + } + } + } + } else { player.sendUrgent( "{0} is not a valid SET command.", subCommand ); @@ -696,6 +720,17 @@ void Sapphire::World::Manager::DebugCommandMgr::get( char* data, Entity::Player& player.sendNotice( "x:{}, y:{}, z:{}", pPopRange->header.transform.translation.x, pPopRange->header.transform.translation.y, pPopRange->header.transform.translation.z ); } } + else if ( ( subCommand == "targetinfo" ) || ( subCommand == "tinfo" ) || subCommand == "ti" ) + { + if( auto target = player.lookupTargetById( player.getTargetId() ) ) + { + if( auto bnpc = target->getAsBNpc() ) + { + player.sendNotice( "BNpcBaseId: {}, BNpcNameId, {}", bnpc->getBNpcBaseId(), bnpc->getBNpcNameId() ); + } + //else if + } + } else { player.sendUrgent( "{0} is not a valid GET command.", subCommand ); 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 ); } diff --git a/src/world/Script/NativeScriptApi.cpp b/src/world/Script/NativeScriptApi.cpp index 0e5624a4..5fdb304c 100644 --- a/src/world/Script/NativeScriptApi.cpp +++ b/src/world/Script/NativeScriptApi.cpp @@ -172,6 +172,26 @@ namespace Sapphire::ScriptAPI { } + void BattleNpcScript::onInit( Entity::BNpc& bnpc ) + { + } + + void BattleNpcScript::onUpdate( Entity::BNpc& bnpc, uint64_t tickCount ) + { + } + + void BattleNpcScript::onHateListAdd( Entity::BNpc& bnpc, Entity::Chara& target ) + { + } + + void BattleNpcScript::onHateListRemove( Entity::BNpc& bnpc, Entity::Chara& target ) + { + } + + void BattleNpcScript::onDeath( Entity::BNpc& bnpc ) + { + } + /////////////////////////////////////////////////////////////////// ZoneScript::ZoneScript( uint32_t zoneId ) : diff --git a/src/world/Script/NativeScriptApi.h b/src/world/Script/NativeScriptApi.h index fa8f46fe..98be4c5f 100644 --- a/src/world/Script/NativeScriptApi.h +++ b/src/world/Script/NativeScriptApi.h @@ -201,6 +201,16 @@ namespace Sapphire::ScriptAPI { public: explicit BattleNpcScript( uint32_t npcId ); + + virtual void onInit( Entity::BNpc& bnpc ); + + virtual void onUpdate( Entity::BNpc& bnpc, uint64_t tickCount ); + + virtual void onHateListAdd( Entity::BNpc& bnpc, Entity::Chara& target ); + + virtual void onHateListRemove( Entity::BNpc& bnpc, Entity::Chara& target ); + + virtual void onDeath( Entity::BNpc& bnpc ); }; /*! diff --git a/src/world/Script/ScriptMgr.cpp b/src/world/Script/ScriptMgr.cpp index e1256fb8..e2399fb2 100644 --- a/src/world/Script/ScriptMgr.cpp +++ b/src/world/Script/ScriptMgr.cpp @@ -9,6 +9,7 @@ #include "Territory/QuestBattle.h" #include "Territory/PublicContent.h" #include "Actor/Player.h" +#include "Actor/BNpc.h" #include "Actor/EventObject.h" #include "ServerMgr.h" #include "Event/EventHandler.h" @@ -651,4 +652,64 @@ bool Sapphire::Scripting::ScriptMgr::onPublicContentEnterTerritory( PublicConten } return false; -} \ No newline at end of file +} + +bool Sapphire::Scripting::ScriptMgr::onBNpcInit( Entity::BNpc& bnpc ) +{ + auto script = m_nativeScriptMgr->getScript< Sapphire::ScriptAPI::BattleNpcScript >( bnpc.getBNpcBaseId() ); + if( script ) + { + script->onInit( bnpc ); + return true; + } + + return false; +} + +bool Sapphire::Scripting::ScriptMgr::onBNpcUpdate( Entity::BNpc& bnpc, uint64_t tickCount ) +{ + auto script = m_nativeScriptMgr->getScript< Sapphire::ScriptAPI::BattleNpcScript >( bnpc.getBNpcBaseId() ); + if( script ) + { + script->onUpdate( bnpc, tickCount ); + return true; + } + + return false; +} + +bool Sapphire::Scripting::ScriptMgr::onBNpcHateListAdd( Entity::BNpc& bnpc, Entity::Chara& target ) +{ + auto script = m_nativeScriptMgr->getScript< Sapphire::ScriptAPI::BattleNpcScript >( bnpc.getBNpcBaseId() ); + if( script ) + { + script->onHateListAdd( bnpc, target ); + return true; + } + + return false; +} + +bool Sapphire::Scripting::ScriptMgr::onBNpcHateListRemove( Entity::BNpc& bnpc, Entity::Chara& target ) +{ + auto script = m_nativeScriptMgr->getScript< Sapphire::ScriptAPI::BattleNpcScript >( bnpc.getBNpcBaseId() ); + if( script ) + { + script->onHateListRemove( bnpc, target ); + return true; + } + + return false; +} + +bool Sapphire::Scripting::ScriptMgr::onBNpcDeath( Entity::BNpc& bnpc ) +{ + auto script = m_nativeScriptMgr->getScript< Sapphire::ScriptAPI::BattleNpcScript >( bnpc.getBNpcBaseId() ); + if( script ) + { + script->onDeath( bnpc ); + return true; + } + + return false; +} diff --git a/src/world/Script/ScriptMgr.h b/src/world/Script/ScriptMgr.h index 61793bd4..35411392 100644 --- a/src/world/Script/ScriptMgr.h +++ b/src/world/Script/ScriptMgr.h @@ -130,6 +130,16 @@ namespace Sapphire::Scripting bool onPublicContentEnterTerritory( PublicContentPtr instance, Entity::Player& player, uint32_t eventId, uint16_t param1, uint16_t param2 ); + bool onBNpcInit( Entity::BNpc& bnpc ); + + bool onBNpcUpdate( Entity::BNpc& bnpc, uint64_t tickCount ); + + bool onBNpcHateListAdd( Entity::BNpc& bnpc, Entity::Chara& target ); + + bool onBNpcHateListRemove( Entity::BNpc& bnpc, Entity::Chara& target ); + + bool onBNpcDeath( Entity::BNpc& bnpc ); + bool loadDir( const std::string& dirname, std::set< std::string >& files, const std::string& ext ); NativeScriptMgr& getNativeScriptHandler();