From 6c5df823061ab0aa72895d32dc15e84ba0d42154 Mon Sep 17 00:00:00 2001 From: Tahir Date: Mon, 6 May 2024 01:45:42 +0100 Subject: [PATCH] more encounter timeline work --- src/world/Encounter/EncounterFight.h | 5 + src/world/Encounter/EncounterTimeline.cpp | 232 ++++++++++++---------- src/world/Encounter/EncounterTimeline.h | 208 +++++++++++++------ 3 files changed, 287 insertions(+), 158 deletions(-) diff --git a/src/world/Encounter/EncounterFight.h b/src/world/Encounter/EncounterFight.h index 7a17cae0..f3fe9c39 100644 --- a/src/world/Encounter/EncounterFight.h +++ b/src/world/Encounter/EncounterFight.h @@ -134,6 +134,11 @@ namespace Sapphire m_bnpcs.erase( layoutId ); } + InstanceContentPtr getInstance() + { + return m_pInstance; + } + protected: uint64_t m_startTime{ 0 }; EncounterState::StateStackPtr m_stateStack; diff --git a/src/world/Encounter/EncounterTimeline.cpp b/src/world/Encounter/EncounterTimeline.cpp index 8dc1a45f..692a5002 100644 --- a/src/world/Encounter/EncounterTimeline.cpp +++ b/src/world/Encounter/EncounterTimeline.cpp @@ -6,21 +6,21 @@ namespace Sapphire { - void EncounterTimeline::EncounterConditionHp::from_json( nlohmann::json& json, EncounterPhasePtr pPhase, EncounterConditionId conditionId ) + void EncounterTimeline::ConditionHp::from_json( nlohmann::json& json, PhasePtr pPhase, ConditionId conditionId ) { - EncounterTimepointCondition::from_json( json, pPhase, conditionId ); + TimepointCondition::from_json( json, pPhase, conditionId ); auto params = json.at( "params" ).get< std::vector< uint32_t > >(); this->actorId = params[ 0 ]; - if( conditionId == EncounterConditionId::HpPctLessThan ) + if( conditionId == ConditionId::HpPctLessThan ) this->hp.val = params[ 1 ]; else this->hp.min = params[ 1 ], this->hp.max = params[ 2 ]; } - bool EncounterTimeline::EncounterConditionHp::canExecute( EncounterFightPtr pFight, uint64_t time ) + bool EncounterTimeline::ConditionHp::canExecute( EncounterFightPtr pFight, uint64_t time ) { auto pBNpc = pFight->getBNpc( actorId ); if( !pBNpc ) @@ -30,9 +30,9 @@ namespace Sapphire switch( m_conditionId ) { - case EncounterConditionId::HpPctLessThan: + case ConditionId::HpPctLessThan: return pBNpc->getHpPercent() < hp.val; - case EncounterConditionId::HpPctBetween: + case ConditionId::HpPctBetween: { auto hpPct = pBNpc->getHpPercent(); return hpPct >= hp.min && hpPct <= hp.max; @@ -41,9 +41,9 @@ namespace Sapphire return false; }; - void EncounterTimeline::EncounterConditionDirectorVar::from_json( nlohmann::json& json, EncounterPhasePtr pPhase, EncounterConditionId conditionId ) + void EncounterTimeline::ConditionDirectorVar::from_json( nlohmann::json& json, PhasePtr pPhase, ConditionId conditionId ) { - EncounterTimepointCondition::from_json( json, pPhase, conditionId ); + TimepointCondition::from_json( json, pPhase, conditionId ); auto params = json.at( "params" ).get< std::vector< uint32_t > >(); @@ -51,110 +51,142 @@ namespace Sapphire this->value = params[ 1 ]; } - bool EncounterTimeline::EncounterConditionDirectorVar::canExecute( EncounterFightPtr pFight, uint64_t time ) + bool EncounterTimeline::ConditionDirectorVar::canExecute( EncounterFightPtr pFight, uint64_t time ) { + auto pInstance = pFight->getInstance(); + + // todo: use something other than InstanceContentPtr + if( !pInstance ) + return false; + switch( m_conditionId ) { - case EncounterConditionId::DirectorVarEquals: - return false; // pFight->getDirectorVar( directorVar ) == value; - case EncounterConditionId::DirectorVarGreaterThan: - return false; // pFight->getDirectorVar( directorVar ) > value; + case ConditionId::DirectorVarEquals: + return pInstance->getDirectorVar( directorVar ) == value; + case ConditionId::DirectorVarGreaterThan: + return pInstance->getDirectorVar( directorVar ) > value; } return false; } + void EncounterTimeline::Timepoint::execute( EncounterFightPtr pFight, uint64_t time ) + { + switch( m_type ) + { + case TimepointDataType::Idle: + { + auto pIdleData = std::dynamic_pointer_cast< TimepointDataIdle, TimepointData >( m_pData ); + auto pBNpc = pFight->getBNpc( pIdleData->m_actorId ); + + if( pBNpc ) + { + // todo: idle + } + } + break; + case TimepointDataType::CastAction: + { + auto pActionData = std::dynamic_pointer_cast< TimepointDataAction, TimepointData >( m_pData ); + + // todo: filter the correct target + // todo: tie to mechanic script? + } + break; + case TimepointDataType::MoveTo: + { + auto pMoveToData = std::dynamic_pointer_cast< TimepointDataMoveTo, TimepointData >( m_pData ); + auto pBNpc = pFight->getBNpc( pMoveToData->m_actorId ); + + // todo: path + if( pBNpc ) + { + pBNpc->setPos( pMoveToData->m_x, pMoveToData->m_y, pMoveToData->m_z ); + pBNpc->setRot( pMoveToData->m_rot ); + pBNpc->sendPositionUpdate(); + } + } + break; + case TimepointDataType::BattleTalk: + { + // auto pBattleTalkData = std::dynamic_pointer_cast< TimepointDataBattleTalk, TimepointData >(); + } + break; + case TimepointDataType::SetDirectorSeq: + case TimepointDataType::SetDirectorVar: + case TimepointDataType::SetDirectorFlag: + { + auto pDirectorData = std::dynamic_pointer_cast< TimepointDataDirector, TimepointData >( m_pData ); + auto pInstance = pFight->getInstance(); + + // todo: this should never not be set? + // todo: probably should use ContentDirector + if( pInstance ) + { + switch( pDirectorData->m_directorOp ) + { + case DirectorOpId::SetDirectorVar: + pInstance->setDirectorVar( pDirectorData->m_data.index, pDirectorData->m_data.value.val ); + break; + case DirectorOpId::SetDirectorVarLR: + pInstance->setDirectorVar( pDirectorData->m_data.index, pDirectorData->m_data.value.left, pDirectorData->m_data.value.right ); + break; + case DirectorOpId::SetDirectorFlag: + pInstance->setDirectorFlags( pDirectorData->m_data.flags ); + break; + case DirectorOpId::SetDirectorSeq: + pInstance->setDirectorSequence( pDirectorData->m_data.seq ); + break; + case DirectorOpId::ClearDirectorFlag: + break; + default: + // probably throw an error here + break; + } + } + } + break; + } + } + EncounterTimeline::EncounterTimelineInfo EncounterTimeline::buildEncounterTimeline( uint32_t encounterId, bool reload ) { static std::map< uint32_t, EncounterTimelineInfo > cache = {}; - const static std::map< std::string, EncounterTimepointDataType > timepointTypeMap = + const static std::map< std::string, TimepointDataType > timepointTypeMap = { - { "idle", EncounterTimepointDataType::Idle }, - { "castAction", EncounterTimepointDataType::CastAction }, - { "moveTo", EncounterTimepointDataType::MoveTo }, - { "logMessage", EncounterTimepointDataType::LogMessage }, - { "setDirectorVar", EncounterTimepointDataType::SetDirectorVar }, - { "addStatusEffect", EncounterTimepointDataType::AddStatusEffect }, - { "removeStatusEffect", EncounterTimepointDataType::RemoveStatusEffect } + { "idle", TimepointDataType::Idle }, + { "castAction", TimepointDataType::CastAction }, + { "moveTo", TimepointDataType::MoveTo }, + { "logMessage", TimepointDataType::LogMessage }, + { "setDirectorVar", TimepointDataType::SetDirectorVar }, + { "setDirectorSeq", TimepointDataType::SetDirectorSeq }, + { "setDirectorFlags", TimepointDataType::SetDirectorFlag }, + { "addStatusEffect", TimepointDataType::AddStatusEffect }, + { "removeStatusEffect", TimepointDataType::RemoveStatusEffect } }; - const static std::map< std::string, EncounterTimepointCallbackType > callbackTypeMap = + const static std::map< std::string, TimepointCallbackType > callbackTypeMap = { - { "onActionInit", EncounterTimepointCallbackType::OnActionInit }, - { "onActionStart", EncounterTimepointCallbackType::OnActionStart }, - { "onActionInterrupt", EncounterTimepointCallbackType::OnActionInterrupt }, - { "onActionExecute", EncounterTimepointCallbackType::OnActionExecute }, + { "onActionInit", TimepointCallbackType::OnActionInit }, + { "onActionStart", TimepointCallbackType::OnActionStart }, + { "onActionInterrupt", TimepointCallbackType::OnActionInterrupt }, + { "onActionExecute", TimepointCallbackType::OnActionExecute }, }; - const static std::map< std::string, EncounterConditionId > conditionIdMap = + const static std::map< std::string, ConditionId > conditionIdMap = { - { "hpPctLessThan", EncounterConditionId::HpPctLessThan }, - { "hpPctBetween", EncounterConditionId::HpPctBetween }, - { "directorVarEquals", EncounterConditionId::DirectorVarEquals }, - { "directorVarGreaterThan", EncounterConditionId::DirectorVarGreaterThan }, + { "hpPctLessThan", ConditionId::HpPctLessThan }, + { "hpPctBetween", ConditionId::HpPctBetween }, + { "directorVarEquals", ConditionId::DirectorVarEquals }, + { "directorVarGreaterThan", ConditionId::DirectorVarGreaterThan }, + { "encounterTimeElapsed", ConditionId::EncounterTimeElapsed }, + { "phaseTimeElapsed", ConditionId::PhaseTimeElapsed } }; EncounterTimelineInfo info; if( cache.find( encounterId ) != cache.end() && !reload ) return cache.at( encounterId ); /* - array of states e.g. - [ - pushStates: - [ - { - condition: "HpPctBetween", params:[ 20, 25 ], state: "phase1", loop: true - }, - { - condition: "RNGMinMax", params:[ 0, 10, 5 ], state: "phase1", loop: true - } - ], - states: - [ - { - name: "idle", - type: "idle", - duration: 5000, - overrideFlags: ["INVULNERABLE"], - data: {} - } - { - name: "phase1", - type: "action", - data: { - actionId: 150, - onFinish: { - type: "addStatusEffect", - data: { - selectFilter: "self", - statusEffectId: 70, - duration: 30000 - } - } - } - } - ] - ] - /* - * - class HpPercentCondition : EncounterTimepointCondition - { - EncounterTimepointConditionId m_type; - std::vector< uint32_t > m_params - - HpPercentCondition( EncounterTimepointConditionId conditionId std::vector< uint32_t params ) : m_type( conditionId ), m_params( params ){} - bool isConditionMet( uint32_t bossHpPct ) - { - switch( m_type ) - { - case EncounterTimepointConditionId::HpLessThanPct: - return bossHpPct < m_params[0]; - case EncounterTimepointConditionId::HpBetweenPct: - return bossHpPct >= m_params[0] && bossHpPct <= m_params[1]; - } - return false; - } - } - class RngCondition : EncounterTimepointCondition + class RngCondition : TimepointCondition { EncounterTimepointConditionId m_type; std::vector< uint32_t > m_params @@ -196,7 +228,7 @@ namespace Sapphire auto json = nlohmann::json::parse( f ); - std::map< std::string, EncounterPhasePtr > phaseNameMap; + std::map< std::string, PhasePtr > phaseNameMap; for( const auto& phaseJ : json.at( "phases" ).items() ) @@ -206,7 +238,7 @@ namespace Sapphire const auto& phaseName = phaseV.at( "name" ).get< std::string >(); const auto& timepoints = phaseV.at( "timepoints" ); - EncounterPhasePtr pPhase = std::make_shared< EncounterPhase >(); + PhasePtr pPhase = std::make_shared< Phase >(); for( const auto& timepoint : timepoints.items() ) { @@ -228,8 +260,8 @@ namespace Sapphire auto loop = pcV.at( "loop" ).get< bool >(); auto phaseRef = pcV.at( "phase" ).get< std::string >(); - EncounterPhasePtr pPhase; - EncounterConditionId conditionId; + PhasePtr pPhase; + ConditionId conditionId; // make sure condition exists if( auto it = conditionIdMap.find( conditionName ); it != conditionIdMap.end() ) @@ -244,20 +276,20 @@ namespace Sapphire throw std::runtime_error( fmt::format( std::string( "EncounterTimeline::buildEncounterTimeline - no state found by name: %s" ), phaseRef ) ); // build the condition - EncounterTimepointConditionPtr pCondition; + TimepointConditionPtr pCondition; switch( conditionId ) { - case EncounterConditionId::HpPctLessThan: - case EncounterConditionId::HpPctBetween: + case ConditionId::HpPctLessThan: + case ConditionId::HpPctBetween: { - auto pHpCondition = std::make_shared< EncounterConditionHp >(); + auto pHpCondition = std::make_shared< ConditionHp >(); pHpCondition->from_json( pcV, pPhase, conditionId ); } break; - case EncounterConditionId::DirectorVarEquals: - case EncounterConditionId::DirectorVarGreaterThan: + case ConditionId::DirectorVarEquals: + case ConditionId::DirectorVarGreaterThan: { - auto pDirectorCondition = std::make_shared< EncounterConditionDirectorVar >(); + auto pDirectorCondition = std::make_shared< ConditionDirectorVar >(); pDirectorCondition->from_json( pcV, pPhase, conditionId ); } break; diff --git a/src/world/Encounter/EncounterTimeline.h b/src/world/Encounter/EncounterTimeline.h index 03a542d6..01fa8c70 100644 --- a/src/world/Encounter/EncounterTimeline.h +++ b/src/world/Encounter/EncounterTimeline.h @@ -6,6 +6,7 @@ #include #include #include +#include #include @@ -15,22 +16,33 @@ namespace Sapphire { public: // EncounterFight::OnTick() { switch EncounterTimepointConditionId } - enum class EncounterConditionId : uint32_t + enum class ConditionId : uint32_t { HpPctLessThan, HpPctBetween, DirectorVarEquals, - DirectorVarGreaterThan + DirectorVarGreaterThan, + PhaseTimeElapsed, + EncounterTimeElapsed + }; + + enum class DirectorOpId + { + SetDirectorVar, + SetDirectorVarLR, + SetDirectorSeq, + SetDirectorFlag, + ClearDirectorFlag }; // TODO: what should this do? - enum class EncounterTimepointOverrideFlags + enum class TimepointOverrideFlags : uint32_t { None, Invulnerable }; - enum class EncounterTimepointDataType : uint32_t + enum class TimepointDataType : uint32_t { Idle, CastAction, @@ -38,11 +50,13 @@ namespace Sapphire LogMessage, BattleTalk, SetDirectorVar, + SetDirectorSeq, + SetDirectorFlag, AddStatusEffect, RemoveStatusEffect }; - enum class EncounterTimepointCallbackType : uint32_t + enum class TimepointCallbackType : uint32_t { OnActionInit, OnActionStart, @@ -50,7 +64,7 @@ namespace Sapphire OnActionExecute }; - enum class TargetSelectFilterIds + enum class TargetSelectFilterIds : uint32_t { Self, Tank, @@ -76,97 +90,175 @@ namespace Sapphire }; - // Generated Structures - - // Generated Callback Structure - struct EncounterTimepointCallbackData : - public std::enable_shared_from_this< EncounterTimepointCallbackData > + using TimepointCallbackFunc = std::function< void( EncounterFightPtr, uint64_t ) >; + // Timepoint Data Objects + struct TimepointCallbackData : + public std::enable_shared_from_this< TimepointCallbackData > { - EncounterTimepointCallbackType m_type; + TimepointCallbackType m_type; + std::vector < TimepointCallbackFunc > m_callbacks; }; - using EncounterTimepointCallbackDataPtr = std::shared_ptr< EncounterTimepointCallbackData >; - using EncounterTimepointCallbacks = std::map< EncounterTimepointCallbackType, EncounterTimepointCallbackDataPtr >; + using TimebackCallbackDataPtr = std::shared_ptr< TimepointCallbackData >; + using TimepointCallbacks = std::map< TimepointCallbackType, TimebackCallbackDataPtr >; - // Generated State Objects - struct EncounterTimepointData : - public std::enable_shared_from_this< EncounterTimepointData > + struct TimepointData : + public std::enable_shared_from_this< TimepointData > { - EncounterTimepointDataType m_type; + TimepointData( TimepointDataType) {} + virtual ~TimepointData() = 0; + TimepointDataType m_type; }; - using EncounterTimepointDataPtr = std::shared_ptr< EncounterTimepointData >; + using TimepointDataPtr = std::shared_ptr< TimepointData >; + struct TimepointDataIdle : public TimepointData + { + uint32_t m_actorId; + uint64_t m_durationMs; + }; - // Generated State Data Objects - struct EncounterTimepointDataStatusEffect : EncounterTimepointData + struct TimepointDataStatusEffect : public TimepointData { uint32_t m_statusEffectId; TargetSelectFilter m_targetFilter; uint32_t m_durationMs; }; - struct EncounterTimepointDataAction : EncounterTimepointData + struct TimepointDataAction : public TimepointData { + uint32_t m_actorId; uint32_t m_actionId; - EncounterTimepointCallbacks m_callbacks; + TimepointCallbacks m_callbacks; }; - struct EncounterTimepointDataMoveTo : EncounterTimepointData + struct TimepointDataMoveTo : public TimepointData { - float x, y, z, rot; + uint32_t m_actorId; + MoveType m_moveType; + float m_x, m_y, m_z, m_rot; }; - struct EncounterTimepoint : - public std::enable_shared_from_this< EncounterTimepoint > + + struct TimepointDataLogMessage : public TimepointData + { + uint32_t m_logMessageType; + uint32_t m_logMessageId; + std::string m_message; + }; + + struct TimepointDataDirector : public TimepointData + { + DirectorOpId m_directorOp; + union + { + struct + { + uint8_t index; + union + { + uint8_t val; + struct + { + uint8_t left, right; + }; + } value; + }; + uint8_t seq; + uint8_t flags; + } m_data; + }; + + class Timepoint : + public std::enable_shared_from_this< Timepoint > { public: - EncounterTimepointDataType m_type; - uint32_t m_duration; - EncounterTimepointOverrideFlags m_overrideFlags; - EncounterTimepointDataPtr m_pData; + TimepointDataType m_type; + uint64_t m_duration{ 0 }; + uint64_t m_executeTime{ 0 }; + TimepointOverrideFlags m_overrideFlags; + TimepointDataPtr m_pData; std::string m_description; - // switch( m_type ) - virtual void execute( EncounterFightPtr pFight, uint64_t time ); - }; - using EncounterTimepointPtr = std::shared_ptr< EncounterTimepoint >; + // todo: repeatable? - class EncounterPhase : - public std::enable_shared_from_this< EncounterPhase > + bool canExecute() + { + return m_executeTime == 0; + } + + bool finished( uint64_t time ) + { + return m_executeTime + m_duration <= time; + } + + void execute( EncounterFightPtr pFight, uint64_t time ); + }; + using TimepointPtr = std::shared_ptr< Timepoint >; + + class Phase : + public std::enable_shared_from_this< Phase > { public: + + // todo: respect looping phases, allow callbacks to push timepoints + std::string m_name; - std::map< std::string, EncounterTimepointPtr > m_timepoints; + std::queue< TimepointPtr > m_timepoints; uint64_t m_startTime{ 0 }; - uint64_t m_currTime{ 0 }; + uint64_t m_lastTimepoint{ 0 }; + + std::queue< TimepointPtr > m_executed; + + // todo: i wrote this very sleep deprived, ensure it is actually sane void execute( EncounterFightPtr pFight, uint64_t time ) { - uint64_t durationMs = time - m_currTime; - for( const auto& timepoint : m_timepoints ) - timepoint.second->execute( pFight, time ); - if( m_startTime == 0 ) m_startTime = time; + if( m_lastTimepoint == 0 ) + m_lastTimepoint = time; - m_currTime = time; + // todo: this is stupid + while( m_timepoints.size() > 0 ) + { + uint64_t phaseElapsed = time - m_startTime; + uint64_t timepointElapsed = time - m_lastTimepoint; + + auto& pTimepoint = m_timepoints.front(); + if( pTimepoint->canExecute() ) + { + pTimepoint->execute( pFight, time ); + m_lastTimepoint = time; + m_executed.push( pTimepoint ); + } + else if( pTimepoint->finished( timepointElapsed ) ) + { + // todo: this is stupid, temp workaround for allowing phases to loop + pTimepoint->m_executeTime = 0; + m_timepoints.pop(); + } + else + { + break; + } + } } }; - using EncounterPhasePtr = std::shared_ptr< EncounterPhase >; + using PhasePtr = std::shared_ptr< Phase >; - class EncounterTimepointCondition : - public std::enable_shared_from_this< EncounterTimepointCondition > + class TimepointCondition : + public std::enable_shared_from_this< TimepointCondition > { public: - EncounterConditionId m_conditionId{ 0 }; - EncounterPhasePtr m_pPhase{ nullptr }; + ConditionId m_conditionId{ 0 }; + PhasePtr m_pPhase{ nullptr }; bool m_loop{ false }; uint64_t m_startTime{ 0 }; uint32_t m_cooldown{ 0 }; - EncounterTimepointCondition() {} - ~EncounterTimepointCondition() {} + TimepointCondition() {} + ~TimepointCondition() {} - virtual void from_json( nlohmann::json& json, EncounterPhasePtr pPhase, EncounterConditionId conditionId ) + virtual void from_json( nlohmann::json& json, PhasePtr pPhase, ConditionId conditionId ) { this->m_conditionId = conditionId; this->m_loop = json.at( "loop" ).get< bool >(); @@ -186,9 +278,9 @@ namespace Sapphire }; }; - using EncounterTimepointConditionPtr = std::shared_ptr< EncounterTimepointCondition >; + using TimepointConditionPtr = std::shared_ptr< TimepointCondition >; - class EncounterConditionHp : EncounterTimepointCondition + class ConditionHp : TimepointCondition { public: uint32_t actorId; @@ -201,21 +293,21 @@ namespace Sapphire }; } hp; - void from_json( nlohmann::json& json, EncounterPhasePtr pPhase, EncounterConditionId conditionId ); + void from_json( nlohmann::json& json, PhasePtr pPhase, ConditionId conditionId ); bool canExecute( EncounterFightPtr pFight, uint64_t time ) override; }; - class EncounterConditionDirectorVar : EncounterTimepointCondition + class ConditionDirectorVar : TimepointCondition { public: uint32_t directorVar; uint32_t value; - void from_json( nlohmann::json& json, EncounterPhasePtr pPhase, EncounterConditionId conditionId ); + void from_json( nlohmann::json& json, PhasePtr pPhase, ConditionId conditionId ); bool canExecute( EncounterFightPtr pFight, uint64_t time ) override; }; - using EncounterTimelineInfo = std::stack< EncounterTimepointConditionPtr >; + using EncounterTimelineInfo = std::queue< TimepointConditionPtr >; public: