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

Basic gambit work, still needs cleanup and improvements

This commit is contained in:
Mordred 2023-03-12 16:33:33 +01:00
parent 5cc7b0c87b
commit d6918a88e0
8 changed files with 218 additions and 18 deletions

View file

@ -1,7 +1,7 @@
{
"7": {
"name": "Attack",
"potency": 0,
"potency": 110,
"comboPotency": 0,
"flankPotency": 0,
"frontPotency": 0,
@ -16,7 +16,7 @@
},
"8": {
"name": "Shot",
"potency": 0,
"potency": 100,
"comboPotency": 0,
"flankPotency": 0,
"frontPotency": 0,

View file

@ -0,0 +1,52 @@
#include <cstdint>
#include <ForwardsZone.h>
#include "GambitTargetCondition.h"
#include "GambitRule.h"
using namespace Sapphire;
using namespace Sapphire::World;
AI::GambitRule::GambitRule( const GambitTargetConditionPtr targetCondition, Action::ActionPtr action, uint32_t coolDown ) :
m_targetCondition( targetCondition ),
m_pAction( std::move( action ) ),
m_lastExecutionMs( 0 ),
m_coolDownMs( coolDown ),
m_isEnabled( true )
{
}
void AI::GambitRule::toggleEnabled()
{
m_isEnabled = !m_isEnabled;
}
bool AI::GambitRule::isEnabled() const
{
return m_isEnabled;
}
uint64_t AI::GambitRule::getLastExecutionMs() const
{
return m_lastExecutionMs;
}
void AI::GambitRule::setLastExecutionMs( uint64_t lastExecution )
{
m_lastExecutionMs = lastExecution;
}
uint32_t AI::GambitRule::getCoolDown() const
{
return m_coolDownMs;
}
AI::GambitTargetConditionPtr AI::GambitRule::getGambitTargetCondition()
{
return m_targetCondition;
}
Action::ActionPtr AI::GambitRule::getActionPtr()
{
return m_pAction;
}

33
src/world/AI/GambitRule.h Normal file
View file

@ -0,0 +1,33 @@
#include <cstdint>
#include <ForwardsZone.h>
#include "GambitTargetCondition.h"
#pragma once
namespace Sapphire::World::AI
{
class GambitRule
{
public:
GambitRule( GambitTargetConditionPtr targetCondition, Action::ActionPtr action, uint32_t coolDown );
~GambitRule() = default;
bool isEnabled() const;
void toggleEnabled();
uint64_t getLastExecutionMs() const;
void setLastExecutionMs( uint64_t lastExecution );
uint32_t getCoolDown() const;
GambitTargetConditionPtr getGambitTargetCondition();
Action::ActionPtr getActionPtr();
private:
GambitTargetConditionPtr m_targetCondition;
Action::ActionPtr m_pAction;
uint32_t m_coolDownMs;
uint64_t m_lastExecutionMs;
bool m_isEnabled;
};
}

View file

@ -0,0 +1,66 @@
#include <cstdint>
#include <ForwardsZone.h>
#include <Actor/BNpc.h>
#pragma once
namespace Sapphire::World::AI
{
enum GambitTargetType : uint8_t
{
Self,
Player,
PlayerAndAlly,
Ally,
BNpc
};
class GambitTargetCondition
{
public:
GambitTargetCondition( GambitTargetType targetType ) : m_targetType( targetType ) {};
virtual ~GambitTargetCondition() = default;
virtual bool isConditionMet( Sapphire::Entity::BNpc& src ) { return false; };
Sapphire::Entity::CharaPtr getTarget() const { return m_pTarget; };
protected:
GambitTargetType m_targetType;
Sapphire::Entity::CharaPtr m_pTarget;
};
class TopHateTargetCondition : public GambitTargetCondition
{
public:
TopHateTargetCondition() : GambitTargetCondition( PlayerAndAlly ) {};
bool isConditionMet( Sapphire::Entity::BNpc& src ) override
{
auto foundChara = src.hateListGetHighest();
if( foundChara )
{
m_pTarget = foundChara;
return true;
}
return false;
};
};
class HPSelfPctLessThan : public GambitTargetCondition
{
public:
HPSelfPctLessThan( uint8_t pct ) : GambitTargetCondition( Self ), m_HpPct( pct ) {};
virtual bool isConditionMet( Sapphire::Entity::BNpc& src )
{
if( src.getHpPercent() < m_HpPct )
{
m_pTarget = src.getAsBNpc();
return true;
}
return false;
};
private:
uint8_t m_HpPct;
};
}

View file

@ -44,7 +44,12 @@
#include <Task/ActionIntegrityTask.h>
#include <Service.h>
#include <Action/Action.h>
#include <AI/GambitRule.h>
#include <AI/GambitTargetCondition.h>
using namespace Sapphire;
using namespace Sapphire::World;
using namespace Sapphire::Common;
using namespace Sapphire::Entity;
using namespace Sapphire::Network::Packets;
@ -319,7 +324,7 @@ uint32_t BNpc::getBNpcNameId() const
void BNpc::spawn( PlayerPtr pTarget )
{
m_lastRoamTargetReached = Util::getTimeSeconds();
m_lastRoamTargetReached = Common::Util::getTimeSeconds();
auto& server = Common::Service< World::WorldServer >::ref();
server.queueForPlayer( pTarget->getCharacterId(), std::make_shared< NpcSpawnPacket >( *this, *pTarget ) );
@ -355,7 +360,7 @@ bool BNpc::moveTo( const FFXIVARR_POSITION3& pos )
}
auto pos1 = pNaviProvider->getMovePos( *this );
auto distance = Util::distance( pos1, pos );
auto distance = Common::Util::distance( pos1, pos );
if( distance < getNaviTargetReachedDistance() )
{
@ -393,7 +398,7 @@ bool BNpc::moveTo( const Chara& targetChara )
}
auto pos1 = pNaviProvider->getMovePos( *this );
auto distance = Util::distance( pos1, targetChara.getPos() );
auto distance = Common::Util::distance( pos1, targetChara.getPos() );
if( distance <= ( getNaviTargetReachedDistance() + targetChara.getRadius() ) )
{
@ -584,7 +589,7 @@ 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() );
m_lastAttack = Util::getTimeMs() + variation;
m_lastAttack = Common::Util::getTimeMs() + variation;
setStance( Stance::Active );
m_state = BNpcState::Combat;
@ -644,7 +649,8 @@ void BNpc::update( uint64_t tickCount )
if( !pNaviProvider )
return;
checkAction();
if( !checkAction() )
processGambits( tickCount );
switch( m_state )
{
@ -665,7 +671,7 @@ void BNpc::update( uint64_t tickCount )
// retail doesn't seem to roam straight after retreating
// todo: perhaps requires more investigation?
m_lastRoamTargetReached = Util::getTimeSeconds();
m_lastRoamTargetReached = Common::Util::getTimeSeconds();
// resetHp
setHp( getMaxHp() );
@ -684,7 +690,7 @@ void BNpc::update( uint64_t tickCount )
if( moveTo( m_roamPos ) )
{
m_lastRoamTargetReached = Util::getTimeSeconds();
m_lastRoamTargetReached = Common::Util::getTimeSeconds();
m_state = BNpcState::Idle;
}
@ -701,12 +707,12 @@ void BNpc::update( uint64_t tickCount )
if( pNaviProvider->syncPosToChara( *this ) )
sendPositionUpdate();
if( !hasFlag( Immobile ) && ( Util::getTimeSeconds() - m_lastRoamTargetReached > roamTick ) )
if( !hasFlag( Immobile ) && ( Common::Util::getTimeSeconds() - m_lastRoamTargetReached > roamTick ) )
{
if( !pNaviProvider )
{
m_lastRoamTargetReached = Util::getTimeSeconds();
m_lastRoamTargetReached = Common::Util::getTimeSeconds();
break;
}
if( m_pInfo->WanderingRange != 0 && getEnemyType() != 0 )
@ -732,7 +738,7 @@ void BNpc::update( uint64_t tickCount )
pNaviProvider->updateAgentParameters( *this );
auto distanceOrig = Util::distance( getPos().x, getPos().y, getPos().z, m_spawnPos.x, m_spawnPos.y, m_spawnPos.z );
auto distanceOrig = Common::Util::distance( getPos(), m_spawnPos );
if( pHatedActor && !pHatedActor->isAlive() )
{
@ -742,8 +748,7 @@ void BNpc::update( uint64_t tickCount )
if( pHatedActor )
{
auto distance = Util::distance( getPos().x, getPos().y, getPos().z,
pHatedActor->getPos().x, pHatedActor->getPos().y, pHatedActor->getPos().z );
auto distance = Common::Util::distance( getPos(), pHatedActor->getPos() );
if( !hasFlag( NoDeaggro ) && ( ( distanceOrig > maxDistanceToOrigin ) || distance > 30.0f ) )
{
@ -828,7 +833,7 @@ void BNpc::onDeath()
setTargetId( INVALID_GAME_OBJECT_ID64 );
m_currentStance = Stance::Passive;
m_state = BNpcState::Dead;
m_timeOfDeath = Util::getTimeSeconds();
m_timeOfDeath = Common::Util::getTimeSeconds();
setOwner( nullptr );
taskMgr.queueTask( World::makeFadeBNpcTask( 10000, getAsBNpc() ) );
@ -885,7 +890,7 @@ void BNpc::checkAggro()
range = std::max< float >( 0.f, range - std::pow( 1.53f, static_cast< float >( levelDiff ) * 0.6f ) );
}
auto distance = Util::distance( getPos(), pClosestChara->getPos() );
auto distance = Common::Util::distance( getPos(), pClosestChara->getPos() );
if( distance < range )
{
@ -916,7 +921,7 @@ void BNpc::checkAggro()
range = std::max< float >( 0.f, range - std::pow( 1.53f, static_cast< float >( levelDiff ) * 0.6f ) );
}
auto distance = Util::distance( getPos(), pClosestChara->getPos() );
auto distance = Common::Util::distance( getPos(), pClosestChara->getPos() );
if( distance < range )
{
@ -969,7 +974,7 @@ void BNpc::autoAttack( CharaPtr pTarget )
auto& actionMgr = Common::Service< World::Manager::ActionMgr >::ref();
auto& exdData = Common::Service< Data::ExdData >::ref();
uint64_t tick = Util::getTimeMs();
uint64_t tick = Common::Util::getTimeMs();
// todo: this needs to use the auto attack delay for the equipped weapon
if( ( tick - m_lastAttack ) > 2500 )
@ -1046,6 +1051,39 @@ uint32_t BNpc::getLayoutId() const
void BNpc::init()
{
auto& exdData = Common::Service< Data::ExdData >::ref();
m_maxHp = Math::CalcStats::calculateMaxHp( *getAsChara() );
m_hp = m_maxHp;
//setup a test gambit
auto testGambitRule = std::make_shared< AI::GambitRule >( std::make_shared< AI::TopHateTargetCondition >(),
Action::make_Action( getAsChara(), 88, 0, exdData.getRow< Excel::Action >( 88 ) ), 5000 );
auto testGambitRule1 = std::make_shared< AI::GambitRule >( std::make_shared< AI::HPSelfPctLessThan >( 50 ),
Action::make_Action( getAsChara(), 120, 0, exdData.getRow< Excel::Action >( 120 ) ), 5000 );
m_gambits.push_back( testGambitRule );
m_gambits.push_back( testGambitRule1 );
}
void BNpc::processGambits( uint64_t tickCount )
{
auto& exdData = Common::Service< Data::ExdData >::ref();
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(), exdData.getRow< Excel::Action >( gambitRule->getActionPtr()->getId() ),
gambitRule->getGambitTargetCondition()->getTarget()->getId(), 0 );
}
}
}

View file

@ -3,6 +3,7 @@
#include <Common.h>
#include "Forwards.h"
#include "ForwardsZone.h"
#include "Chara.h"
#include "Npc.h"
#include <set>
@ -148,6 +149,8 @@ namespace Sapphire::Entity
uint32_t getLayoutId() const;
void processGambits( uint64_t tickCount );
private:
uint32_t m_bNpcBaseId;
uint32_t m_bNpcNameId;
@ -189,6 +192,7 @@ namespace Sapphire::Entity
Common::FFXIVARR_POSITION3 m_naviTarget;
CharaPtr m_pOwner;
std::vector< World::AI::GambitRulePtr > m_gambits;
};

View file

@ -7,6 +7,7 @@ file( GLOB SERVER_SOURCE_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
*.cpp
Actor/*.cpp
Action/*.cpp
AI/*.cpp
ContentFinder/*.cpp
DebugCommand/*.cpp
Event/*.cpp

View file

@ -47,6 +47,12 @@ namespace World::Territory::Housing
TYPE_FORWARD( HousingInteriorTerritory );
}
namespace World::AI
{
TYPE_FORWARD( GambitTargetCondition );
TYPE_FORWARD( GambitRule );
}
namespace Inventory
{
using InventoryContainerPair = std::pair< Common::InventoryType, uint8_t >;