mirror of
https://github.com/SapphireServer/Sapphire.git
synced 2025-04-27 22:57:45 +00:00
Basic gambit work, still needs cleanup and improvements
This commit is contained in:
parent
5cc7b0c87b
commit
d6918a88e0
8 changed files with 218 additions and 18 deletions
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"7": {
|
"7": {
|
||||||
"name": "Attack",
|
"name": "Attack",
|
||||||
"potency": 0,
|
"potency": 110,
|
||||||
"comboPotency": 0,
|
"comboPotency": 0,
|
||||||
"flankPotency": 0,
|
"flankPotency": 0,
|
||||||
"frontPotency": 0,
|
"frontPotency": 0,
|
||||||
|
@ -16,7 +16,7 @@
|
||||||
},
|
},
|
||||||
"8": {
|
"8": {
|
||||||
"name": "Shot",
|
"name": "Shot",
|
||||||
"potency": 0,
|
"potency": 100,
|
||||||
"comboPotency": 0,
|
"comboPotency": 0,
|
||||||
"flankPotency": 0,
|
"flankPotency": 0,
|
||||||
"frontPotency": 0,
|
"frontPotency": 0,
|
||||||
|
|
52
src/world/AI/GambitRule.cpp
Normal file
52
src/world/AI/GambitRule.cpp
Normal 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
33
src/world/AI/GambitRule.h
Normal 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;
|
||||||
|
};
|
||||||
|
}
|
66
src/world/AI/GambitTargetCondition.h
Normal file
66
src/world/AI/GambitTargetCondition.h
Normal 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;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -44,7 +44,12 @@
|
||||||
#include <Task/ActionIntegrityTask.h>
|
#include <Task/ActionIntegrityTask.h>
|
||||||
#include <Service.h>
|
#include <Service.h>
|
||||||
|
|
||||||
|
#include <Action/Action.h>
|
||||||
|
#include <AI/GambitRule.h>
|
||||||
|
#include <AI/GambitTargetCondition.h>
|
||||||
|
|
||||||
using namespace Sapphire;
|
using namespace Sapphire;
|
||||||
|
using namespace Sapphire::World;
|
||||||
using namespace Sapphire::Common;
|
using namespace Sapphire::Common;
|
||||||
using namespace Sapphire::Entity;
|
using namespace Sapphire::Entity;
|
||||||
using namespace Sapphire::Network::Packets;
|
using namespace Sapphire::Network::Packets;
|
||||||
|
@ -319,7 +324,7 @@ uint32_t BNpc::getBNpcNameId() const
|
||||||
|
|
||||||
void BNpc::spawn( PlayerPtr pTarget )
|
void BNpc::spawn( PlayerPtr pTarget )
|
||||||
{
|
{
|
||||||
m_lastRoamTargetReached = Util::getTimeSeconds();
|
m_lastRoamTargetReached = Common::Util::getTimeSeconds();
|
||||||
|
|
||||||
auto& server = Common::Service< World::WorldServer >::ref();
|
auto& server = Common::Service< World::WorldServer >::ref();
|
||||||
server.queueForPlayer( pTarget->getCharacterId(), std::make_shared< NpcSpawnPacket >( *this, *pTarget ) );
|
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 pos1 = pNaviProvider->getMovePos( *this );
|
||||||
auto distance = Util::distance( pos1, pos );
|
auto distance = Common::Util::distance( pos1, pos );
|
||||||
|
|
||||||
if( distance < getNaviTargetReachedDistance() )
|
if( distance < getNaviTargetReachedDistance() )
|
||||||
{
|
{
|
||||||
|
@ -393,7 +398,7 @@ bool BNpc::moveTo( const Chara& targetChara )
|
||||||
}
|
}
|
||||||
|
|
||||||
auto pos1 = pNaviProvider->getMovePos( *this );
|
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() ) )
|
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& pRNGMgr = Common::Service< World::Manager::RNGMgr >::ref();
|
||||||
auto variation = static_cast< uint32_t >( pRNGMgr.getRandGenerator< float >( 500, 1000 ).next() );
|
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 );
|
setStance( Stance::Active );
|
||||||
m_state = BNpcState::Combat;
|
m_state = BNpcState::Combat;
|
||||||
|
@ -644,7 +649,8 @@ void BNpc::update( uint64_t tickCount )
|
||||||
if( !pNaviProvider )
|
if( !pNaviProvider )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
checkAction();
|
if( !checkAction() )
|
||||||
|
processGambits( tickCount );
|
||||||
|
|
||||||
switch( m_state )
|
switch( m_state )
|
||||||
{
|
{
|
||||||
|
@ -665,7 +671,7 @@ void BNpc::update( uint64_t tickCount )
|
||||||
|
|
||||||
// retail doesn't seem to roam straight after retreating
|
// retail doesn't seem to roam straight after retreating
|
||||||
// todo: perhaps requires more investigation?
|
// todo: perhaps requires more investigation?
|
||||||
m_lastRoamTargetReached = Util::getTimeSeconds();
|
m_lastRoamTargetReached = Common::Util::getTimeSeconds();
|
||||||
|
|
||||||
// resetHp
|
// resetHp
|
||||||
setHp( getMaxHp() );
|
setHp( getMaxHp() );
|
||||||
|
@ -684,7 +690,7 @@ void BNpc::update( uint64_t tickCount )
|
||||||
|
|
||||||
if( moveTo( m_roamPos ) )
|
if( moveTo( m_roamPos ) )
|
||||||
{
|
{
|
||||||
m_lastRoamTargetReached = Util::getTimeSeconds();
|
m_lastRoamTargetReached = Common::Util::getTimeSeconds();
|
||||||
m_state = BNpcState::Idle;
|
m_state = BNpcState::Idle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -701,12 +707,12 @@ void BNpc::update( uint64_t tickCount )
|
||||||
if( pNaviProvider->syncPosToChara( *this ) )
|
if( pNaviProvider->syncPosToChara( *this ) )
|
||||||
sendPositionUpdate();
|
sendPositionUpdate();
|
||||||
|
|
||||||
if( !hasFlag( Immobile ) && ( Util::getTimeSeconds() - m_lastRoamTargetReached > roamTick ) )
|
if( !hasFlag( Immobile ) && ( Common::Util::getTimeSeconds() - m_lastRoamTargetReached > roamTick ) )
|
||||||
{
|
{
|
||||||
|
|
||||||
if( !pNaviProvider )
|
if( !pNaviProvider )
|
||||||
{
|
{
|
||||||
m_lastRoamTargetReached = Util::getTimeSeconds();
|
m_lastRoamTargetReached = Common::Util::getTimeSeconds();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if( m_pInfo->WanderingRange != 0 && getEnemyType() != 0 )
|
if( m_pInfo->WanderingRange != 0 && getEnemyType() != 0 )
|
||||||
|
@ -732,7 +738,7 @@ void BNpc::update( uint64_t tickCount )
|
||||||
|
|
||||||
pNaviProvider->updateAgentParameters( *this );
|
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() )
|
if( pHatedActor && !pHatedActor->isAlive() )
|
||||||
{
|
{
|
||||||
|
@ -742,8 +748,7 @@ void BNpc::update( uint64_t tickCount )
|
||||||
|
|
||||||
if( pHatedActor )
|
if( pHatedActor )
|
||||||
{
|
{
|
||||||
auto distance = Util::distance( getPos().x, getPos().y, getPos().z,
|
auto distance = Common::Util::distance( getPos(), pHatedActor->getPos() );
|
||||||
pHatedActor->getPos().x, pHatedActor->getPos().y, pHatedActor->getPos().z );
|
|
||||||
|
|
||||||
if( !hasFlag( NoDeaggro ) && ( ( distanceOrig > maxDistanceToOrigin ) || distance > 30.0f ) )
|
if( !hasFlag( NoDeaggro ) && ( ( distanceOrig > maxDistanceToOrigin ) || distance > 30.0f ) )
|
||||||
{
|
{
|
||||||
|
@ -828,7 +833,7 @@ void BNpc::onDeath()
|
||||||
setTargetId( INVALID_GAME_OBJECT_ID64 );
|
setTargetId( INVALID_GAME_OBJECT_ID64 );
|
||||||
m_currentStance = Stance::Passive;
|
m_currentStance = Stance::Passive;
|
||||||
m_state = BNpcState::Dead;
|
m_state = BNpcState::Dead;
|
||||||
m_timeOfDeath = Util::getTimeSeconds();
|
m_timeOfDeath = Common::Util::getTimeSeconds();
|
||||||
setOwner( nullptr );
|
setOwner( nullptr );
|
||||||
|
|
||||||
taskMgr.queueTask( World::makeFadeBNpcTask( 10000, getAsBNpc() ) );
|
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 ) );
|
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 )
|
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 ) );
|
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 )
|
if( distance < range )
|
||||||
{
|
{
|
||||||
|
@ -969,7 +974,7 @@ void BNpc::autoAttack( CharaPtr pTarget )
|
||||||
auto& actionMgr = Common::Service< World::Manager::ActionMgr >::ref();
|
auto& actionMgr = Common::Service< World::Manager::ActionMgr >::ref();
|
||||||
auto& exdData = Common::Service< Data::ExdData >::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
|
// todo: this needs to use the auto attack delay for the equipped weapon
|
||||||
if( ( tick - m_lastAttack ) > 2500 )
|
if( ( tick - m_lastAttack ) > 2500 )
|
||||||
|
@ -1046,6 +1051,39 @@ uint32_t BNpc::getLayoutId() const
|
||||||
|
|
||||||
void BNpc::init()
|
void BNpc::init()
|
||||||
{
|
{
|
||||||
|
auto& exdData = Common::Service< Data::ExdData >::ref();
|
||||||
m_maxHp = Math::CalcStats::calculateMaxHp( *getAsChara() );
|
m_maxHp = Math::CalcStats::calculateMaxHp( *getAsChara() );
|
||||||
m_hp = m_maxHp;
|
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 );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include <Common.h>
|
#include <Common.h>
|
||||||
|
|
||||||
#include "Forwards.h"
|
#include "Forwards.h"
|
||||||
|
#include "ForwardsZone.h"
|
||||||
#include "Chara.h"
|
#include "Chara.h"
|
||||||
#include "Npc.h"
|
#include "Npc.h"
|
||||||
#include <set>
|
#include <set>
|
||||||
|
@ -148,6 +149,8 @@ namespace Sapphire::Entity
|
||||||
|
|
||||||
uint32_t getLayoutId() const;
|
uint32_t getLayoutId() const;
|
||||||
|
|
||||||
|
void processGambits( uint64_t tickCount );
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint32_t m_bNpcBaseId;
|
uint32_t m_bNpcBaseId;
|
||||||
uint32_t m_bNpcNameId;
|
uint32_t m_bNpcNameId;
|
||||||
|
@ -189,6 +192,7 @@ namespace Sapphire::Entity
|
||||||
Common::FFXIVARR_POSITION3 m_naviTarget;
|
Common::FFXIVARR_POSITION3 m_naviTarget;
|
||||||
|
|
||||||
CharaPtr m_pOwner;
|
CharaPtr m_pOwner;
|
||||||
|
std::vector< World::AI::GambitRulePtr > m_gambits;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ file( GLOB SERVER_SOURCE_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
*.cpp
|
*.cpp
|
||||||
Actor/*.cpp
|
Actor/*.cpp
|
||||||
Action/*.cpp
|
Action/*.cpp
|
||||||
|
AI/*.cpp
|
||||||
ContentFinder/*.cpp
|
ContentFinder/*.cpp
|
||||||
DebugCommand/*.cpp
|
DebugCommand/*.cpp
|
||||||
Event/*.cpp
|
Event/*.cpp
|
||||||
|
|
|
@ -47,6 +47,12 @@ namespace World::Territory::Housing
|
||||||
TYPE_FORWARD( HousingInteriorTerritory );
|
TYPE_FORWARD( HousingInteriorTerritory );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace World::AI
|
||||||
|
{
|
||||||
|
TYPE_FORWARD( GambitTargetCondition );
|
||||||
|
TYPE_FORWARD( GambitRule );
|
||||||
|
}
|
||||||
|
|
||||||
namespace Inventory
|
namespace Inventory
|
||||||
{
|
{
|
||||||
using InventoryContainerPair = std::pair< Common::InventoryType, uint8_t >;
|
using InventoryContainerPair = std::pair< Common::InventoryType, uint8_t >;
|
||||||
|
|
Loading…
Add table
Reference in a new issue