mirror of
https://github.com/SapphireServer/Sapphire.git
synced 2025-04-28 15:17:46 +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/Chara.h>
|
||||
#include <Actor/Player.h>
|
||||
#include <Manager/PartyMgr.h>
|
||||
#include <Manager/RNGMgr.h>
|
||||
#include <Util/UtilMath.h>
|
||||
#include <Service.h>
|
||||
|
@ -11,27 +13,28 @@
|
|||
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;
|
||||
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;
|
||||
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();
|
||||
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;
|
||||
// todo: pets, companions, enpc
|
||||
if( pSrc->isPlayer() )
|
||||
{
|
||||
auto pBNpcTarget = pTarget->getAsBNpc();
|
||||
|
@ -52,24 +55,24 @@ namespace Sapphire::World::AI
|
|||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
switch( pTarget->getRole() )
|
||||
|
@ -86,13 +89,13 @@ namespace Sapphire::World::AI
|
|||
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 );
|
||||
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();
|
||||
bool ret = false;
|
||||
|
@ -103,7 +106,7 @@ namespace Sapphire::World::AI
|
|||
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();
|
||||
bool ret = false;
|
||||
|
@ -126,10 +129,40 @@ namespace Sapphire::World::AI
|
|||
return m_negate ? !ret : ret;
|
||||
}
|
||||
|
||||
void Snapshot::createSnapshot( Entity::CharaPtr pSrc, const std::set< Entity::GameObjectPtr >& inRange,
|
||||
int count, bool fillWithRandom, const std::set< uint32_t >& exclude )
|
||||
bool PartyMemberFilter::isApplicable( Entity::CharaPtr& pSrc, Entity::CharaPtr& pTarget ) const
|
||||
{
|
||||
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();
|
||||
for( const auto& pActor : inRange )
|
||||
|
@ -138,12 +171,17 @@ namespace Sapphire::World::AI
|
|||
if( pChara == nullptr )
|
||||
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;
|
||||
for( const auto& filter : m_filters )
|
||||
{
|
||||
if( !filter->isConditionMet( pSrc, pChara ) )
|
||||
if( !filter->isApplicable( pSrc, pChara ) )
|
||||
{
|
||||
matches = false;
|
||||
break;
|
||||
|
@ -157,12 +195,13 @@ namespace Sapphire::World::AI
|
|||
entry.m_pos = pChara->getPos();
|
||||
entry.m_rot = pChara->getRot();
|
||||
|
||||
m_targets.push_back( entry );
|
||||
if( m_targets.size() == count ) break;
|
||||
m_results.push_back( entry );
|
||||
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;
|
||||
for( const auto& pActor : inRange )
|
||||
|
@ -171,48 +210,54 @@ namespace Sapphire::World::AI
|
|||
if( pChara == nullptr )
|
||||
continue;
|
||||
|
||||
if( exclude.find( pChara->getId() ) == exclude.end() && std::find_if( m_targets.begin(), m_targets.end(),
|
||||
[ &pChara ]( CharaEntry entry ) { return entry.m_entityId == pChara->getId(); } ) == m_targets.end() )
|
||||
auto excludeIt = std::find_if( exclude.begin(), exclude.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 );
|
||||
}
|
||||
}
|
||||
while( m_targets.size() < count && !remaining.empty() )
|
||||
while( m_results.size() < count && !remaining.empty() )
|
||||
{
|
||||
// idk
|
||||
std::shuffle( remaining.begin(), remaining.end(), *RNGMgr.getRNGEngine().get() );
|
||||
std::shuffle( remaining.begin(), remaining.end(), *RNGMgr.getRNGEngine() );
|
||||
|
||||
auto pChara = remaining.back();
|
||||
CharaEntry entry;
|
||||
entry.m_entityId = pChara->getId();
|
||||
entry.m_pos = pChara->getPos();
|
||||
entry.m_rot = pChara->getRot();
|
||||
m_targets.emplace_back( entry );
|
||||
m_results.emplace_back( entry );
|
||||
remaining.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
// sort by distance at the end always
|
||||
auto srcPos = pSrc->getPos();
|
||||
std::sort( m_targets.begin(), m_targets.end(),
|
||||
std::sort( m_results.begin(), m_results.end(),
|
||||
[ srcPos ]( CharaEntry l, CharaEntry r )
|
||||
{
|
||||
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
|
||||
{
|
||||
return m_targets;
|
||||
return m_results;
|
||||
}
|
||||
|
||||
const std::vector< uint32_t > Snapshot::getTargetIds() const
|
||||
{
|
||||
std::vector< uint32_t > ret( m_targets.size() );
|
||||
for( auto i = 0; i < m_targets.size(); ++i )
|
||||
ret[ i ] = m_targets[ i ].m_entityId;
|
||||
return ret;
|
||||
return m_targetIds;
|
||||
}
|
||||
|
||||
};// namespace Sapphire::World::AI
|
|
@ -35,6 +35,8 @@ namespace Sapphire::World::AI
|
|||
TopAggro,
|
||||
SecondAggro,
|
||||
|
||||
PartyMember,
|
||||
|
||||
AllianceA,
|
||||
AllianceB,
|
||||
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;
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 :
|
||||
public std::enable_shared_from_this< Snapshot >
|
||||
{
|
||||
public:
|
||||
struct CharaEntry
|
||||
{
|
||||
uint32_t m_entityId;
|
||||
|
@ -210,17 +224,20 @@ namespace Sapphire::World::AI
|
|||
float m_rot;
|
||||
// todo: status effects?
|
||||
};
|
||||
using Results = std::vector< CharaEntry >;
|
||||
using TargetIds = std::vector< uint32_t >;
|
||||
private:
|
||||
std::vector< TargetSelectFilterPtr > m_filters;
|
||||
std::vector< CharaEntry > m_targets;
|
||||
std::vector< CharaEntry > m_results;
|
||||
std::vector< uint32_t > m_targetIds;
|
||||
|
||||
public:
|
||||
Snapshot( const std::vector< TargetSelectFilterPtr > filters ) :
|
||||
public:
|
||||
Snapshot( const std::vector< TargetSelectFilterPtr >& filters ) :
|
||||
m_filters( filters )
|
||||
{
|
||||
}
|
||||
void createSnapshot( Entity::CharaPtr pSrc, const std::set< Entity::GameObjectPtr >& inRange,
|
||||
int count, bool fillWithRandom, const std::set< uint32_t >& exclude = {} );
|
||||
int count, bool fillWithRandom, const std::vector< uint32_t >& exclude = {} );
|
||||
|
||||
// returns actors sorted by distance
|
||||
const std::vector< CharaEntry >& getResults() const;
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
#include "EncounterFight.h"
|
||||
#include "EncounterTimeline.h"
|
||||
|
||||
#include "Timepoint.h"
|
||||
#include "TimelineActorState.h"
|
||||
#include "TimelineActor.h"
|
||||
#include "PhaseCondition.h"
|
||||
#include "Selector.h"
|
||||
|
||||
#include <Action/Action.h>
|
||||
|
||||
#include <Actor/BNpc.h>
|
||||
|
@ -15,349 +20,11 @@
|
|||
#include <Service.h>
|
||||
|
||||
#include <Territory/QuestBattle.h>
|
||||
#include <Territory/InstanceContent.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
|
||||
|
@ -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
|
||||
//
|
||||
|
||||
void EncounterTimeline::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 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 )
|
||||
TimelinePack EncounterTimeline::getEncounterPack( const std::string& name, bool reload )
|
||||
{
|
||||
static std::map< std::string, TimelinePack > cache = {};
|
||||
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, 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
|
||||
for( const auto& actorJ : json.at( "actors" ).items() )
|
||||
{
|
||||
|
@ -976,47 +280,66 @@ namespace Sapphire
|
|||
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)
|
||||
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;
|
||||
m_name = name;
|
||||
}
|
||||
|
||||
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() )
|
||||
return it->second;
|
||||
m_selectors.emplace( std::make_pair( name, selector ) );
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void EncounterTimeline::TimelineActor::resetSubActors( TerritoryPtr pTeri )
|
||||
void TimelinePack::setStartTime( uint64_t time )
|
||||
{
|
||||
for( auto& subActor : m_subActors )
|
||||
{
|
||||
if( subActor.second )
|
||||
{
|
||||
auto pBNpc = subActor.second;
|
||||
pTeri->removeActor( pBNpc );
|
||||
// todo: despawn?
|
||||
subActor.second = nullptr;
|
||||
}
|
||||
}
|
||||
m_startTime = time;
|
||||
}
|
||||
|
||||
}// 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 <memory>
|
||||
|
||||
|
@ -8,841 +10,71 @@
|
|||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include <Common.h>
|
||||
#include <Forwards.h>
|
||||
#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
|
||||
{
|
||||
enum class TimelinePackType : uint32_t
|
||||
{
|
||||
Solo,
|
||||
EncounterFight
|
||||
};
|
||||
|
||||
// todo: actually handle solo stuff properly (or tie to zone director/content director at least)
|
||||
class TimelinePack
|
||||
{
|
||||
TimelinePackType m_type{ TimelinePackType::EncounterFight };
|
||||
std::vector< TimelineActor > m_actors;
|
||||
std::string m_name;
|
||||
|
||||
std::unordered_map< std::string, Selector > m_selectors;
|
||||
|
||||
uint64_t m_startTime{ 0 };
|
||||
|
||||
public:
|
||||
TimelinePack() {}
|
||||
TimelinePack( const TimelinePack& rhs ) :
|
||||
m_type( rhs.m_type ),
|
||||
m_name( rhs.m_name ),
|
||||
m_actors( rhs.m_actors ),
|
||||
m_startTime( 0 )
|
||||
{
|
||||
}
|
||||
|
||||
TimelinePack( TimelinePackType type ) : m_type( type ) {}
|
||||
|
||||
void setName( const std::string& name );
|
||||
|
||||
void addSelector( const std::string& name, const Selector& selector );
|
||||
|
||||
void createSnapshot( const std::string& selectorName, Entity::CharaPtr pSrc,
|
||||
const std::vector< uint32_t >& exclude );
|
||||
|
||||
const World::AI::Snapshot::Results& getSnapshotResults( const std::string& selectorName );
|
||||
|
||||
const World::AI::Snapshot::TargetIds& getSnapshotTargetIds( const std::string& selectorName );
|
||||
|
||||
void addTimelineActor( const TimelineActor& actor );
|
||||
|
||||
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
|
||||
{
|
||||
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
|
||||
{
|
||||
Solo,
|
||||
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)
|
||||
class TimelinePack
|
||||
{
|
||||
TimelinePackType m_type{TimelinePackType::EncounterFight};
|
||||
std::vector< TimelineActor > m_actors;
|
||||
std::string m_name;
|
||||
|
||||
uint64_t m_startTime{ 0 };
|
||||
public:
|
||||
TimelinePack() { }
|
||||
TimelinePack( const TimelinePack& rhs ) :
|
||||
m_type( rhs.m_type ),
|
||||
m_name( rhs.m_name ),
|
||||
m_actors( rhs.m_actors ),
|
||||
m_startTime( 0 )
|
||||
{
|
||||
|
||||
}
|
||||
TimelinePack( TimelinePackType type ) : m_type( type ) {}
|
||||
|
||||
void setName( const std::string& name )
|
||||
{
|
||||
m_name = name;
|
||||
}
|
||||
|
||||
void addTimelineActor( const TimelineActor& actor )
|
||||
{
|
||||
m_actors.emplace_back( actor );
|
||||
}
|
||||
|
||||
void setStartTime( uint64_t time )
|
||||
{
|
||||
m_startTime = time;
|
||||
}
|
||||
|
||||
uint64_t getStartTime() const
|
||||
{
|
||||
return m_startTime;
|
||||
}
|
||||
|
||||
void update( TerritoryPtr pTeri, uint64_t time )
|
||||
{
|
||||
for( auto& actor : m_actors )
|
||||
actor.update( pTeri, *this, time );
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// 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:
|
||||
|
||||
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