diff --git a/src/world/Encounter/EncounterTimeline.cpp b/src/world/Encounter/EncounterTimeline.cpp index 692a5002..2e102c9c 100644 --- a/src/world/Encounter/EncounterTimeline.cpp +++ b/src/world/Encounter/EncounterTimeline.cpp @@ -6,9 +6,9 @@ namespace Sapphire { - void EncounterTimeline::ConditionHp::from_json( nlohmann::json& json, PhasePtr pPhase, ConditionId conditionId ) + void EncounterTimeline::ConditionHp::from_json( nlohmann::json& json, Phase phase, ConditionId conditionId ) { - TimepointCondition::from_json( json, pPhase, conditionId ); + TimepointCondition::from_json( json, phase, conditionId ); auto params = json.at( "params" ).get< std::vector< uint32_t > >(); @@ -19,6 +19,38 @@ namespace Sapphire else this->hp.min = params[ 1 ], this->hp.max = params[ 2 ]; } + + void EncounterTimeline::ConditionDirectorVar::from_json( nlohmann::json& json, Phase phase, ConditionId conditionId ) + { + TimepointCondition::from_json( json, phase, conditionId ); + + auto params = json.at( "params" ).get< std::vector< uint32_t > >(); + + switch( conditionId ) + { + case ConditionId::DirectorVarEquals: + case ConditionId::DirectorVarGreaterThan: + { + param.var = params[ 0 ]; + param.value = params[ 1 ]; + } + break; + case ConditionId::DirectorFlagsEquals: + case ConditionId::DirectorFlagsGreaterThan: + { + param.flag = params[ 0 ]; + } + break; + case ConditionId::DirectorSeqEquals: + case ConditionId::DirectorSeqGreaterThan: + { + param.seq = params[ 0 ]; + } + break; + default: + break; + } + } bool EncounterTimeline::ConditionHp::canExecute( EncounterFightPtr pFight, uint64_t time ) { @@ -41,16 +73,6 @@ namespace Sapphire return false; }; - void EncounterTimeline::ConditionDirectorVar::from_json( nlohmann::json& json, PhasePtr pPhase, ConditionId conditionId ) - { - TimepointCondition::from_json( json, pPhase, conditionId ); - - auto params = json.at( "params" ).get< std::vector< uint32_t > >(); - - this->directorVar = params[ 0 ]; - this->value = params[ 1 ]; - } - bool EncounterTimeline::ConditionDirectorVar::canExecute( EncounterFightPtr pFight, uint64_t time ) { auto pInstance = pFight->getInstance(); @@ -62,9 +84,17 @@ namespace Sapphire switch( m_conditionId ) { case ConditionId::DirectorVarEquals: - return pInstance->getDirectorVar( directorVar ) == value; + return pInstance->getDirectorVar( param.var ) == param.value; case ConditionId::DirectorVarGreaterThan: - return pInstance->getDirectorVar( directorVar ) > value; + return pInstance->getDirectorVar( param.var ) > param.value; + case ConditionId::DirectorFlagsEquals: + return pInstance->getFlags() == param.flag; + case ConditionId::DirectorFlagsGreaterThan: + return pInstance->getFlags() > param.flag; + case ConditionId::DirectorSeqEquals: + return pInstance->getSequence() == param.seq; + case ConditionId::DirectorSeqGreaterThan: + return pInstance->getSequence() > param.seq; } return false; } @@ -75,7 +105,7 @@ namespace Sapphire { case TimepointDataType::Idle: { - auto pIdleData = std::dynamic_pointer_cast< TimepointDataIdle, TimepointData >( m_pData ); + auto pIdleData = std::dynamic_pointer_cast< TimepointDataIdle, TimepointData >( getData() ); auto pBNpc = pFight->getBNpc( pIdleData->m_actorId ); if( pBNpc ) @@ -86,7 +116,7 @@ namespace Sapphire break; case TimepointDataType::CastAction: { - auto pActionData = std::dynamic_pointer_cast< TimepointDataAction, TimepointData >( m_pData ); + auto pActionData = std::dynamic_pointer_cast< TimepointDataAction, TimepointData >( getData() ); // todo: filter the correct target // todo: tie to mechanic script? @@ -94,7 +124,7 @@ namespace Sapphire break; case TimepointDataType::MoveTo: { - auto pMoveToData = std::dynamic_pointer_cast< TimepointDataMoveTo, TimepointData >( m_pData ); + auto pMoveToData = std::dynamic_pointer_cast< TimepointDataMoveTo, TimepointData >( getData() ); auto pBNpc = pFight->getBNpc( pMoveToData->m_actorId ); // todo: path @@ -115,7 +145,7 @@ namespace Sapphire case TimepointDataType::SetDirectorVar: case TimepointDataType::SetDirectorFlag: { - auto pDirectorData = std::dynamic_pointer_cast< TimepointDataDirector, TimepointData >( m_pData ); + auto pDirectorData = std::dynamic_pointer_cast< TimepointDataDirector, TimepointData >( getData() ); auto pInstance = pFight->getInstance(); // todo: this should never not be set? @@ -148,10 +178,9 @@ namespace Sapphire } } - EncounterTimeline::EncounterTimelineInfo EncounterTimeline::buildEncounterTimeline( uint32_t encounterId, bool reload ) + void EncounterTimeline::Timepoint::from_json( const nlohmann::json& json ) { - static std::map< uint32_t, EncounterTimelineInfo > cache = {}; - const static std::map< std::string, TimepointDataType > timepointTypeMap = + const static std::unordered_map< std::string, TimepointDataType > timepointTypeMap = { { "idle", TimepointDataType::Idle }, { "castAction", TimepointDataType::CastAction }, @@ -164,7 +193,25 @@ namespace Sapphire { "removeStatusEffect", TimepointDataType::RemoveStatusEffect } }; - const static std::map< std::string, TimepointCallbackType > callbackTypeMap = + const static std::unordered_map< std::string, TimepointOverrideFlags > overrideFlagMap = + { + {} + }; + + const static std::unordered_map< std::string, TargetSelectFilterId > targetFilterMap = + { + { "self", TargetSelectFilterId::Self }, + { "tank", TargetSelectFilterId::Tank }, + { "healer", TargetSelectFilterId::Healer }, + { "dps", TargetSelectFilterId::Dps }, + { "dpsMelee", TargetSelectFilterId::DpsMelee }, + { "dpsRanged", TargetSelectFilterId::DpsRanged }, + { "furthest", TargetSelectFilterId::Furthest }, + { "aggro1", TargetSelectFilterId::Aggro1 }, + { "aggro2", TargetSelectFilterId::Aggro2 } + }; + + const static std::unordered_map< std::string, TimepointCallbackType > callbackTypeMap = { { "onActionInit", TimepointCallbackType::OnActionInit }, { "onActionStart", TimepointCallbackType::OnActionStart }, @@ -172,7 +219,42 @@ namespace Sapphire { "onActionExecute", TimepointCallbackType::OnActionExecute }, }; - const static std::map< std::string, ConditionId > conditionIdMap = + 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::MoveTo: + { + auto dataJ = json.at( "data" ); + auto posJ = dataJ.at( "pos" ); + auto x = posJ.at( "x" ).get< float >(); + auto y = posJ.at( "y" ).get< float >(); + auto z = posJ.at( "z" ).get< float >(); + auto rot = dataJ.at( "rot" ).get< float >(); + auto pathReq = dataJ.at( "pathRequested" ).get< bool >() ? MoveType::WalkPath : MoveType::Teleport; + auto actorId = dataJ.at( "actorId" ).get< uint32_t >(); + + m_pData = std::make_shared< TimepointDataMoveTo >( actorId, pathReq, x, y, z, rot ); + } + break; + default: + break; + } + } + EncounterTimeline::EncounterTimelineInfo EncounterTimeline::buildEncounterTimeline( uint32_t encounterId, bool reload ) + { + static std::map< uint32_t, EncounterTimelineInfo > cache = {}; + const static std::unordered_map< std::string, ConditionId > conditionIdMap = { { "hpPctLessThan", ConditionId::HpPctLessThan }, { "hpPctBetween", ConditionId::HpPctBetween }, @@ -228,7 +310,7 @@ namespace Sapphire auto json = nlohmann::json::parse( f ); - std::map< std::string, PhasePtr > phaseNameMap; + std::map< std::string, Phase > phaseNameMap; for( const auto& phaseJ : json.at( "phases" ).items() ) @@ -236,21 +318,22 @@ namespace Sapphire auto phaseV = phaseJ.value(); const auto id = phaseV.at( "id" ).get< uint32_t >(); const auto& phaseName = phaseV.at( "name" ).get< std::string >(); - const auto& timepoints = phaseV.at( "timepoints" ); + const auto& timepointsJ = phaseV.at( "timepoints" ); - PhasePtr pPhase = std::make_shared< Phase >(); - - for( const auto& timepoint : timepoints.items() ) + Phase phase; + for( const auto& timepointJ : timepointsJ.items() ) { - + auto timepointV = timepointJ.value(); + Timepoint timepoint; + timepoint.from_json( timepointV ); - + phase.m_timepoints.push( timepoint ); } if( phaseNameMap.find( phaseName ) != phaseNameMap.end() ) throw std::runtime_error( fmt::format( std::string( "EncounterTimeline::buildEncounterTimeline - duplicate phase by name: %s" ), phaseName ) ); - phaseNameMap.emplace( std::make_pair( phaseName, pPhase ) ); + phaseNameMap.emplace( std::make_pair( phaseName, phase ) ); } for( const auto& pcJ : json.at( "phaseConditions" ).items() ) { @@ -260,9 +343,8 @@ namespace Sapphire auto loop = pcV.at( "loop" ).get< bool >(); auto phaseRef = pcV.at( "phase" ).get< std::string >(); - PhasePtr pPhase; ConditionId conditionId; - + // make sure condition exists if( auto it = conditionIdMap.find( conditionName ); it != conditionIdMap.end() ) conditionId = it->second; @@ -270,33 +352,37 @@ namespace Sapphire throw std::runtime_error( fmt::format( std::string( "EncounterTimeline::buildEncounterTimeline - no condition id found by name: %s" ), conditionName ) ); // make sure phase we're referencing exists - if( auto it = phaseNameMap.find( phaseRef ); it != phaseNameMap.end() ) - pPhase = it->second; - else - throw std::runtime_error( fmt::format( std::string( "EncounterTimeline::buildEncounterTimeline - no state found by name: %s" ), phaseRef ) ); - - // build the condition - TimepointConditionPtr pCondition; - switch( conditionId ) + if( auto phaseIt = phaseNameMap.find( phaseRef ); phaseIt != phaseNameMap.end() ) { - case ConditionId::HpPctLessThan: - case ConditionId::HpPctBetween: + Phase& phase = phaseIt->second; + + // build the condition + TimepointConditionPtr pCondition; + switch( conditionId ) { - auto pHpCondition = std::make_shared< ConditionHp >(); - pHpCondition->from_json( pcV, pPhase, conditionId ); - } - break; - case ConditionId::DirectorVarEquals: - case ConditionId::DirectorVarGreaterThan: - { - auto pDirectorCondition = std::make_shared< ConditionDirectorVar >(); - pDirectorCondition->from_json( pcV, pPhase, conditionId ); - } - break; - default: + case ConditionId::HpPctLessThan: + case ConditionId::HpPctBetween: + { + auto pHpCondition = std::make_shared< ConditionHp >(); + pHpCondition->from_json( pcV, phase, conditionId ); + } break; + case ConditionId::DirectorVarEquals: + case ConditionId::DirectorVarGreaterThan: + { + auto pDirectorCondition = std::make_shared< ConditionDirectorVar >(); + pDirectorCondition->from_json( pcV, phase, conditionId ); + } + break; + default: + break; + } + info.push( pCondition ); + } + else + { + throw std::runtime_error( fmt::format( std::string( "EncounterTimeline::buildEncounterTimeline - no state found by name: %s" ), phaseRef ) ); } - info.push( pCondition ); } if( reload ) cache[ encounterId ] = info; diff --git a/src/world/Encounter/EncounterTimeline.h b/src/world/Encounter/EncounterTimeline.h index 01fa8c70..2836f22a 100644 --- a/src/world/Encounter/EncounterTimeline.h +++ b/src/world/Encounter/EncounterTimeline.h @@ -22,6 +22,10 @@ namespace Sapphire HpPctBetween, DirectorVarEquals, DirectorVarGreaterThan, + DirectorSeqEquals, + DirectorSeqGreaterThan, + DirectorFlagsEquals, + DirectorFlagsGreaterThan, PhaseTimeElapsed, EncounterTimeElapsed }; @@ -64,7 +68,7 @@ namespace Sapphire OnActionExecute }; - enum class TargetSelectFilterIds : uint32_t + enum class TargetSelectFilterId : uint32_t { Self, Tank, @@ -86,7 +90,7 @@ namespace Sapphire struct TargetSelectFilter { - TargetSelectFilterIds m_flags; + TargetSelectFilterId m_flags; }; @@ -105,9 +109,9 @@ namespace Sapphire struct TimepointData : public std::enable_shared_from_this< TimepointData > { - TimepointData( TimepointDataType) {} - virtual ~TimepointData() = 0; - TimepointDataType m_type; + TimepointData( TimepointDataType type ) : m_type( type ) {} + virtual ~TimepointData(){}; + TimepointDataType m_type{ 0 }; }; using TimepointDataPtr = std::shared_ptr< TimepointData >; @@ -115,13 +119,41 @@ namespace Sapphire { uint32_t m_actorId; uint64_t m_durationMs; + + TimepointDataIdle( uint32_t actorId, uint64_t durationMs ) : + TimepointData( TimepointDataType::Idle ), + m_actorId( actorId ), + m_durationMs( durationMs ) + { + } }; - struct TimepointDataStatusEffect : public TimepointData + 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 @@ -129,6 +161,14 @@ namespace Sapphire uint32_t m_actorId; uint32_t m_actionId; TimepointCallbacks m_callbacks; + + TimepointDataAction( uint32_t actorId, uint32_t actionId, TimepointCallbacks callbacks ) : + TimepointData( TimepointDataType::CastAction ), + m_actorId( actorId ), + m_actionId( actionId ), + m_callbacks( callbacks ) + { + } }; struct TimepointDataMoveTo : public TimepointData @@ -136,14 +176,29 @@ namespace Sapphire uint32_t m_actorId; MoveType m_moveType; float m_x, m_y, m_z, m_rot; + + TimepointDataMoveTo( uint32_t actorId, MoveType moveType, float x, float y, float z, float rot ) : + TimepointData( TimepointDataType::MoveTo ), + m_actorId( actorId ), + m_moveType( moveType ), + m_x( x ), m_y( y ), m_z( z ), m_rot( rot ) + { + } }; struct TimepointDataLogMessage : public TimepointData { - uint32_t m_logMessageType; - uint32_t m_logMessageId; - std::string m_message; + uint32_t m_messageId; + uint32_t m_params[ 6 ]{ 0 }; + + TimepointDataLogMessage( uint32_t messageId, std::vector< uint32_t > params ) : + TimepointData( TimepointDataType::LogMessage ), + m_messageId( messageId ) + { + for( auto i = 0; i < params.size(); ++i ) + m_params[i] = params[i]; + } }; struct TimepointDataDirector : public TimepointData @@ -166,6 +221,7 @@ namespace Sapphire uint8_t seq; uint8_t flags; } m_data; + }; class Timepoint : @@ -181,6 +237,11 @@ namespace Sapphire // todo: repeatable? + const TimepointDataPtr getData() const + { + return m_pData; + } + bool canExecute() { return m_executeTime == 0; @@ -191,9 +252,9 @@ namespace Sapphire return m_executeTime + m_duration <= time; } + void from_json( const nlohmann::json& json ); void execute( EncounterFightPtr pFight, uint64_t time ); }; - using TimepointPtr = std::shared_ptr< Timepoint >; class Phase : public std::enable_shared_from_this< Phase > @@ -203,11 +264,11 @@ namespace Sapphire // todo: respect looping phases, allow callbacks to push timepoints std::string m_name; - std::queue< TimepointPtr > m_timepoints; + std::queue< Timepoint > m_timepoints; uint64_t m_startTime{ 0 }; uint64_t m_lastTimepoint{ 0 }; - std::queue< TimepointPtr > m_executed; + std::queue< Timepoint > m_executed; // todo: i wrote this very sleep deprived, ensure it is actually sane void execute( EncounterFightPtr pFight, uint64_t time ) @@ -223,17 +284,17 @@ namespace Sapphire uint64_t phaseElapsed = time - m_startTime; uint64_t timepointElapsed = time - m_lastTimepoint; - auto& pTimepoint = m_timepoints.front(); - if( pTimepoint->canExecute() ) + auto& timepoint = m_timepoints.front(); + if( timepoint.canExecute() ) { - pTimepoint->execute( pFight, time ); + timepoint.execute( pFight, time ); m_lastTimepoint = time; - m_executed.push( pTimepoint ); + m_executed.push( timepoint ); } - else if( pTimepoint->finished( timepointElapsed ) ) + else if( timepoint.finished( timepointElapsed ) ) { // todo: this is stupid, temp workaround for allowing phases to loop - pTimepoint->m_executeTime = 0; + timepoint.m_executeTime = 0; m_timepoints.pop(); } else @@ -242,6 +303,11 @@ namespace Sapphire } } } + + bool completed() + { + return m_timepoints.size() == 0; + } }; using PhasePtr = std::shared_ptr< Phase >; @@ -250,7 +316,7 @@ namespace Sapphire { public: ConditionId m_conditionId{ 0 }; - PhasePtr m_pPhase{ nullptr }; + Phase m_phase; bool m_loop{ false }; uint64_t m_startTime{ 0 }; uint32_t m_cooldown{ 0 }; @@ -258,20 +324,30 @@ namespace Sapphire TimepointCondition() {} ~TimepointCondition() {} - virtual void from_json( nlohmann::json& json, PhasePtr pPhase, ConditionId conditionId ) + virtual void from_json( nlohmann::json& json, Phase phase, ConditionId conditionId ) { this->m_conditionId = conditionId; this->m_loop = json.at( "loop" ).get< bool >(); this->m_cooldown = json.at( "cooldown" ).get< uint32_t >(); - this->m_pPhase = pPhase; + this->m_phase = phase; } void execute( EncounterFightPtr pFight, uint64_t time ) { m_startTime = time; - m_pPhase->execute( pFight, time ); + m_phase.execute( pFight, time ); }; + bool completed() + { + return m_phase.completed(); + } + + bool canLoop() + { + return m_phase.completed() && m_loop; + } + virtual bool canExecute( EncounterFightPtr pFight, uint64_t time ) { return false; @@ -293,17 +369,26 @@ namespace Sapphire }; } hp; - void from_json( nlohmann::json& json, PhasePtr pPhase, ConditionId conditionId ); + void from_json( nlohmann::json& json, Phase phase, ConditionId conditionId ); bool canExecute( EncounterFightPtr pFight, uint64_t time ) override; }; class ConditionDirectorVar : TimepointCondition { public: - uint32_t directorVar; - uint32_t value; - void from_json( nlohmann::json& json, PhasePtr pPhase, ConditionId conditionId ); + union + { + struct + { + uint32_t var; + uint32_t value; + }; + uint8_t seq; + uint8_t flag; + } param; + + void from_json( nlohmann::json& json, Phase phase, ConditionId conditionId ); bool canExecute( EncounterFightPtr pFight, uint64_t time ) override; };