1
Fork 0
mirror of https://github.com/SapphireServer/Sapphire.git synced 2025-05-06 10:47:45 +00:00

Added the framework for a fsm for future use on bnpcs.

This commit is contained in:
Mordred 2023-03-17 15:43:44 +01:00
parent fe9a2ef974
commit e72fe535f6
7 changed files with 189 additions and 41 deletions

38
src/world/AI/Fsm.cpp Normal file
View file

@ -0,0 +1,38 @@
#include <cstdint>
#include <ForwardsZone.h>
#include <Actor/BNpc.h>
#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 );
}

23
src/world/AI/Fsm.h Normal file
View file

@ -0,0 +1,23 @@
#include <cstdint>
#include <ForwardsZone.h>
#include <Actor/BNpc.h>
#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;
};
}

View file

@ -0,0 +1,23 @@
#include <cstdint>
#include <ForwardsZone.h>
#include <Actor/BNpc.h>
#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;
};
};
}

37
src/world/AI/FsmState.h Normal file
View file

@ -0,0 +1,37 @@
#include <cstdint>
#include <ForwardsZone.h>
#include <Actor/BNpc.h>
#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;
};
}

View file

@ -0,0 +1,22 @@
#include <cstdint>
#include <ForwardsZone.h>
#include <Actor/BNpc.h>
#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;
};
}

View file

@ -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()

View file

@ -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