mirror of
https://github.com/SapphireServer/Sapphire.git
synced 2025-04-28 23:27:45 +00:00
split encounter timeline stuff into separate files
- todo: use selector in CastAction timepoint
This commit is contained in:
parent
8fb9846c0b
commit
7cc3f65571
14 changed files with 2091 additions and 1625 deletions
|
@ -4,6 +4,8 @@
|
||||||
|
|
||||||
#include <Actor/BNpc.h>
|
#include <Actor/BNpc.h>
|
||||||
#include <Actor/Chara.h>
|
#include <Actor/Chara.h>
|
||||||
|
#include <Actor/Player.h>
|
||||||
|
#include <Manager/PartyMgr.h>
|
||||||
#include <Manager/RNGMgr.h>
|
#include <Manager/RNGMgr.h>
|
||||||
#include <Util/UtilMath.h>
|
#include <Util/UtilMath.h>
|
||||||
#include <Service.h>
|
#include <Service.h>
|
||||||
|
@ -11,27 +13,28 @@
|
||||||
namespace Sapphire::World::AI
|
namespace Sapphire::World::AI
|
||||||
{
|
{
|
||||||
|
|
||||||
bool InsideRadiusFilter::isConditionMet( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const
|
bool InsideRadiusFilter::isApplicable( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const
|
||||||
{
|
{
|
||||||
bool ret = Common::Util::distance( pSrc->getPos(), pTarget->getPos() ) <= m_distance;
|
bool ret = Common::Util::distance( pSrc->getPos(), pTarget->getPos() ) <= m_distance;
|
||||||
return m_negate ? !ret : ret;
|
return m_negate ? !ret : ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OutsideRadiusFilter::isConditionMet( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const
|
bool OutsideRadiusFilter::isApplicable( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const
|
||||||
{
|
{
|
||||||
bool ret = Common::Util::distance( pSrc->getPos(), pTarget->getPos() ) >= m_distance;
|
bool ret = Common::Util::distance( pSrc->getPos(), pTarget->getPos() ) >= m_distance;
|
||||||
return m_negate ? !ret : ret;
|
return m_negate ? !ret : ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PlayerFilter::isConditionMet( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const
|
bool PlayerFilter::isApplicable( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const
|
||||||
{
|
{
|
||||||
bool ret = pTarget->isPlayer();
|
bool ret = pTarget->isPlayer();
|
||||||
return m_negate ? !ret : ret;
|
return m_negate ? !ret : ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AllyFilter::isConditionMet( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const
|
bool AllyFilter::isApplicable( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const
|
||||||
{
|
{
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
|
// todo: pets, companions, enpc
|
||||||
if( pSrc->isPlayer() )
|
if( pSrc->isPlayer() )
|
||||||
{
|
{
|
||||||
auto pBNpcTarget = pTarget->getAsBNpc();
|
auto pBNpcTarget = pTarget->getAsBNpc();
|
||||||
|
@ -52,24 +55,24 @@ namespace Sapphire::World::AI
|
||||||
return m_negate ? !ret : ret;
|
return m_negate ? !ret : ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OwnBattalionFilter::isConditionMet( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const
|
bool OwnBattalionFilter::isApplicable( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TankFilter::isConditionMet( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const
|
bool TankFilter::isApplicable( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const
|
||||||
{
|
{
|
||||||
bool ret = pTarget->getRole() == Common::Role::Tank;
|
bool ret = pTarget->getRole() == Common::Role::Tank;
|
||||||
return m_negate ? !ret : ret;
|
return m_negate ? !ret : ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HealerFilter::isConditionMet( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const
|
bool HealerFilter::isApplicable( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const
|
||||||
{
|
{
|
||||||
bool ret = pTarget->getRole() == Common::Role::Healer;
|
bool ret = pTarget->getRole() == Common::Role::Healer;
|
||||||
return m_negate ? !ret : ret;
|
return m_negate ? !ret : ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DpsFilter::isConditionMet( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const
|
bool DpsFilter::isApplicable( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const
|
||||||
{
|
{
|
||||||
bool ret = true;
|
bool ret = true;
|
||||||
switch( pTarget->getRole() )
|
switch( pTarget->getRole() )
|
||||||
|
@ -86,13 +89,13 @@ namespace Sapphire::World::AI
|
||||||
return m_negate ? !ret : ret;
|
return m_negate ? !ret : ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HasStatusEffectFilter::isConditionMet( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const
|
bool HasStatusEffectFilter::isApplicable( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const
|
||||||
{
|
{
|
||||||
auto ret = pTarget->hasStatusEffect( m_statusId );
|
auto ret = pTarget->hasStatusEffect( m_statusId );
|
||||||
return m_negate ? !ret : ret;
|
return m_negate ? !ret : ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TopAggroFilter ::isConditionMet( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const
|
bool TopAggroFilter::isApplicable( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const
|
||||||
{
|
{
|
||||||
auto pBNpc = pSrc->getAsBNpc();
|
auto pBNpc = pSrc->getAsBNpc();
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
|
@ -103,7 +106,7 @@ namespace Sapphire::World::AI
|
||||||
return m_negate ? !ret : ret;
|
return m_negate ? !ret : ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SecondAggroFilter ::isConditionMet( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const
|
bool SecondAggroFilter::isApplicable( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const
|
||||||
{
|
{
|
||||||
auto pBNpc = pSrc->getAsBNpc();
|
auto pBNpc = pSrc->getAsBNpc();
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
|
@ -126,10 +129,40 @@ namespace Sapphire::World::AI
|
||||||
return m_negate ? !ret : ret;
|
return m_negate ? !ret : ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Snapshot::createSnapshot( Entity::CharaPtr pSrc, const std::set< Entity::GameObjectPtr >& inRange,
|
bool PartyMemberFilter::isApplicable( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const
|
||||||
int count, bool fillWithRandom, const std::set< uint32_t >& exclude )
|
|
||||||
{
|
{
|
||||||
m_targets.clear();
|
bool ret = false;
|
||||||
|
// todo: pets, companions, enpc
|
||||||
|
if( auto pPlayer = pSrc->getAsPlayer() )
|
||||||
|
{
|
||||||
|
if( auto pTargetPlayer = pTarget->getAsPlayer() )
|
||||||
|
{
|
||||||
|
ret = pPlayer->getPartyId() == pTargetPlayer->getPartyId();
|
||||||
|
}
|
||||||
|
else if( auto pBNpc = pTarget->getAsBNpc() )
|
||||||
|
{
|
||||||
|
ret = pBNpc->getBNpcType() == 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if( auto pBNpc = pSrc->getAsBNpc() )
|
||||||
|
{
|
||||||
|
if( auto pTargetPlayer = pTarget->getAsPlayer() )
|
||||||
|
{
|
||||||
|
ret = pPlayer->getPartyId() == pTargetPlayer->getPartyId();
|
||||||
|
}
|
||||||
|
else if( auto pTargetBNpc = pTarget->getAsBNpc() )
|
||||||
|
{
|
||||||
|
ret = pBNpc->getBNpcType() == pTargetBNpc->getEnemyType();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return m_negate ? !ret : ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Snapshot::createSnapshot( Entity::CharaPtr pSrc, const std::set< Entity::GameObjectPtr >& inRange,
|
||||||
|
int count, bool fillWithRandom, const std::vector< uint32_t >& exclude )
|
||||||
|
{
|
||||||
|
m_results.clear();
|
||||||
|
m_targetIds.clear();
|
||||||
|
|
||||||
auto& RNGMgr = Common::Service< World::Manager::RNGMgr >::ref();
|
auto& RNGMgr = Common::Service< World::Manager::RNGMgr >::ref();
|
||||||
for( const auto& pActor : inRange )
|
for( const auto& pActor : inRange )
|
||||||
|
@ -138,12 +171,17 @@ namespace Sapphire::World::AI
|
||||||
if( pChara == nullptr )
|
if( pChara == nullptr )
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if( exclude.find( pChara->getId() ) != exclude.end() ) continue;
|
// exclude this character from the result set
|
||||||
|
auto excludeIt = std::find_if( exclude.begin(), exclude.end(),
|
||||||
|
[ pChara ]( uint32_t id ) { return pChara->getId() == id; }
|
||||||
|
);
|
||||||
|
if( excludeIt != exclude.end() )
|
||||||
|
continue;
|
||||||
|
|
||||||
bool matches = true;
|
bool matches = true;
|
||||||
for( const auto& filter : m_filters )
|
for( const auto& filter : m_filters )
|
||||||
{
|
{
|
||||||
if( !filter->isConditionMet( pSrc, pChara ) )
|
if( !filter->isApplicable( pSrc, pChara ) )
|
||||||
{
|
{
|
||||||
matches = false;
|
matches = false;
|
||||||
break;
|
break;
|
||||||
|
@ -157,12 +195,13 @@ namespace Sapphire::World::AI
|
||||||
entry.m_pos = pChara->getPos();
|
entry.m_pos = pChara->getPos();
|
||||||
entry.m_rot = pChara->getRot();
|
entry.m_rot = pChara->getRot();
|
||||||
|
|
||||||
m_targets.push_back( entry );
|
m_results.push_back( entry );
|
||||||
if( m_targets.size() == count ) break;
|
if( m_results.size() == count ) break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if( fillWithRandom && m_targets.size() < count )
|
// fallback to fill with random entries if we dont have enough valid results
|
||||||
|
if( fillWithRandom && m_results.size() < count )
|
||||||
{
|
{
|
||||||
std::vector< Entity::CharaPtr > remaining;
|
std::vector< Entity::CharaPtr > remaining;
|
||||||
for( const auto& pActor : inRange )
|
for( const auto& pActor : inRange )
|
||||||
|
@ -171,48 +210,54 @@ namespace Sapphire::World::AI
|
||||||
if( pChara == nullptr )
|
if( pChara == nullptr )
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if( exclude.find( pChara->getId() ) == exclude.end() && std::find_if( m_targets.begin(), m_targets.end(),
|
auto excludeIt = std::find_if( exclude.begin(), exclude.end(),
|
||||||
[ &pChara ]( CharaEntry entry ) { return entry.m_entityId == pChara->getId(); } ) == m_targets.end() )
|
[ pChara ]( uint32_t id ) { return pChara->getId() == id; }
|
||||||
|
);
|
||||||
|
|
||||||
|
if( excludeIt == exclude.end() && std::find_if( m_results.begin(), m_results.end(),
|
||||||
|
[ &pChara ]( CharaEntry entry ) { return entry.m_entityId == pChara->getId(); } ) == m_results.end() )
|
||||||
{
|
{
|
||||||
remaining.push_back( pChara );
|
remaining.push_back( pChara );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
while( m_targets.size() < count && !remaining.empty() )
|
while( m_results.size() < count && !remaining.empty() )
|
||||||
{
|
{
|
||||||
// idk
|
// idk
|
||||||
std::shuffle( remaining.begin(), remaining.end(), *RNGMgr.getRNGEngine().get() );
|
std::shuffle( remaining.begin(), remaining.end(), *RNGMgr.getRNGEngine() );
|
||||||
|
|
||||||
auto pChara = remaining.back();
|
auto pChara = remaining.back();
|
||||||
CharaEntry entry;
|
CharaEntry entry;
|
||||||
entry.m_entityId = pChara->getId();
|
entry.m_entityId = pChara->getId();
|
||||||
entry.m_pos = pChara->getPos();
|
entry.m_pos = pChara->getPos();
|
||||||
entry.m_rot = pChara->getRot();
|
entry.m_rot = pChara->getRot();
|
||||||
m_targets.emplace_back( entry );
|
m_results.emplace_back( entry );
|
||||||
remaining.pop_back();
|
remaining.pop_back();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// sort by distance at the end always
|
// sort by distance at the end always
|
||||||
auto srcPos = pSrc->getPos();
|
auto srcPos = pSrc->getPos();
|
||||||
std::sort( m_targets.begin(), m_targets.end(),
|
std::sort( m_results.begin(), m_results.end(),
|
||||||
[ srcPos ]( CharaEntry l, CharaEntry r )
|
[ srcPos ]( CharaEntry l, CharaEntry r )
|
||||||
{
|
{
|
||||||
return Common::Util::distance( srcPos, l.m_pos ) < Common::Util::distance( srcPos, r.m_pos );
|
return Common::Util::distance( srcPos, l.m_pos ) < Common::Util::distance( srcPos, r.m_pos );
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// we might want the target ids separately
|
||||||
|
m_targetIds.resize( m_results.size() );
|
||||||
|
for( auto i = 0; i < m_results.size(); ++i )
|
||||||
|
m_targetIds[ i ] = m_results[ i ].m_entityId;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::vector< Snapshot::CharaEntry >& Snapshot::getResults() const
|
const std::vector< Snapshot::CharaEntry >& Snapshot::getResults() const
|
||||||
{
|
{
|
||||||
return m_targets;
|
return m_results;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::vector< uint32_t > Snapshot::getTargetIds() const
|
const std::vector< uint32_t > Snapshot::getTargetIds() const
|
||||||
{
|
{
|
||||||
std::vector< uint32_t > ret( m_targets.size() );
|
return m_targetIds;
|
||||||
for( auto i = 0; i < m_targets.size(); ++i )
|
|
||||||
ret[ i ] = m_targets[ i ].m_entityId;
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
};// namespace Sapphire::World::AI
|
};// namespace Sapphire::World::AI
|
|
@ -35,6 +35,8 @@ namespace Sapphire::World::AI
|
||||||
TopAggro,
|
TopAggro,
|
||||||
SecondAggro,
|
SecondAggro,
|
||||||
|
|
||||||
|
PartyMember,
|
||||||
|
|
||||||
AllianceA,
|
AllianceA,
|
||||||
AllianceB,
|
AllianceB,
|
||||||
AllianceC
|
AllianceC
|
||||||
|
@ -54,7 +56,7 @@ namespace Sapphire::World::AI
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isConditionMet( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const
|
virtual bool isApplicable( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
@ -78,7 +80,7 @@ namespace Sapphire::World::AI
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isConditionMet( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const;
|
bool isApplicable( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class OutsideRadiusFilter : public TargetSelectFilter
|
class OutsideRadiusFilter : public TargetSelectFilter
|
||||||
|
@ -92,7 +94,7 @@ namespace Sapphire::World::AI
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isConditionMet( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const;
|
bool isApplicable( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class PlayerFilter : public TargetSelectFilter
|
class PlayerFilter : public TargetSelectFilter
|
||||||
|
@ -103,7 +105,7 @@ namespace Sapphire::World::AI
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isConditionMet( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const;
|
bool isApplicable( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class AllyFilter : public TargetSelectFilter
|
class AllyFilter : public TargetSelectFilter
|
||||||
|
@ -114,7 +116,7 @@ namespace Sapphire::World::AI
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isConditionMet( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const;
|
bool isApplicable( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class OwnBattalionFilter : public TargetSelectFilter
|
class OwnBattalionFilter : public TargetSelectFilter
|
||||||
|
@ -125,7 +127,7 @@ namespace Sapphire::World::AI
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isConditionMet( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const;
|
bool isApplicable( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class TankFilter : public TargetSelectFilter
|
class TankFilter : public TargetSelectFilter
|
||||||
|
@ -136,7 +138,7 @@ namespace Sapphire::World::AI
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isConditionMet( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const;
|
bool isApplicable( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class HealerFilter : public TargetSelectFilter
|
class HealerFilter : public TargetSelectFilter
|
||||||
|
@ -147,7 +149,7 @@ namespace Sapphire::World::AI
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isConditionMet( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const;
|
bool isApplicable( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class DpsFilter : public TargetSelectFilter
|
class DpsFilter : public TargetSelectFilter
|
||||||
|
@ -158,7 +160,7 @@ namespace Sapphire::World::AI
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isConditionMet( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const;
|
bool isApplicable( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class HasStatusEffectFilter : public TargetSelectFilter
|
class HasStatusEffectFilter : public TargetSelectFilter
|
||||||
|
@ -172,7 +174,7 @@ namespace Sapphire::World::AI
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isConditionMet( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const;
|
bool isApplicable( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class TopAggroFilter : public TargetSelectFilter
|
class TopAggroFilter : public TargetSelectFilter
|
||||||
|
@ -183,7 +185,7 @@ namespace Sapphire::World::AI
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isConditionMet( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const;
|
bool isApplicable( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class SecondAggroFilter : public TargetSelectFilter
|
class SecondAggroFilter : public TargetSelectFilter
|
||||||
|
@ -194,7 +196,18 @@ namespace Sapphire::World::AI
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isConditionMet( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const;
|
bool isApplicable( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PartyMemberFilter : public TargetSelectFilter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PartyMemberFilter( bool negate ) :
|
||||||
|
TargetSelectFilter( Type::PartyMember, negate )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isApplicable( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -203,6 +216,7 @@ namespace Sapphire::World::AI
|
||||||
class Snapshot :
|
class Snapshot :
|
||||||
public std::enable_shared_from_this< Snapshot >
|
public std::enable_shared_from_this< Snapshot >
|
||||||
{
|
{
|
||||||
|
public:
|
||||||
struct CharaEntry
|
struct CharaEntry
|
||||||
{
|
{
|
||||||
uint32_t m_entityId;
|
uint32_t m_entityId;
|
||||||
|
@ -210,17 +224,20 @@ namespace Sapphire::World::AI
|
||||||
float m_rot;
|
float m_rot;
|
||||||
// todo: status effects?
|
// todo: status effects?
|
||||||
};
|
};
|
||||||
|
using Results = std::vector< CharaEntry >;
|
||||||
|
using TargetIds = std::vector< uint32_t >;
|
||||||
private:
|
private:
|
||||||
std::vector< TargetSelectFilterPtr > m_filters;
|
std::vector< TargetSelectFilterPtr > m_filters;
|
||||||
std::vector< CharaEntry > m_targets;
|
std::vector< CharaEntry > m_results;
|
||||||
|
std::vector< uint32_t > m_targetIds;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Snapshot( const std::vector< TargetSelectFilterPtr > filters ) :
|
Snapshot( const std::vector< TargetSelectFilterPtr >& filters ) :
|
||||||
m_filters( filters )
|
m_filters( filters )
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
void createSnapshot( Entity::CharaPtr pSrc, const std::set< Entity::GameObjectPtr >& inRange,
|
void createSnapshot( Entity::CharaPtr pSrc, const std::set< Entity::GameObjectPtr >& inRange,
|
||||||
int count, bool fillWithRandom, const std::set< uint32_t >& exclude = {} );
|
int count, bool fillWithRandom, const std::vector< uint32_t >& exclude = {} );
|
||||||
|
|
||||||
// returns actors sorted by distance
|
// returns actors sorted by distance
|
||||||
const std::vector< CharaEntry >& getResults() const;
|
const std::vector< CharaEntry >& getResults() const;
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
#include "EncounterFight.h"
|
|
||||||
#include "EncounterTimeline.h"
|
#include "EncounterTimeline.h"
|
||||||
|
|
||||||
|
#include "Timepoint.h"
|
||||||
|
#include "TimelineActorState.h"
|
||||||
|
#include "TimelineActor.h"
|
||||||
|
#include "PhaseCondition.h"
|
||||||
|
#include "Selector.h"
|
||||||
|
|
||||||
#include <Action/Action.h>
|
#include <Action/Action.h>
|
||||||
|
|
||||||
#include <Actor/BNpc.h>
|
#include <Actor/BNpc.h>
|
||||||
|
@ -15,349 +20,11 @@
|
||||||
#include <Service.h>
|
#include <Service.h>
|
||||||
|
|
||||||
#include <Territory/QuestBattle.h>
|
#include <Territory/QuestBattle.h>
|
||||||
|
#include <Territory/InstanceContent.h>
|
||||||
#include <Util/UtilMath.h>
|
#include <Util/UtilMath.h>
|
||||||
|
|
||||||
namespace Sapphire
|
namespace Sapphire::Encounter
|
||||||
{
|
{
|
||||||
bool EncounterTimeline::ConditionHp::isConditionMet( ConditionState& state, TerritoryPtr pTeri, TimelinePack& pack, uint64_t time ) const
|
|
||||||
{
|
|
||||||
auto pBNpc = pTeri->getActiveBNpcByLayoutId( layoutId );
|
|
||||||
if( !pBNpc )
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// todo: check time elapsed
|
|
||||||
|
|
||||||
switch( m_conditionType )
|
|
||||||
{
|
|
||||||
case ConditionType::HpPctLessThan:
|
|
||||||
return pBNpc->getHpPercent() < hp.val;
|
|
||||||
case ConditionType::HpPctBetween:
|
|
||||||
{
|
|
||||||
auto hpPct = pBNpc->getHpPercent();
|
|
||||||
return hpPct >= hp.min && hpPct <= hp.max;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
bool EncounterTimeline::ConditionDirectorVar::isConditionMet( ConditionState& state, TerritoryPtr pTeri, TimelinePack& pack, uint64_t time ) const
|
|
||||||
{
|
|
||||||
|
|
||||||
Event::DirectorPtr pDirector = pTeri->getAsInstanceContent();
|
|
||||||
if( pDirector == nullptr )
|
|
||||||
pDirector = pTeri->getAsQuestBattle();
|
|
||||||
|
|
||||||
switch( m_conditionType )
|
|
||||||
{
|
|
||||||
case ConditionType::DirectorVarEquals:
|
|
||||||
return pDirector->getDirectorVar( param.index ) == param.value;
|
|
||||||
case ConditionType::DirectorVarGreaterThan:
|
|
||||||
return pDirector->getDirectorVar( param.index ) > param.value;
|
|
||||||
case ConditionType::DirectorFlagsEquals:
|
|
||||||
return pDirector->getFlags() == param.flags;
|
|
||||||
case ConditionType::DirectorFlagsGreaterThan:
|
|
||||||
return pDirector->getFlags() > param.flags;
|
|
||||||
case ConditionType::DirectorSeqEquals:
|
|
||||||
return pDirector->getSequence() == param.seq;
|
|
||||||
case ConditionType::DirectorSeqGreaterThan:
|
|
||||||
return pDirector->getSequence() > param.seq;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool EncounterTimeline::ConditionCombatState::isConditionMet( ConditionState& state, TerritoryPtr pTeri, TimelinePack& pack, uint64_t time ) const
|
|
||||||
{
|
|
||||||
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:
|
|
||||||
return pBattleNpc->getState() == Entity::BNpcState::Idle;
|
|
||||||
case CombatStateType::Combat:
|
|
||||||
return pBattleNpc->getState() == Entity::BNpcState::Combat;
|
|
||||||
case CombatStateType::Retreat:
|
|
||||||
return pBattleNpc->getState() == Entity::BNpcState::Retreat;
|
|
||||||
case CombatStateType::Roaming:
|
|
||||||
return pBattleNpc->getState() == Entity::BNpcState::Roaming;
|
|
||||||
case CombatStateType::JustDied:
|
|
||||||
return pBattleNpc->getState() == Entity::BNpcState::JustDied;
|
|
||||||
case CombatStateType::Dead:
|
|
||||||
return pBattleNpc->getState() == Entity::BNpcState::Dead;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool EncounterTimeline::ConditionEncounterTimeElapsed::isConditionMet( ConditionState& state, TerritoryPtr pTeri, TimelinePack& pack, uint64_t time ) const
|
|
||||||
{
|
|
||||||
auto elapsed = time - pack.getStartTime();
|
|
||||||
// todo: check encounter time
|
|
||||||
return elapsed >= this->duration;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool EncounterTimeline::ConditionBNpcFlags::isConditionMet( ConditionState& state, TerritoryPtr pTeri, TimelinePack& pack, uint64_t time ) const
|
|
||||||
{
|
|
||||||
auto pBNpc = pTeri->getActiveBNpcByLayoutId( this->layoutId );
|
|
||||||
return pBNpc && pBNpc->hasFlag( this->flags );
|
|
||||||
}
|
|
||||||
|
|
||||||
void EncounterTimeline::Timepoint::update( TimepointState& state, TimelineActor& self, TerritoryPtr pTeri, uint64_t time ) const
|
|
||||||
{
|
|
||||||
state.m_lastTick = time;
|
|
||||||
|
|
||||||
// todo: separate execute and update?
|
|
||||||
if( state.m_finished )
|
|
||||||
return;
|
|
||||||
|
|
||||||
switch( m_type )
|
|
||||||
{
|
|
||||||
case TimepointDataType::Idle:
|
|
||||||
{
|
|
||||||
// just wait up the duration of this timepoint
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case TimepointDataType::CastAction:
|
|
||||||
{
|
|
||||||
auto pActionData = std::dynamic_pointer_cast< TimepointDataAction, TimepointData >( getData() );
|
|
||||||
auto pBNpc = pTeri->getActiveBNpcByLayoutId( pActionData->m_layoutId );
|
|
||||||
// todo: filter the correct target
|
|
||||||
// todo: tie to mechanic script?
|
|
||||||
// todo: mechanic should probably just be an Action::onTick, with instance/director passed to it
|
|
||||||
if( pBNpc)
|
|
||||||
{
|
|
||||||
auto actionMgr = Common::Service< Sapphire::World::Manager::ActionMgr >::ref();
|
|
||||||
|
|
||||||
// todo: this is probably wrong
|
|
||||||
if( pBNpc->getCurrentAction() && pBNpc->getCurrentAction()->getId() != pActionData->m_actionId )
|
|
||||||
actionMgr.handleTargetedAction( *pBNpc.get(), pActionData->m_actionId, pBNpc->getTargetId(), 0 );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case TimepointDataType::MoveTo:
|
|
||||||
{
|
|
||||||
auto pMoveToData = std::dynamic_pointer_cast< TimepointDataMoveTo, TimepointData >( getData() );
|
|
||||||
auto pBNpc = pTeri->getActiveBNpcByLayoutId( pMoveToData->m_layoutId );
|
|
||||||
|
|
||||||
if( pBNpc )
|
|
||||||
{
|
|
||||||
auto currPos = pBNpc->getPos();
|
|
||||||
Common::FFXIVARR_POSITION3 targetPos = { pMoveToData->m_x, pMoveToData->m_y, pMoveToData->m_z };
|
|
||||||
|
|
||||||
auto distance = Common::Util::distance( currPos, targetPos );
|
|
||||||
if( distance > 0.5f )
|
|
||||||
{
|
|
||||||
if( pMoveToData->m_moveType == MoveType::WalkPath )
|
|
||||||
pBNpc->moveTo( targetPos );
|
|
||||||
else
|
|
||||||
pBNpc->setPos( pMoveToData->m_x, pMoveToData->m_y, pMoveToData->m_z );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// if we are at the pos, stop waiting
|
|
||||||
//state.m_finished = true;
|
|
||||||
}
|
|
||||||
pBNpc->setRot( pMoveToData->m_rot );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case TimepointDataType::LogMessage:
|
|
||||||
{
|
|
||||||
auto pLogMessage = std::dynamic_pointer_cast< TimepointDataLogMessage, TimepointData >( getData() );
|
|
||||||
auto params = pLogMessage->m_params;
|
|
||||||
|
|
||||||
// todo: probably should use ContentDirector
|
|
||||||
|
|
||||||
{
|
|
||||||
auto& playerMgr = Common::Service< Sapphire::World::Manager::PlayerMgr >::ref();
|
|
||||||
for( auto player : pTeri->getPlayers() )
|
|
||||||
{
|
|
||||||
auto pPlayer = player.second;
|
|
||||||
if( pPlayer )
|
|
||||||
playerMgr.sendLogMessage( *pPlayer.get(), pLogMessage->m_messageId,
|
|
||||||
params[ 0 ], params[ 1 ], params[ 2 ], params[ 3 ], params[ 4 ] );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case TimepointDataType::BattleTalk:
|
|
||||||
{
|
|
||||||
// todo: BattleTalk
|
|
||||||
auto pBtData = std::dynamic_pointer_cast< TimepointDataBattleTalk, TimepointData >( getData() );
|
|
||||||
auto params = pBtData->m_params;
|
|
||||||
|
|
||||||
|
|
||||||
auto& playerMgr = Common::Service< Sapphire::World::Manager::PlayerMgr >::ref();
|
|
||||||
for( auto player : pTeri->getPlayers() )
|
|
||||||
{
|
|
||||||
auto pPlayer = player.second;
|
|
||||||
if( pPlayer )
|
|
||||||
playerMgr.sendBattleTalk( *pPlayer.get(), pBtData->m_battleTalkId, pBtData->m_handlerId,
|
|
||||||
pBtData->m_kind, pBtData->m_nameId, pBtData->m_talkerId,
|
|
||||||
params[ 0 ], params[ 1 ], params[ 2 ], params[ 3 ],
|
|
||||||
params[ 4 ], params[ 5 ], params[ 6 ], params[ 7 ] );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case TimepointDataType::DirectorSeq:
|
|
||||||
case TimepointDataType::DirectorVar:
|
|
||||||
case TimepointDataType::DirectorVarLR:
|
|
||||||
case TimepointDataType::DirectorFlags:
|
|
||||||
{
|
|
||||||
auto pDirectorData = std::dynamic_pointer_cast< TimepointDataDirector, TimepointData >( getData() );
|
|
||||||
|
|
||||||
uint32_t val = 0;
|
|
||||||
uint32_t param = 0;
|
|
||||||
|
|
||||||
// todo: expand for fates
|
|
||||||
Event::DirectorPtr pDirector = pTeri->getAsInstanceContent();
|
|
||||||
if( pDirector == nullptr )
|
|
||||||
pDirector = pTeri->getAsQuestBattle();
|
|
||||||
|
|
||||||
// todo: this should never not be set?
|
|
||||||
// todo: probably should use ContentDirector
|
|
||||||
// todo: this needs to resend packets too
|
|
||||||
if( pDirector )
|
|
||||||
{
|
|
||||||
switch( m_type )
|
|
||||||
{
|
|
||||||
case TimepointDataType::DirectorVar:
|
|
||||||
val = pDirector->getDirectorVar( pDirectorData->m_data.index );
|
|
||||||
param = pDirectorData->m_data.value.val;
|
|
||||||
break;
|
|
||||||
case TimepointDataType::DirectorFlags:
|
|
||||||
val = pDirector->getFlags();
|
|
||||||
param = pDirectorData->m_data.flags;
|
|
||||||
break;
|
|
||||||
case TimepointDataType::DirectorSeq:
|
|
||||||
val = pDirector->getSequence();
|
|
||||||
param = pDirectorData->m_data.seq;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch( pDirectorData->m_directorOp )
|
|
||||||
{
|
|
||||||
case DirectorOpId::Set: val = param; break;
|
|
||||||
case DirectorOpId::Add: val += param; break;
|
|
||||||
case DirectorOpId::Sub: val -= param; break;
|
|
||||||
case DirectorOpId::Mul: val *= param; break;
|
|
||||||
case DirectorOpId::Div: val /= param; break;
|
|
||||||
case DirectorOpId::Mod: val %= param; break;
|
|
||||||
case DirectorOpId::Sll: val = val << param; break;
|
|
||||||
case DirectorOpId::Srl: val = val >> param; break;
|
|
||||||
case DirectorOpId::Or: val |= param; break;
|
|
||||||
case DirectorOpId::Xor: val ^= param; break;
|
|
||||||
case DirectorOpId::Nor: val = ~( val | param ); break;
|
|
||||||
case DirectorOpId::And: val &= param; break;
|
|
||||||
default: break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// todo: resend packets
|
|
||||||
switch( m_type )
|
|
||||||
{
|
|
||||||
case TimepointDataType::DirectorVar:
|
|
||||||
pDirector->setDirectorVar( pDirectorData->m_data.index, val );
|
|
||||||
break;
|
|
||||||
case TimepointDataType::DirectorFlags:
|
|
||||||
pDirector->setDirectorFlags( val );
|
|
||||||
break;
|
|
||||||
case TimepointDataType::DirectorSeq:
|
|
||||||
pDirector->setDirectorSequence( val );
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case TimepointDataType::AddStatusEffect:
|
|
||||||
{
|
|
||||||
// todo:
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case TimepointDataType::RemoveStatusEffect:
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case TimepointDataType::SpawnBNpc:
|
|
||||||
{
|
|
||||||
auto pSpawnData = std::dynamic_pointer_cast< TimepointDataSpawnBNpc, TimepointData >( getData() );
|
|
||||||
auto pBNpc = pTeri->getActiveBNpcByLayoutId( pSpawnData->m_layoutId );
|
|
||||||
|
|
||||||
if( pBNpc )
|
|
||||||
{
|
|
||||||
pBNpc->clearFlags();
|
|
||||||
pBNpc->setFlag( pSpawnData->m_flags );
|
|
||||||
pBNpc->init();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case TimepointDataType::SetBNpcFlags:
|
|
||||||
{
|
|
||||||
auto pBNpcFlagData = std::dynamic_pointer_cast< TimepointDataBNpcFlags, TimepointData >( getData() );
|
|
||||||
auto pBNpc = pTeri->getActiveBNpcByLayoutId( pBNpcFlagData->m_layoutId );
|
|
||||||
|
|
||||||
if( pBNpc )
|
|
||||||
{
|
|
||||||
pBNpc->clearFlags();
|
|
||||||
pBNpc->setFlag( pBNpcFlagData->m_flags );
|
|
||||||
// todo: resend some bnpc packet/actrl?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case TimepointDataType::SetEObjState:
|
|
||||||
{
|
|
||||||
auto pEObjData = std::dynamic_pointer_cast< TimepointDataEObjState, TimepointData >( getData() );
|
|
||||||
|
|
||||||
auto pInstance = pTeri->getAsInstanceContent();
|
|
||||||
auto pQBattle = pTeri->getAsQuestBattle();
|
|
||||||
|
|
||||||
// todo: event objects on quest battles
|
|
||||||
// todo: SetEObjAnimationFlag?
|
|
||||||
|
|
||||||
if( pInstance )
|
|
||||||
{
|
|
||||||
auto pEObj = pInstance->getEObjById( pEObjData->m_eobjId );
|
|
||||||
if( pEObj )
|
|
||||||
{
|
|
||||||
pEObj->setState( pEObjData->m_state );
|
|
||||||
// todo: resend the eobj spawn packet?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case TimepointDataType::SetBgm:
|
|
||||||
{
|
|
||||||
auto pBgmData = std::dynamic_pointer_cast< TimepointDataBGM, TimepointData >( getData() );
|
|
||||||
auto pInstance = pTeri->getAsInstanceContent();
|
|
||||||
auto pQBattle = pTeri->getAsQuestBattle();
|
|
||||||
|
|
||||||
// todo: quest battles refactor to inherit InstanceContent
|
|
||||||
if( pInstance )
|
|
||||||
{
|
|
||||||
pInstance->setCurrentBGM( pBgmData->m_bgmId );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case TimepointDataType::SetCondition:
|
|
||||||
{
|
|
||||||
auto pConditionData = std::dynamic_pointer_cast< TimepointDataCondition, TimepointData >( getData() );
|
|
||||||
|
|
||||||
// todo: dont reset so things can resume? idk
|
|
||||||
self.resetConditionState( pConditionData->m_conditionId );
|
|
||||||
self.setConditionStateEnabled( pConditionData->m_conditionId, pConditionData->m_enabled );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if( m_type != TimepointDataType::MoveTo && m_type != TimepointDataType::CastAction )
|
|
||||||
state.m_finished = true;
|
|
||||||
|
|
||||||
state.m_finished = state.m_finished || state.m_startTime + m_duration <= time;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
class RngCondition : TimepointCondition
|
class RngCondition : TimepointCondition
|
||||||
|
@ -421,384 +88,11 @@ namespace Sapphire
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void EncounterTimeline::Timepoint::execute( TimepointState& state, TimelineActor& self, TerritoryPtr pTeri, uint64_t time ) const
|
|
||||||
{
|
|
||||||
state.m_startTime = time;
|
|
||||||
update( state, self, pTeri, time );
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// parsing stuff below
|
// parsing stuff below
|
||||||
//
|
//
|
||||||
|
|
||||||
void EncounterTimeline::ConditionHp::from_json( nlohmann::json& json, Phase& phase, ConditionType condition,
|
TimelinePack EncounterTimeline::getEncounterPack( const std::string& name, bool reload )
|
||||||
const std::unordered_map< std::string, TimelineActor >& actors )
|
|
||||||
{
|
|
||||||
PhaseCondition::from_json( json, phase, condition );
|
|
||||||
|
|
||||||
auto paramData = json.at( "paramData" );
|
|
||||||
auto actorRef = paramData.at( "sourceActor" ).get< std::string >();
|
|
||||||
|
|
||||||
// resolve the actor whose hp we are checking
|
|
||||||
if( auto it = actors.find( actorRef ); it != actors.end() )
|
|
||||||
this->layoutId = it->second.m_layoutId;
|
|
||||||
else
|
|
||||||
throw std::runtime_error( fmt::format( std::string( "EncounterTimeline::ConditionHp::from_json unable to find actor by name: %s" ), actorRef ) );
|
|
||||||
|
|
||||||
switch( condition )
|
|
||||||
{
|
|
||||||
case ConditionType::HpPctLessThan:
|
|
||||||
this->hp.val = paramData.at( "hp" ).get< uint32_t >();
|
|
||||||
break;
|
|
||||||
case ConditionType::HpPctBetween:
|
|
||||||
this->hp.min = paramData.at( "hpMin" ).get< uint32_t >(),
|
|
||||||
this->hp.max = paramData.at( "hpMax" ).get< uint32_t >();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void EncounterTimeline::ConditionDirectorVar::from_json( nlohmann::json& json, Phase& phase, ConditionType condition )
|
|
||||||
{
|
|
||||||
PhaseCondition::from_json( json, phase, condition );
|
|
||||||
|
|
||||||
auto paramData = json.at( "paramData" );
|
|
||||||
|
|
||||||
switch( condition )
|
|
||||||
{
|
|
||||||
case ConditionType::DirectorVarEquals:
|
|
||||||
case ConditionType::DirectorVarGreaterThan:
|
|
||||||
{
|
|
||||||
param.index = paramData.at( "idx" ).get< uint32_t >();
|
|
||||||
param.value = paramData.at( "val" ).get< uint32_t >();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ConditionType::DirectorFlagsEquals:
|
|
||||||
case ConditionType::DirectorFlagsGreaterThan:
|
|
||||||
{
|
|
||||||
param.flags = paramData.at( "flags" ).get< uint32_t >();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ConditionType::DirectorSeqEquals:
|
|
||||||
case ConditionType::DirectorSeqGreaterThan:
|
|
||||||
{
|
|
||||||
param.seq = paramData.at( "seq" ).get< uint32_t >();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void EncounterTimeline::ConditionCombatState::from_json( nlohmann::json& json, Phase& phase, ConditionType condition,
|
|
||||||
const std::unordered_map< std::string, TimelineActor >& actors )
|
|
||||||
{
|
|
||||||
PhaseCondition::from_json( json, phase, condition );
|
|
||||||
|
|
||||||
auto paramData = json.at( "paramData" );
|
|
||||||
auto actorRef = paramData.at( "sourceActor" ).get< std::string >();
|
|
||||||
|
|
||||||
// resolve the actor whose name we are checking
|
|
||||||
if( auto it = actors.find( actorRef ); it != actors.end() )
|
|
||||||
this->layoutId = it->second.m_layoutId;
|
|
||||||
else
|
|
||||||
throw std::runtime_error( fmt::format( std::string( "EncounterTimeline::ConditionCombatState::from_json unable to find actor by name: %s" ), actorRef ) );
|
|
||||||
|
|
||||||
this->combatState = paramData.at( "combatState" ).get< CombatStateType >();
|
|
||||||
}
|
|
||||||
|
|
||||||
void EncounterTimeline::ConditionEncounterTimeElapsed::from_json( nlohmann::json& json, Phase& phase, ConditionType condition )
|
|
||||||
{
|
|
||||||
PhaseCondition::from_json( json, phase, condition );
|
|
||||||
|
|
||||||
auto paramData = json.at( "paramData" );
|
|
||||||
auto duration = paramData.at( "duration" ).get< uint64_t >();
|
|
||||||
|
|
||||||
this->duration = duration;
|
|
||||||
}
|
|
||||||
|
|
||||||
void EncounterTimeline::ConditionBNpcFlags::from_json( nlohmann::json& json, Phase& phase, ConditionType condition,
|
|
||||||
const std::unordered_map< std::string, TimelineActor >& actors )
|
|
||||||
{
|
|
||||||
PhaseCondition::from_json( json, phase, condition );
|
|
||||||
auto actorRef = json.at( "actor" ).get< std::string >();
|
|
||||||
|
|
||||||
// resolve the actor whose name we are checking
|
|
||||||
if( auto it = actors.find( actorRef ); it != actors.end() )
|
|
||||||
this->layoutId = it->second.m_layoutId;
|
|
||||||
else
|
|
||||||
throw std::runtime_error( fmt::format( std::string( "EncounterTimeline::ConditionBNpcFlags::from_json unable to find actor by name: %s" ), actorRef ) );
|
|
||||||
|
|
||||||
this->flags = json.at( "flags" ).get< uint32_t >();
|
|
||||||
// todo: BNpcHasFlags
|
|
||||||
}
|
|
||||||
|
|
||||||
void EncounterTimeline::Timepoint::from_json( const nlohmann::json& json, const std::unordered_map< std::string, TimelineActor>& actors, uint32_t selfLayoutId )
|
|
||||||
{
|
|
||||||
const static std::unordered_map< std::string, TimepointDataType > timepointTypeMap =
|
|
||||||
{
|
|
||||||
{ "idle", TimepointDataType::Idle },
|
|
||||||
{ "castAction", TimepointDataType::CastAction },
|
|
||||||
{ "moveTo", TimepointDataType::MoveTo },
|
|
||||||
{ "logMessage", TimepointDataType::LogMessage },
|
|
||||||
{ "battleTalk", TimepointDataType::BattleTalk },
|
|
||||||
{ "directorVar", TimepointDataType::DirectorVar },
|
|
||||||
{ "directorSeq", TimepointDataType::DirectorSeq },
|
|
||||||
{ "directorFlags", TimepointDataType::DirectorFlags },
|
|
||||||
{ "addStatusEffect", TimepointDataType::AddStatusEffect },
|
|
||||||
{ "removeStatusEffect", TimepointDataType::RemoveStatusEffect },
|
|
||||||
{ "spawnBNpc", TimepointDataType::SpawnBNpc },
|
|
||||||
{ "bNpcFlags", TimepointDataType::SetBNpcFlags },
|
|
||||||
{ "setEObjState", TimepointDataType::SetEObjState },
|
|
||||||
{ "setCondition", TimepointDataType::SetCondition },
|
|
||||||
{ "snapshot", TimepointDataType::Snapshot }
|
|
||||||
};
|
|
||||||
|
|
||||||
const static std::unordered_map< std::string, TimepointOverrideFlags > overrideFlagMap =
|
|
||||||
{
|
|
||||||
{}
|
|
||||||
};
|
|
||||||
|
|
||||||
const static std::unordered_map< std::string, TimepointCallbackType > callbackTypeMap =
|
|
||||||
{
|
|
||||||
{ "onActionInit", TimepointCallbackType::OnActionInit },
|
|
||||||
{ "onActionStart", TimepointCallbackType::OnActionStart },
|
|
||||||
{ "onActionInterrupt", TimepointCallbackType::OnActionInterrupt },
|
|
||||||
{ "onActionExecute", TimepointCallbackType::OnActionExecute },
|
|
||||||
};
|
|
||||||
|
|
||||||
const static std::unordered_map< std::string, DirectorOpId > directorOpMap =
|
|
||||||
{
|
|
||||||
{ "set", DirectorOpId::Set },
|
|
||||||
{ "add", DirectorOpId::Add },
|
|
||||||
{ "sub", DirectorOpId::Sub },
|
|
||||||
{ "mul", DirectorOpId::Mul },
|
|
||||||
{ "div", DirectorOpId::Div },
|
|
||||||
{ "mod", DirectorOpId::Mod },
|
|
||||||
{ "sll", DirectorOpId::Sll },
|
|
||||||
{ "srl", DirectorOpId::Srl },
|
|
||||||
{ "or", DirectorOpId::Or },
|
|
||||||
{ "xor", DirectorOpId::Xor },
|
|
||||||
{ "nor", DirectorOpId::Nor },
|
|
||||||
{ "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 >();
|
|
||||||
if( auto it = timepointTypeMap.find( typeStr ); it != timepointTypeMap.end() )
|
|
||||||
tpType = it->second;
|
|
||||||
else
|
|
||||||
throw std::runtime_error( fmt::format( "Timepoint::from_json unable to find timepoint by type: %s", typeStr ) );
|
|
||||||
|
|
||||||
m_duration = json.at( "duration" ).get< uint64_t >();
|
|
||||||
//m_overrideFlags = json.at( "overrideFlags" ).get< TimepointOverrideFlags >();
|
|
||||||
m_description = json.at( "description" ).get< std::string >();
|
|
||||||
|
|
||||||
switch( tpType )
|
|
||||||
{
|
|
||||||
case TimepointDataType::Idle:
|
|
||||||
{
|
|
||||||
m_pData = std::make_shared< TimepointDataIdle >( selfLayoutId, m_duration );
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case TimepointDataType::CastAction:
|
|
||||||
{
|
|
||||||
// todo: CastAction
|
|
||||||
// todo: parse and build callback funcs
|
|
||||||
auto& dataJ = json.at( "data" );
|
|
||||||
auto actorRef = json.at( "sourceActor" ).get< std::string >();
|
|
||||||
auto actionId = json.at( "actionId" ).get< uint32_t >();
|
|
||||||
|
|
||||||
uint32_t layoutId = 0xE0000000;
|
|
||||||
if( auto it = actors.find( actorRef ); it != actors.end() )
|
|
||||||
layoutId = it->second.m_layoutId;
|
|
||||||
else
|
|
||||||
throw std::runtime_error( fmt::format( "EncounterTimeline::Timepoint::from_json: CastAction invalid actor ref: %s", actorRef ) );
|
|
||||||
|
|
||||||
m_pData = std::make_shared< TimepointDataAction >( layoutId, actionId );
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case TimepointDataType::MoveTo:
|
|
||||||
{
|
|
||||||
auto& dataJ = json.at( "data" );
|
|
||||||
auto pos = dataJ.at( "pos" ).get< std::vector< float > >();
|
|
||||||
auto rot = dataJ.at( "rot" ).get< float >();
|
|
||||||
auto pathReq = dataJ.at( "pathRequested" ).get< bool >() ? MoveType::WalkPath : MoveType::Teleport;
|
|
||||||
|
|
||||||
m_pData = std::make_shared< TimepointDataMoveTo >( selfLayoutId, pathReq, pos[ 0 ], pos[ 1 ], pos[ 2 ], rot );
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case TimepointDataType::LogMessage:
|
|
||||||
{
|
|
||||||
auto& dataJ = json.at( "data" );
|
|
||||||
auto messageId = dataJ.at( "messageId" ).get< uint32_t >();
|
|
||||||
auto params = dataJ.at( "params" ).get< std::vector< uint32_t > >();
|
|
||||||
|
|
||||||
m_pData = std::make_shared< TimepointDataLogMessage >( messageId, params );
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case TimepointDataType::BattleTalk:
|
|
||||||
{
|
|
||||||
auto& dataJ = json.at( "data" );
|
|
||||||
auto params = dataJ.at( "params" ).get< std::vector< uint32_t > >();
|
|
||||||
|
|
||||||
auto pBattleTalkData = std::make_shared< TimepointDataBattleTalk >( params );
|
|
||||||
|
|
||||||
pBattleTalkData->m_battleTalkId = dataJ.at( "battleTalkId" ).get< uint32_t >();
|
|
||||||
pBattleTalkData->m_handlerId = dataJ.at( "handlerId" ).get< uint32_t >();
|
|
||||||
pBattleTalkData->m_kind = dataJ.at( "kind" ).get< uint32_t >();
|
|
||||||
pBattleTalkData->m_nameId = dataJ.at( "nameId" ).get< uint32_t >();
|
|
||||||
pBattleTalkData->m_talkerId = dataJ.at( "talkerId" ).get< uint32_t >();
|
|
||||||
|
|
||||||
m_pData = pBattleTalkData;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
//
|
|
||||||
// Directors
|
|
||||||
//
|
|
||||||
case TimepointDataType::DirectorVar:
|
|
||||||
{
|
|
||||||
auto& dataJ = json.at( "data" );
|
|
||||||
auto index = dataJ.at( "idx" ).get< uint32_t >();
|
|
||||||
auto val = dataJ.at( "val" ).get< uint32_t >();
|
|
||||||
auto opStr = dataJ.at( "opc" ).get< std::string >();
|
|
||||||
DirectorOpId op = directorOpMap.find( opStr )->second;
|
|
||||||
|
|
||||||
auto pDirectorData = std::make_shared< TimepointDataDirector >( tpType, op );
|
|
||||||
pDirectorData->m_data.index = index;
|
|
||||||
pDirectorData->m_data.value.val = val;
|
|
||||||
|
|
||||||
m_pData = pDirectorData;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case TimepointDataType::DirectorVarLR:
|
|
||||||
{
|
|
||||||
auto& dataJ = json.at( "data" );
|
|
||||||
auto index = dataJ.at( "idx" ).get< uint32_t >();
|
|
||||||
auto left = dataJ.at( "left" ).get< uint32_t >();
|
|
||||||
auto right = dataJ.at( "right" ).get< uint32_t >();
|
|
||||||
auto opStr = dataJ.at( "opc" ).get< std::string >();
|
|
||||||
DirectorOpId op = directorOpMap.find( opStr )->second;
|
|
||||||
|
|
||||||
auto pDirectorData = std::make_shared< TimepointDataDirector >( tpType, op );
|
|
||||||
pDirectorData->m_data.index = index;
|
|
||||||
pDirectorData->m_data.value.left = left;
|
|
||||||
pDirectorData->m_data.value.right = right;
|
|
||||||
|
|
||||||
m_pData = pDirectorData;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case TimepointDataType::DirectorSeq:
|
|
||||||
{
|
|
||||||
auto& dataJ = json.at( "data" );
|
|
||||||
auto seq = dataJ.at( "val" ).get< uint32_t >();
|
|
||||||
auto opStr = dataJ.at( "opc" ).get< std::string >();
|
|
||||||
DirectorOpId op = directorOpMap.find( opStr )->second;
|
|
||||||
|
|
||||||
auto pDirectorData = std::make_shared< TimepointDataDirector >( tpType, op );
|
|
||||||
pDirectorData->m_data.seq = seq;
|
|
||||||
|
|
||||||
m_pData = pDirectorData;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case TimepointDataType::DirectorFlags:
|
|
||||||
{
|
|
||||||
auto& dataJ = json.at( "data" );
|
|
||||||
auto flags = dataJ.at( "val" ).get< uint32_t >();
|
|
||||||
auto opStr = dataJ.at( "opc" ).get< std::string >();
|
|
||||||
DirectorOpId op = directorOpMap.at( opStr );
|
|
||||||
|
|
||||||
auto pDirectorData = std::make_shared< TimepointDataDirector >( tpType, op );
|
|
||||||
pDirectorData->m_data.flags = flags;
|
|
||||||
|
|
||||||
m_pData = pDirectorData;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
//
|
|
||||||
// Status Effects
|
|
||||||
//
|
|
||||||
case TimepointDataType::AddStatusEffect:
|
|
||||||
case TimepointDataType::RemoveStatusEffect:
|
|
||||||
{
|
|
||||||
// todo: add/remove status effects
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
|
|
||||||
case TimepointDataType::SpawnBNpc:
|
|
||||||
{
|
|
||||||
auto& dataJ = json.at( "data" );
|
|
||||||
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
|
|
||||||
|
|
||||||
uint32_t layoutId = 0xE0000000;
|
|
||||||
if( auto it = actors.find( actorRef ); it != actors.end() )
|
|
||||||
layoutId = it->second.m_layoutId;
|
|
||||||
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, bnpcType );
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case TimepointDataType::SetBNpcFlags:
|
|
||||||
{
|
|
||||||
auto& dataJ = json.at( "data" );
|
|
||||||
auto actorRef = dataJ.at( "spawnActor" ).get< std::string >();
|
|
||||||
auto flags = dataJ.at( "flags" ).get< uint32_t >();
|
|
||||||
|
|
||||||
// todo: hateSrc
|
|
||||||
|
|
||||||
uint32_t layoutId = 0xE0000000;
|
|
||||||
if( auto it = actors.find( actorRef ); it != actors.end() )
|
|
||||||
layoutId = it->second.m_layoutId;
|
|
||||||
else
|
|
||||||
throw std::runtime_error( fmt::format( std::string( "EncounterTimeline::Timepoint::from_json: SetBNpcFlags invalid actor ref: %s" ), actorRef ) );
|
|
||||||
|
|
||||||
m_pData = std::make_shared< TimepointDataBNpcFlags >( layoutId, flags );
|
|
||||||
// todo: SetBNpcFlags
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TimepointDataType::SetEObjState:
|
|
||||||
{
|
|
||||||
auto& dataJ = json.at( "data" );
|
|
||||||
|
|
||||||
// todo: SetEObjState
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TimepointDataType::SetCondition:
|
|
||||||
{
|
|
||||||
auto& dataJ = json.at( "data" );
|
|
||||||
auto conditionId = dataJ.at( "conditionId" ).get< uint32_t >();
|
|
||||||
auto enabled = dataJ.at( "enabled" ).get< bool >();
|
|
||||||
|
|
||||||
m_pData = std::make_shared< TimepointDataCondition >( conditionId, enabled );
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
EncounterTimeline::TimelinePack EncounterTimeline::getEncounterPack( const std::string& name, bool reload )
|
|
||||||
{
|
{
|
||||||
static std::map< std::string, TimelinePack > cache = {};
|
static std::map< std::string, TimelinePack > cache = {};
|
||||||
const static std::unordered_map< std::string, ConditionType > conditionMap =
|
const static std::unordered_map< std::string, ConditionType > conditionMap =
|
||||||
|
@ -837,6 +131,16 @@ namespace Sapphire
|
||||||
std::unordered_map< std::string, TimelineActor > actorNameMap;
|
std::unordered_map< std::string, TimelineActor > actorNameMap;
|
||||||
std::unordered_map< std::string, std::map< std::string, Phase > > actorNamePhaseMap;
|
std::unordered_map< std::string, std::map< std::string, Phase > > actorNamePhaseMap;
|
||||||
|
|
||||||
|
for( const auto& selectorJ : json.at( "selectors" ).items() )
|
||||||
|
{
|
||||||
|
auto selectorV = selectorJ.value();
|
||||||
|
auto name = selectorV.at( "name" );
|
||||||
|
Selector selector;
|
||||||
|
selector.from_json( selectorV );
|
||||||
|
|
||||||
|
pack.addSelector( name, selector );
|
||||||
|
}
|
||||||
|
|
||||||
// first run through cache actor info
|
// first run through cache actor info
|
||||||
for( const auto& actorJ : json.at( "actors" ).items() )
|
for( const auto& actorJ : json.at( "actors" ).items() )
|
||||||
{
|
{
|
||||||
|
@ -976,47 +280,66 @@ namespace Sapphire
|
||||||
return pack;
|
return pack;
|
||||||
}
|
}
|
||||||
|
|
||||||
Entity::BNpcPtr EncounterTimeline::TimelineActor::spawnSubActor( TerritoryPtr pTeri, const std::string& name )
|
void TimelinePack::setName( const std::string& name )
|
||||||
{
|
{
|
||||||
// todo: retail straight up respawns sub actors, even bnpc parts (qarn adjudicator body parts respawn each time with new ids)
|
m_name = name;
|
||||||
auto flags = Entity::BNpcFlag::Invincible | Entity::BNpcFlag::Untargetable |
|
|
||||||
Entity::BNpcFlag::Immobile | Entity::BNpcFlag::AutoAttackDisabled |
|
|
||||||
Entity::BNpcFlag::TurningDisabled;
|
|
||||||
|
|
||||||
auto pActor = getSubActor( name );
|
|
||||||
if( pActor == nullptr )
|
|
||||||
{
|
|
||||||
auto pParent = pTeri->getActiveBNpcByLayoutId( m_layoutId );
|
|
||||||
pActor = pTeri->createBNpcFromLayoutId( m_layoutId, 1000, pParent->getBNpcType() );
|
|
||||||
m_subActors[ name ] = pActor;
|
|
||||||
}
|
|
||||||
pActor->setInvincibilityType( Common::InvincibilityIgnoreDamage );
|
|
||||||
pActor->setFlag( flags );
|
|
||||||
pActor->init();
|
|
||||||
|
|
||||||
pTeri->pushActor( pActor );
|
|
||||||
return pActor;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Entity::BNpcPtr EncounterTimeline::TimelineActor::getSubActor( const std::string& name )
|
void TimelinePack::addSelector( const std::string& name, const Selector& selector )
|
||||||
{
|
{
|
||||||
if( auto it = m_subActors.find( name ); it != m_subActors.end() )
|
m_selectors.emplace( std::make_pair( name, selector ) );
|
||||||
return it->second;
|
}
|
||||||
|
|
||||||
|
void TimelinePack::createSnapshot( const std::string& selectorName, Entity::CharaPtr pSrc, const std::vector< uint32_t >& exclude )
|
||||||
|
{
|
||||||
|
if( auto it = m_selectors.find( selectorName ); it != m_selectors.end() )
|
||||||
|
it->second.createSnapshot( pSrc, exclude );
|
||||||
|
}
|
||||||
|
|
||||||
|
const World::AI::Snapshot::Results& TimelinePack::getSnapshotResults( const std::string& selectorName )
|
||||||
|
{
|
||||||
|
if( auto it = m_selectors.find( selectorName ); it != m_selectors.end() )
|
||||||
|
return it->second.getResults();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const World::AI::Snapshot::TargetIds& TimelinePack::getSnapshotTargetIds( const std::string& selectorName )
|
||||||
|
{
|
||||||
|
if( auto it = m_selectors.find( selectorName ); it != m_selectors.end() )
|
||||||
|
return it->second.getTargetIds();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void TimelinePack::addTimelineActor( const TimelineActor& actor )
|
||||||
|
{
|
||||||
|
m_actors.emplace_back( actor );
|
||||||
|
}
|
||||||
|
|
||||||
|
Entity::BNpcPtr TimelinePack::getBNpcByActorRef( const std::string& name, TerritoryPtr pTeri, const std::string& subActorName )
|
||||||
|
{
|
||||||
|
for( const auto& actor : m_actors )
|
||||||
|
{
|
||||||
|
if( actor.m_name == name )
|
||||||
|
{
|
||||||
|
return actor.getBNpcByRef( name, pTeri );
|
||||||
|
}
|
||||||
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EncounterTimeline::TimelineActor::resetSubActors( TerritoryPtr pTeri )
|
void TimelinePack::setStartTime( uint64_t time )
|
||||||
{
|
{
|
||||||
for( auto& subActor : m_subActors )
|
m_startTime = time;
|
||||||
{
|
|
||||||
if( subActor.second )
|
|
||||||
{
|
|
||||||
auto pBNpc = subActor.second;
|
|
||||||
pTeri->removeActor( pBNpc );
|
|
||||||
// todo: despawn?
|
|
||||||
subActor.second = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}// namespace Sapphire
|
uint64_t TimelinePack::getStartTime() const
|
||||||
|
{
|
||||||
|
return m_startTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TimelinePack::update( TerritoryPtr pTeri, uint64_t time )
|
||||||
|
{
|
||||||
|
for( auto& actor : m_actors )
|
||||||
|
actor.update( pTeri, *this, time );
|
||||||
|
}
|
||||||
|
}// namespace Sapphire::Encounter
|
|
@ -1,3 +1,5 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
@ -8,841 +10,71 @@
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <Common.h>
|
|
||||||
#include <Forwards.h>
|
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
namespace Sapphire
|
#include <AI/TargetHelper.h>
|
||||||
|
#include <Territory/Territory.h>
|
||||||
|
#include <Common.h>
|
||||||
|
#include <Forwards.h>
|
||||||
|
|
||||||
|
#include "Selector.h"
|
||||||
|
#include "Forwards.h"
|
||||||
|
|
||||||
|
namespace Sapphire::Encounter
|
||||||
{
|
{
|
||||||
class EncounterTimeline
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
// forwards
|
|
||||||
class TimelineActor;
|
|
||||||
class TimelinePack;
|
|
||||||
|
|
||||||
//
|
|
||||||
// State tracking objects (per actor)
|
|
||||||
//
|
|
||||||
// todo: move ConditionState/TimepointState to Chara::GambitState?
|
|
||||||
|
|
||||||
struct TimepointState
|
|
||||||
{
|
|
||||||
uint64_t m_startTime{ 0 };
|
|
||||||
uint64_t m_lastTick{ 0 };
|
|
||||||
bool m_finished{ false };
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ConditionState
|
|
||||||
{
|
|
||||||
uint64_t m_startTime{ 0 };
|
|
||||||
bool m_loop{ false };
|
|
||||||
bool m_completed{ false };
|
|
||||||
bool m_enabled{ false };
|
|
||||||
|
|
||||||
struct
|
|
||||||
{
|
|
||||||
uint64_t m_startTime{ 0 };
|
|
||||||
uint64_t m_lastTimepointTime{ 0 };
|
|
||||||
uint32_t m_lastTimepointIndex{ 0 };
|
|
||||||
|
|
||||||
std::vector< TimepointState > m_timepointStates;
|
|
||||||
} m_phaseInfo;
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
|
||||||
// Enums
|
|
||||||
//
|
|
||||||
// EncounterFight::OnTick() { switch EncounterTimepointcondition }
|
|
||||||
enum class ConditionType : uint32_t
|
|
||||||
{
|
|
||||||
HpPctLessThan,
|
|
||||||
HpPctBetween,
|
|
||||||
|
|
||||||
DirectorVarEquals,
|
|
||||||
DirectorVarGreaterThan,
|
|
||||||
DirectorSeqEquals,
|
|
||||||
DirectorSeqGreaterThan,
|
|
||||||
DirectorFlagsEquals,
|
|
||||||
DirectorFlagsGreaterThan,
|
|
||||||
|
|
||||||
EncounterTimeElapsed,
|
|
||||||
CombatState,
|
|
||||||
BNpcHasFlags
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class DirectorOpId
|
|
||||||
{
|
|
||||||
Set, // idx = val
|
|
||||||
Add, // idx += val
|
|
||||||
Sub, // idx -= val
|
|
||||||
Mul, // idx *= val
|
|
||||||
Div, // idx /= val
|
|
||||||
Mod, // idx %= val
|
|
||||||
Sll, // idx << val
|
|
||||||
Srl, // idx >> val
|
|
||||||
Or, // idx |= val
|
|
||||||
Xor, // idx ^= val
|
|
||||||
Nor, // idx ~= val
|
|
||||||
And // idx &= val
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: what should this do?
|
|
||||||
enum class TimepointOverrideFlags : uint32_t
|
|
||||||
{
|
|
||||||
None,
|
|
||||||
Invulnerable
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class TimepointDataType : uint32_t
|
|
||||||
{
|
|
||||||
Idle,
|
|
||||||
CastAction,
|
|
||||||
MoveTo,
|
|
||||||
|
|
||||||
LogMessage,
|
|
||||||
BattleTalk,
|
|
||||||
|
|
||||||
DirectorVar,
|
|
||||||
DirectorVarLR,
|
|
||||||
DirectorSeq,
|
|
||||||
DirectorFlags,
|
|
||||||
|
|
||||||
AddStatusEffect,
|
|
||||||
RemoveStatusEffect,
|
|
||||||
|
|
||||||
SpawnBNpc,
|
|
||||||
SetBNpcFlags,
|
|
||||||
SetEObjState,
|
|
||||||
SetBgm,
|
|
||||||
|
|
||||||
SetCondition,
|
|
||||||
|
|
||||||
Snapshot
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class TimepointCallbackType : uint32_t
|
|
||||||
{
|
|
||||||
OnActionInit,
|
|
||||||
OnActionStart,
|
|
||||||
OnActionInterrupt,
|
|
||||||
OnActionExecute
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class TargetSelectFilterFlags : uint32_t
|
|
||||||
{
|
|
||||||
Self = 0x00000000,
|
|
||||||
|
|
||||||
Player = 0x00000001,
|
|
||||||
|
|
||||||
EnemyBattalion = 0x00000002,
|
|
||||||
OwnBattalion = 0x00000004,
|
|
||||||
|
|
||||||
Tank = 0x00000010,
|
|
||||||
Healer = 0x00000020,
|
|
||||||
Dps = 0x00000040,
|
|
||||||
Melee = 0x00000080,
|
|
||||||
Ranged = 0x00000100,
|
|
||||||
Closest = 0x00000200,
|
|
||||||
Furthest = 0x00000400,
|
|
||||||
|
|
||||||
Aggro1 = 0x10000000,
|
|
||||||
Aggro2 = 0x20000000,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class MoveType : uint32_t
|
|
||||||
{
|
|
||||||
WalkPath,
|
|
||||||
Teleport
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class TimelineActorType : uint32_t
|
|
||||||
{
|
|
||||||
LayerSetObject,
|
|
||||||
BattleNpc
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class CombatStateType
|
|
||||||
{
|
|
||||||
Idle,
|
|
||||||
Combat,
|
|
||||||
Retreat,
|
|
||||||
Roaming,
|
|
||||||
JustDied,
|
|
||||||
Dead
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class TimelinePackType : uint32_t
|
enum class TimelinePackType : uint32_t
|
||||||
{
|
{
|
||||||
Solo,
|
Solo,
|
||||||
EncounterFight
|
EncounterFight
|
||||||
};
|
};
|
||||||
|
|
||||||
struct TargetSelectFilter
|
|
||||||
{
|
|
||||||
TargetSelectFilterFlags m_flags;
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
|
||||||
// Timepoint.m_pData objects
|
|
||||||
//
|
|
||||||
using TimepointCallbackFunc = std::function< void( TerritoryPtr, uint64_t ) >;
|
|
||||||
// Timepoint Data Objects
|
|
||||||
struct TimepointCallbackData :
|
|
||||||
public std::enable_shared_from_this< TimepointCallbackData >
|
|
||||||
{
|
|
||||||
TimepointCallbackType m_type;
|
|
||||||
std::vector < TimepointCallbackFunc > m_callbacks;
|
|
||||||
};
|
|
||||||
using TimebackCallbackDataPtr = std::shared_ptr< TimepointCallbackData >;
|
|
||||||
using TimepointCallbacks = std::unordered_map< TimepointCallbackType, TimebackCallbackDataPtr >;
|
|
||||||
|
|
||||||
|
|
||||||
struct TimepointData :
|
|
||||||
public std::enable_shared_from_this< TimepointData >
|
|
||||||
{
|
|
||||||
TimepointData( TimepointDataType type ) : m_type( type ) {}
|
|
||||||
virtual ~TimepointData(){};
|
|
||||||
TimepointDataType m_type{ 0 };
|
|
||||||
};
|
|
||||||
using TimepointDataPtr = std::shared_ptr< TimepointData >;
|
|
||||||
|
|
||||||
struct TimepointDataIdle : public TimepointData
|
|
||||||
{
|
|
||||||
uint32_t m_layoutId{ 0xE0000000 };
|
|
||||||
uint64_t m_durationMs;
|
|
||||||
|
|
||||||
TimepointDataIdle( uint32_t layoutId, uint64_t durationMs ) :
|
|
||||||
TimepointData( TimepointDataType::Idle ),
|
|
||||||
m_layoutId( layoutId ),
|
|
||||||
m_durationMs( durationMs )
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct TimepointDataAddStatusEffect : public TimepointData
|
|
||||||
{
|
|
||||||
uint32_t m_statusEffectId;
|
|
||||||
TargetSelectFilter m_targetFilter;
|
|
||||||
uint32_t m_durationMs;
|
|
||||||
|
|
||||||
TimepointDataAddStatusEffect( uint32_t statusId, TargetSelectFilter targFilter, uint32_t durationMs ) :
|
|
||||||
TimepointData( TimepointDataType::AddStatusEffect ),
|
|
||||||
m_statusEffectId( statusId ),
|
|
||||||
m_targetFilter( targFilter ),
|
|
||||||
m_durationMs( durationMs )
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct TimepointDataRemoveStatusEffect : public TimepointData
|
|
||||||
{
|
|
||||||
uint32_t m_statusEffectId;
|
|
||||||
TargetSelectFilter m_targetFilter;
|
|
||||||
|
|
||||||
TimepointDataRemoveStatusEffect( uint32_t statusId, TargetSelectFilter targFilter ) :
|
|
||||||
TimepointData( TimepointDataType::RemoveStatusEffect ),
|
|
||||||
m_statusEffectId( statusId ),
|
|
||||||
m_targetFilter( targFilter )
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct TimepointDataAction : public TimepointData
|
|
||||||
{
|
|
||||||
uint32_t m_layoutId{ 0xE0000000 };
|
|
||||||
uint32_t m_actionId;
|
|
||||||
//TimepointCallbacks m_callbacks;
|
|
||||||
|
|
||||||
TimepointDataAction( uint32_t layoutId, uint32_t actionId ) :
|
|
||||||
TimepointData( TimepointDataType::CastAction ),
|
|
||||||
m_layoutId( layoutId ),
|
|
||||||
m_actionId( actionId )
|
|
||||||
//m_callbacks( callbacks )
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct TimepointDataMoveTo : public TimepointData
|
|
||||||
{
|
|
||||||
uint32_t m_layoutId{ 0xE0000000 };
|
|
||||||
MoveType m_moveType;
|
|
||||||
float m_x, m_y, m_z, m_rot;
|
|
||||||
|
|
||||||
TimepointDataMoveTo( uint32_t layoutId, MoveType moveType, float x, float y, float z, float rot ) :
|
|
||||||
TimepointData( TimepointDataType::MoveTo ),
|
|
||||||
m_layoutId( layoutId ),
|
|
||||||
m_moveType( moveType ),
|
|
||||||
m_x( x ), m_y( y ), m_z( z ), m_rot( rot )
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct TimepointDataLogMessage : public TimepointData
|
|
||||||
{
|
|
||||||
uint32_t m_messageId;
|
|
||||||
uint32_t m_params[ 5 ]{ 0 };
|
|
||||||
|
|
||||||
TimepointDataLogMessage( uint32_t messageId, const std::vector< uint32_t >& params ) :
|
|
||||||
TimepointData( TimepointDataType::LogMessage ),
|
|
||||||
m_messageId( messageId )
|
|
||||||
{
|
|
||||||
for( auto i = 0; i < params.size() && i < 5; ++i )
|
|
||||||
m_params[i] = params[i];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct TimepointDataBattleTalk : public TimepointData
|
|
||||||
{
|
|
||||||
uint32_t m_battleTalkId;
|
|
||||||
uint32_t m_handlerId;
|
|
||||||
uint32_t m_kind;
|
|
||||||
uint32_t m_nameId;
|
|
||||||
uint32_t m_talkerId;
|
|
||||||
|
|
||||||
uint32_t m_params[ 8 ]{ 0 };
|
|
||||||
|
|
||||||
TimepointDataBattleTalk( const std::vector< uint32_t >& params ) :
|
|
||||||
TimepointData( TimepointDataType::BattleTalk )
|
|
||||||
{
|
|
||||||
for( auto i = 0; i < params.size() && i < 8; ++i )
|
|
||||||
m_params[i] = params[i];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct TimepointDataDirector : public TimepointData
|
|
||||||
{
|
|
||||||
DirectorOpId m_directorOp{ 0 };
|
|
||||||
union
|
|
||||||
{
|
|
||||||
struct
|
|
||||||
{
|
|
||||||
uint8_t index;
|
|
||||||
union
|
|
||||||
{
|
|
||||||
uint8_t val;
|
|
||||||
struct
|
|
||||||
{
|
|
||||||
uint8_t left, right;
|
|
||||||
};
|
|
||||||
} value;
|
|
||||||
};
|
|
||||||
uint8_t seq;
|
|
||||||
uint8_t flags;
|
|
||||||
} m_data{ 0 };
|
|
||||||
|
|
||||||
TimepointDataDirector( TimepointDataType type, DirectorOpId op ) :
|
|
||||||
TimepointData( type ),
|
|
||||||
m_directorOp( op )
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct TimepointDataSpawnBNpc : public TimepointData
|
|
||||||
{
|
|
||||||
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, uint32_t type ) :
|
|
||||||
TimepointData( TimepointDataType::SpawnBNpc ),
|
|
||||||
m_layoutId( layoutId ),
|
|
||||||
m_flags( flags ),
|
|
||||||
m_type( type )
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct TimepointDataBNpcFlags : public TimepointData
|
|
||||||
{
|
|
||||||
uint32_t m_layoutId{ 0xE0000000 };
|
|
||||||
uint32_t m_flags{ 0 };
|
|
||||||
|
|
||||||
TimepointDataBNpcFlags( uint32_t layoutId, uint32_t flags ) :
|
|
||||||
TimepointData( TimepointDataType::SetBNpcFlags ),
|
|
||||||
m_layoutId( layoutId ),
|
|
||||||
m_flags( flags )
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct TimepointDataEObjState : public TimepointData
|
|
||||||
{
|
|
||||||
uint32_t m_eobjId{ 0xE0000000 };
|
|
||||||
uint32_t m_state{ 0 };
|
|
||||||
|
|
||||||
TimepointDataEObjState( uint32_t eobjId, uint32_t state ) :
|
|
||||||
TimepointData( TimepointDataType::SetEObjState ),
|
|
||||||
m_eobjId( eobjId ),
|
|
||||||
m_state( state )
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct TimepointDataBGM : public TimepointData
|
|
||||||
{
|
|
||||||
uint32_t m_bgmId{ 0 };
|
|
||||||
|
|
||||||
TimepointDataBGM( uint32_t bgmId ):
|
|
||||||
TimepointData( TimepointDataType::SetBgm ),
|
|
||||||
m_bgmId( bgmId )
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct TimepointDataCondition : public TimepointData
|
|
||||||
{
|
|
||||||
// todo: rng?
|
|
||||||
uint32_t m_conditionId;
|
|
||||||
bool m_enabled;
|
|
||||||
|
|
||||||
TimepointDataCondition( uint32_t conditionId, bool enabled ) :
|
|
||||||
TimepointData( TimepointDataType::SetCondition ),
|
|
||||||
m_conditionId( conditionId ),
|
|
||||||
m_enabled( enabled )
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// todo: refactor all this to allow solo actor to use
|
|
||||||
class Timepoint :
|
|
||||||
public std::enable_shared_from_this< Timepoint >
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
TimepointDataType m_type;
|
|
||||||
uint64_t m_duration{ 0 }; // milliseconds
|
|
||||||
TimepointOverrideFlags m_overrideFlags;
|
|
||||||
TimepointDataPtr m_pData;
|
|
||||||
std::string m_description;
|
|
||||||
|
|
||||||
// todo: repeatable?
|
|
||||||
|
|
||||||
const TimepointDataPtr getData() const
|
|
||||||
{
|
|
||||||
return m_pData;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool canExecute( const TimepointState& state, uint64_t elapsed ) const
|
|
||||||
{
|
|
||||||
return state.m_startTime == 0; // & &m_duration <= elapsed;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool durationElapsed( uint64_t elapsed ) const
|
|
||||||
{
|
|
||||||
return m_duration < elapsed;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool finished( const TimepointState& state, uint64_t elapsed ) const
|
|
||||||
{
|
|
||||||
return durationElapsed( elapsed ) || state.m_finished;
|
|
||||||
}
|
|
||||||
|
|
||||||
void reset( TimepointState& state ) const
|
|
||||||
{
|
|
||||||
state.m_startTime = 0;
|
|
||||||
state.m_lastTick = 0;
|
|
||||||
state.m_finished = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void from_json( const nlohmann::json& json, const std::unordered_map< std::string, TimelineActor >& actors, uint32_t selfLayoutId );
|
|
||||||
// todo: separate execute/update into onStart and onTick?
|
|
||||||
void update( TimepointState& state, TimelineActor& self, TerritoryPtr pTeri, uint64_t time ) const;
|
|
||||||
void execute( TimepointState& state, TimelineActor& self, TerritoryPtr pTeri, uint64_t time ) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Phase :
|
|
||||||
public std::enable_shared_from_this< Phase >
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
|
|
||||||
// todo: allow callbacks to push timepoints
|
|
||||||
|
|
||||||
std::string m_name;
|
|
||||||
std::vector< Timepoint > m_timepoints;
|
|
||||||
|
|
||||||
// todo: i wrote this very sleep deprived, ensure it is actually sane
|
|
||||||
void execute( ConditionState& state, TimelineActor& self, TerritoryPtr pTeri, uint64_t time ) const
|
|
||||||
{
|
|
||||||
if( state.m_startTime == 0 )
|
|
||||||
state.m_startTime = time;
|
|
||||||
if( state.m_phaseInfo.m_startTime == 0 )
|
|
||||||
state.m_phaseInfo.m_startTime = time;
|
|
||||||
|
|
||||||
if( state.m_phaseInfo.m_lastTimepointTime == 0 )
|
|
||||||
{
|
|
||||||
state.m_phaseInfo.m_lastTimepointTime = time;
|
|
||||||
state.m_phaseInfo.m_timepointStates.clear();
|
|
||||||
state.m_phaseInfo.m_timepointStates.resize( m_timepoints.size() );
|
|
||||||
}
|
|
||||||
|
|
||||||
for( auto i = state.m_phaseInfo.m_lastTimepointIndex; i < m_timepoints.size(); )
|
|
||||||
{
|
|
||||||
uint64_t phaseElapsed = time - state.m_phaseInfo.m_startTime;
|
|
||||||
uint64_t timepointElapsed = time - state.m_phaseInfo.m_lastTimepointTime;
|
|
||||||
|
|
||||||
auto& tpState = state.m_phaseInfo.m_timepointStates[ i ];
|
|
||||||
auto& timepoint = m_timepoints[ i ];
|
|
||||||
|
|
||||||
if( timepoint.canExecute( tpState, timepointElapsed ) )
|
|
||||||
{
|
|
||||||
timepoint.execute( tpState, self, pTeri, time );
|
|
||||||
state.m_phaseInfo.m_lastTimepointTime = time;
|
|
||||||
}
|
|
||||||
else if( !timepoint.finished( tpState, timepointElapsed ) )
|
|
||||||
{
|
|
||||||
timepoint.update( tpState, self, pTeri, time );
|
|
||||||
}
|
|
||||||
|
|
||||||
if( timepoint.durationElapsed( timepointElapsed ) && timepoint.finished( tpState, timepointElapsed ) )
|
|
||||||
{
|
|
||||||
// timepoint.reset( tpState );
|
|
||||||
// make sure this timepoint isnt run again unless phase loops
|
|
||||||
++i;
|
|
||||||
state.m_phaseInfo.m_lastTimepointIndex = i;
|
|
||||||
|
|
||||||
if( i == m_timepoints.size() )
|
|
||||||
{
|
|
||||||
state.m_phaseInfo.m_lastTimepointIndex++;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void reset( ConditionState& state ) const
|
|
||||||
{
|
|
||||||
state.m_phaseInfo.m_startTime = 0;
|
|
||||||
state.m_phaseInfo.m_lastTimepointIndex = 0;
|
|
||||||
state.m_phaseInfo.m_lastTimepointTime = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool completed( const ConditionState& state ) const
|
|
||||||
{
|
|
||||||
return state.m_phaseInfo.m_lastTimepointIndex > m_timepoints.size();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
using PhasePtr = std::shared_ptr< Phase >;
|
|
||||||
|
|
||||||
class PhaseCondition :
|
|
||||||
public std::enable_shared_from_this< PhaseCondition >
|
|
||||||
{
|
|
||||||
protected:
|
|
||||||
ConditionType m_conditionType{ 0 };
|
|
||||||
Phase m_phase;
|
|
||||||
std::string m_description;
|
|
||||||
uint32_t m_cooldown{ 0 };
|
|
||||||
bool m_loop{ false };
|
|
||||||
bool m_enabled{ true };
|
|
||||||
uint32_t m_id{ 0 };
|
|
||||||
|
|
||||||
public:
|
|
||||||
PhaseCondition() {}
|
|
||||||
~PhaseCondition() {}
|
|
||||||
|
|
||||||
virtual void from_json( nlohmann::json& json, Phase& phase, ConditionType condition )
|
|
||||||
{
|
|
||||||
this->m_conditionType = condition;
|
|
||||||
this->m_loop = json.at( "loop" ).get< bool >();
|
|
||||||
//this->m_cooldown = json.at( "cooldown" ).get< uint32_t >();
|
|
||||||
this->m_phase = phase;
|
|
||||||
this->m_description = json.at( "description" ).get< std::string >();
|
|
||||||
this->m_enabled = json.at( "enabled" ).get< bool >();
|
|
||||||
this->m_id = json.at( "id" ).get< uint32_t >();
|
|
||||||
}
|
|
||||||
|
|
||||||
void execute( ConditionState& state, TimelineActor& self, TerritoryPtr pTeri, TimelinePack& pack, uint64_t time ) const
|
|
||||||
{
|
|
||||||
state.m_startTime = time;
|
|
||||||
m_phase.execute( state, self, pTeri, time );
|
|
||||||
};
|
|
||||||
|
|
||||||
void update( ConditionState& state, TimelineActor& self, TerritoryPtr pTeri, TimelinePack& pack, uint64_t time ) const
|
|
||||||
{
|
|
||||||
m_phase.execute( state, self, pTeri, time );
|
|
||||||
}
|
|
||||||
|
|
||||||
void setEnabled( ConditionState& state, bool enabled )
|
|
||||||
{
|
|
||||||
state.m_enabled = enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
void reset( ConditionState& state ) const
|
|
||||||
{
|
|
||||||
state.m_startTime = 0;
|
|
||||||
state.m_enabled = isDefaultEnabled();
|
|
||||||
m_phase.reset( state );
|
|
||||||
}
|
|
||||||
|
|
||||||
bool inProgress( const ConditionState& state ) const
|
|
||||||
{
|
|
||||||
return state.m_startTime != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// todo: better naming
|
|
||||||
bool isStateEnabled( const ConditionState& state ) const
|
|
||||||
{
|
|
||||||
return state.m_enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isDefaultEnabled() const
|
|
||||||
{
|
|
||||||
return m_enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool completed( const ConditionState& state ) const
|
|
||||||
{
|
|
||||||
return m_phase.completed( state );
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isLoopable() const
|
|
||||||
{
|
|
||||||
return m_loop;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool loopReady( ConditionState& state, uint64_t time ) const
|
|
||||||
{
|
|
||||||
return m_phase.completed( state ) && m_loop && ( state.m_startTime + m_cooldown <= time );
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual bool isConditionMet( ConditionState& state, TerritoryPtr pTeri, TimelinePack& pack, uint64_t time ) const
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
uint32_t getId() const
|
|
||||||
{
|
|
||||||
return m_id;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
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::unordered_map< std::string, Entity::BNpcPtr > m_subActors;
|
|
||||||
public:
|
|
||||||
uint32_t m_layoutId{ 0 };
|
|
||||||
uint32_t m_hp{ 0 };
|
|
||||||
std::string m_name;
|
|
||||||
|
|
||||||
TimelineActor() { }
|
|
||||||
TimelineActor( const TimelineActor& rhs ) :
|
|
||||||
m_layoutId( rhs.m_layoutId ),
|
|
||||||
m_hp( rhs.m_hp ),
|
|
||||||
m_name( rhs.m_name ),
|
|
||||||
m_phaseConditions( rhs.m_phaseConditions ),
|
|
||||||
m_conditionStates( rhs.m_conditionStates )
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void addPhaseCondition( PhaseConditionPtr pCondition )
|
|
||||||
{
|
|
||||||
m_phaseConditions.emplace( std::make_pair( pCondition->getId(), pCondition ) );
|
|
||||||
m_conditionStates[ pCondition->getId() ] = {};
|
|
||||||
m_conditionStates[ pCondition->getId() ].m_enabled = pCondition->isDefaultEnabled();
|
|
||||||
}
|
|
||||||
|
|
||||||
// todo: make this sane
|
|
||||||
void update( TerritoryPtr pTeri, TimelinePack& pack, uint64_t time )
|
|
||||||
{
|
|
||||||
// todo: handle interrupts
|
|
||||||
for( const auto& condition : m_phaseConditions)
|
|
||||||
{
|
|
||||||
const auto& pCondition = condition.second;
|
|
||||||
auto& state = m_conditionStates[ pCondition->getId() ];
|
|
||||||
|
|
||||||
// ignore if not enabled, unless overriden to enable
|
|
||||||
if( !pCondition->isStateEnabled( state ) )
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if( pCondition->completed( state ) )
|
|
||||||
{
|
|
||||||
if( pCondition->isLoopable() )
|
|
||||||
{
|
|
||||||
if( pCondition->loopReady( state, time ) )
|
|
||||||
pCondition->reset( state );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if( pCondition->inProgress( state ) )
|
|
||||||
{
|
|
||||||
pCondition->update( state, *this, pTeri, pack, time );
|
|
||||||
}
|
|
||||||
else if( pCondition->isConditionMet( state, pTeri, pack, time ) )
|
|
||||||
{
|
|
||||||
pCondition->execute( state, *this, pTeri, pack, time );
|
|
||||||
|
|
||||||
if( pack.getStartTime() == 0 )
|
|
||||||
pack.setStartTime( state.m_startTime );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void resetConditionState( uint32_t conditionId )
|
|
||||||
{
|
|
||||||
if( auto it = m_phaseConditions.find( conditionId ); it != m_phaseConditions.end() )
|
|
||||||
{
|
|
||||||
auto& state = m_conditionStates.at( it->first );
|
|
||||||
it->second->reset( state );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void setConditionStateEnabled( uint32_t conditionId, bool enabled )
|
|
||||||
{
|
|
||||||
if( auto it = m_conditionStates.find( conditionId ); it != m_conditionStates.end() )
|
|
||||||
{
|
|
||||||
auto& state = m_conditionStates.at( it->first );
|
|
||||||
state.m_enabled = enabled;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void resetAllConditionStates()
|
|
||||||
{
|
|
||||||
for( const auto& condition : m_phaseConditions )
|
|
||||||
{
|
|
||||||
const auto& pCondition = condition.second;
|
|
||||||
auto& state = m_conditionStates.at( condition.first );
|
|
||||||
|
|
||||||
pCondition->reset( state );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Entity::BNpcPtr spawnSubActor( TerritoryPtr pTeri, const std::string& name );
|
|
||||||
Entity::BNpcPtr getSubActor( const std::string& name );
|
|
||||||
void resetSubActors( TerritoryPtr pTeri );
|
|
||||||
};
|
|
||||||
|
|
||||||
// todo: actually handle solo stuff properly (or tie to zone director/content director at least)
|
// todo: actually handle solo stuff properly (or tie to zone director/content director at least)
|
||||||
class TimelinePack
|
class TimelinePack
|
||||||
{
|
{
|
||||||
TimelinePackType m_type{TimelinePackType::EncounterFight};
|
TimelinePackType m_type{ TimelinePackType::EncounterFight };
|
||||||
std::vector< TimelineActor > m_actors;
|
std::vector< TimelineActor > m_actors;
|
||||||
std::string m_name;
|
std::string m_name;
|
||||||
|
|
||||||
|
std::unordered_map< std::string, Selector > m_selectors;
|
||||||
|
|
||||||
uint64_t m_startTime{ 0 };
|
uint64_t m_startTime{ 0 };
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TimelinePack() { }
|
TimelinePack() {}
|
||||||
TimelinePack( const TimelinePack& rhs ) :
|
TimelinePack( const TimelinePack& rhs ) :
|
||||||
m_type( rhs.m_type ),
|
m_type( rhs.m_type ),
|
||||||
m_name( rhs.m_name ),
|
m_name( rhs.m_name ),
|
||||||
m_actors( rhs.m_actors ),
|
m_actors( rhs.m_actors ),
|
||||||
m_startTime( 0 )
|
m_startTime( 0 )
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TimelinePack( TimelinePackType type ) : m_type( type ) {}
|
TimelinePack( TimelinePackType type ) : m_type( type ) {}
|
||||||
|
|
||||||
void setName( const std::string& name )
|
void setName( const std::string& name );
|
||||||
{
|
|
||||||
m_name = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
void addTimelineActor( const TimelineActor& actor )
|
void addSelector( const std::string& name, const Selector& selector );
|
||||||
{
|
|
||||||
m_actors.emplace_back( actor );
|
|
||||||
}
|
|
||||||
|
|
||||||
void setStartTime( uint64_t time )
|
void createSnapshot( const std::string& selectorName, Entity::CharaPtr pSrc,
|
||||||
{
|
const std::vector< uint32_t >& exclude );
|
||||||
m_startTime = time;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t getStartTime() const
|
const World::AI::Snapshot::Results& getSnapshotResults( const std::string& selectorName );
|
||||||
{
|
|
||||||
return m_startTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
void update( TerritoryPtr pTeri, uint64_t time )
|
const World::AI::Snapshot::TargetIds& getSnapshotTargetIds( const std::string& selectorName );
|
||||||
{
|
|
||||||
for( auto& actor : m_actors )
|
void addTimelineActor( const TimelineActor& actor );
|
||||||
actor.update( pTeri, *this, time );
|
|
||||||
}
|
Entity::BNpcPtr getBNpcByActorRef( const std::string& name, TerritoryPtr pTeri, const std::string& subActorName = {} );
|
||||||
|
|
||||||
|
void setStartTime( uint64_t time );
|
||||||
|
|
||||||
|
uint64_t getStartTime() const;
|
||||||
|
|
||||||
|
void update( TerritoryPtr pTeri, uint64_t time );
|
||||||
};
|
};
|
||||||
|
|
||||||
//
|
class EncounterTimeline
|
||||||
// Conditions
|
|
||||||
//
|
|
||||||
class ConditionHp : PhaseCondition
|
|
||||||
{
|
{
|
||||||
public:
|
|
||||||
uint32_t layoutId;
|
|
||||||
union
|
|
||||||
{
|
|
||||||
uint8_t val;
|
|
||||||
struct
|
|
||||||
{
|
|
||||||
uint8_t min, max;
|
|
||||||
};
|
|
||||||
} hp;
|
|
||||||
|
|
||||||
void from_json( nlohmann::json& json, Phase& phase, ConditionType condition,
|
|
||||||
const std::unordered_map< std::string, TimelineActor >& actors );
|
|
||||||
|
|
||||||
bool isConditionMet( ConditionState& state, TerritoryPtr pTeri, TimelinePack& pack, uint64_t time ) const override;
|
|
||||||
};
|
|
||||||
|
|
||||||
class ConditionDirectorVar : PhaseCondition
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
union
|
|
||||||
{
|
|
||||||
struct
|
|
||||||
{
|
|
||||||
uint32_t index;
|
|
||||||
uint32_t value;
|
|
||||||
};
|
|
||||||
uint8_t seq;
|
|
||||||
uint8_t flags;
|
|
||||||
} param;
|
|
||||||
|
|
||||||
void from_json( nlohmann::json& json, Phase& phase, ConditionType condition );
|
|
||||||
bool isConditionMet( ConditionState& state, TerritoryPtr pTeri, TimelinePack& pack, uint64_t time ) const override;
|
|
||||||
};
|
|
||||||
|
|
||||||
class ConditionEncounterTimeElapsed : PhaseCondition
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
uint64_t duration;
|
|
||||||
|
|
||||||
void from_json( nlohmann::json& json, Phase& phase, ConditionType condition );
|
|
||||||
bool isConditionMet( ConditionState& state, TerritoryPtr pTeri, TimelinePack& pack, uint64_t time ) const override;
|
|
||||||
};
|
|
||||||
|
|
||||||
class ConditionCombatState : PhaseCondition
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
uint32_t layoutId;
|
|
||||||
CombatStateType combatState;
|
|
||||||
|
|
||||||
void from_json( nlohmann::json& json, Phase& phase, ConditionType condition, const std::unordered_map< std::string, TimelineActor >& actors );
|
|
||||||
bool isConditionMet( ConditionState& state, TerritoryPtr pTeri, TimelinePack& pack, uint64_t time ) const override;
|
|
||||||
};
|
|
||||||
|
|
||||||
class ConditionBNpcFlags : PhaseCondition
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
uint32_t layoutId;
|
|
||||||
uint32_t flags;
|
|
||||||
|
|
||||||
void from_json( nlohmann::json& json, Phase& phase, ConditionType condition, const std::unordered_map< std::string, TimelineActor >& actors );
|
|
||||||
bool isConditionMet( ConditionState& state, TerritoryPtr pTeri, TimelinePack& pack, uint64_t time ) const override;
|
|
||||||
};
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
TimelinePack getEncounterPack( const std::string& name, bool reload = false );
|
TimelinePack getEncounterPack( const std::string& name, bool reload = false );
|
||||||
|
|
16
src/world/Encounter/Forwards.h
Normal file
16
src/world/Encounter/Forwards.h
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace Sapphire::Encounter
|
||||||
|
{
|
||||||
|
class Selector;
|
||||||
|
class TimelineActor;
|
||||||
|
class Phase;
|
||||||
|
class PhaseCondition;
|
||||||
|
class Timepoint;
|
||||||
|
|
||||||
|
class TimelinePack;
|
||||||
|
|
||||||
|
using PhaseConditionPtr = std::shared_ptr< PhaseCondition >;
|
||||||
|
}
|
271
src/world/Encounter/PhaseCondition.cpp
Normal file
271
src/world/Encounter/PhaseCondition.cpp
Normal file
|
@ -0,0 +1,271 @@
|
||||||
|
#include "PhaseCondition.h"
|
||||||
|
|
||||||
|
#include "EncounterTimeline.h"
|
||||||
|
#include "TimelineActor.h"
|
||||||
|
#include "TimelineActorState.h"
|
||||||
|
|
||||||
|
#include <Actor/Chara.h>
|
||||||
|
#include <Actor/BNpc.h>
|
||||||
|
#include <Actor/Player.h>
|
||||||
|
|
||||||
|
#include <Territory/Territory.h>
|
||||||
|
#include <Territory/InstanceContent.h>
|
||||||
|
#include <Territory/QuestBattle.h>
|
||||||
|
|
||||||
|
namespace Sapphire::Encounter
|
||||||
|
{
|
||||||
|
bool ConditionHp::isConditionMet( ConditionState& state, TerritoryPtr pTeri, TimelinePack& pack, uint64_t time ) const
|
||||||
|
{
|
||||||
|
auto pBNpc = pTeri->getActiveBNpcByLayoutId( layoutId );
|
||||||
|
if( !pBNpc )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// todo: check time elapsed
|
||||||
|
|
||||||
|
switch( m_conditionType )
|
||||||
|
{
|
||||||
|
case ConditionType::HpPctLessThan:
|
||||||
|
return pBNpc->getHpPercent() < hp.val;
|
||||||
|
case ConditionType::HpPctBetween:
|
||||||
|
{
|
||||||
|
auto hpPct = pBNpc->getHpPercent();
|
||||||
|
return hpPct >= hp.min && hpPct <= hp.max;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool ConditionDirectorVar::isConditionMet( ConditionState& state, TerritoryPtr pTeri, TimelinePack& pack, uint64_t time ) const
|
||||||
|
{
|
||||||
|
|
||||||
|
Event::DirectorPtr pDirector = pTeri->getAsInstanceContent();
|
||||||
|
if( pDirector == nullptr )
|
||||||
|
pDirector = pTeri->getAsQuestBattle();
|
||||||
|
|
||||||
|
switch( m_conditionType )
|
||||||
|
{
|
||||||
|
case ConditionType::DirectorVarEquals:
|
||||||
|
return pDirector->getDirectorVar( param.index ) == param.value;
|
||||||
|
case ConditionType::DirectorVarGreaterThan:
|
||||||
|
return pDirector->getDirectorVar( param.index ) > param.value;
|
||||||
|
case ConditionType::DirectorFlagsEquals:
|
||||||
|
return pDirector->getFlags() == param.flags;
|
||||||
|
case ConditionType::DirectorFlagsGreaterThan:
|
||||||
|
return pDirector->getFlags() > param.flags;
|
||||||
|
case ConditionType::DirectorSeqEquals:
|
||||||
|
return pDirector->getSequence() == param.seq;
|
||||||
|
case ConditionType::DirectorSeqGreaterThan:
|
||||||
|
return pDirector->getSequence() > param.seq;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConditionCombatState::isConditionMet( ConditionState& state, TerritoryPtr pTeri, TimelinePack& pack, uint64_t time ) const
|
||||||
|
{
|
||||||
|
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:
|
||||||
|
return pBattleNpc->getState() == Entity::BNpcState::Idle;
|
||||||
|
case CombatStateType::Combat:
|
||||||
|
return pBattleNpc->getState() == Entity::BNpcState::Combat;
|
||||||
|
case CombatStateType::Retreat:
|
||||||
|
return pBattleNpc->getState() == Entity::BNpcState::Retreat;
|
||||||
|
case CombatStateType::Roaming:
|
||||||
|
return pBattleNpc->getState() == Entity::BNpcState::Roaming;
|
||||||
|
case CombatStateType::JustDied:
|
||||||
|
return pBattleNpc->getState() == Entity::BNpcState::JustDied;
|
||||||
|
case CombatStateType::Dead:
|
||||||
|
return pBattleNpc->getState() == Entity::BNpcState::Dead;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConditionEncounterTimeElapsed::isConditionMet( ConditionState& state, TerritoryPtr pTeri, TimelinePack& pack, uint64_t time ) const
|
||||||
|
{
|
||||||
|
auto elapsed = time - pack.getStartTime();
|
||||||
|
// todo: check encounter time
|
||||||
|
return elapsed >= this->duration;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConditionBNpcFlags::isConditionMet( ConditionState& state, TerritoryPtr pTeri, TimelinePack& pack, uint64_t time ) const
|
||||||
|
{
|
||||||
|
auto pBNpc = pTeri->getActiveBNpcByLayoutId( this->layoutId );
|
||||||
|
return pBNpc && pBNpc->hasFlag( this->flags );
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConditionHp::from_json( nlohmann::json& json, Phase& phase, ConditionType condition,
|
||||||
|
const std::unordered_map< std::string, TimelineActor >& actors )
|
||||||
|
{
|
||||||
|
PhaseCondition::from_json( json, phase, condition );
|
||||||
|
|
||||||
|
auto& paramData = json.at( "paramData" );
|
||||||
|
auto actorRef = paramData.at( "sourceActor" ).get< std::string >();
|
||||||
|
|
||||||
|
// resolve the actor whose hp we are checking
|
||||||
|
if( auto it = actors.find( actorRef ); it != actors.end() )
|
||||||
|
this->layoutId = it->second.m_layoutId;
|
||||||
|
else
|
||||||
|
throw std::runtime_error( fmt::format( std::string( "EncounterTimeline::ConditionHp::from_json unable to find actor by name: %s" ), actorRef ) );
|
||||||
|
|
||||||
|
switch( condition )
|
||||||
|
{
|
||||||
|
case ConditionType::HpPctLessThan:
|
||||||
|
this->hp.val = paramData.at( "hp" ).get< uint32_t >();
|
||||||
|
break;
|
||||||
|
case ConditionType::HpPctBetween:
|
||||||
|
this->hp.min = paramData.at( "hpMin" ).get< uint32_t >(),
|
||||||
|
this->hp.max = paramData.at( "hpMax" ).get< uint32_t >();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConditionDirectorVar::from_json( nlohmann::json& json, Phase& phase, ConditionType condition )
|
||||||
|
{
|
||||||
|
PhaseCondition::from_json( json, phase, condition );
|
||||||
|
|
||||||
|
auto& paramData = json.at( "paramData" );
|
||||||
|
|
||||||
|
switch( condition )
|
||||||
|
{
|
||||||
|
case ConditionType::DirectorVarEquals:
|
||||||
|
case ConditionType::DirectorVarGreaterThan:
|
||||||
|
{
|
||||||
|
param.index = paramData.at( "idx" ).get< uint32_t >();
|
||||||
|
param.value = paramData.at( "val" ).get< uint32_t >();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ConditionType::DirectorFlagsEquals:
|
||||||
|
case ConditionType::DirectorFlagsGreaterThan:
|
||||||
|
{
|
||||||
|
param.flags = paramData.at( "flags" ).get< uint32_t >();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ConditionType::DirectorSeqEquals:
|
||||||
|
case ConditionType::DirectorSeqGreaterThan:
|
||||||
|
{
|
||||||
|
param.seq = paramData.at( "seq" ).get< uint32_t >();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConditionCombatState::from_json( nlohmann::json& json, Phase& phase, ConditionType condition,
|
||||||
|
const std::unordered_map< std::string, TimelineActor >& actors )
|
||||||
|
{
|
||||||
|
PhaseCondition::from_json( json, phase, condition );
|
||||||
|
|
||||||
|
auto& paramData = json.at( "paramData" );
|
||||||
|
auto actorRef = paramData.at( "sourceActor" ).get< std::string >();
|
||||||
|
|
||||||
|
// resolve the actor whose name we are checking
|
||||||
|
if( auto it = actors.find( actorRef ); it != actors.end() )
|
||||||
|
this->layoutId = it->second.m_layoutId;
|
||||||
|
else
|
||||||
|
throw std::runtime_error( fmt::format( std::string( "EncounterTimeline::ConditionCombatState::from_json unable to find actor by name: %s" ), actorRef ) );
|
||||||
|
|
||||||
|
this->combatState = paramData.at( "combatState" ).get< CombatStateType >();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConditionEncounterTimeElapsed::from_json( nlohmann::json& json, Phase& phase, ConditionType condition )
|
||||||
|
{
|
||||||
|
PhaseCondition::from_json( json, phase, condition );
|
||||||
|
|
||||||
|
auto& paramData = json.at( "paramData" );
|
||||||
|
auto duration = paramData.at( "duration" ).get< uint64_t >();
|
||||||
|
|
||||||
|
this->duration = duration;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConditionBNpcFlags::from_json( nlohmann::json& json, Phase& phase, ConditionType condition,
|
||||||
|
const std::unordered_map< std::string, TimelineActor >& actors )
|
||||||
|
{
|
||||||
|
PhaseCondition::from_json( json, phase, condition );
|
||||||
|
auto& paramData = json.at( "paramData" );
|
||||||
|
auto actorRef = paramData.at( "sourceActor" ).get< std::string >();
|
||||||
|
|
||||||
|
// resolve the actor whose name we are checking
|
||||||
|
if( auto it = actors.find( actorRef ); it != actors.end() )
|
||||||
|
this->layoutId = it->second.m_layoutId;
|
||||||
|
else
|
||||||
|
throw std::runtime_error( fmt::format( std::string( "EncounterTimeline::ConditionBNpcFlags::from_json unable to find actor by name: %s" ), actorRef ) );
|
||||||
|
|
||||||
|
this->flags = json.at( "flags" ).get< uint32_t >();
|
||||||
|
// todo: BNpcHasFlags
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo: i wrote this very sleep deprived, ensure it is actually sane
|
||||||
|
|
||||||
|
void Phase::execute( ConditionState& state, TimelineActor& self, TerritoryPtr pTeri, uint64_t time ) const
|
||||||
|
{
|
||||||
|
if( state.m_startTime == 0 )
|
||||||
|
{
|
||||||
|
state.m_startTime = time;
|
||||||
|
self.spawnAllSubActors( pTeri );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( state.m_phaseInfo.m_startTime == 0 )
|
||||||
|
state.m_phaseInfo.m_startTime = time;
|
||||||
|
|
||||||
|
if( state.m_phaseInfo.m_lastTimepointTime == 0 )
|
||||||
|
{
|
||||||
|
state.m_phaseInfo.m_lastTimepointTime = time;
|
||||||
|
state.m_phaseInfo.m_timepointStates.clear();
|
||||||
|
state.m_phaseInfo.m_timepointStates.resize( m_timepoints.size() );
|
||||||
|
}
|
||||||
|
|
||||||
|
for( auto i = state.m_phaseInfo.m_lastTimepointIndex; i < m_timepoints.size(); )
|
||||||
|
{
|
||||||
|
uint64_t phaseElapsed = time - state.m_phaseInfo.m_startTime;
|
||||||
|
uint64_t timepointElapsed = time - state.m_phaseInfo.m_lastTimepointTime;
|
||||||
|
|
||||||
|
auto& tpState = state.m_phaseInfo.m_timepointStates[ i ];
|
||||||
|
auto& timepoint = m_timepoints[ i ];
|
||||||
|
|
||||||
|
if( timepoint.canExecute( tpState, timepointElapsed ) )
|
||||||
|
{
|
||||||
|
timepoint.execute( tpState, self, pTeri, time );
|
||||||
|
state.m_phaseInfo.m_lastTimepointTime = time;
|
||||||
|
}
|
||||||
|
else if( !timepoint.finished( tpState, timepointElapsed ) )
|
||||||
|
{
|
||||||
|
timepoint.update( tpState, self, pTeri, time );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( timepoint.durationElapsed( timepointElapsed ) && timepoint.finished( tpState, timepointElapsed ) )
|
||||||
|
{
|
||||||
|
// timepoint.reset( tpState );
|
||||||
|
// make sure this timepoint isnt run again unless phase loops
|
||||||
|
++i;
|
||||||
|
state.m_phaseInfo.m_lastTimepointIndex = i;
|
||||||
|
|
||||||
|
if( i == m_timepoints.size() )
|
||||||
|
{
|
||||||
|
state.m_phaseInfo.m_lastTimepointIndex++;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Phase::reset( ConditionState& state ) const
|
||||||
|
{
|
||||||
|
state.m_phaseInfo.m_startTime = 0;
|
||||||
|
state.m_phaseInfo.m_lastTimepointIndex = 0;
|
||||||
|
state.m_phaseInfo.m_lastTimepointTime = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Phase::completed( const ConditionState& state ) const
|
||||||
|
{
|
||||||
|
return state.m_phaseInfo.m_lastTimepointIndex > m_timepoints.size();
|
||||||
|
}
|
||||||
|
}// namespace Sapphire::Encounter
|
217
src/world/Encounter/PhaseCondition.h
Normal file
217
src/world/Encounter/PhaseCondition.h
Normal file
|
@ -0,0 +1,217 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include "Timepoint.h"
|
||||||
|
#include "TimelineActorState.h"
|
||||||
|
|
||||||
|
namespace Sapphire::Encounter
|
||||||
|
{
|
||||||
|
enum class CombatStateType
|
||||||
|
{
|
||||||
|
Idle,
|
||||||
|
Combat,
|
||||||
|
Retreat,
|
||||||
|
Roaming,
|
||||||
|
JustDied,
|
||||||
|
Dead
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class ConditionType : uint32_t
|
||||||
|
{
|
||||||
|
HpPctLessThan,
|
||||||
|
HpPctBetween,
|
||||||
|
|
||||||
|
DirectorVarEquals,
|
||||||
|
DirectorVarGreaterThan,
|
||||||
|
DirectorSeqEquals,
|
||||||
|
DirectorSeqGreaterThan,
|
||||||
|
DirectorFlagsEquals,
|
||||||
|
DirectorFlagsGreaterThan,
|
||||||
|
|
||||||
|
EncounterTimeElapsed,
|
||||||
|
CombatState,
|
||||||
|
BNpcHasFlags
|
||||||
|
};
|
||||||
|
|
||||||
|
class Phase :
|
||||||
|
public std::enable_shared_from_this< Phase >
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
// todo: allow callbacks to push timepoints
|
||||||
|
|
||||||
|
std::string m_name;
|
||||||
|
std::vector< Timepoint > m_timepoints;
|
||||||
|
|
||||||
|
// todo: i wrote this very sleep deprived, ensure it is actually sane
|
||||||
|
void execute( ConditionState& state, TimelineActor& self, TerritoryPtr pTeri, uint64_t time ) const;
|
||||||
|
|
||||||
|
void reset( ConditionState& state ) const;
|
||||||
|
|
||||||
|
bool completed( const ConditionState& state ) const;
|
||||||
|
};
|
||||||
|
using PhasePtr = std::shared_ptr< Phase >;
|
||||||
|
|
||||||
|
class PhaseCondition : public std::enable_shared_from_this< PhaseCondition >
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
ConditionType m_conditionType{ 0 };
|
||||||
|
Phase m_phase;
|
||||||
|
std::string m_description;
|
||||||
|
uint32_t m_cooldown{ 0 };
|
||||||
|
bool m_loop{ false };
|
||||||
|
bool m_enabled{ true };
|
||||||
|
uint32_t m_id{ 0 };
|
||||||
|
|
||||||
|
public:
|
||||||
|
PhaseCondition() {}
|
||||||
|
~PhaseCondition() {}
|
||||||
|
|
||||||
|
virtual void from_json( nlohmann::json& json, Phase& phase, ConditionType condition )
|
||||||
|
{
|
||||||
|
this->m_conditionType = condition;
|
||||||
|
this->m_loop = json.at( "loop" ).get< bool >();
|
||||||
|
//this->m_cooldown = json.at( "cooldown" ).get< uint32_t >();
|
||||||
|
this->m_phase = phase;
|
||||||
|
this->m_description = json.at( "description" ).get< std::string >();
|
||||||
|
this->m_enabled = json.at( "enabled" ).get< bool >();
|
||||||
|
this->m_id = json.at( "id" ).get< uint32_t >();
|
||||||
|
}
|
||||||
|
|
||||||
|
void execute( ConditionState& state, TimelineActor& self, TerritoryPtr pTeri, TimelinePack& pack, uint64_t time ) const
|
||||||
|
{
|
||||||
|
state.m_startTime = time;
|
||||||
|
m_phase.execute( state, self, pTeri, time );
|
||||||
|
};
|
||||||
|
|
||||||
|
void update( ConditionState& state, TimelineActor& self, TerritoryPtr pTeri, TimelinePack& pack, uint64_t time ) const
|
||||||
|
{
|
||||||
|
m_phase.execute( state, self, pTeri, time );
|
||||||
|
}
|
||||||
|
|
||||||
|
void setEnabled( ConditionState& state, bool enabled )
|
||||||
|
{
|
||||||
|
state.m_enabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset( ConditionState& state ) const
|
||||||
|
{
|
||||||
|
state.m_startTime = 0;
|
||||||
|
state.m_enabled = isDefaultEnabled();
|
||||||
|
m_phase.reset( state );
|
||||||
|
}
|
||||||
|
|
||||||
|
bool inProgress( const ConditionState& state ) const
|
||||||
|
{
|
||||||
|
return state.m_startTime != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo: better naming
|
||||||
|
bool isStateEnabled( const ConditionState& state ) const
|
||||||
|
{
|
||||||
|
return state.m_enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isDefaultEnabled() const
|
||||||
|
{
|
||||||
|
return m_enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool completed( const ConditionState& state ) const
|
||||||
|
{
|
||||||
|
return m_phase.completed( state );
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isLoopable() const
|
||||||
|
{
|
||||||
|
return m_loop;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool loopReady( ConditionState& state, uint64_t time ) const
|
||||||
|
{
|
||||||
|
return m_phase.completed( state ) && m_loop && ( state.m_startTime + m_cooldown <= time );
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool isConditionMet( ConditionState& state, TerritoryPtr pTeri, TimelinePack& pack, uint64_t time ) const
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
uint32_t getId() const
|
||||||
|
{
|
||||||
|
return m_id;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
using PhaseConditionPtr = std::shared_ptr< PhaseCondition >;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Conditions
|
||||||
|
//
|
||||||
|
class ConditionHp : PhaseCondition
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
uint32_t layoutId;
|
||||||
|
union
|
||||||
|
{
|
||||||
|
uint8_t val;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
uint8_t min, max;
|
||||||
|
};
|
||||||
|
} hp;
|
||||||
|
|
||||||
|
void from_json( nlohmann::json& json, Phase& phase, ConditionType condition,
|
||||||
|
const std::unordered_map< std::string, TimelineActor >& actors );
|
||||||
|
|
||||||
|
bool isConditionMet( ConditionState& state, TerritoryPtr pTeri, TimelinePack& pack, uint64_t time ) const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ConditionDirectorVar : PhaseCondition
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
union
|
||||||
|
{
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
uint32_t index;
|
||||||
|
uint32_t value;
|
||||||
|
};
|
||||||
|
uint8_t seq;
|
||||||
|
uint8_t flags;
|
||||||
|
} param;
|
||||||
|
|
||||||
|
void from_json( nlohmann::json& json, Phase& phase, ConditionType condition );
|
||||||
|
bool isConditionMet( ConditionState& state, TerritoryPtr pTeri, TimelinePack& pack, uint64_t time ) const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ConditionEncounterTimeElapsed : PhaseCondition
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
uint64_t duration;
|
||||||
|
|
||||||
|
void from_json( nlohmann::json& json, Phase& phase, ConditionType condition );
|
||||||
|
bool isConditionMet( ConditionState& state, TerritoryPtr pTeri, TimelinePack& pack, uint64_t time ) const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ConditionCombatState : PhaseCondition
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
uint32_t layoutId;
|
||||||
|
CombatStateType combatState;
|
||||||
|
|
||||||
|
void from_json( nlohmann::json& json, Phase& phase, ConditionType condition, const std::unordered_map< std::string, TimelineActor >& actors );
|
||||||
|
bool isConditionMet( ConditionState& state, TerritoryPtr pTeri, TimelinePack& pack, uint64_t time ) const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ConditionBNpcFlags : PhaseCondition
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
uint32_t layoutId;
|
||||||
|
uint32_t flags;
|
||||||
|
|
||||||
|
void from_json( nlohmann::json& json, Phase& phase, ConditionType condition, const std::unordered_map< std::string, TimelineActor >& actors );
|
||||||
|
bool isConditionMet( ConditionState& state, TerritoryPtr pTeri, TimelinePack& pack, uint64_t time ) const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
}// namespace Sapphire::Encounter
|
137
src/world/Encounter/Selector.cpp
Normal file
137
src/world/Encounter/Selector.cpp
Normal file
|
@ -0,0 +1,137 @@
|
||||||
|
#include "Selector.h"
|
||||||
|
|
||||||
|
#include <Actor/Chara.h>
|
||||||
|
|
||||||
|
namespace Sapphire::Encounter
|
||||||
|
{
|
||||||
|
void Selector::from_json( const nlohmann::json& json )
|
||||||
|
{
|
||||||
|
const static std::unordered_map< std::string, World::AI::TargetSelectFilter::Type > filterMap =
|
||||||
|
{
|
||||||
|
{ "insideRadius", World::AI::TargetSelectFilter::Type::InsideRadius },
|
||||||
|
{ "outsideRadius", World::AI::TargetSelectFilter::Type::OutsideRadius },
|
||||||
|
|
||||||
|
{ "player", World::AI::TargetSelectFilter::Type::Player },
|
||||||
|
{ "ally", World::AI::TargetSelectFilter::Type::Ally },
|
||||||
|
{ "ownBattalion", World::AI::TargetSelectFilter::Type::OwnBattalion },
|
||||||
|
|
||||||
|
{ "tank", World::AI::TargetSelectFilter::Type::Tank },
|
||||||
|
{ "healer", World::AI::TargetSelectFilter::Type::Healer },
|
||||||
|
{ "dps", World::AI::TargetSelectFilter::Type::Dps },
|
||||||
|
|
||||||
|
{ "hasStatusEffect", World::AI::TargetSelectFilter::Type::HasStatusEffect },
|
||||||
|
|
||||||
|
{ "topAggro", World::AI::TargetSelectFilter::Type::TopAggro },
|
||||||
|
{ "secondAggro", World::AI::TargetSelectFilter::Type::SecondAggro },
|
||||||
|
{ "partyMember", World::AI::TargetSelectFilter::Type::PartyMember }
|
||||||
|
};
|
||||||
|
|
||||||
|
m_name = json.at( "name" ).get< std::string >();
|
||||||
|
m_fillWithRandom = json.at( "fillRandomEntries" ).get< bool >();
|
||||||
|
m_count = json.at( "count" ).get< uint32_t >();
|
||||||
|
|
||||||
|
std::vector< World::AI::TargetSelectFilterPtr > filters;
|
||||||
|
auto filtersJ = json.at( "filters" ).items();
|
||||||
|
for( const auto& filterJ : filtersJ )
|
||||||
|
{
|
||||||
|
auto filterV = filterJ.value();
|
||||||
|
auto name = filterV.at( "type" ).get< std::string >();
|
||||||
|
auto typeId = filterMap.find( name )->second;
|
||||||
|
auto negate = filterV.at( "negate" ).get< bool >();
|
||||||
|
|
||||||
|
World::AI::TargetSelectFilterPtr pFilter = nullptr;
|
||||||
|
|
||||||
|
switch( typeId )
|
||||||
|
{
|
||||||
|
case World::AI::TargetSelectFilter::Type::InsideRadius:
|
||||||
|
{
|
||||||
|
auto radius = filterV.at( "param" ).get< uint32_t >();
|
||||||
|
pFilter = std::make_shared< World::AI::InsideRadiusFilter >( static_cast< float >( radius ), negate );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case World::AI::TargetSelectFilter::Type::OutsideRadius:
|
||||||
|
{
|
||||||
|
auto radius = filterV.at( "param" ).get< uint32_t >();
|
||||||
|
pFilter = std::make_shared< World::AI::OutsideRadiusFilter >( static_cast< float >( radius ), negate );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case World::AI::TargetSelectFilter::Type::Player:
|
||||||
|
{
|
||||||
|
pFilter = std::make_shared< World::AI::PlayerFilter >( negate );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case World::AI::TargetSelectFilter::Type::Ally:
|
||||||
|
{
|
||||||
|
pFilter = std::make_shared< World::AI::AllyFilter >( negate );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case World::AI::TargetSelectFilter::Type::OwnBattalion:
|
||||||
|
{
|
||||||
|
pFilter = std::make_shared< World::AI::OwnBattalionFilter >( negate );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case World::AI::TargetSelectFilter::Type::Tank:
|
||||||
|
{
|
||||||
|
pFilter = std::make_shared< World::AI::TankFilter >( negate );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case World::AI::TargetSelectFilter::Type::Healer:
|
||||||
|
{
|
||||||
|
pFilter = std::make_shared< World::AI::HealerFilter >( negate );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case World::AI::TargetSelectFilter::Type::Dps:
|
||||||
|
{
|
||||||
|
pFilter = std::make_shared< World::AI::DpsFilter >( negate );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case World::AI::TargetSelectFilter::Type::HasStatusEffect:
|
||||||
|
{
|
||||||
|
auto statusId = filterV.at( "param" ).get< uint32_t >();
|
||||||
|
pFilter = std::make_shared< World::AI::HasStatusEffectFilter >( statusId, negate );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case World::AI::TargetSelectFilter::Type::TopAggro:
|
||||||
|
{
|
||||||
|
pFilter = std::make_shared< World::AI::TopAggroFilter >( negate );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case World::AI::TargetSelectFilter::Type::SecondAggro:
|
||||||
|
{
|
||||||
|
pFilter = std::make_shared< World::AI::SecondAggroFilter >( negate );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case World::AI::TargetSelectFilter::Type::PartyMember:
|
||||||
|
{
|
||||||
|
pFilter = std::make_shared< World::AI::PartyMemberFilter >( negate );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
filters.push_back( pFilter );
|
||||||
|
}
|
||||||
|
m_snapshot = World::AI::Snapshot( filters );
|
||||||
|
}
|
||||||
|
|
||||||
|
void Selector::createSnapshot( Entity::CharaPtr pSrc, const std::vector< uint32_t >& exclude )
|
||||||
|
{
|
||||||
|
m_snapshot.createSnapshot( pSrc, pSrc->getInRangeActors(), m_count, m_fillWithRandom, exclude );
|
||||||
|
}
|
||||||
|
|
||||||
|
const World::AI::Snapshot::Results& Selector::getResults()
|
||||||
|
{
|
||||||
|
return m_snapshot.getResults();
|
||||||
|
}
|
||||||
|
|
||||||
|
const World::AI::Snapshot::TargetIds& Selector::getTargetIds()
|
||||||
|
{
|
||||||
|
return m_snapshot.getTargetIds();
|
||||||
|
}
|
||||||
|
}// namespace Sapphire::Encounter
|
25
src/world/Encounter/Selector.h
Normal file
25
src/world/Encounter/Selector.h
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <AI/TargetHelper.h>
|
||||||
|
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
namespace Sapphire::Encounter
|
||||||
|
{
|
||||||
|
class Selector
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
std::string m_name;
|
||||||
|
bool m_fillWithRandom{ true };
|
||||||
|
uint32_t m_count;
|
||||||
|
World::AI::Snapshot m_snapshot;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Selector() : m_snapshot( {} ){};
|
||||||
|
void createSnapshot( Entity::CharaPtr pSrc, const std::vector< uint32_t >& exclude = {} );
|
||||||
|
const World::AI::Snapshot::Results& getResults();
|
||||||
|
const World::AI::Snapshot::TargetIds& getTargetIds();
|
||||||
|
void from_json( const nlohmann::json& json );
|
||||||
|
};
|
||||||
|
};// namespace Sapphire::Encounter
|
171
src/world/Encounter/TimelineActor.cpp
Normal file
171
src/world/Encounter/TimelineActor.cpp
Normal file
|
@ -0,0 +1,171 @@
|
||||||
|
#include "TimelineActor.h"
|
||||||
|
|
||||||
|
#include "EncounterTimeline.h"
|
||||||
|
|
||||||
|
#include <Actor/BNpc.h>
|
||||||
|
|
||||||
|
namespace Sapphire::Encounter
|
||||||
|
{
|
||||||
|
void TimelineActor::addPhaseCondition( PhaseConditionPtr pCondition )
|
||||||
|
{
|
||||||
|
m_phaseConditions.emplace( std::make_pair( pCondition->getId(), pCondition ) );
|
||||||
|
m_conditionStates[ pCondition->getId() ] = {};
|
||||||
|
m_conditionStates[ pCondition->getId() ].m_enabled = pCondition->isDefaultEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo: make this sane
|
||||||
|
|
||||||
|
void TimelineActor::update( TerritoryPtr pTeri, TimelinePack& pack, uint64_t time )
|
||||||
|
{
|
||||||
|
// todo: handle interrupts
|
||||||
|
for( const auto& condition : m_phaseConditions )
|
||||||
|
{
|
||||||
|
const auto& pCondition = condition.second;
|
||||||
|
auto& state = m_conditionStates[ pCondition->getId() ];
|
||||||
|
|
||||||
|
// ignore if not enabled, unless overriden to enable
|
||||||
|
if( !pCondition->isStateEnabled( state ) )
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if( pCondition->completed( state ) )
|
||||||
|
{
|
||||||
|
if( pCondition->isLoopable() )
|
||||||
|
{
|
||||||
|
if( pCondition->loopReady( state, time ) )
|
||||||
|
pCondition->reset( state );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if( pCondition->inProgress( state ) )
|
||||||
|
{
|
||||||
|
pCondition->update( state, *this, pTeri, pack, time );
|
||||||
|
}
|
||||||
|
else if( pCondition->isConditionMet( state, pTeri, pack, time ) )
|
||||||
|
{
|
||||||
|
pCondition->execute( state, *this, pTeri, pack, time );
|
||||||
|
|
||||||
|
if( pack.getStartTime() == 0 )
|
||||||
|
pack.setStartTime( state.m_startTime );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TimelineActor::resetConditionState( uint32_t conditionId )
|
||||||
|
{
|
||||||
|
if( auto it = m_phaseConditions.find( conditionId ); it != m_phaseConditions.end() )
|
||||||
|
{
|
||||||
|
auto& state = m_conditionStates.at( it->first );
|
||||||
|
it->second->reset( state );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TimelineActor::setConditionStateEnabled( uint32_t conditionId, bool enabled )
|
||||||
|
{
|
||||||
|
if( auto it = m_conditionStates.find( conditionId ); it != m_conditionStates.end() )
|
||||||
|
{
|
||||||
|
auto& state = m_conditionStates.at( it->first );
|
||||||
|
state.m_enabled = enabled;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TimelineActor::resetAllConditionStates()
|
||||||
|
{
|
||||||
|
for( const auto& condition : m_phaseConditions )
|
||||||
|
{
|
||||||
|
const auto& pCondition = condition.second;
|
||||||
|
auto& state = m_conditionStates.at( condition.first );
|
||||||
|
|
||||||
|
pCondition->reset( state );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TimelineActor::spawnAllSubActors( TerritoryPtr pTeri )
|
||||||
|
{
|
||||||
|
std::vector< std::string > toSpawn;
|
||||||
|
for( const auto& subActor : m_subActors )
|
||||||
|
if( getSubActor( subActor.first ) == nullptr )
|
||||||
|
toSpawn.push_back( subActor.first );
|
||||||
|
|
||||||
|
for( const auto& name : toSpawn )
|
||||||
|
spawnSubActor( name, pTeri );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void TimelineActor::addPlaceholderSubactor( const std::string& name )
|
||||||
|
{
|
||||||
|
// populate m_subActors with nullptr BNpcs
|
||||||
|
// then spawn them all in first timepoint and ref them by name subsequently
|
||||||
|
if( getSubActor( name ) == nullptr )
|
||||||
|
m_subActors.emplace( std::make_pair( name, nullptr ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
Entity::BNpcPtr TimelineActor::getBNpcByRef( const std::string& name, TerritoryPtr pTeri ) const
|
||||||
|
{
|
||||||
|
if( name == m_name )
|
||||||
|
return pTeri->getActiveBNpcByLayoutId( m_layoutId );
|
||||||
|
return getSubActor( name );
|
||||||
|
}
|
||||||
|
|
||||||
|
void TimelineActor::resetAllSubActors( TerritoryPtr pTeri )
|
||||||
|
{
|
||||||
|
for( auto& subActor : m_subActors )
|
||||||
|
{
|
||||||
|
if( subActor.second )
|
||||||
|
{
|
||||||
|
auto inRange = subActor.second->getInRangeActors();
|
||||||
|
for( const auto& pActor : inRange )
|
||||||
|
{
|
||||||
|
if( auto pPlayer = pActor->getAsPlayer() )
|
||||||
|
subActor.second->despawn( pPlayer );
|
||||||
|
}
|
||||||
|
pTeri->removeActor( subActor.second );
|
||||||
|
subActor.second = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Entity::BNpcPtr TimelineActor::spawnSubActor( const std::string& name, TerritoryPtr pTeri )
|
||||||
|
{
|
||||||
|
// todo: retail straight up respawns sub actors, even bnpc parts (qarn adjudicator body parts respawn each time with new ids)
|
||||||
|
auto flags = Entity::BNpcFlag::Invincible | Entity::BNpcFlag::Untargetable |
|
||||||
|
Entity::BNpcFlag::Immobile | Entity::BNpcFlag::AutoAttackDisabled |
|
||||||
|
Entity::BNpcFlag::TurningDisabled;
|
||||||
|
|
||||||
|
auto pActor = getSubActor( name );
|
||||||
|
if( pActor == nullptr )
|
||||||
|
{
|
||||||
|
auto pParent = pTeri->getActiveBNpcByLayoutId( m_layoutId );
|
||||||
|
pActor = pTeri->createBNpcFromLayoutId( m_layoutId, 1000, pParent->getBNpcType() );
|
||||||
|
m_subActors[ name ] = pActor;
|
||||||
|
|
||||||
|
pActor->setInvincibilityType( Common::InvincibilityIgnoreDamage );
|
||||||
|
pActor->setFlag( flags );
|
||||||
|
pActor->init();
|
||||||
|
|
||||||
|
pTeri->pushActor( pActor );
|
||||||
|
}
|
||||||
|
|
||||||
|
return pActor;
|
||||||
|
}
|
||||||
|
|
||||||
|
Entity::BNpcPtr TimelineActor::getSubActor( const std::string& name ) const
|
||||||
|
{
|
||||||
|
if( auto it = m_subActors.find( name ); it != m_subActors.end() )
|
||||||
|
return it->second;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TimelineActor::resetSubActors( TerritoryPtr pTeri )
|
||||||
|
{
|
||||||
|
for( auto& subActor : m_subActors )
|
||||||
|
{
|
||||||
|
if( subActor.second )
|
||||||
|
{
|
||||||
|
auto pBNpc = subActor.second;
|
||||||
|
pTeri->removeActor( pBNpc );
|
||||||
|
// todo: despawn?
|
||||||
|
subActor.second = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
69
src/world/Encounter/TimelineActor.h
Normal file
69
src/world/Encounter/TimelineActor.h
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include "PhaseCondition.h"
|
||||||
|
#include "TimelineActorState.h"
|
||||||
|
|
||||||
|
namespace Sapphire::Encounter
|
||||||
|
{
|
||||||
|
enum class TimelineActorType : uint32_t
|
||||||
|
{
|
||||||
|
LayerSetObject,
|
||||||
|
BattleNpc
|
||||||
|
};
|
||||||
|
|
||||||
|
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::unordered_map< std::string, Entity::BNpcPtr > m_subActors;
|
||||||
|
|
||||||
|
public:
|
||||||
|
uint32_t m_layoutId{ 0 };
|
||||||
|
uint32_t m_hp{ 0 };
|
||||||
|
std::string m_name;
|
||||||
|
|
||||||
|
TimelineActor() {}
|
||||||
|
TimelineActor( const TimelineActor& rhs ) :
|
||||||
|
m_layoutId( rhs.m_layoutId ),
|
||||||
|
m_hp( rhs.m_hp ),
|
||||||
|
m_name( rhs.m_name ),
|
||||||
|
m_phaseConditions( rhs.m_phaseConditions )
|
||||||
|
{
|
||||||
|
// yeah
|
||||||
|
for( const auto& subActor : rhs.m_subActors )
|
||||||
|
m_subActors.emplace( std::make_pair( subActor.first, nullptr ) );
|
||||||
|
|
||||||
|
m_conditionStates = rhs.m_conditionStates;
|
||||||
|
for( const auto& state : rhs.m_phaseConditions )
|
||||||
|
state.second->reset( m_conditionStates[ state.first ] );
|
||||||
|
}
|
||||||
|
|
||||||
|
void addPhaseCondition( PhaseConditionPtr pCondition );
|
||||||
|
|
||||||
|
// todo: make this sane
|
||||||
|
void update( TerritoryPtr pTeri, TimelinePack& pack, uint64_t time );
|
||||||
|
|
||||||
|
void resetConditionState( uint32_t conditionId );
|
||||||
|
|
||||||
|
void setConditionStateEnabled( uint32_t conditionId, bool enabled );
|
||||||
|
|
||||||
|
void resetAllConditionStates();
|
||||||
|
|
||||||
|
void spawnAllSubActors( TerritoryPtr pTeri );
|
||||||
|
|
||||||
|
void resetAllSubActors( TerritoryPtr pTeri );
|
||||||
|
|
||||||
|
// get self or subactor
|
||||||
|
Entity::BNpcPtr getBNpcByRef( const std::string& name, TerritoryPtr pTeri ) const;
|
||||||
|
|
||||||
|
// todo: i hate this but it's the only way to ref subactors while staying self contained
|
||||||
|
void addPlaceholderSubactor( const std::string& name );
|
||||||
|
|
||||||
|
Entity::BNpcPtr spawnSubActor( const std::string& name, TerritoryPtr pTeri );
|
||||||
|
Entity::BNpcPtr getSubActor( const std::string& name ) const;
|
||||||
|
void resetSubActors( TerritoryPtr pTeri );
|
||||||
|
};
|
||||||
|
}
|
31
src/world/Encounter/TimelineActorState.h
Normal file
31
src/world/Encounter/TimelineActorState.h
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace Sapphire::Encounter
|
||||||
|
{
|
||||||
|
struct TimepointState
|
||||||
|
{
|
||||||
|
uint64_t m_startTime{ 0 };
|
||||||
|
uint64_t m_lastTick{ 0 };
|
||||||
|
bool m_finished{ false };
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ConditionState
|
||||||
|
{
|
||||||
|
uint64_t m_startTime{ 0 };
|
||||||
|
bool m_loop{ false };
|
||||||
|
bool m_completed{ false };
|
||||||
|
bool m_enabled{ false };
|
||||||
|
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
uint64_t m_startTime{ 0 };
|
||||||
|
uint64_t m_lastTimepointTime{ 0 };
|
||||||
|
uint32_t m_lastTimepointIndex{ 0 };
|
||||||
|
|
||||||
|
std::vector< TimepointState > m_timepointStates;
|
||||||
|
} m_phaseInfo;
|
||||||
|
};
|
||||||
|
}; // namespace Sapphire::Encounter
|
617
src/world/Encounter/Timepoint.cpp
Normal file
617
src/world/Encounter/Timepoint.cpp
Normal file
|
@ -0,0 +1,617 @@
|
||||||
|
#include "Timepoint.h"
|
||||||
|
#include "TimelineActor.h"
|
||||||
|
|
||||||
|
#include <Action/Action.h>
|
||||||
|
|
||||||
|
#include <Actor/BNpc.h>
|
||||||
|
#include <Actor/Chara.h>
|
||||||
|
#include <Actor/EventObject.h>
|
||||||
|
#include <Actor/Player.h>
|
||||||
|
|
||||||
|
#include <Event/Director.h>
|
||||||
|
|
||||||
|
#include <Manager/ActionMgr.h>
|
||||||
|
#include <Manager/PlayerMgr.h>
|
||||||
|
#include <Service.h>
|
||||||
|
|
||||||
|
#include <Territory/QuestBattle.h>
|
||||||
|
#include <Territory/InstanceContent.h>
|
||||||
|
#include <Util/UtilMath.h>
|
||||||
|
|
||||||
|
namespace Sapphire::Encounter
|
||||||
|
{
|
||||||
|
const TimepointDataPtr Timepoint::getData() const
|
||||||
|
{
|
||||||
|
return m_pData;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Timepoint::canExecute( const TimepointState& state, uint64_t elapsed ) const
|
||||||
|
{
|
||||||
|
return state.m_startTime == 0;// & &m_duration <= elapsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Timepoint::durationElapsed( uint64_t elapsed ) const
|
||||||
|
{
|
||||||
|
return m_duration < elapsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Timepoint::finished( const TimepointState& state, uint64_t elapsed ) const
|
||||||
|
{
|
||||||
|
return durationElapsed( elapsed ) || state.m_finished;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Timepoint::reset( TimepointState& state ) const
|
||||||
|
{
|
||||||
|
state.m_startTime = 0;
|
||||||
|
state.m_lastTick = 0;
|
||||||
|
state.m_finished = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Timepoint::from_json( const nlohmann::json& json, const std::unordered_map< std::string, TimelineActor >& actors, uint32_t selfLayoutId )
|
||||||
|
{
|
||||||
|
const static std::unordered_map< std::string, TimepointDataType > timepointTypeMap =
|
||||||
|
{
|
||||||
|
{ "idle", TimepointDataType::Idle },
|
||||||
|
{ "castAction", TimepointDataType::CastAction },
|
||||||
|
{ "moveTo", TimepointDataType::MoveTo },
|
||||||
|
{ "logMessage", TimepointDataType::LogMessage },
|
||||||
|
{ "battleTalk", TimepointDataType::BattleTalk },
|
||||||
|
{ "directorVar", TimepointDataType::DirectorVar },
|
||||||
|
{ "directorSeq", TimepointDataType::DirectorSeq },
|
||||||
|
{ "directorFlags", TimepointDataType::DirectorFlags },
|
||||||
|
{ "addStatusEffect", TimepointDataType::AddStatusEffect },
|
||||||
|
{ "removeStatusEffect", TimepointDataType::RemoveStatusEffect },
|
||||||
|
{ "spawnBNpc", TimepointDataType::SpawnBNpc },
|
||||||
|
{ "bNpcFlags", TimepointDataType::SetBNpcFlags },
|
||||||
|
{ "setEObjState", TimepointDataType::SetEObjState },
|
||||||
|
{ "setCondition", TimepointDataType::SetCondition },
|
||||||
|
{ "snapshot", TimepointDataType::Snapshot }
|
||||||
|
};
|
||||||
|
|
||||||
|
const static std::unordered_map< std::string, DirectorOpId > directorOpMap =
|
||||||
|
{
|
||||||
|
{ "set", DirectorOpId::Set },
|
||||||
|
{ "add", DirectorOpId::Add },
|
||||||
|
{ "sub", DirectorOpId::Sub },
|
||||||
|
{ "mul", DirectorOpId::Mul },
|
||||||
|
{ "div", DirectorOpId::Div },
|
||||||
|
{ "mod", DirectorOpId::Mod },
|
||||||
|
{ "sll", DirectorOpId::Sll },
|
||||||
|
{ "srl", DirectorOpId::Srl },
|
||||||
|
{ "or", DirectorOpId::Or },
|
||||||
|
{ "xor", DirectorOpId::Xor },
|
||||||
|
{ "nor", DirectorOpId::Nor },
|
||||||
|
{ "and", DirectorOpId::And }
|
||||||
|
};
|
||||||
|
|
||||||
|
const static std::unordered_map< std::string, Common::BNpcType > bnpcTypeMap =
|
||||||
|
{
|
||||||
|
{ "bnpc", Common::BNpcType::Enemy },
|
||||||
|
{ "ally", Common::BNpcType::Friendly }// todo: rename this
|
||||||
|
};
|
||||||
|
|
||||||
|
const static std::unordered_map< std::string, ActionTargetType > actionTypeMap =
|
||||||
|
{
|
||||||
|
{ "none", ActionTargetType::None },
|
||||||
|
{ "self", ActionTargetType::Self },
|
||||||
|
{ "target", ActionTargetType::Target },
|
||||||
|
{ "selector", ActionTargetType::Selector }
|
||||||
|
};
|
||||||
|
|
||||||
|
TimepointDataType tpType{ 0 };
|
||||||
|
|
||||||
|
auto typeStr = json.at( "type" ).get< std::string >();
|
||||||
|
if( auto it = timepointTypeMap.find( typeStr ); it != timepointTypeMap.end() )
|
||||||
|
tpType = it->second;
|
||||||
|
else
|
||||||
|
throw std::runtime_error( fmt::format( "Timepoint::from_json unable to find timepoint by type: %s", typeStr ) );
|
||||||
|
|
||||||
|
m_duration = json.at( "duration" ).get< uint64_t >();
|
||||||
|
//m_overrideFlags = json.at( "overrideFlags" ).get< TimepointOverrideFlags >();
|
||||||
|
m_description = json.at( "description" ).get< std::string >();
|
||||||
|
|
||||||
|
switch( tpType )
|
||||||
|
{
|
||||||
|
case TimepointDataType::Idle:
|
||||||
|
{
|
||||||
|
m_pData = std::make_shared< TimepointDataIdle >( m_duration );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TimepointDataType::CastAction:
|
||||||
|
{
|
||||||
|
// todo: CastAction
|
||||||
|
// todo: parse and build callback funcs
|
||||||
|
auto& dataJ = json.at( "data" );
|
||||||
|
auto sourceRef = dataJ.at( "sourceActor" ).get< std::string >();
|
||||||
|
auto actionId = dataJ.at( "actionId" ).get< uint32_t >();
|
||||||
|
auto targetType = actionTypeMap.find( dataJ.at( "targetType" ).get< std::string >() )->second;
|
||||||
|
auto selectorRef = dataJ.at( "selectorName" ).get< std::string >();
|
||||||
|
auto selectorIndex = dataJ.at( "selectorIndex" ).get< uint32_t >();
|
||||||
|
|
||||||
|
m_pData = std::make_shared< TimepointDataAction >( sourceRef, actionId, targetType,
|
||||||
|
selectorRef, selectorIndex );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TimepointDataType::MoveTo:
|
||||||
|
{
|
||||||
|
auto& dataJ = json.at( "data" );
|
||||||
|
auto pos = dataJ.at( "pos" ).get< std::vector< float > >();
|
||||||
|
auto rot = dataJ.at( "rot" ).get< float >();
|
||||||
|
auto pathReq = dataJ.at( "pathRequested" ).get< bool >() ? MoveType::WalkPath : MoveType::Teleport;
|
||||||
|
|
||||||
|
// todo: moveTo
|
||||||
|
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TimepointDataType::LogMessage:
|
||||||
|
{
|
||||||
|
auto& dataJ = json.at( "data" );
|
||||||
|
auto messageId = dataJ.at( "messageId" ).get< uint32_t >();
|
||||||
|
auto params = dataJ.at( "params" ).get< std::vector< uint32_t > >();
|
||||||
|
|
||||||
|
m_pData = std::make_shared< TimepointDataLogMessage >( messageId, params );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TimepointDataType::BattleTalk:
|
||||||
|
{
|
||||||
|
auto& dataJ = json.at( "data" );
|
||||||
|
auto params = dataJ.at( "params" ).get< std::vector< uint32_t > >();
|
||||||
|
|
||||||
|
auto pBattleTalkData = std::make_shared< TimepointDataBattleTalk >( params );
|
||||||
|
|
||||||
|
pBattleTalkData->m_battleTalkId = dataJ.at( "battleTalkId" ).get< uint32_t >();
|
||||||
|
pBattleTalkData->m_handlerId = dataJ.at( "handlerId" ).get< uint32_t >();
|
||||||
|
pBattleTalkData->m_kind = dataJ.at( "kind" ).get< uint32_t >();
|
||||||
|
pBattleTalkData->m_nameId = dataJ.at( "nameId" ).get< uint32_t >();
|
||||||
|
pBattleTalkData->m_talkerId = dataJ.at( "talkerId" ).get< uint32_t >();
|
||||||
|
|
||||||
|
m_pData = pBattleTalkData;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Directors
|
||||||
|
//
|
||||||
|
case TimepointDataType::DirectorVar:
|
||||||
|
{
|
||||||
|
auto& dataJ = json.at( "data" );
|
||||||
|
auto index = dataJ.at( "idx" ).get< uint32_t >();
|
||||||
|
auto val = dataJ.at( "val" ).get< uint32_t >();
|
||||||
|
auto opStr = dataJ.at( "opc" ).get< std::string >();
|
||||||
|
DirectorOpId op = directorOpMap.find( opStr )->second;
|
||||||
|
|
||||||
|
auto pDirectorData = std::make_shared< TimepointDataDirector >( tpType, op );
|
||||||
|
pDirectorData->m_data.index = index;
|
||||||
|
pDirectorData->m_data.value.val = val;
|
||||||
|
|
||||||
|
m_pData = pDirectorData;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TimepointDataType::DirectorVarLR:
|
||||||
|
{
|
||||||
|
auto& dataJ = json.at( "data" );
|
||||||
|
auto index = dataJ.at( "idx" ).get< uint32_t >();
|
||||||
|
auto left = dataJ.at( "left" ).get< uint32_t >();
|
||||||
|
auto right = dataJ.at( "right" ).get< uint32_t >();
|
||||||
|
auto opStr = dataJ.at( "opc" ).get< std::string >();
|
||||||
|
DirectorOpId op = directorOpMap.find( opStr )->second;
|
||||||
|
|
||||||
|
auto pDirectorData = std::make_shared< TimepointDataDirector >( tpType, op );
|
||||||
|
pDirectorData->m_data.index = index;
|
||||||
|
pDirectorData->m_data.value.left = left;
|
||||||
|
pDirectorData->m_data.value.right = right;
|
||||||
|
|
||||||
|
m_pData = pDirectorData;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TimepointDataType::DirectorSeq:
|
||||||
|
{
|
||||||
|
auto& dataJ = json.at( "data" );
|
||||||
|
auto seq = dataJ.at( "val" ).get< uint32_t >();
|
||||||
|
auto opStr = dataJ.at( "opc" ).get< std::string >();
|
||||||
|
DirectorOpId op = directorOpMap.find( opStr )->second;
|
||||||
|
|
||||||
|
auto pDirectorData = std::make_shared< TimepointDataDirector >( tpType, op );
|
||||||
|
pDirectorData->m_data.seq = seq;
|
||||||
|
|
||||||
|
m_pData = pDirectorData;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TimepointDataType::DirectorFlags:
|
||||||
|
{
|
||||||
|
auto& dataJ = json.at( "data" );
|
||||||
|
auto flags = dataJ.at( "val" ).get< uint32_t >();
|
||||||
|
auto opStr = dataJ.at( "opc" ).get< std::string >();
|
||||||
|
DirectorOpId op = directorOpMap.at( opStr );
|
||||||
|
|
||||||
|
auto pDirectorData = std::make_shared< TimepointDataDirector >( tpType, op );
|
||||||
|
pDirectorData->m_data.flags = flags;
|
||||||
|
|
||||||
|
m_pData = pDirectorData;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Status Effects
|
||||||
|
//
|
||||||
|
case TimepointDataType::AddStatusEffect:
|
||||||
|
case TimepointDataType::RemoveStatusEffect:
|
||||||
|
{
|
||||||
|
// todo: add/remove status effects
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
|
case TimepointDataType::SpawnBNpc:
|
||||||
|
{
|
||||||
|
auto& dataJ = json.at( "data" );
|
||||||
|
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
|
||||||
|
|
||||||
|
uint32_t layoutId = 0xE0000000;
|
||||||
|
if( auto it = actors.find( actorRef ); it != actors.end() )
|
||||||
|
layoutId = it->second.m_layoutId;
|
||||||
|
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, bnpcType );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TimepointDataType::SetBNpcFlags:
|
||||||
|
{
|
||||||
|
auto& dataJ = json.at( "data" );
|
||||||
|
auto actorRef = dataJ.at( "spawnActor" ).get< std::string >();
|
||||||
|
auto flags = dataJ.at( "flags" ).get< uint32_t >();
|
||||||
|
|
||||||
|
// todo: hateSrc
|
||||||
|
|
||||||
|
uint32_t layoutId = 0xE0000000;
|
||||||
|
if( auto it = actors.find( actorRef ); it != actors.end() )
|
||||||
|
layoutId = it->second.m_layoutId;
|
||||||
|
else
|
||||||
|
throw std::runtime_error( fmt::format( std::string( "EncounterTimeline::Timepoint::from_json: SetBNpcFlags invalid actor ref: %s" ), actorRef ) );
|
||||||
|
|
||||||
|
m_pData = std::make_shared< TimepointDataBNpcFlags >( layoutId, flags );
|
||||||
|
// todo: SetBNpcFlags
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TimepointDataType::SetEObjState:
|
||||||
|
{
|
||||||
|
auto& dataJ = json.at( "data" );
|
||||||
|
|
||||||
|
// todo: SetEObjState
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TimepointDataType::SetCondition:
|
||||||
|
{
|
||||||
|
auto& dataJ = json.at( "data" );
|
||||||
|
auto conditionId = dataJ.at( "conditionId" ).get< uint32_t >();
|
||||||
|
auto enabled = dataJ.at( "enabled" ).get< bool >();
|
||||||
|
|
||||||
|
m_pData = std::make_shared< TimepointDataCondition >( conditionId, enabled );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TimepointDataType::Snapshot:
|
||||||
|
{
|
||||||
|
auto& dataJ = json.at( "data" );
|
||||||
|
auto selectorName = dataJ.at( "selectorName" ).get< std::string >();
|
||||||
|
auto actorRef = dataJ.at( "sourceActor" ).get< std::string >();
|
||||||
|
// todo:
|
||||||
|
|
||||||
|
m_pData = std::make_shared< TimepointDataSnapshot >( selectorName, actorRef );
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Timepoint::execute( TimepointState& state, TimelineActor& self, TerritoryPtr pTeri, uint64_t time ) const
|
||||||
|
{
|
||||||
|
state.m_startTime = time;
|
||||||
|
update( state, self, pTeri, time );
|
||||||
|
}
|
||||||
|
|
||||||
|
void Timepoint::update( TimepointState& state, TimelineActor& self, TerritoryPtr pTeri, uint64_t time ) const
|
||||||
|
{
|
||||||
|
state.m_lastTick = time;
|
||||||
|
|
||||||
|
// todo: separate execute and update?
|
||||||
|
if( state.m_finished )
|
||||||
|
return;
|
||||||
|
|
||||||
|
switch( m_type )
|
||||||
|
{
|
||||||
|
case TimepointDataType::Idle:
|
||||||
|
{
|
||||||
|
// just wait up the duration of this timepoint
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TimepointDataType::CastAction:
|
||||||
|
{
|
||||||
|
auto pActionData = std::dynamic_pointer_cast< TimepointDataAction, TimepointData >( getData() );
|
||||||
|
auto pBNpc = self.getBNpcByRef( pActionData->m_sourceRef, pTeri );
|
||||||
|
// todo: filter the correct target
|
||||||
|
// todo: tie to mechanic script?
|
||||||
|
// todo: mechanic should probably just be an Action::onTick, with instance/director passed to it
|
||||||
|
if( pBNpc )
|
||||||
|
{
|
||||||
|
uint32_t targetId = pBNpc->getId();
|
||||||
|
switch( pActionData->m_targetType )
|
||||||
|
{
|
||||||
|
case ActionTargetType::Target:
|
||||||
|
targetId = pBNpc->getTargetId();
|
||||||
|
break;
|
||||||
|
case ActionTargetType::Selector:
|
||||||
|
{
|
||||||
|
// todo: selector
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
auto actionMgr = Common::Service< Sapphire::World::Manager::ActionMgr >::ref();
|
||||||
|
|
||||||
|
// todo: this is probably wrong
|
||||||
|
if( pBNpc->getCurrentAction() && pBNpc->getCurrentAction()->getId() != pActionData->m_actionId )
|
||||||
|
actionMgr.handleTargetedAction( *pBNpc.get(), pActionData->m_actionId, targetId, 0 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TimepointDataType::MoveTo:
|
||||||
|
{
|
||||||
|
auto pMoveToData = std::dynamic_pointer_cast< TimepointDataMoveTo, TimepointData >( getData() );
|
||||||
|
auto pBNpc = self.getBNpcByRef( pMoveToData->m_actorRef, pTeri );
|
||||||
|
|
||||||
|
if( pBNpc )
|
||||||
|
{
|
||||||
|
auto currPos = pBNpc->getPos();
|
||||||
|
Common::FFXIVARR_POSITION3 targetPos = { pMoveToData->m_x, pMoveToData->m_y, pMoveToData->m_z };
|
||||||
|
|
||||||
|
auto distance = Common::Util::distance( currPos, targetPos );
|
||||||
|
if( distance > 0.5f )
|
||||||
|
{
|
||||||
|
if( pMoveToData->m_moveType == MoveType::WalkPath )
|
||||||
|
pBNpc->moveTo( targetPos );
|
||||||
|
else
|
||||||
|
pBNpc->setPos( pMoveToData->m_x, pMoveToData->m_y, pMoveToData->m_z );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// if we are at the pos, stop waiting
|
||||||
|
//state.m_finished = true;
|
||||||
|
}
|
||||||
|
pBNpc->setRot( pMoveToData->m_rot );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TimepointDataType::LogMessage:
|
||||||
|
{
|
||||||
|
auto pLogMessage = std::dynamic_pointer_cast< TimepointDataLogMessage, TimepointData >( getData() );
|
||||||
|
auto params = pLogMessage->m_params;
|
||||||
|
|
||||||
|
// todo: probably should use ContentDirector
|
||||||
|
|
||||||
|
{
|
||||||
|
auto& playerMgr = Common::Service< Sapphire::World::Manager::PlayerMgr >::ref();
|
||||||
|
for( auto player : pTeri->getPlayers() )
|
||||||
|
{
|
||||||
|
auto pPlayer = player.second;
|
||||||
|
if( pPlayer )
|
||||||
|
playerMgr.sendLogMessage( *pPlayer.get(), pLogMessage->m_messageId,
|
||||||
|
params[ 0 ], params[ 1 ], params[ 2 ], params[ 3 ], params[ 4 ] );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TimepointDataType::BattleTalk:
|
||||||
|
{
|
||||||
|
// todo: BattleTalk
|
||||||
|
auto pBtData = std::dynamic_pointer_cast< TimepointDataBattleTalk, TimepointData >( getData() );
|
||||||
|
auto params = pBtData->m_params;
|
||||||
|
|
||||||
|
|
||||||
|
auto& playerMgr = Common::Service< Sapphire::World::Manager::PlayerMgr >::ref();
|
||||||
|
for( auto player : pTeri->getPlayers() )
|
||||||
|
{
|
||||||
|
auto pPlayer = player.second;
|
||||||
|
if( pPlayer )
|
||||||
|
playerMgr.sendBattleTalk( *pPlayer.get(), pBtData->m_battleTalkId, pBtData->m_handlerId,
|
||||||
|
pBtData->m_kind, pBtData->m_nameId, pBtData->m_talkerId,
|
||||||
|
params[ 0 ], params[ 1 ], params[ 2 ], params[ 3 ],
|
||||||
|
params[ 4 ], params[ 5 ], params[ 6 ], params[ 7 ] );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TimepointDataType::DirectorSeq:
|
||||||
|
case TimepointDataType::DirectorVar:
|
||||||
|
case TimepointDataType::DirectorVarLR:
|
||||||
|
case TimepointDataType::DirectorFlags:
|
||||||
|
{
|
||||||
|
auto pDirectorData = std::dynamic_pointer_cast< TimepointDataDirector, TimepointData >( getData() );
|
||||||
|
|
||||||
|
uint32_t val = 0;
|
||||||
|
uint32_t param = 0;
|
||||||
|
|
||||||
|
// todo: expand for fates
|
||||||
|
Event::DirectorPtr pDirector = pTeri->getAsInstanceContent();
|
||||||
|
if( pDirector == nullptr )
|
||||||
|
pDirector = pTeri->getAsQuestBattle();
|
||||||
|
|
||||||
|
// todo: this should never not be set?
|
||||||
|
// todo: probably should use ContentDirector
|
||||||
|
// todo: this needs to resend packets too
|
||||||
|
if( pDirector )
|
||||||
|
{
|
||||||
|
switch( m_type )
|
||||||
|
{
|
||||||
|
case TimepointDataType::DirectorVar:
|
||||||
|
val = pDirector->getDirectorVar( pDirectorData->m_data.index );
|
||||||
|
param = pDirectorData->m_data.value.val;
|
||||||
|
break;
|
||||||
|
case TimepointDataType::DirectorFlags:
|
||||||
|
val = pDirector->getFlags();
|
||||||
|
param = pDirectorData->m_data.flags;
|
||||||
|
break;
|
||||||
|
case TimepointDataType::DirectorSeq:
|
||||||
|
val = pDirector->getSequence();
|
||||||
|
param = pDirectorData->m_data.seq;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch( pDirectorData->m_directorOp )
|
||||||
|
{
|
||||||
|
case DirectorOpId::Set:
|
||||||
|
val = param;
|
||||||
|
break;
|
||||||
|
case DirectorOpId::Add:
|
||||||
|
val += param;
|
||||||
|
break;
|
||||||
|
case DirectorOpId::Sub:
|
||||||
|
val -= param;
|
||||||
|
break;
|
||||||
|
case DirectorOpId::Mul:
|
||||||
|
val *= param;
|
||||||
|
break;
|
||||||
|
case DirectorOpId::Div:
|
||||||
|
val /= param;
|
||||||
|
break;
|
||||||
|
case DirectorOpId::Mod:
|
||||||
|
val %= param;
|
||||||
|
break;
|
||||||
|
case DirectorOpId::Sll:
|
||||||
|
val = val << param;
|
||||||
|
break;
|
||||||
|
case DirectorOpId::Srl:
|
||||||
|
val = val >> param;
|
||||||
|
break;
|
||||||
|
case DirectorOpId::Or:
|
||||||
|
val |= param;
|
||||||
|
break;
|
||||||
|
case DirectorOpId::Xor:
|
||||||
|
val ^= param;
|
||||||
|
break;
|
||||||
|
case DirectorOpId::Nor:
|
||||||
|
val = ~( val | param );
|
||||||
|
break;
|
||||||
|
case DirectorOpId::And:
|
||||||
|
val &= param;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo: resend packets
|
||||||
|
switch( m_type )
|
||||||
|
{
|
||||||
|
case TimepointDataType::DirectorVar:
|
||||||
|
pDirector->setDirectorVar( pDirectorData->m_data.index, val );
|
||||||
|
break;
|
||||||
|
case TimepointDataType::DirectorFlags:
|
||||||
|
pDirector->setDirectorFlags( val );
|
||||||
|
break;
|
||||||
|
case TimepointDataType::DirectorSeq:
|
||||||
|
pDirector->setDirectorSequence( val );
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TimepointDataType::AddStatusEffect:
|
||||||
|
{
|
||||||
|
// todo:
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TimepointDataType::RemoveStatusEffect:
|
||||||
|
{
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TimepointDataType::SpawnBNpc:
|
||||||
|
{
|
||||||
|
auto pSpawnData = std::dynamic_pointer_cast< TimepointDataSpawnBNpc, TimepointData >( getData() );
|
||||||
|
auto pBNpc = pTeri->getActiveBNpcByLayoutId( pSpawnData->m_layoutId );
|
||||||
|
|
||||||
|
if( pBNpc )
|
||||||
|
{
|
||||||
|
pBNpc->clearFlags();
|
||||||
|
pBNpc->setFlag( pSpawnData->m_flags );
|
||||||
|
pBNpc->init();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TimepointDataType::SetBNpcFlags:
|
||||||
|
{
|
||||||
|
auto pBNpcFlagData = std::dynamic_pointer_cast< TimepointDataBNpcFlags, TimepointData >( getData() );
|
||||||
|
auto pBNpc = pTeri->getActiveBNpcByLayoutId( pBNpcFlagData->m_layoutId );
|
||||||
|
|
||||||
|
if( pBNpc )
|
||||||
|
{
|
||||||
|
pBNpc->clearFlags();
|
||||||
|
pBNpc->setFlag( pBNpcFlagData->m_flags );
|
||||||
|
// todo: resend some bnpc packet/actrl?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TimepointDataType::SetEObjState:
|
||||||
|
{
|
||||||
|
auto pEObjData = std::dynamic_pointer_cast< TimepointDataEObjState, TimepointData >( getData() );
|
||||||
|
|
||||||
|
auto pInstance = pTeri->getAsInstanceContent();
|
||||||
|
auto pQBattle = pTeri->getAsQuestBattle();
|
||||||
|
|
||||||
|
// todo: event objects on quest battles
|
||||||
|
// todo: SetEObjAnimationFlag?
|
||||||
|
|
||||||
|
if( pInstance )
|
||||||
|
{
|
||||||
|
auto pEObj = pInstance->getEObjById( pEObjData->m_eobjId );
|
||||||
|
if( pEObj )
|
||||||
|
{
|
||||||
|
pEObj->setState( pEObjData->m_state );
|
||||||
|
// todo: resend the eobj spawn packet?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TimepointDataType::SetBgm:
|
||||||
|
{
|
||||||
|
auto pBgmData = std::dynamic_pointer_cast< TimepointDataBGM, TimepointData >( getData() );
|
||||||
|
auto pInstance = pTeri->getAsInstanceContent();
|
||||||
|
auto pQBattle = pTeri->getAsQuestBattle();
|
||||||
|
|
||||||
|
// todo: quest battles refactor to inherit InstanceContent
|
||||||
|
if( pInstance )
|
||||||
|
{
|
||||||
|
pInstance->setCurrentBGM( pBgmData->m_bgmId );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TimepointDataType::SetCondition:
|
||||||
|
{
|
||||||
|
auto pConditionData = std::dynamic_pointer_cast< TimepointDataCondition, TimepointData >( getData() );
|
||||||
|
|
||||||
|
// todo: dont reset so things can resume? idk
|
||||||
|
self.resetConditionState( pConditionData->m_conditionId );
|
||||||
|
self.setConditionStateEnabled( pConditionData->m_conditionId, pConditionData->m_enabled );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( m_type != TimepointDataType::MoveTo && m_type != TimepointDataType::CastAction )
|
||||||
|
state.m_finished = true;
|
||||||
|
|
||||||
|
state.m_finished = state.m_finished || state.m_startTime + m_duration <= time;
|
||||||
|
}
|
||||||
|
}
|
295
src/world/Encounter/Timepoint.h
Normal file
295
src/world/Encounter/Timepoint.h
Normal file
|
@ -0,0 +1,295 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "TimelineActorState.h"
|
||||||
|
#include "Forwards.h"
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <ForwardsZone.h>
|
||||||
|
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
namespace Sapphire::Encounter
|
||||||
|
{
|
||||||
|
enum class TimepointDataType : uint32_t
|
||||||
|
{
|
||||||
|
Idle,
|
||||||
|
CastAction,
|
||||||
|
MoveTo,
|
||||||
|
|
||||||
|
LogMessage,
|
||||||
|
BattleTalk,
|
||||||
|
|
||||||
|
DirectorVar,
|
||||||
|
DirectorVarLR,
|
||||||
|
DirectorSeq,
|
||||||
|
DirectorFlags,
|
||||||
|
|
||||||
|
AddStatusEffect,
|
||||||
|
RemoveStatusEffect,
|
||||||
|
|
||||||
|
SpawnBNpc,
|
||||||
|
SetBNpcFlags,
|
||||||
|
SetEObjState,
|
||||||
|
SetBgm,
|
||||||
|
|
||||||
|
SetCondition,
|
||||||
|
|
||||||
|
Snapshot
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class ActionTargetType : uint32_t
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
Self,
|
||||||
|
Target,
|
||||||
|
Selector
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class MoveType : uint32_t
|
||||||
|
{
|
||||||
|
WalkPath,
|
||||||
|
Teleport
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class DirectorOpId
|
||||||
|
{
|
||||||
|
Set,// idx = val
|
||||||
|
Add,// idx += val
|
||||||
|
Sub,// idx -= val
|
||||||
|
Mul,// idx *= val
|
||||||
|
Div,// idx /= val
|
||||||
|
Mod,// idx %= val
|
||||||
|
Sll,// idx << val
|
||||||
|
Srl,// idx >> val
|
||||||
|
Or, // idx |= val
|
||||||
|
Xor,// idx ^= val
|
||||||
|
Nor,// idx ~= val
|
||||||
|
And // idx &= val
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
|
// Timepoint.m_pData objects
|
||||||
|
//
|
||||||
|
struct TimepointData : public std::enable_shared_from_this< TimepointData > {
|
||||||
|
TimepointData( TimepointDataType type ) : m_type( type ) {}
|
||||||
|
virtual ~TimepointData(){};
|
||||||
|
TimepointDataType m_type{ 0 };
|
||||||
|
};
|
||||||
|
using TimepointDataPtr = std::shared_ptr< TimepointData >;
|
||||||
|
|
||||||
|
struct TimepointDataIdle : public TimepointData {
|
||||||
|
uint64_t m_durationMs;
|
||||||
|
|
||||||
|
TimepointDataIdle( uint64_t durationMs ) :
|
||||||
|
TimepointData( TimepointDataType::Idle ),
|
||||||
|
m_durationMs( durationMs )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TimepointDataAction : public TimepointData
|
||||||
|
{
|
||||||
|
std::string m_sourceRef;
|
||||||
|
uint32_t m_actionId;
|
||||||
|
ActionTargetType m_targetType;
|
||||||
|
std::string m_selectorRef;
|
||||||
|
uint32_t m_selectorIndex;
|
||||||
|
|
||||||
|
TimepointDataAction( const std::string& sourceRef, uint32_t actionId,
|
||||||
|
ActionTargetType type, const std::string& selectorRef,
|
||||||
|
uint32_t selectorIndex = 0 ) :
|
||||||
|
TimepointData( TimepointDataType::CastAction ),
|
||||||
|
m_sourceRef( sourceRef ),
|
||||||
|
m_actionId( actionId ),
|
||||||
|
m_targetType( type ),
|
||||||
|
m_selectorRef( selectorRef ),
|
||||||
|
m_selectorIndex( selectorIndex )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TimepointDataMoveTo : public TimepointData {
|
||||||
|
// todo: use internal id
|
||||||
|
std::string m_actorRef;
|
||||||
|
MoveType m_moveType;
|
||||||
|
float m_x, m_y, m_z, m_rot;
|
||||||
|
|
||||||
|
TimepointDataMoveTo( const std::string& actorRef, MoveType moveType,
|
||||||
|
float x, float y, float z, float rot ) :
|
||||||
|
TimepointData( TimepointDataType::MoveTo ),
|
||||||
|
m_actorRef( actorRef ),
|
||||||
|
m_moveType( moveType ),
|
||||||
|
m_x( x ), m_y( y ), m_z( z ), m_rot( rot )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TimepointDataLogMessage : public TimepointData {
|
||||||
|
uint32_t m_messageId;
|
||||||
|
uint32_t m_params[ 5 ]{ 0 };
|
||||||
|
|
||||||
|
TimepointDataLogMessage( uint32_t messageId, const std::vector< uint32_t >& params ) :
|
||||||
|
TimepointData( TimepointDataType::LogMessage ),
|
||||||
|
m_messageId( messageId )
|
||||||
|
{
|
||||||
|
for( auto i = 0; i < params.size() && i < 5; ++i )
|
||||||
|
m_params[ i ] = params[ i ];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TimepointDataBattleTalk : public TimepointData {
|
||||||
|
uint32_t m_battleTalkId;
|
||||||
|
uint32_t m_handlerId;
|
||||||
|
uint32_t m_kind;
|
||||||
|
uint32_t m_nameId;
|
||||||
|
uint32_t m_talkerId;
|
||||||
|
|
||||||
|
uint32_t m_params[ 8 ]{ 0 };
|
||||||
|
|
||||||
|
TimepointDataBattleTalk( const std::vector< uint32_t >& params ) : TimepointData( TimepointDataType::BattleTalk )
|
||||||
|
{
|
||||||
|
for( auto i = 0; i < params.size() && i < 8; ++i )
|
||||||
|
m_params[ i ] = params[ i ];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TimepointDataDirector : public TimepointData
|
||||||
|
{
|
||||||
|
DirectorOpId m_directorOp{ 0 };
|
||||||
|
union
|
||||||
|
{
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
uint8_t index;
|
||||||
|
union
|
||||||
|
{
|
||||||
|
uint8_t val;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
uint8_t left, right;
|
||||||
|
};
|
||||||
|
} value;
|
||||||
|
};
|
||||||
|
uint8_t seq;
|
||||||
|
uint8_t flags;
|
||||||
|
} m_data{ 0 };
|
||||||
|
|
||||||
|
TimepointDataDirector( TimepointDataType type, DirectorOpId op ) :
|
||||||
|
TimepointData( type ),
|
||||||
|
m_directorOp( op )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TimepointDataSpawnBNpc : public TimepointData
|
||||||
|
{
|
||||||
|
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, uint32_t type ) :
|
||||||
|
TimepointData( TimepointDataType::SpawnBNpc ),
|
||||||
|
m_layoutId( layoutId ),
|
||||||
|
m_flags( flags ),
|
||||||
|
m_type( type )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TimepointDataBNpcFlags : public TimepointData
|
||||||
|
{
|
||||||
|
uint32_t m_layoutId{ 0xE0000000 };
|
||||||
|
uint32_t m_flags{ 0 };
|
||||||
|
|
||||||
|
TimepointDataBNpcFlags( uint32_t layoutId, uint32_t flags ) :
|
||||||
|
TimepointData( TimepointDataType::SetBNpcFlags ),
|
||||||
|
m_layoutId( layoutId ),
|
||||||
|
m_flags( flags )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TimepointDataEObjState : public TimepointData
|
||||||
|
{
|
||||||
|
uint32_t m_eobjId{ 0xE0000000 };
|
||||||
|
uint32_t m_state{ 0 };
|
||||||
|
|
||||||
|
TimepointDataEObjState( uint32_t eobjId, uint32_t state ) :
|
||||||
|
TimepointData( TimepointDataType::SetEObjState ),
|
||||||
|
m_eobjId( eobjId ),
|
||||||
|
m_state( state )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TimepointDataBGM : public TimepointData
|
||||||
|
{
|
||||||
|
uint32_t m_bgmId{ 0 };
|
||||||
|
|
||||||
|
TimepointDataBGM( uint32_t bgmId ) :
|
||||||
|
TimepointData( TimepointDataType::SetBgm ),
|
||||||
|
m_bgmId( bgmId )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TimepointDataCondition : public TimepointData
|
||||||
|
{
|
||||||
|
// todo: rng?
|
||||||
|
uint32_t m_conditionId;
|
||||||
|
bool m_enabled;
|
||||||
|
|
||||||
|
TimepointDataCondition( uint32_t conditionId, bool enabled ) :
|
||||||
|
TimepointData( TimepointDataType::SetCondition ),
|
||||||
|
m_conditionId( conditionId ),
|
||||||
|
m_enabled( enabled )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TimepointDataSnapshot : public TimepointData
|
||||||
|
{
|
||||||
|
// todo: rng?
|
||||||
|
std::string m_name;
|
||||||
|
std::string m_actorRef;
|
||||||
|
|
||||||
|
TimepointDataSnapshot( const std::string& name, const std::string& actorRef ) :
|
||||||
|
TimepointData( TimepointDataType::Snapshot ),
|
||||||
|
m_name( name ),
|
||||||
|
m_actorRef( actorRef )
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// todo: refactor all this to allow solo actor to use
|
||||||
|
class Timepoint : public std::enable_shared_from_this< Timepoint >
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TimepointDataType m_type;
|
||||||
|
uint64_t m_duration{ 0 };// milliseconds
|
||||||
|
TimepointDataPtr m_pData;
|
||||||
|
std::string m_description;
|
||||||
|
|
||||||
|
// todo: repeatable?
|
||||||
|
|
||||||
|
const TimepointDataPtr getData() const;
|
||||||
|
|
||||||
|
bool canExecute( const TimepointState& state, uint64_t elapsed ) const;
|
||||||
|
|
||||||
|
bool durationElapsed( uint64_t elapsed ) const;
|
||||||
|
|
||||||
|
bool finished( const TimepointState& state, uint64_t elapsed ) const;
|
||||||
|
|
||||||
|
void reset( TimepointState& state ) const;
|
||||||
|
|
||||||
|
void from_json( const nlohmann::json& json, const std::unordered_map< std::string, TimelineActor >& actors, uint32_t selfLayoutId );
|
||||||
|
// todo: separate execute/update into onStart and onTick?
|
||||||
|
void update( TimepointState& state, TimelineActor& self, TerritoryPtr pTeri, uint64_t time ) const;
|
||||||
|
void execute( TimepointState& state, TimelineActor& self, TerritoryPtr pTeri, uint64_t time ) const;
|
||||||
|
};
|
||||||
|
}// namespace Sapphire::Encounter
|
Loading…
Add table
Reference in a new issue