mirror of
https://github.com/SapphireServer/Sapphire.git
synced 2025-05-07 19:27: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 );
|
server().queueForPlayers( getInRangePlayerIds(), movePacket );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::set< std::shared_ptr< HateListEntry > >& BNpc::getHateList() const
|
||||||
|
{
|
||||||
|
return m_hateList;
|
||||||
|
}
|
||||||
|
|
||||||
void BNpc::hateListClear()
|
void BNpc::hateListClear()
|
||||||
{
|
{
|
||||||
for( auto& listEntry : m_hateList )
|
for( auto& listEntry : m_hateList )
|
||||||
|
@ -825,6 +830,11 @@ void BNpc::setFlag( uint32_t flag )
|
||||||
m_flags |= flag;
|
m_flags |= flag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BNpc::removeFlag( uint32_t flag )
|
||||||
|
{
|
||||||
|
m_flags &= ~flag;
|
||||||
|
}
|
||||||
|
|
||||||
void BNpc::clearFlags()
|
void BNpc::clearFlags()
|
||||||
{
|
{
|
||||||
m_flags = 0;
|
m_flags = 0;
|
||||||
|
|
|
@ -18,6 +18,7 @@ namespace Sapphire::Entity
|
||||||
uint32_t m_hateAmount;
|
uint32_t m_hateAmount;
|
||||||
CharaPtr m_pChara;
|
CharaPtr m_pChara;
|
||||||
};
|
};
|
||||||
|
using HateListEntryPtr = std::shared_ptr< HateListEntry >;
|
||||||
|
|
||||||
enum class BNpcState
|
enum class BNpcState
|
||||||
{
|
{
|
||||||
|
@ -39,6 +40,7 @@ namespace Sapphire::Entity
|
||||||
NoDeaggro = 0x10,
|
NoDeaggro = 0x10,
|
||||||
Untargetable = 0x20,
|
Untargetable = 0x20,
|
||||||
AutoAttackDisabled = 0x40,
|
AutoAttackDisabled = 0x40,
|
||||||
|
Invisible = 0x80,
|
||||||
|
|
||||||
Intermission = 0x77 // for transition phases to ensure boss only moves/acts when scripted
|
Intermission = 0x77 // for transition phases to ensure boss only moves/acts when scripted
|
||||||
};
|
};
|
||||||
|
@ -106,6 +108,7 @@ namespace Sapphire::Entity
|
||||||
BNpcState getState() const;
|
BNpcState getState() const;
|
||||||
void setState( BNpcState state );
|
void setState( BNpcState state );
|
||||||
|
|
||||||
|
const std::set< std::shared_ptr< HateListEntry > >& getHateList() const;
|
||||||
void hateListClear();
|
void hateListClear();
|
||||||
uint32_t hateListGetValue( const Sapphire::Entity::CharaPtr& pChara );
|
uint32_t hateListGetValue( const Sapphire::Entity::CharaPtr& pChara );
|
||||||
uint32_t hateListGetHighestValue();
|
uint32_t hateListGetHighestValue();
|
||||||
|
@ -115,7 +118,7 @@ namespace Sapphire::Entity
|
||||||
void hateListUpdate( const CharaPtr& pChara, int32_t hateAmount );
|
void hateListUpdate( const CharaPtr& pChara, int32_t hateAmount );
|
||||||
void hateListRemove( const CharaPtr& pChara );
|
void hateListRemove( const CharaPtr& pChara );
|
||||||
bool hateListHasActor( const CharaPtr& pChara );
|
bool hateListHasActor( const CharaPtr& pChara );
|
||||||
|
|
||||||
void aggro( const CharaPtr& pChara );
|
void aggro( const CharaPtr& pChara );
|
||||||
void deaggro( const CharaPtr& pChara );
|
void deaggro( const CharaPtr& pChara );
|
||||||
|
|
||||||
|
@ -143,6 +146,7 @@ namespace Sapphire::Entity
|
||||||
|
|
||||||
bool hasFlag( uint32_t flag ) const;
|
bool hasFlag( uint32_t flag ) const;
|
||||||
void setFlag( uint32_t flags );
|
void setFlag( uint32_t flags );
|
||||||
|
void removeFlag( uint32_t flag );
|
||||||
void clearFlags();
|
void clearFlags();
|
||||||
|
|
||||||
void calculateStats() override;
|
void calculateStats() override;
|
||||||
|
|
|
@ -69,6 +69,7 @@ namespace Sapphire
|
||||||
{
|
{
|
||||||
auto pBattleNpc = pTeri->getActiveBNpcByLayoutId( this->layoutId );
|
auto pBattleNpc = pTeri->getActiveBNpcByLayoutId( this->layoutId );
|
||||||
|
|
||||||
|
// todo: these should really use callbacks when the state transitions or we could miss this tick
|
||||||
switch( combatState )
|
switch( combatState )
|
||||||
{
|
{
|
||||||
case CombatStateType::Idle:
|
case CombatStateType::Idle:
|
||||||
|
@ -114,14 +115,7 @@ namespace Sapphire
|
||||||
{
|
{
|
||||||
case TimepointDataType::Idle:
|
case TimepointDataType::Idle:
|
||||||
{
|
{
|
||||||
auto pIdleData = std::dynamic_pointer_cast< TimepointDataIdle, TimepointData >( getData() );
|
// just wait up the duration of this timepoint
|
||||||
auto pBNpc = pTeri->getActiveBNpcByLayoutId( pIdleData->m_layoutId );
|
|
||||||
|
|
||||||
if( pBNpc )
|
|
||||||
{
|
|
||||||
// todo: idle
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case TimepointDataType::CastAction:
|
case TimepointDataType::CastAction:
|
||||||
|
@ -162,7 +156,7 @@ namespace Sapphire
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// if we are at the pos, stop waiting
|
// if we are at the pos, stop waiting
|
||||||
state.m_finished = true;
|
//state.m_finished = true;
|
||||||
}
|
}
|
||||||
pBNpc->setRot( pMoveToData->m_rot );
|
pBNpc->setRot( pMoveToData->m_rot );
|
||||||
}
|
}
|
||||||
|
@ -557,7 +551,8 @@ namespace Sapphire
|
||||||
{ "spawnBNpc", TimepointDataType::SpawnBNpc },
|
{ "spawnBNpc", TimepointDataType::SpawnBNpc },
|
||||||
{ "bNpcFlags", TimepointDataType::SetBNpcFlags },
|
{ "bNpcFlags", TimepointDataType::SetBNpcFlags },
|
||||||
{ "setEObjState", TimepointDataType::SetEObjState },
|
{ "setEObjState", TimepointDataType::SetEObjState },
|
||||||
{ "setCondition", TimepointDataType::SetCondition }
|
{ "setCondition", TimepointDataType::SetCondition },
|
||||||
|
{ "snapshot", TimepointDataType::Snapshot }
|
||||||
};
|
};
|
||||||
|
|
||||||
const static std::unordered_map< std::string, TimepointOverrideFlags > overrideFlagMap =
|
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 =
|
const static std::unordered_map< std::string, TimepointCallbackType > callbackTypeMap =
|
||||||
{
|
{
|
||||||
{ "onActionInit", TimepointCallbackType::OnActionInit },
|
{ "onActionInit", TimepointCallbackType::OnActionInit },
|
||||||
|
@ -602,6 +584,12 @@ namespace Sapphire
|
||||||
{ "and", DirectorOpId::And }
|
{ "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 };
|
TimepointDataType tpType{ 0 };
|
||||||
|
|
||||||
auto typeStr = json.at( "type" ).get< std::string >();
|
auto typeStr = json.at( "type" ).get< std::string >();
|
||||||
|
@ -727,7 +715,7 @@ namespace Sapphire
|
||||||
auto& dataJ = json.at( "data" );
|
auto& dataJ = json.at( "data" );
|
||||||
auto flags = dataJ.at( "val" ).get< uint32_t >();
|
auto flags = dataJ.at( "val" ).get< uint32_t >();
|
||||||
auto opStr = dataJ.at( "opc" ).get< std::string >();
|
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 );
|
auto pDirectorData = std::make_shared< TimepointDataDirector >( tpType, op );
|
||||||
pDirectorData->m_data.flags = flags;
|
pDirectorData->m_data.flags = flags;
|
||||||
|
@ -753,6 +741,9 @@ namespace Sapphire
|
||||||
auto hateSrcJ = dataJ.at( "hateSrc" );
|
auto hateSrcJ = dataJ.at( "hateSrc" );
|
||||||
auto actorRef = dataJ.at( "spawnActor" ).get< std::string >();
|
auto actorRef = dataJ.at( "spawnActor" ).get< std::string >();
|
||||||
auto flags = dataJ.at( "flags" ).get< uint32_t >();
|
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
|
// todo: hateSrc
|
||||||
|
|
||||||
|
@ -762,7 +753,7 @@ namespace Sapphire
|
||||||
else
|
else
|
||||||
throw std::runtime_error( fmt::format( std::string( "EncounterTimeline::Timepoint::from_json: SpawnBNpc invalid actor ref: %s" ), actorRef ) );
|
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;
|
break;
|
||||||
case TimepointDataType::SetBNpcFlags:
|
case TimepointDataType::SetBNpcFlags:
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <map>
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <stack>
|
#include <stack>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <queue>
|
|
||||||
|
|
||||||
#include <Common.h>
|
#include <Common.h>
|
||||||
#include <Forwards.h>
|
#include <Forwards.h>
|
||||||
|
@ -116,7 +116,9 @@ namespace Sapphire
|
||||||
SetEObjState,
|
SetEObjState,
|
||||||
SetBgm,
|
SetBgm,
|
||||||
|
|
||||||
SetCondition
|
SetCondition,
|
||||||
|
|
||||||
|
Snapshot
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class TimepointCallbackType : uint32_t
|
enum class TimepointCallbackType : uint32_t
|
||||||
|
@ -193,7 +195,7 @@ namespace Sapphire
|
||||||
std::vector < TimepointCallbackFunc > m_callbacks;
|
std::vector < TimepointCallbackFunc > m_callbacks;
|
||||||
};
|
};
|
||||||
using TimebackCallbackDataPtr = std::shared_ptr< TimepointCallbackData >;
|
using TimebackCallbackDataPtr = std::shared_ptr< TimepointCallbackData >;
|
||||||
using TimepointCallbacks = std::map< TimepointCallbackType, TimebackCallbackDataPtr >;
|
using TimepointCallbacks = std::unordered_map< TimepointCallbackType, TimebackCallbackDataPtr >;
|
||||||
|
|
||||||
|
|
||||||
struct TimepointData :
|
struct TimepointData :
|
||||||
|
@ -341,12 +343,15 @@ namespace Sapphire
|
||||||
{
|
{
|
||||||
uint32_t m_layoutId{ 0xE0000000 };
|
uint32_t m_layoutId{ 0xE0000000 };
|
||||||
uint32_t m_flags{ 0 };
|
uint32_t m_flags{ 0 };
|
||||||
|
uint32_t m_type{ 0 };
|
||||||
|
|
||||||
// todo: hate type, source
|
// todo: hate type, source
|
||||||
|
|
||||||
TimepointDataSpawnBNpc( uint32_t layoutId, uint32_t flags ) :
|
TimepointDataSpawnBNpc( uint32_t layoutId, uint32_t flags, uint32_t type ) :
|
||||||
TimepointData( TimepointDataType::SpawnBNpc ),
|
TimepointData( TimepointDataType::SpawnBNpc ),
|
||||||
m_layoutId( layoutId ),
|
m_layoutId( layoutId ),
|
||||||
m_flags( flags)
|
m_flags( flags ),
|
||||||
|
m_type( type )
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -615,13 +620,21 @@ namespace Sapphire
|
||||||
};
|
};
|
||||||
using PhaseConditionPtr = std::shared_ptr< PhaseCondition >;
|
using PhaseConditionPtr = std::shared_ptr< PhaseCondition >;
|
||||||
|
|
||||||
|
// todo: bnpc parts
|
||||||
|
class TimelineBNpcPart
|
||||||
|
{
|
||||||
|
uint32_t m_hp{ 0 };
|
||||||
|
std::string m_name;
|
||||||
|
};
|
||||||
|
|
||||||
class TimelineActor
|
class TimelineActor
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
std::unordered_map< uint32_t, PhaseConditionPtr > m_phaseConditions;
|
std::unordered_map< uint32_t, PhaseConditionPtr > m_phaseConditions;
|
||||||
std::unordered_map< uint32_t, ConditionState > m_conditionStates;
|
std::unordered_map< uint32_t, ConditionState > m_conditionStates;
|
||||||
|
|
||||||
// PARENTNAME_SUBACTOR_1, ..., PARENTNAME_SUBACTOR_69
|
// PARENTNAME_SUBACTOR_1, ..., PARENTNAME_SUBACTOR_69
|
||||||
std::map< std::string, Entity::BNpcPtr > m_subActors;
|
std::unordered_map< std::string, Entity::BNpcPtr > m_subActors;
|
||||||
public:
|
public:
|
||||||
uint32_t m_layoutId{ 0 };
|
uint32_t m_layoutId{ 0 };
|
||||||
uint32_t m_hp{ 0 };
|
uint32_t m_hp{ 0 };
|
||||||
|
|
Loading…
Add table
Reference in a new issue