From e72fe535f65d907a88ef0218511bcbe156b018ae Mon Sep 17 00:00:00 2001 From: Mordred Date: Fri, 17 Mar 2023 15:43:44 +0100 Subject: [PATCH] Added the framework for a fsm for future use on bnpcs. --- src/world/AI/Fsm.cpp | 38 +++++++++++++++++ src/world/AI/Fsm.h | 23 ++++++++++ src/world/AI/FsmCondition.h | 23 ++++++++++ src/world/AI/FsmState.h | 37 ++++++++++++++++ src/world/AI/FsmTransition.h | 22 ++++++++++ src/world/Actor/BNpc.cpp | 82 ++++++++++++++++++------------------ src/world/ForwardsZone.h | 5 +++ 7 files changed, 189 insertions(+), 41 deletions(-) create mode 100644 src/world/AI/Fsm.cpp create mode 100644 src/world/AI/Fsm.h create mode 100644 src/world/AI/FsmCondition.h create mode 100644 src/world/AI/FsmState.h create mode 100644 src/world/AI/FsmTransition.h diff --git a/src/world/AI/Fsm.cpp b/src/world/AI/Fsm.cpp new file mode 100644 index 00000000..ce78c6d5 --- /dev/null +++ b/src/world/AI/Fsm.cpp @@ -0,0 +1,38 @@ +#include +#include +#include +#include "Fsm.h" +#include "FsmState.h" + +#pragma once + +using namespace Sapphire; +using namespace Sapphire::World; + +AI::FsmStatePtr AI::Fsm::addState( FsmStatePtr state ) +{ + m_states.push_back( state ); + return state; +} + +void AI::Fsm::setCurrentState( FsmStatePtr state ) +{ + m_pCurrentState = state; +} + +void AI::Fsm::update( Entity::BNpc& bnpc, float deltaTime ) +{ + if( !m_pCurrentState ) + return; + + FsmTransitionPtr transition = m_pCurrentState->getTriggeredTransition( bnpc ); + + if( transition ) + { + m_pCurrentState->onExit( bnpc ); + m_pCurrentState = transition->getTargetState(); + m_pCurrentState->onEnter( bnpc ); + } + + m_pCurrentState->onUpdate( bnpc, deltaTime ); +} diff --git a/src/world/AI/Fsm.h b/src/world/AI/Fsm.h new file mode 100644 index 00000000..4477f8de --- /dev/null +++ b/src/world/AI/Fsm.h @@ -0,0 +1,23 @@ +#include +#include +#include + +#pragma once + +namespace Sapphire::World::AI +{ + class Fsm + { + public: + Fsm() = default; + ~Fsm() = default; + + FsmStatePtr addState( FsmStatePtr state ); + void setCurrentState( FsmStatePtr state ); + virtual void update( Entity::BNpc& bnpc, float deltaTime ); + + protected: + std::vector< FsmStatePtr > m_states; + FsmStatePtr m_pCurrentState; + }; +} \ No newline at end of file diff --git a/src/world/AI/FsmCondition.h b/src/world/AI/FsmCondition.h new file mode 100644 index 00000000..41619ac0 --- /dev/null +++ b/src/world/AI/FsmCondition.h @@ -0,0 +1,23 @@ +#include +#include +#include + +#pragma once + +namespace Sapphire::World::AI +{ + class FsmCondition + { + public: + FsmCondition() = default; + virtual ~FsmCondition() = default; + + virtual bool isConditionMet( Sapphire::Entity::BNpc& src ) const = 0; + virtual bool update( Sapphire::Entity::BNpc& src, float time ) + { + if( isConditionMet( src ) ) + return true; + return false; + }; + }; +} \ No newline at end of file diff --git a/src/world/AI/FsmState.h b/src/world/AI/FsmState.h new file mode 100644 index 00000000..cb1b7466 --- /dev/null +++ b/src/world/AI/FsmState.h @@ -0,0 +1,37 @@ +#include +#include +#include +#include "FsmTransition.h" + +#pragma once + +namespace Sapphire::World::AI +{ + class FsmState + { + public: + virtual ~FsmState() = default; + + virtual void onUpdate( Entity::BNpc& bnpc, float deltaTime ) = 0; + virtual void onEnter( Entity::BNpc& bnpc ) { } + virtual void onExit( Entity::BNpc& bnpc ) { } + + void addTransition( FsmTransitionPtr transition ) + { + m_transitions.push_back( transition ); + } + + FsmTransitionPtr getTriggeredTransition( Entity::BNpc& bnpc ) + { + for( auto transition : m_transitions ) + { + if( transition->hasTriggered( bnpc ) ) + return transition; + } + return nullptr; + } + + private: + std::vector< FsmTransitionPtr > m_transitions; + }; +} \ No newline at end of file diff --git a/src/world/AI/FsmTransition.h b/src/world/AI/FsmTransition.h new file mode 100644 index 00000000..982dd874 --- /dev/null +++ b/src/world/AI/FsmTransition.h @@ -0,0 +1,22 @@ +#include +#include +#include +#include "FsmCondition.h" + +#pragma once + +namespace Sapphire::World::AI +{ + class FsmTransition + { + public: + FsmTransition( FsmStatePtr targetState, FsmConditionPtr condition ) : m_pTargetState( targetState ), m_pCondition( condition ) { } + virtual ~FsmTransition() = default; + + FsmStatePtr getTargetState() { return m_pTargetState; } + bool hasTriggered( Entity::BNpc& bnpc ) { return m_pCondition->isConditionMet( bnpc ); } + private: + FsmStatePtr m_pTargetState; + FsmConditionPtr m_pCondition; + }; +} \ No newline at end of file diff --git a/src/world/Actor/BNpc.cpp b/src/world/Actor/BNpc.cpp index 30a3235c..dd38c784 100644 --- a/src/world/Actor/BNpc.cpp +++ b/src/world/Actor/BNpc.cpp @@ -649,6 +649,8 @@ void BNpc::update( uint64_t tickCount ) if( !pNaviProvider ) return; + Chara::update( tickCount ); + if( !checkAction() ) processGambits( tickCount ); @@ -740,63 +742,61 @@ void BNpc::update( uint64_t tickCount ) auto distanceOrig = Common::Util::distance( getPos(), m_spawnPos ); - if( pHatedActor && !pHatedActor->isAlive() ) + if( !pHatedActor->isAlive() || getTerritoryId() != pHatedActor->getTerritoryId() ) { hateListRemove( pHatedActor ); pHatedActor = hateListGetHighest(); } - if( pHatedActor ) - { - auto distance = Common::Util::distance( getPos(), pHatedActor->getPos() ); - - if( !hasFlag( NoDeaggro ) && ( ( distanceOrig > maxDistanceToOrigin ) || distance > 30.0f ) ) - { - hateListClear(); - changeTarget( INVALID_GAME_OBJECT_ID64 ); - setStance( Stance::Passive ); - setOwner( nullptr ); - m_state = BNpcState::Retreat; - break; - } - - if( distance > ( getNaviTargetReachedDistance() + pHatedActor->getRadius() ) ) - { - if( hasFlag( Immobile ) ) - break; - - if( pNaviProvider ) - pNaviProvider->setMoveTarget( *this, pHatedActor->getPos() ); - - moveTo( *pHatedActor ); - } - - if( pNaviProvider->syncPosToChara( *this ) ) - sendPositionUpdate(); - - if( distance < ( getNaviTargetReachedDistance() + pHatedActor->getRadius() ) ) - { - if( !hasFlag( TurningDisabled ) && face( pHatedActor->getPos() ) ) - sendPositionUpdate(); - - // in combat range. ATTACK! - autoAttack( pHatedActor ); - } - } - else + if( !pHatedActor ) { changeTarget( INVALID_GAME_OBJECT_ID64 ); setStance( Stance::Passive ); //setOwner( nullptr ); m_state = BNpcState::Retreat; pNaviProvider->updateAgentParameters( *this ); + break; } + + auto distance = Common::Util::distance( getPos(), pHatedActor->getPos() ); + + if( !hasFlag( NoDeaggro ) && ( ( distanceOrig > maxDistanceToOrigin ) || distance > 30.0f ) ) + { + hateListClear(); + changeTarget( INVALID_GAME_OBJECT_ID64 ); + setStance( Stance::Passive ); + setOwner( nullptr ); + m_state = BNpcState::Retreat; + break; + } + + if( distance > ( getNaviTargetReachedDistance() + pHatedActor->getRadius() ) ) + { + if( hasFlag( Immobile ) ) + break; + + if( pNaviProvider ) + pNaviProvider->setMoveTarget( *this, pHatedActor->getPos() ); + + moveTo( *pHatedActor ); + } + + if( pNaviProvider->syncPosToChara( *this ) ) + sendPositionUpdate(); + + if( distance < ( getNaviTargetReachedDistance() + pHatedActor->getRadius() ) ) + { + if( !hasFlag( TurningDisabled ) && face( pHatedActor->getPos() ) ) + sendPositionUpdate(); + + // in combat range. ATTACK! + autoAttack( pHatedActor ); + } + } break; } - - Chara::update( tickCount ); } void BNpc::restHp() diff --git a/src/world/ForwardsZone.h b/src/world/ForwardsZone.h index ef95ab0a..e17eb916 100644 --- a/src/world/ForwardsZone.h +++ b/src/world/ForwardsZone.h @@ -54,6 +54,11 @@ namespace World::AI TYPE_FORWARD( HPSelfPctLessThanTargetCondition ); TYPE_FORWARD( GambitRule ); + + TYPE_FORWARD( FsmCondition ); + TYPE_FORWARD( FsmState ); + TYPE_FORWARD( FsmTransition ); + TYPE_FORWARD( Fsm ); } namespace Inventory