From f96c633c6cecf2936bc838162d9ee447b2ca77d5 Mon Sep 17 00:00:00 2001 From: Tahir Date: Fri, 10 May 2024 15:18:06 +0100 Subject: [PATCH] wip: encounter timepoint separate update from execute - todo: implement conditions and timepoint parsing for remaining types - todo: test lmao --- src/world/Encounter/EncounterTimeline.cpp | 119 +++++++++++++++++----- src/world/Encounter/EncounterTimeline.h | 24 ++++- 2 files changed, 112 insertions(+), 31 deletions(-) diff --git a/src/world/Encounter/EncounterTimeline.cpp b/src/world/Encounter/EncounterTimeline.cpp index 8843906b..1f7b543e 100644 --- a/src/world/Encounter/EncounterTimeline.cpp +++ b/src/world/Encounter/EncounterTimeline.cpp @@ -4,6 +4,8 @@ #include #include +#include + namespace Sapphire { bool EncounterTimeline::ConditionHp::isConditionMet( EncounterFightPtr pFight, uint64_t time ) @@ -83,8 +85,9 @@ namespace Sapphire return false; } - void EncounterTimeline::Timepoint::execute( EncounterFightPtr pFight, uint64_t time ) + void EncounterTimeline::Timepoint::update( EncounterFightPtr pFight, uint64_t time ) { + m_lastTick = time; switch( m_type ) { case TimepointDataType::Idle: @@ -111,12 +114,25 @@ namespace Sapphire auto pMoveToData = std::dynamic_pointer_cast< TimepointDataMoveTo, TimepointData >( getData() ); auto pBNpc = pFight->getBNpc( pMoveToData->m_actorId ); - // todo: path if( pBNpc ) { - pBNpc->setPos( pMoveToData->m_x, pMoveToData->m_y, pMoveToData->m_z ); + 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 + m_finished = true; + } pBNpc->setRot( pMoveToData->m_rot ); - pBNpc->sendPositionUpdate(); } } break; @@ -133,7 +149,7 @@ namespace Sapphire auto pInstance = pFight->getInstance(); // todo: this should never not be set? - // todo: probably should use ContentDirector + // todo: probably should use ContentDirector if( pInstance ) { switch( pDirectorData->m_directorOp ) @@ -160,6 +176,75 @@ namespace Sapphire } break; } + m_finished = m_finished || m_executeTime + m_duration <= time; + } + + /* + class RngCondition : TimepointCondition + { + EncounterTimepointConditionId m_type; + std::vector< uint32_t > m_params + + RngCondition( EncounterTimepointConditionId conditionId std::vector< uint32_t params ) : m_type( conditionId ), m_params( params ){} + bool isConditionMet( uint32_t shit ) + { + switch( m_type ) + { + case EncounterTimepointConditionId::RngMinMax: + return RNGMgr::generate( params[0], params[1] ) == params[2]; + } + return false; + } + } + enum class ActionCallbackType : uint32_t + { + OnActionInit, + OnActionStart, + OnActionInterrupt, + OnActionExecute + }; + + using CallbackFunc = std::function< void < CharaPtr, Action > >; + + std::unordered_map< ActionCallbackType, CallbackFunc > m_actionCallbacks; + void Chara::registerActionCallback( ActionCallbackType type, CallbackFunc callback ) + { + m_actionCallbacks[ type ].push_back( callback ); + } + + // call this when changing EncounterPack + void Chara::clearActionCallbacks() + { + for( auto& callback : m_actionCallbacks ) + callback.second.clear() + } + + void Chara::onActionInterrupt() + { + auto action = getCurrentAction(); + for( auto& callback : m_actionCallbacks[ ActionCallbackType::OnActionInterrupt ] ) + callback( this, action ); + } + + void EncounterTimeline::Timepoint::execute( EncounterFightPtr pFight, uint64_t time ) + { + switch( m_type ) + { + case TimepointDataType::CastAction: + { + auto pActionData = std::dynamic_pointer_cast< TimepointDataAction, TimepointData >( getData() ); + + // todo: filter the correct target + // todo: tie to mechanic script? + } + } + } + */ + + void EncounterTimeline::Timepoint::execute( EncounterFightPtr pFight, uint64_t time ) + { + m_executeTime = time; + update( pFight, time ); } // @@ -333,24 +418,6 @@ namespace Sapphire TimelinePack pack; if( cache.find( encounterId ) != cache.end() && !reload ) return cache.at( encounterId ); - /* - class RngCondition : TimepointCondition - { - EncounterTimepointConditionId m_type; - std::vector< uint32_t > m_params - - RngCondition( EncounterTimepointConditionId conditionId std::vector< uint32_t params ) : m_type( conditionId ), m_params( params ){} - bool isConditionMet( uint32_t shit ) - { - switch( m_type ) - { - case EncounterTimepointConditionId::RngMinMax: - return RNGMgr::generate( params[0], params[1] ) == params[2]; - } - return false; - } - } - */ std::string encounter_name( fmt::format( std::string( "data/EncounterTimelines/EncounterTimeline%u.json" ), encounterId ) ); @@ -385,7 +452,7 @@ namespace Sapphire auto actorV = actorJ.value(); std::string actorName = actorV.at( "name" ); - TimelineActor& actor = actorNameMap[actorName]; + TimelineActor& actor = actorNameMap[ actorName ]; // todo: are phases linked by actor, or global in the json for( const auto& phaseJ : json.at( "phases" ).items() ) { @@ -420,14 +487,14 @@ namespace Sapphire for( const auto& pcJ : json.at( "phaseConditions" ).items() ) { auto pcV = pcJ.value(); - auto conditionName = pcV.at( "condition" ).get< std::string>(); + auto conditionName = pcV.at( "condition" ).get< std::string >(); auto description = pcV.at( "description" ).get< std::string >(); auto loop = pcV.at( "loop" ).get< bool >(); auto phaseRef = pcV.at( "targetPhase" ).get< std::string >(); auto actorRef = pcV.at( "targetActor" ).get< std::string >(); ConditionId conditionId; - + // make sure condition exists if( auto it = conditionIdMap.find( conditionName ); it != conditionIdMap.end() ) conditionId = it->second; diff --git a/src/world/Encounter/EncounterTimeline.h b/src/world/Encounter/EncounterTimeline.h index 6830e8af..0698eed0 100644 --- a/src/world/Encounter/EncounterTimeline.h +++ b/src/world/Encounter/EncounterTimeline.h @@ -260,9 +260,11 @@ namespace Sapphire 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? @@ -273,15 +275,24 @@ namespace Sapphire bool canExecute( uint64_t elapsed ) { - return m_executeTime == 0 && m_duration <= elapsed; + return m_executeTime == 0; // & &m_duration <= elapsed; } bool finished( uint64_t time ) { - return m_executeTime + m_duration <= time; + return m_executeTime + m_duration <= time || m_finished; + } + + void reset() + { + m_executeTime = 0; + m_lastTick = 0; + 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( EncounterFightPtr pFight, uint64_t time ); void execute( EncounterFightPtr pFight, uint64_t time ); }; @@ -320,13 +331,16 @@ namespace Sapphire m_lastTimepointTime = time; m_executed.push( timepoint ); } + else if( !timepoint.finished( timepointElapsed ) ) + { + timepoint.update( pFight, time ); + } - // fire off all timepoints if( timepoint.finished( timepointElapsed ) ) { - // todo: this is stupid, temp workaround for allowing phases to loop - timepoint.m_executeTime = 0; + timepoint.reset(); m_lastTimepointIndex = i; + // make sure this timepoint isnt run again unless phase loops ++i; continue; }