diff --git a/src/world/AI/GambitPack.cpp b/src/world/AI/GambitPack.cpp new file mode 100644 index 00000000..03a8e0de --- /dev/null +++ b/src/world/AI/GambitPack.cpp @@ -0,0 +1,135 @@ +#include +#include +#include +#include +#include +#include "GambitTargetCondition.h" +#include "GambitRule.h" +#include "GambitPack.h" + +using namespace Sapphire; +using namespace Sapphire::World; + +AI::GambitTimeLinePack::GambitTimeLinePack( int8_t loopCount ) : + GambitPack( GambitPackType::TimeLine ), + m_loopCount( loopCount ), + m_currentIndex( 0 ), + m_currentLoop( 0 ), + m_startTimeMs( 0 ) +{ + +} + +void AI::GambitTimeLinePack::start() +{ + m_startTimeMs = Common::Util::getTimeMs(); +} + +void AI::GambitTimeLinePack::addTimeLine( const GambitRulePtr& pRule, uint32_t offsetInSeconds ) +{ + auto timeLine = std::make_pair( pRule, offsetInSeconds ); + m_gambits.push_back( timeLine ); +} + +void AI::GambitTimeLinePack::addTimeLine( const GambitTargetConditionPtr& targetCondition, const Action::ActionPtr& action, uint32_t offsetInSeconds ) +{ + auto pRule = make_GambitRule( targetCondition, action, 0 ); + auto timeLine = std::make_pair( pRule, offsetInSeconds ); + m_gambits.push_back( timeLine ); +} + +uint8_t AI::GambitTimeLinePack::getLoopCount() const +{ + return m_loopCount; +} + +uint8_t AI::GambitTimeLinePack::getCurrentIndex() const +{ + return m_currentIndex; +} + +void AI::GambitTimeLinePack::update( Entity::BNpc& bnpc, uint64_t tickCount ) +{ + if( m_startTimeMs == 0 || m_gambits.empty() ) + return; + + auto& actionMgr = Common::Service< World::Manager::ActionMgr >::ref(); + + if( m_gambits.size() <= m_currentIndex ) + { + if( m_currentLoop < m_loopCount || m_loopCount == -1 ) + { + m_currentIndex = 0; + m_currentLoop++; + m_startTimeMs = Common::Util::getTimeMs(); + } + else + { + m_startTimeMs = 0; + m_currentLoop = 0; + return; + } + } + + auto currentTimeLine = m_gambits.at( m_currentIndex ); + auto& pRule = currentTimeLine.first; + auto offset = currentTimeLine.second * 1000; + + if( tickCount - m_startTimeMs >= offset ) + { + if( pRule->getGambitTargetCondition()->isConditionMet( bnpc ) ) + { + pRule->setLastExecutionMs( tickCount ); + actionMgr.handleTargetedAction( bnpc, pRule->getActionPtr()->getId(), pRule->getGambitTargetCondition()->getTarget()->getId(), 0 ); + } + m_currentIndex++; + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +AI::GambitRuleSetPack::GambitRuleSetPack() : GambitPack( GambitPackType::RuleSetList ) +{ + +} + +void AI::GambitRuleSetPack::addRule( const GambitRulePtr& pRule ) +{ + m_gambits.push_back( pRule ); +} + +void AI::GambitRuleSetPack::addRule( const GambitTargetConditionPtr& targetCondition, const Action::ActionPtr& action, uint32_t coolDown ) +{ + auto pRule = make_GambitRule( targetCondition, action, coolDown ); + m_gambits.push_back( pRule ); +} + +void AI::GambitRuleSetPack::update( Entity::BNpc& bnpc, uint64_t tickCount ) +{ + auto& actionMgr = Common::Service< World::Manager::ActionMgr >::ref(); + for( auto& gambitRule : m_gambits ) + { + if( !gambitRule->isEnabled() ) + continue; + + if( ( tickCount - gambitRule->getLastExecutionMs() ) > gambitRule->getCoolDown() ) + { + if( !gambitRule->getGambitTargetCondition()->isConditionMet( bnpc ) ) + continue; + + gambitRule->setLastExecutionMs( tickCount ); + actionMgr.handleTargetedAction( bnpc, gambitRule->getActionPtr()->getId(), gambitRule->getGambitTargetCondition()->getTarget()->getId(), 0 ); + break; + } + } +} + +AI::GambitTimeLinePackPtr AI::GambitPack::getAsTimeLine() +{ + return std::dynamic_pointer_cast< GambitTimeLinePack, GambitPack >( shared_from_this() ); +} + +AI::GambitRuleSetPackPtr AI::GambitPack::getAsRuleSet() +{ + return std::dynamic_pointer_cast< GambitRuleSetPack, GambitPack >( shared_from_this() ); +} diff --git a/src/world/AI/GambitPack.h b/src/world/AI/GambitPack.h new file mode 100644 index 00000000..caf62f9c --- /dev/null +++ b/src/world/AI/GambitPack.h @@ -0,0 +1,60 @@ +#include +#include +#include "GambitTargetCondition.h" +#include "GambitRule.h" + +#pragma once + +namespace Sapphire::World::AI +{ + enum class GambitPackType : uint8_t + { + None, + RuleSetList, + TimeLine + }; + + class GambitPack : public std::enable_shared_from_this< GambitPack > + { + public: + GambitPack( GambitPackType type ) : m_type( type ) { }; + virtual ~GambitPack() = default; + GambitPackType getType() const { return m_type; } + virtual void update( Entity::BNpc& bnpc, uint64_t tickCount ) = 0; + GambitTimeLinePackPtr getAsTimeLine(); + GambitRuleSetPackPtr getAsRuleSet(); + private: + GambitPackType m_type; + }; + + class GambitTimeLinePack : public GambitPack + { + public: + GambitTimeLinePack( int8_t loopCount ); + void update( Entity::BNpc& bnpc, uint64_t tickCount ); + void addTimeLine( const GambitRulePtr& pRule, uint32_t offsetInSeconds ); + void addTimeLine( const GambitTargetConditionPtr& targetCondition, const Action::ActionPtr& action, uint32_t offsetInSeconds ); + uint8_t getLoopCount() const; + uint8_t getCurrentIndex() const; + void start(); + + private: + std::vector< std::pair< GambitRulePtr, uint32_t > > m_gambits; + + uint64_t m_startTimeMs; + uint8_t m_currentIndex; + int8_t m_loopCount; + uint8_t m_currentLoop; + }; + + class GambitRuleSetPack : public GambitPack + { + public: + GambitRuleSetPack(); + void addRule( const GambitRulePtr& pRule ); + void addRule( const GambitTargetConditionPtr& targetCondition, const Action::ActionPtr& action, uint32_t coolDown ); + void update( Entity::BNpc& bnpc, uint64_t tickCount ); + private: + std::vector< GambitRulePtr > m_gambits; + }; +} \ No newline at end of file diff --git a/src/world/Actor/BNpc.cpp b/src/world/Actor/BNpc.cpp index 04107a5a..9f9429b5 100644 --- a/src/world/Actor/BNpc.cpp +++ b/src/world/Actor/BNpc.cpp @@ -46,6 +46,7 @@ #include #include +#include #include #include #include @@ -598,6 +599,11 @@ void BNpc::aggro( const Sapphire::Entity::CharaPtr& pChara ) auto& pRNGMgr = Common::Service< World::Manager::RNGMgr >::ref(); auto variation = static_cast< uint32_t >( pRNGMgr.getRandGenerator< float >( 500, 1000 ).next() ); + if( m_pGambitPack && m_pGambitPack->getAsTimeLine() ) + { + m_pGambitPack->getAsTimeLine()->start(); + } + m_lastAttack = Common::Util::getTimeMs() + variation; setStance( Stance::Active ); @@ -911,9 +917,22 @@ void BNpc::init() //setup a test gambit auto testGambitRule = AI::make_GambitRule( AI::make_TopHateTargetCondition(), Action::make_Action( getAsChara(), 88, 0 ), 5000 ); auto testGambitRule1 = AI::make_GambitRule( AI::make_HPSelfPctLessThanTargetCondition( 50 ), Action::make_Action( getAsChara(), 120, 0 ), 5000 ); +/* + auto gambitPack = AI::make_GambitRuleSetPack(); + gambitPack->addRule( AI::make_TopHateTargetCondition(), Action::make_Action( getAsChara(), 88, 0 ), 5000 ); + gambitPack->addRule( AI::make_HPSelfPctLessThanTargetCondition( 50 ), Action::make_Action( getAsChara(), 120, 0 ), 10000 ); + m_pGambitPack = gambitPack; +*/ - m_gambits.push_back( testGambitRule ); - m_gambits.push_back( testGambitRule1 ); + auto gambitPack = AI::make_GambitTimeLinePack( -1 ); + gambitPack->addTimeLine( AI::make_TopHateTargetCondition(), Action::make_Action( getAsChara(), 88, 0 ), 2 ); + gambitPack->addTimeLine( AI::make_TopHateTargetCondition(), Action::make_Action( getAsChara(), 89, 0 ), 4 ); + gambitPack->addTimeLine( AI::make_TopHateTargetCondition(), Action::make_Action( getAsChara(), 90, 0 ), 6 ); + gambitPack->addTimeLine( AI::make_TopHateTargetCondition(), Action::make_Action( getAsChara(), 91, 0 ), 8 ); + gambitPack->addTimeLine( AI::make_TopHateTargetCondition(), Action::make_Action( getAsChara(), 92, 0 ), 10 ); + gambitPack->addTimeLine( AI::make_TopHateTargetCondition(), Action::make_Action( getAsChara(), 81, 0 ), 12 ); + gambitPack->addTimeLine( AI::make_TopHateTargetCondition(), Action::make_Action( getAsChara(), 82, 0 ), 14 ); + m_pGambitPack = gambitPack; using namespace AI::Fsm; m_fsm = make_StateMachine(); @@ -945,23 +964,8 @@ void BNpc::init() void BNpc::processGambits( uint64_t tickCount ) { - auto& actionMgr = Common::Service< World::Manager::ActionMgr >::ref(); - for( auto& gambitRule : m_gambits ) - { - if( !gambitRule->isEnabled() ) - continue; - - if( ( tickCount - gambitRule->getLastExecutionMs() ) > gambitRule->getCoolDown() ) - { - if( !gambitRule->getGambitTargetCondition()->isConditionMet( *this ) ) - continue; - - gambitRule->setLastExecutionMs( tickCount ); - actionMgr.handleTargetedAction( *this, gambitRule->getActionPtr()->getId(), gambitRule->getGambitTargetCondition()->getTarget()->getId(), 0 ); - break; - } - - } + m_tp = 1000; + m_pGambitPack->update( *this, tickCount ); } uint32_t BNpc::getLastRoamTargetReachedTime() const diff --git a/src/world/Actor/BNpc.h b/src/world/Actor/BNpc.h index a8804f72..7076ae6e 100644 --- a/src/world/Actor/BNpc.h +++ b/src/world/Actor/BNpc.h @@ -206,7 +206,7 @@ namespace Sapphire::Entity Common::FFXIVARR_POSITION3 m_naviTarget; CharaPtr m_pOwner; - std::vector< World::AI::GambitRulePtr > m_gambits; + World::AI::GambitPackPtr m_pGambitPack; std::shared_ptr< World::AI::Fsm::StateMachine > m_fsm; diff --git a/src/world/ForwardsZone.h b/src/world/ForwardsZone.h index de26643f..398e98b8 100644 --- a/src/world/ForwardsZone.h +++ b/src/world/ForwardsZone.h @@ -54,6 +54,9 @@ namespace World::AI TYPE_FORWARD( HPSelfPctLessThanTargetCondition ); TYPE_FORWARD( GambitRule ); + TYPE_FORWARD( GambitPack ); + TYPE_FORWARD( GambitTimeLinePack ); + TYPE_FORWARD( GambitRuleSetPack ); } namespace World::AI::Fsm diff --git a/src/world/Manager/PlayerMgr.cpp b/src/world/Manager/PlayerMgr.cpp index 28636bbb..15f7ad51 100644 --- a/src/world/Manager/PlayerMgr.cpp +++ b/src/world/Manager/PlayerMgr.cpp @@ -313,7 +313,7 @@ void PlayerMgr::onUpdate( Entity::Player& player, uint64_t tickCount ) void PlayerMgr::checkAutoAttack( Entity::Player& player, uint64_t tickCount ) const { auto mainWeap = player.getItemAt( Common::GearSet0, Common::MainHand ); - if( !mainWeap || !player.isAutoattackOn() || player.checkAction() || !player.getTargetId() || player.getStance() != Common::Active ) + if( !mainWeap || player.checkAction() || !player.isAutoattackOn() || !player.getTargetId() || player.getStance() != Common::Active ) return; for( const auto& actor : player.getInRangeActors() )