mirror of
https://github.com/SapphireServer/Sapphire.git
synced 2025-05-07 03:07:45 +00:00
wip: initial target select filter work
- todo: hook this up to EncounterTimeline
This commit is contained in:
parent
011cf1b6d2
commit
8fb9846c0b
6 changed files with 501 additions and 33 deletions
218
src/world/AI/TargetHelper.cpp
Normal file
218
src/world/AI/TargetHelper.cpp
Normal file
|
@ -0,0 +1,218 @@
|
|||
#include "TargetHelper.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <Actor/BNpc.h>
|
||||
#include <Actor/Chara.h>
|
||||
#include <Manager/RNGMgr.h>
|
||||
#include <Util/UtilMath.h>
|
||||
#include <Service.h>
|
||||
|
||||
namespace Sapphire::World::AI
|
||||
{
|
||||
|
||||
bool InsideRadiusFilter::isConditionMet( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const
|
||||
{
|
||||
bool ret = Common::Util::distance( pSrc->getPos(), pTarget->getPos() ) <= m_distance;
|
||||
return m_negate ? !ret : ret;
|
||||
}
|
||||
|
||||
bool OutsideRadiusFilter::isConditionMet( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const
|
||||
{
|
||||
bool ret = Common::Util::distance( pSrc->getPos(), pTarget->getPos() ) >= m_distance;
|
||||
return m_negate ? !ret : ret;
|
||||
}
|
||||
|
||||
bool PlayerFilter::isConditionMet( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const
|
||||
{
|
||||
bool ret = pTarget->isPlayer();
|
||||
return m_negate ? !ret : ret;
|
||||
}
|
||||
|
||||
bool AllyFilter::isConditionMet( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const
|
||||
{
|
||||
bool ret = false;
|
||||
if( pSrc->isPlayer() )
|
||||
{
|
||||
auto pBNpcTarget = pTarget->getAsBNpc();
|
||||
if( pBNpcTarget && pBNpcTarget->getEnemyType() == 0 )
|
||||
ret = true;
|
||||
else if( pTarget->isPlayer() )
|
||||
ret = true;
|
||||
}
|
||||
else if( pSrc->isBattleNpc() )
|
||||
{
|
||||
auto pBNpcTarget = pTarget->getAsBNpc();
|
||||
if( pBNpcTarget && pBNpcTarget->getEnemyType() == 0 )
|
||||
ret = true;
|
||||
else if( pTarget->isPlayer() )
|
||||
ret = true;
|
||||
}
|
||||
|
||||
return m_negate ? !ret : ret;
|
||||
}
|
||||
|
||||
bool OwnBattalionFilter::isConditionMet( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TankFilter::isConditionMet( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const
|
||||
{
|
||||
bool ret = pTarget->getRole() == Common::Role::Tank;
|
||||
return m_negate ? !ret : ret;
|
||||
}
|
||||
|
||||
bool HealerFilter::isConditionMet( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const
|
||||
{
|
||||
bool ret = pTarget->getRole() == Common::Role::Healer;
|
||||
return m_negate ? !ret : ret;
|
||||
}
|
||||
|
||||
bool DpsFilter::isConditionMet( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const
|
||||
{
|
||||
bool ret = true;
|
||||
switch( pTarget->getRole() )
|
||||
{
|
||||
case Common::Role::Melee:
|
||||
case Common::Role::RangedMagical:
|
||||
case Common::Role::RangedPhysical:
|
||||
ret = true;
|
||||
break;
|
||||
default:
|
||||
ret = false;
|
||||
break;
|
||||
}
|
||||
return m_negate ? !ret : ret;
|
||||
}
|
||||
|
||||
bool HasStatusEffectFilter::isConditionMet( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const
|
||||
{
|
||||
auto ret = pTarget->hasStatusEffect( m_statusId );
|
||||
return m_negate ? !ret : ret;
|
||||
}
|
||||
|
||||
bool TopAggroFilter ::isConditionMet( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const
|
||||
{
|
||||
auto pBNpc = pSrc->getAsBNpc();
|
||||
bool ret = false;
|
||||
if( pBNpc )
|
||||
{
|
||||
ret = pBNpc->hateListGetHighest() == pTarget;
|
||||
}
|
||||
return m_negate ? !ret : ret;
|
||||
}
|
||||
|
||||
bool SecondAggroFilter ::isConditionMet( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const
|
||||
{
|
||||
auto pBNpc = pSrc->getAsBNpc();
|
||||
bool ret = false;
|
||||
if( pBNpc )
|
||||
{
|
||||
// todo: this is so dumb
|
||||
|
||||
auto hateList = pBNpc->getHateList();
|
||||
std::vector sorted( hateList.begin(), hateList.end() );
|
||||
std::sort( sorted.begin(), sorted.end(), []( Entity::HateListEntryPtr a, Entity::HateListEntryPtr b ) {
|
||||
return a->m_hateAmount > b->m_hateAmount; } );
|
||||
|
||||
Entity::CharaPtr pChara = nullptr;
|
||||
auto topIt = sorted.begin();
|
||||
if( topIt != sorted.end() && ++topIt != sorted.end() )
|
||||
pChara = topIt->get()->m_pChara;
|
||||
|
||||
ret = pChara == pTarget;
|
||||
}
|
||||
return m_negate ? !ret : ret;
|
||||
}
|
||||
|
||||
void Snapshot::createSnapshot( Entity::CharaPtr pSrc, const std::set< Entity::GameObjectPtr >& inRange,
|
||||
int count, bool fillWithRandom, const std::set< uint32_t >& exclude )
|
||||
{
|
||||
m_targets.clear();
|
||||
|
||||
auto& RNGMgr = Common::Service< World::Manager::RNGMgr >::ref();
|
||||
for( const auto& pActor : inRange )
|
||||
{
|
||||
auto pChara = pActor->getAsChara();
|
||||
if( pChara == nullptr )
|
||||
continue;
|
||||
|
||||
if( exclude.find( pChara->getId() ) != exclude.end() ) continue;
|
||||
|
||||
bool matches = true;
|
||||
for( const auto& filter : m_filters )
|
||||
{
|
||||
if( !filter->isConditionMet( pSrc, pChara ) )
|
||||
{
|
||||
matches = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if( matches )
|
||||
{
|
||||
CharaEntry entry;
|
||||
entry.m_entityId = pChara->getId();
|
||||
entry.m_pos = pChara->getPos();
|
||||
entry.m_rot = pChara->getRot();
|
||||
|
||||
m_targets.push_back( entry );
|
||||
if( m_targets.size() == count ) break;
|
||||
}
|
||||
}
|
||||
|
||||
if( fillWithRandom && m_targets.size() < count )
|
||||
{
|
||||
std::vector< Entity::CharaPtr > remaining;
|
||||
for( const auto& pActor : inRange )
|
||||
{
|
||||
auto pChara = pActor->getAsChara();
|
||||
if( pChara == nullptr )
|
||||
continue;
|
||||
|
||||
if( exclude.find( pChara->getId() ) == exclude.end() && std::find_if( m_targets.begin(), m_targets.end(),
|
||||
[ &pChara ]( CharaEntry entry ) { return entry.m_entityId == pChara->getId(); } ) == m_targets.end() )
|
||||
{
|
||||
remaining.push_back( pChara );
|
||||
}
|
||||
}
|
||||
while( m_targets.size() < count && !remaining.empty() )
|
||||
{
|
||||
// idk
|
||||
std::shuffle( remaining.begin(), remaining.end(), *RNGMgr.getRNGEngine().get() );
|
||||
|
||||
auto pChara = remaining.back();
|
||||
CharaEntry entry;
|
||||
entry.m_entityId = pChara->getId();
|
||||
entry.m_pos = pChara->getPos();
|
||||
entry.m_rot = pChara->getRot();
|
||||
m_targets.emplace_back( entry );
|
||||
remaining.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
// sort by distance at the end always
|
||||
auto srcPos = pSrc->getPos();
|
||||
std::sort( m_targets.begin(), m_targets.end(),
|
||||
[ srcPos ]( CharaEntry l, CharaEntry r )
|
||||
{
|
||||
return Common::Util::distance( srcPos, l.m_pos ) < Common::Util::distance( srcPos, r.m_pos );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
const std::vector< Snapshot::CharaEntry >& Snapshot::getResults() const
|
||||
{
|
||||
return m_targets;
|
||||
}
|
||||
|
||||
const std::vector< uint32_t > Snapshot::getTargetIds() const
|
||||
{
|
||||
std::vector< uint32_t > ret( m_targets.size() );
|
||||
for( auto i = 0; i < m_targets.size(); ++i )
|
||||
ret[ i ] = m_targets[ i ].m_entityId;
|
||||
return ret;
|
||||
}
|
||||
|
||||
};// namespace Sapphire::World::AI
|
232
src/world/AI/TargetHelper.h
Normal file
232
src/world/AI/TargetHelper.h
Normal file
|
@ -0,0 +1,232 @@
|
|||
#ifndef _TARGETHELPER_H
|
||||
#define _TARGETHELPER_H
|
||||
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
#include <ForwardsZone.h>
|
||||
|
||||
namespace Sapphire::World::AI
|
||||
{
|
||||
//
|
||||
// Filters
|
||||
//
|
||||
class TargetSelectFilter :
|
||||
public std::enable_shared_from_this< TargetSelectFilter >
|
||||
{
|
||||
public:
|
||||
enum class Type
|
||||
{
|
||||
InsideRadius,
|
||||
OutsideRadius,
|
||||
|
||||
Player,
|
||||
Ally,
|
||||
OwnBattalion,
|
||||
|
||||
Tank,
|
||||
Healer,
|
||||
Dps,
|
||||
|
||||
HasStatusEffect,
|
||||
|
||||
TopAggro,
|
||||
SecondAggro,
|
||||
|
||||
AllianceA,
|
||||
AllianceB,
|
||||
AllianceC
|
||||
};
|
||||
|
||||
protected:
|
||||
Type m_type;
|
||||
bool m_negate{ false };
|
||||
|
||||
public:
|
||||
TargetSelectFilter( Type type, bool negate ) :
|
||||
m_type( type ),
|
||||
m_negate( negate )
|
||||
{
|
||||
}
|
||||
virtual ~TargetSelectFilter()
|
||||
{
|
||||
}
|
||||
|
||||
bool isConditionMet( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const
|
||||
{
|
||||
return false;
|
||||
};
|
||||
|
||||
bool isNegate() const
|
||||
{
|
||||
return m_negate;
|
||||
}
|
||||
};
|
||||
using TargetSelectFilterPtr = std::shared_ptr< TargetSelectFilter >;
|
||||
|
||||
class InsideRadiusFilter : public TargetSelectFilter
|
||||
{
|
||||
private:
|
||||
float m_distance{ 0 };
|
||||
|
||||
public:
|
||||
InsideRadiusFilter( float distance, bool negate ) :
|
||||
TargetSelectFilter( Type::InsideRadius, negate ),
|
||||
m_distance( distance )
|
||||
{
|
||||
}
|
||||
|
||||
bool isConditionMet( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const;
|
||||
};
|
||||
|
||||
class OutsideRadiusFilter : public TargetSelectFilter
|
||||
{
|
||||
private:
|
||||
float m_distance{ 0 };
|
||||
public:
|
||||
OutsideRadiusFilter( float distance, bool negate ) :
|
||||
TargetSelectFilter( Type::OutsideRadius, negate ),
|
||||
m_distance( distance )
|
||||
{
|
||||
}
|
||||
|
||||
bool isConditionMet( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const;
|
||||
};
|
||||
|
||||
class PlayerFilter : public TargetSelectFilter
|
||||
{
|
||||
public:
|
||||
PlayerFilter( bool negate ) :
|
||||
TargetSelectFilter( Type::Player, negate )
|
||||
{
|
||||
}
|
||||
|
||||
bool isConditionMet( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const;
|
||||
};
|
||||
|
||||
class AllyFilter : public TargetSelectFilter
|
||||
{
|
||||
public:
|
||||
AllyFilter( bool negate ) :
|
||||
TargetSelectFilter( Type::Ally, negate )
|
||||
{
|
||||
}
|
||||
|
||||
bool isConditionMet( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const;
|
||||
};
|
||||
|
||||
class OwnBattalionFilter : public TargetSelectFilter
|
||||
{
|
||||
public:
|
||||
OwnBattalionFilter( bool negate ) :
|
||||
TargetSelectFilter( Type::OwnBattalion, negate )
|
||||
{
|
||||
}
|
||||
|
||||
bool isConditionMet( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const;
|
||||
};
|
||||
|
||||
class TankFilter : public TargetSelectFilter
|
||||
{
|
||||
public:
|
||||
TankFilter( bool negate ) :
|
||||
TargetSelectFilter( Type::Tank, negate )
|
||||
{
|
||||
}
|
||||
|
||||
bool isConditionMet( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const;
|
||||
};
|
||||
|
||||
class HealerFilter : public TargetSelectFilter
|
||||
{
|
||||
public:
|
||||
HealerFilter( bool negate ) :
|
||||
TargetSelectFilter( Type::Healer, negate )
|
||||
{
|
||||
}
|
||||
|
||||
bool isConditionMet( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const;
|
||||
};
|
||||
|
||||
class DpsFilter : public TargetSelectFilter
|
||||
{
|
||||
public:
|
||||
DpsFilter( bool negate ) :
|
||||
TargetSelectFilter( Type::Dps, negate )
|
||||
{
|
||||
}
|
||||
|
||||
bool isConditionMet( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const;
|
||||
};
|
||||
|
||||
class HasStatusEffectFilter : public TargetSelectFilter
|
||||
{
|
||||
private:
|
||||
uint32_t m_statusId{ 0 };
|
||||
public:
|
||||
HasStatusEffectFilter( uint32_t statusId, bool negate ) :
|
||||
TargetSelectFilter( Type::HasStatusEffect, negate ),
|
||||
m_statusId( statusId )
|
||||
{
|
||||
}
|
||||
|
||||
bool isConditionMet( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const;
|
||||
};
|
||||
|
||||
class TopAggroFilter : public TargetSelectFilter
|
||||
{
|
||||
public:
|
||||
TopAggroFilter( bool negate ) :
|
||||
TargetSelectFilter( Type::TopAggro, negate )
|
||||
{
|
||||
}
|
||||
|
||||
bool isConditionMet( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const;
|
||||
};
|
||||
|
||||
class SecondAggroFilter : public TargetSelectFilter
|
||||
{
|
||||
public:
|
||||
SecondAggroFilter( bool negate ) :
|
||||
TargetSelectFilter( Type::SecondAggro, negate )
|
||||
{
|
||||
}
|
||||
|
||||
bool isConditionMet( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const;
|
||||
};
|
||||
|
||||
//
|
||||
// Helpers
|
||||
//
|
||||
class Snapshot :
|
||||
public std::enable_shared_from_this< Snapshot >
|
||||
{
|
||||
struct CharaEntry
|
||||
{
|
||||
uint32_t m_entityId;
|
||||
Common::FFXIVARR_POSITION3 m_pos;
|
||||
float m_rot;
|
||||
// todo: status effects?
|
||||
};
|
||||
private:
|
||||
std::vector< TargetSelectFilterPtr > m_filters;
|
||||
std::vector< CharaEntry > m_targets;
|
||||
|
||||
public:
|
||||
Snapshot( const std::vector< TargetSelectFilterPtr > filters ) :
|
||||
m_filters( filters )
|
||||
{
|
||||
}
|
||||
void createSnapshot( Entity::CharaPtr pSrc, const std::set< Entity::GameObjectPtr >& inRange,
|
||||
int count, bool fillWithRandom, const std::set< uint32_t >& exclude = {} );
|
||||
|
||||
// returns actors sorted by distance
|
||||
const std::vector< CharaEntry >& getResults() const;
|
||||
const std::vector< uint32_t > getTargetIds() const;
|
||||
};
|
||||
using SnapshotPtr = std::shared_ptr< Snapshot >;
|
||||
}// namespace Sapphire::World::AI
|
||||
|
||||
#endif
|
|
@ -442,6 +442,11 @@ void BNpc::sendPositionUpdate()
|
|||
server().queueForPlayers( getInRangePlayerIds(), movePacket );
|
||||
}
|
||||
|
||||
const std::set< std::shared_ptr< HateListEntry > >& BNpc::getHateList() const
|
||||
{
|
||||
return m_hateList;
|
||||
}
|
||||
|
||||
void BNpc::hateListClear()
|
||||
{
|
||||
for( auto& listEntry : m_hateList )
|
||||
|
@ -825,6 +830,11 @@ void BNpc::setFlag( uint32_t flag )
|
|||
m_flags |= flag;
|
||||
}
|
||||
|
||||
void BNpc::removeFlag( uint32_t flag )
|
||||
{
|
||||
m_flags &= ~flag;
|
||||
}
|
||||
|
||||
void BNpc::clearFlags()
|
||||
{
|
||||
m_flags = 0;
|
||||
|
|
|
@ -18,6 +18,7 @@ namespace Sapphire::Entity
|
|||
uint32_t m_hateAmount;
|
||||
CharaPtr m_pChara;
|
||||
};
|
||||
using HateListEntryPtr = std::shared_ptr< HateListEntry >;
|
||||
|
||||
enum class BNpcState
|
||||
{
|
||||
|
@ -39,6 +40,7 @@ namespace Sapphire::Entity
|
|||
NoDeaggro = 0x10,
|
||||
Untargetable = 0x20,
|
||||
AutoAttackDisabled = 0x40,
|
||||
Invisible = 0x80,
|
||||
|
||||
Intermission = 0x77 // for transition phases to ensure boss only moves/acts when scripted
|
||||
};
|
||||
|
@ -106,6 +108,7 @@ namespace Sapphire::Entity
|
|||
BNpcState getState() const;
|
||||
void setState( BNpcState state );
|
||||
|
||||
const std::set< std::shared_ptr< HateListEntry > >& getHateList() const;
|
||||
void hateListClear();
|
||||
uint32_t hateListGetValue( const Sapphire::Entity::CharaPtr& pChara );
|
||||
uint32_t hateListGetHighestValue();
|
||||
|
@ -115,7 +118,7 @@ namespace Sapphire::Entity
|
|||
void hateListUpdate( const CharaPtr& pChara, int32_t hateAmount );
|
||||
void hateListRemove( const CharaPtr& pChara );
|
||||
bool hateListHasActor( const CharaPtr& pChara );
|
||||
|
||||
|
||||
void aggro( const CharaPtr& pChara );
|
||||
void deaggro( const CharaPtr& pChara );
|
||||
|
||||
|
@ -143,6 +146,7 @@ namespace Sapphire::Entity
|
|||
|
||||
bool hasFlag( uint32_t flag ) const;
|
||||
void setFlag( uint32_t flags );
|
||||
void removeFlag( uint32_t flag );
|
||||
void clearFlags();
|
||||
|
||||
void calculateStats() override;
|
||||
|
|
|
@ -69,6 +69,7 @@ namespace Sapphire
|
|||
{
|
||||
auto pBattleNpc = pTeri->getActiveBNpcByLayoutId( this->layoutId );
|
||||
|
||||
// todo: these should really use callbacks when the state transitions or we could miss this tick
|
||||
switch( combatState )
|
||||
{
|
||||
case CombatStateType::Idle:
|
||||
|
@ -114,14 +115,7 @@ namespace Sapphire
|
|||
{
|
||||
case TimepointDataType::Idle:
|
||||
{
|
||||
auto pIdleData = std::dynamic_pointer_cast< TimepointDataIdle, TimepointData >( getData() );
|
||||
auto pBNpc = pTeri->getActiveBNpcByLayoutId( pIdleData->m_layoutId );
|
||||
|
||||
if( pBNpc )
|
||||
{
|
||||
// todo: idle
|
||||
|
||||
}
|
||||
// just wait up the duration of this timepoint
|
||||
}
|
||||
break;
|
||||
case TimepointDataType::CastAction:
|
||||
|
@ -162,7 +156,7 @@ namespace Sapphire
|
|||
else
|
||||
{
|
||||
// if we are at the pos, stop waiting
|
||||
state.m_finished = true;
|
||||
//state.m_finished = true;
|
||||
}
|
||||
pBNpc->setRot( pMoveToData->m_rot );
|
||||
}
|
||||
|
@ -557,7 +551,8 @@ namespace Sapphire
|
|||
{ "spawnBNpc", TimepointDataType::SpawnBNpc },
|
||||
{ "bNpcFlags", TimepointDataType::SetBNpcFlags },
|
||||
{ "setEObjState", TimepointDataType::SetEObjState },
|
||||
{ "setCondition", TimepointDataType::SetCondition }
|
||||
{ "setCondition", TimepointDataType::SetCondition },
|
||||
{ "snapshot", TimepointDataType::Snapshot }
|
||||
};
|
||||
|
||||
const static std::unordered_map< std::string, TimepointOverrideFlags > overrideFlagMap =
|
||||
|
@ -565,19 +560,6 @@ namespace Sapphire
|
|||
{}
|
||||
};
|
||||
|
||||
const static std::unordered_map< std::string, TargetSelectFilterFlags > targetFilterMap =
|
||||
{
|
||||
{ "self", TargetSelectFilterFlags::Self },
|
||||
{ "tank", TargetSelectFilterFlags::Tank },
|
||||
{ "healer", TargetSelectFilterFlags::Healer },
|
||||
{ "dps", TargetSelectFilterFlags::Dps },
|
||||
{ "melee", TargetSelectFilterFlags::Melee },
|
||||
{ "ranged", TargetSelectFilterFlags::Ranged },
|
||||
{ "furthest", TargetSelectFilterFlags::Furthest },
|
||||
{ "aggro1", TargetSelectFilterFlags::Aggro1 },
|
||||
{ "aggro2", TargetSelectFilterFlags::Aggro2 }
|
||||
};
|
||||
|
||||
const static std::unordered_map< std::string, TimepointCallbackType > callbackTypeMap =
|
||||
{
|
||||
{ "onActionInit", TimepointCallbackType::OnActionInit },
|
||||
|
@ -602,6 +584,12 @@ namespace Sapphire
|
|||
{ "and", DirectorOpId::And }
|
||||
};
|
||||
|
||||
const static std::unordered_map< std::string, Common::BNpcType > bnpcTypeMap =
|
||||
{
|
||||
{ "bnpc", Common::BNpcType::Enemy },
|
||||
{ "ally", Common::BNpcType::Friendly } // todo: rename this
|
||||
};
|
||||
|
||||
TimepointDataType tpType{ 0 };
|
||||
|
||||
auto typeStr = json.at( "type" ).get< std::string >();
|
||||
|
@ -727,7 +715,7 @@ namespace Sapphire
|
|||
auto& dataJ = json.at( "data" );
|
||||
auto flags = dataJ.at( "val" ).get< uint32_t >();
|
||||
auto opStr = dataJ.at( "opc" ).get< std::string >();
|
||||
DirectorOpId op = directorOpMap.find( opStr )->second;
|
||||
DirectorOpId op = directorOpMap.at( opStr );
|
||||
|
||||
auto pDirectorData = std::make_shared< TimepointDataDirector >( tpType, op );
|
||||
pDirectorData->m_data.flags = flags;
|
||||
|
@ -753,6 +741,9 @@ namespace Sapphire
|
|||
auto hateSrcJ = dataJ.at( "hateSrc" );
|
||||
auto actorRef = dataJ.at( "spawnActor" ).get< std::string >();
|
||||
auto flags = dataJ.at( "flags" ).get< uint32_t >();
|
||||
// todo: batallion
|
||||
// auto battalion = dataJ.at( "batallion" ).get< uint32_t >();
|
||||
auto bnpcType = bnpcTypeMap.at( dataJ.at( "type" ).get< std::string >() );
|
||||
|
||||
// todo: hateSrc
|
||||
|
||||
|
@ -762,7 +753,7 @@ namespace Sapphire
|
|||
else
|
||||
throw std::runtime_error( fmt::format( std::string( "EncounterTimeline::Timepoint::from_json: SpawnBNpc invalid actor ref: %s" ), actorRef ) );
|
||||
|
||||
m_pData = std::make_shared< TimepointDataSpawnBNpc >( layoutId, flags );
|
||||
m_pData = std::make_shared< TimepointDataSpawnBNpc >( layoutId, flags, bnpcType );
|
||||
}
|
||||
break;
|
||||
case TimepointDataType::SetBNpcFlags:
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
#include <fstream>
|
||||
#include <memory>
|
||||
#include <map>
|
||||
|
||||
#include <optional>
|
||||
#include <stack>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <queue>
|
||||
|
||||
#include <Common.h>
|
||||
#include <Forwards.h>
|
||||
|
@ -116,7 +116,9 @@ namespace Sapphire
|
|||
SetEObjState,
|
||||
SetBgm,
|
||||
|
||||
SetCondition
|
||||
SetCondition,
|
||||
|
||||
Snapshot
|
||||
};
|
||||
|
||||
enum class TimepointCallbackType : uint32_t
|
||||
|
@ -193,7 +195,7 @@ namespace Sapphire
|
|||
std::vector < TimepointCallbackFunc > m_callbacks;
|
||||
};
|
||||
using TimebackCallbackDataPtr = std::shared_ptr< TimepointCallbackData >;
|
||||
using TimepointCallbacks = std::map< TimepointCallbackType, TimebackCallbackDataPtr >;
|
||||
using TimepointCallbacks = std::unordered_map< TimepointCallbackType, TimebackCallbackDataPtr >;
|
||||
|
||||
|
||||
struct TimepointData :
|
||||
|
@ -341,12 +343,15 @@ namespace Sapphire
|
|||
{
|
||||
uint32_t m_layoutId{ 0xE0000000 };
|
||||
uint32_t m_flags{ 0 };
|
||||
uint32_t m_type{ 0 };
|
||||
|
||||
// todo: hate type, source
|
||||
|
||||
TimepointDataSpawnBNpc( uint32_t layoutId, uint32_t flags ) :
|
||||
TimepointDataSpawnBNpc( uint32_t layoutId, uint32_t flags, uint32_t type ) :
|
||||
TimepointData( TimepointDataType::SpawnBNpc ),
|
||||
m_layoutId( layoutId ),
|
||||
m_flags( flags)
|
||||
m_flags( flags ),
|
||||
m_type( type )
|
||||
{
|
||||
}
|
||||
};
|
||||
|
@ -615,13 +620,21 @@ namespace Sapphire
|
|||
};
|
||||
using PhaseConditionPtr = std::shared_ptr< PhaseCondition >;
|
||||
|
||||
// todo: bnpc parts
|
||||
class TimelineBNpcPart
|
||||
{
|
||||
uint32_t m_hp{ 0 };
|
||||
std::string m_name;
|
||||
};
|
||||
|
||||
class TimelineActor
|
||||
{
|
||||
protected:
|
||||
std::unordered_map< uint32_t, PhaseConditionPtr > m_phaseConditions;
|
||||
std::unordered_map< uint32_t, ConditionState > m_conditionStates;
|
||||
|
||||
// PARENTNAME_SUBACTOR_1, ..., PARENTNAME_SUBACTOR_69
|
||||
std::map< std::string, Entity::BNpcPtr > m_subActors;
|
||||
std::unordered_map< std::string, Entity::BNpcPtr > m_subActors;
|
||||
public:
|
||||
uint32_t m_layoutId{ 0 };
|
||||
uint32_t m_hp{ 0 };
|
||||
|
|
Loading…
Add table
Reference in a new issue