From 43f925408610bb5dbcf90dff72af516c79188ba6 Mon Sep 17 00:00:00 2001 From: Tahir Date: Fri, 10 May 2024 22:46:33 +0100 Subject: [PATCH] wip: refactor encounter timeline stuff to only modify state objects, cache should now work as intended - tldr: fast as fuck boi --- src/world/Encounter/EncounterTimeline.cpp | 36 ++-- src/world/Encounter/EncounterTimeline.h | 213 ++++++++++++++-------- 2 files changed, 153 insertions(+), 96 deletions(-) diff --git a/src/world/Encounter/EncounterTimeline.cpp b/src/world/Encounter/EncounterTimeline.cpp index 4dcdb56c..106b6e75 100644 --- a/src/world/Encounter/EncounterTimeline.cpp +++ b/src/world/Encounter/EncounterTimeline.cpp @@ -9,7 +9,7 @@ namespace Sapphire { - bool EncounterTimeline::ConditionHp::isConditionMet( InstanceContentPtr pInstance, uint64_t time ) + bool EncounterTimeline::ConditionHp::isConditionMet( ConditionState& state, InstanceContentPtr pInstance, TimelinePack& pack, uint64_t time ) const { auto pBNpc = pInstance->getActiveBNpcByLayoutId( layoutId ); if( !pBNpc ) @@ -30,7 +30,7 @@ namespace Sapphire return false; }; - bool EncounterTimeline::ConditionDirectorVar::isConditionMet( InstanceContentPtr pInstance, uint64_t time ) + bool EncounterTimeline::ConditionDirectorVar::isConditionMet( ConditionState& state, InstanceContentPtr pInstance, TimelinePack& pack, uint64_t time ) const { // todo: use something other than InstanceContentPtr if( !pInstance ) @@ -54,7 +54,7 @@ namespace Sapphire return false; } - bool EncounterTimeline::ConditionCombatState::isConditionMet( InstanceContentPtr pInstance, uint64_t time ) + bool EncounterTimeline::ConditionCombatState::isConditionMet( ConditionState& state, InstanceContentPtr pInstance, TimelinePack& pack, uint64_t time ) const { auto pBattleNpc = pInstance->getActiveBNpcByLayoutId( this->layoutId ); @@ -78,27 +78,22 @@ namespace Sapphire return false; } - bool EncounterTimeline::ConditionEncounterTimeElapsed::isConditionMet( InstanceContentPtr pInstance, uint64_t time ) + bool EncounterTimeline::ConditionEncounterTimeElapsed::isConditionMet( ConditionState& state, InstanceContentPtr pInstance, TimelinePack& pack, uint64_t time ) const { - if( pInstance == nullptr ) - { - // die idk - return false; - } - + auto elapsed = time - pack.getStartTime(); // todo: check encounter time - return false; + return elapsed >= this->duration; } - bool EncounterTimeline::ConditionBNpcFlags::isConditionMet( InstanceContentPtr pInstance, uint64_t time ) + bool EncounterTimeline::ConditionBNpcFlags::isConditionMet( ConditionState& state, InstanceContentPtr pInstance, TimelinePack& pack, uint64_t time ) const { auto pBNpc = pInstance->getActiveBNpcByLayoutId( this->layoutId ); return pBNpc && pBNpc->hasFlag( this->flags ); } - void EncounterTimeline::Timepoint::update( InstanceContentPtr pInstance, uint64_t time ) + void EncounterTimeline::Timepoint::update( TimepointState& state, InstanceContentPtr pInstance, uint64_t time ) const { - m_lastTick = time; + state.m_lastTick = time; switch( m_type ) { case TimepointDataType::Idle: @@ -141,7 +136,7 @@ namespace Sapphire else { // if we are at the pos, stop waiting - m_finished = true; + state.m_finished = true; } pBNpc->setRot( pMoveToData->m_rot ); } @@ -237,7 +232,7 @@ namespace Sapphire } break; } - m_finished = m_finished || m_executeTime + m_duration <= time; + state.m_finished = state.m_finished || state.m_startTime + m_duration <= time; } /* @@ -302,10 +297,10 @@ namespace Sapphire } */ - void EncounterTimeline::Timepoint::execute( InstanceContentPtr pInstance, uint64_t time ) + void EncounterTimeline::Timepoint::execute( TimepointState& state, InstanceContentPtr pInstance, uint64_t time ) const { - m_executeTime = time; - update( pInstance, time ); + state.m_startTime = time; + update( state, pInstance, time ); } // @@ -719,7 +714,6 @@ namespace Sapphire // build the condition PhaseConditionPtr pCondition; - pCondition->m_description = description; switch( conditionId ) { case ConditionId::HpPctLessThan: @@ -755,7 +749,7 @@ namespace Sapphire default: break; } - actor.m_phaseConditions.push_back( pCondition ); + actor.addPhaseCondition( pCondition ); } } else diff --git a/src/world/Encounter/EncounterTimeline.h b/src/world/Encounter/EncounterTimeline.h index a50ef874..5f6273b5 100644 --- a/src/world/Encounter/EncounterTimeline.h +++ b/src/world/Encounter/EncounterTimeline.h @@ -19,6 +19,30 @@ namespace Sapphire public: // forwards class TimelineActor; + class TimelinePack; + + 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 }; + + struct + { + uint64_t m_startTime{ 0 }; + uint64_t m_lastTimepointTime{ 0 }; + uint32_t m_lastTimepointIndex{ 0 }; + + std::vector< TimepointState > m_timepointStates; + } m_phaseInfo; + }; // EncounterFight::OnTick() { switch EncounterTimepointConditionId } enum class ConditionId : uint32_t @@ -59,14 +83,18 @@ namespace Sapphire Idle, CastAction, MoveTo, + LogMessage, BattleTalk, + SetDirectorVar, SetDirectorVarLR, SetDirectorSeq, SetDirectorFlag, + AddStatusEffect, RemoveStatusEffect, + SetBNpcFlags, SetEObjState, SetBgm @@ -320,12 +348,9 @@ namespace Sapphire public: TimepointDataType m_type; uint64_t m_duration{ 0 }; - uint64_t m_executeTime{ 0 }; - uint64_t m_lastTick{ 0 }; TimepointOverrideFlags m_overrideFlags; TimepointDataPtr m_pData; std::string m_description; - bool m_finished{ false }; // todo: repeatable? @@ -334,27 +359,27 @@ namespace Sapphire return m_pData; } - bool canExecute( uint64_t elapsed ) + bool canExecute( TimepointState& state, uint64_t elapsed ) const { - return m_executeTime == 0; // & &m_duration <= elapsed; + return state.m_startTime == 0; // & &m_duration <= elapsed; } - bool finished( uint64_t time ) + bool finished( TimepointState& state, uint64_t time ) const { - return m_executeTime + m_duration <= time || m_finished; + return state.m_startTime + m_duration <= time || state.m_finished; } - void reset() + void reset( TimepointState& state ) const { - m_executeTime = 0; - m_lastTick = 0; - m_finished = false; + 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( InstanceContentPtr pInstance, uint64_t time ); - void execute( InstanceContentPtr pInstance, uint64_t time ); + void update( TimepointState& state, InstanceContentPtr pInstance, uint64_t time ) const; + void execute( TimepointState& state, InstanceContentPtr pInstance, uint64_t time ) const; }; class Phase : @@ -366,52 +391,65 @@ namespace Sapphire std::string m_name; std::vector< Timepoint > m_timepoints; - uint32_t m_lastTimepointIndex{ 0 }; - uint64_t m_startTime{ 0 }; - uint64_t m_lastTimepointTime{ 0 }; - - std::queue< Timepoint > m_executed; // todo: i wrote this very sleep deprived, ensure it is actually sane - void execute( InstanceContentPtr pInstance, uint64_t time ) + void execute( ConditionState& state, InstanceContentPtr pInstance, uint64_t time ) const { - if( m_startTime == 0 ) - m_startTime = time; - if( m_lastTimepointTime == 0 ) - m_lastTimepointTime = time; + 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; - for( auto i = m_lastTimepointIndex; i < m_timepoints.size(); ) + for( auto i = state.m_phaseInfo.m_lastTimepointIndex; i < m_timepoints.size(); ) { - uint64_t phaseElapsed = time - m_startTime; - uint64_t timepointElapsed = time - m_lastTimepointTime; - + 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( timepointElapsed ) ) + if( timepoint.canExecute( tpState, timepointElapsed ) ) { - timepoint.execute( pInstance, time ); - m_lastTimepointTime = time; - m_executed.push( timepoint ); + timepoint.execute( tpState, pInstance, time ); + state.m_phaseInfo.m_lastTimepointTime = time; } - else if( !timepoint.finished( timepointElapsed ) ) + else if( !timepoint.finished( tpState, timepointElapsed ) ) { - timepoint.update( pInstance, time ); + timepoint.update( tpState, pInstance, time ); } - if( timepoint.finished( timepointElapsed ) ) + if( timepoint.finished( tpState, timepointElapsed ) ) { - timepoint.reset(); - m_lastTimepointIndex = i; + 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; } } - bool completed() + void reset( ConditionState& state ) const { - return m_timepoints.size() == 0; + state.m_phaseInfo.m_startTime = 0; + state.m_phaseInfo.m_lastTimepointIndex = 0; + state.m_phaseInfo.m_lastTimepointTime = 0; + + state.m_phaseInfo.m_timepointStates.clear(); + for( auto i = 0; i < m_timepoints.size(); ++i ) + state.m_phaseInfo.m_timepointStates.push_back( {} ); + } + + bool completed( ConditionState& state ) const + { + return state.m_phaseInfo.m_lastTimepointIndex > m_timepoints.size(); } }; using PhasePtr = std::shared_ptr< Phase >; @@ -419,14 +457,13 @@ namespace Sapphire class PhaseCondition : public std::enable_shared_from_this< PhaseCondition > { - public: + protected: ConditionId m_conditionId{ 0 }; Phase m_phase; - bool m_loop{ false }; - uint64_t m_startTime{ 0 }; - uint32_t m_cooldown{ 0 }; std::string m_description; - + uint32_t m_cooldown{ 0 }; + bool m_loop{ false }; + public: PhaseCondition() {} ~PhaseCondition() {} @@ -436,45 +473,47 @@ namespace Sapphire 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 >(); } - void execute( InstanceContentPtr pInstance, uint64_t time ) + void execute( ConditionState& state, InstanceContentPtr pInstance, TimelinePack& pack, uint64_t time ) const { - m_startTime = time; - m_phase.execute( pInstance, time ); + state.m_startTime = time; + m_phase.execute( state, pInstance, time ); }; - void update( InstanceContentPtr pInstance, uint64_t time ) + void update( ConditionState& state, InstanceContentPtr pInstance, TimelinePack& pack, uint64_t time ) const { - m_phase.execute( pInstance, time ); + m_phase.execute( state, pInstance, time ); } - void reset() + void reset( ConditionState& state ) const { - m_startTime = 0; + state.m_startTime = 0; + m_phase.reset( state ); } - bool inProgress() + bool inProgress( ConditionState& state ) const { - return m_startTime != 0; + return state.m_startTime != 0; } - bool completed() + bool completed( ConditionState& state ) const { - return m_phase.completed(); + return m_phase.completed( state ); } - bool isLoopable() + bool isLoopable() const { return m_loop; } - bool loopReady( uint64_t time ) + bool loopReady( ConditionState& state, uint64_t time ) const { - return m_phase.completed() && m_loop && ( m_startTime + m_cooldown <= time ); + return m_phase.completed( state ) && m_loop && ( state.m_startTime + m_cooldown <= time ); } - virtual bool isConditionMet( InstanceContentPtr pInstance, uint64_t time ) + virtual bool isConditionMet( ConditionState& state, InstanceContentPtr pInstance, TimelinePack& pack, uint64_t time ) const { return false; }; @@ -484,54 +523,78 @@ namespace Sapphire class TimelineActor { + protected: + std::vector< PhaseConditionPtr > m_phaseConditions; + std::queue< PhaseConditionPtr > m_phaseHistory; + std::vector< ConditionState > m_conditionStates; public: uint32_t m_layoutId{ 0 }; uint32_t m_hp{ 0 }; std::string m_name; - std::vector< PhaseConditionPtr > m_phaseConditions; - std::queue< PhaseConditionPtr > m_phaseHistory; + void addPhaseCondition( PhaseConditionPtr pCondition ) + { + m_phaseConditions.push_back( pCondition ); + m_conditionStates.push_back( {} ); + } - void update( InstanceContentPtr pInstance, uint64_t time ) + // todo: make this sane and pass info down + void update( InstanceContentPtr pInstance, TimelinePack& pack, uint64_t time ) { // todo: handle auto attacks and make it so they dont fire during boss intermission phases // todo: handle interrupts - for( const auto& pCondition : m_phaseConditions ) + for( auto i = 0; i < m_phaseConditions.size(); ++i ) { - if( pCondition->completed() ) + const auto& pCondition = m_phaseConditions[ i ]; + auto& state = m_conditionStates[ i ]; + + if( pCondition->completed( state ) ) { if( pCondition->isLoopable() ) { - if( pCondition->loopReady( time ) ) - pCondition->reset(); + if( pCondition->loopReady( state, time ) ) + pCondition->reset( state ); } } - else if( pCondition->inProgress() ) + else if( pCondition->inProgress( state ) ) { - pCondition->update( pInstance, time ); + pCondition->update( state, pInstance, pack, time ); } - else if( pCondition->isConditionMet( pInstance, time ) ) + else if( pCondition->isConditionMet( state, pInstance, pack, time ) ) { - pCondition->execute( pInstance, time ); + pCondition->execute( state, pInstance, pack, time ); m_phaseHistory.push( pCondition ); } } } }; + // todo: actually handle solo stuff properly (or tie to zone director/content director at least) class TimelinePack { TimelinePackType m_type{TimelinePackType::Solo}; std::vector< TimelineActor > m_actors; std::string m_name; + uint64_t m_startTime{ 0 }; public: TimelinePack() { } TimelinePack( TimelinePackType type ) : m_type( type ) {} + + void setStartTime( uint64_t time ) + { + m_startTime = time; + } + + uint64_t getStartTime() const + { + return m_startTime; + } + void update( InstanceContentPtr pInstance, uint64_t time ) { for( auto& actor : m_actors ) - actor.update( pInstance, time ); + actor.update( pInstance, *this, time ); } }; @@ -554,7 +617,7 @@ namespace Sapphire void from_json( nlohmann::json& json, Phase& phase, ConditionId conditionId, const std::unordered_map< std::string, TimelineActor >& actors ); - bool isConditionMet( InstanceContentPtr pInstance, uint64_t time ) override; + bool isConditionMet( ConditionState& state, InstanceContentPtr pInstance, TimelinePack& pack, uint64_t time ) const override; }; class ConditionDirectorVar : PhaseCondition @@ -572,7 +635,7 @@ namespace Sapphire } param; void from_json( nlohmann::json& json, Phase& phase, ConditionId conditionId ); - bool isConditionMet( InstanceContentPtr pInstance, uint64_t time ) override; + bool isConditionMet( ConditionState& state, InstanceContentPtr pInstance, TimelinePack& pack, uint64_t time ) const override; }; class ConditionEncounterTimeElapsed : PhaseCondition @@ -581,7 +644,7 @@ namespace Sapphire uint64_t duration; void from_json( nlohmann::json& json, Phase& phase, ConditionId conditionId ); - bool isConditionMet( InstanceContentPtr pInstance, uint64_t time ) override; + bool isConditionMet( ConditionState& state, InstanceContentPtr pInstance, TimelinePack& pack, uint64_t time ) const override; }; class ConditionCombatState : PhaseCondition @@ -591,7 +654,7 @@ namespace Sapphire CombatStateType combatState; void from_json( nlohmann::json& json, Phase& phase, ConditionId conditionId, const std::unordered_map< std::string, TimelineActor >& actors ); - bool isConditionMet( InstanceContentPtr pInstance, uint64_t time ) override; + bool isConditionMet( ConditionState& state, InstanceContentPtr pInstance, TimelinePack& pack, uint64_t time ) const override; }; class ConditionBNpcFlags : PhaseCondition @@ -601,7 +664,7 @@ namespace Sapphire uint32_t flags; void from_json( nlohmann::json& json, Phase& phase, ConditionId conditionId, const std::unordered_map< std::string, TimelineActor >& actors ); - bool isConditionMet( InstanceContentPtr pInstance, uint64_t time ) override; + bool isConditionMet( ConditionState& state, InstanceContentPtr pInstance, TimelinePack& pack, uint64_t time ) const override; }; public: