From 75a2fcbb63cc839c86d231ece743a5fbde694e66 Mon Sep 17 00:00:00 2001 From: Tahir Date: Mon, 24 Jun 2024 22:45:06 +0100 Subject: [PATCH] reference conditions globally in timeline - add PhaseActive and GetAction conditions - refactor ifrit to use controller --- data/EncounterTimelines/IfritNormal.json | 804 +++++++++++++--------- src/world/Encounter/EncounterTimeline.cpp | 43 +- src/world/Encounter/EncounterTimeline.h | 6 + src/world/Encounter/PhaseCondition.cpp | 52 +- src/world/Encounter/PhaseCondition.h | 30 +- src/world/Encounter/TimelineActor.cpp | 36 +- src/world/Encounter/TimelineActor.h | 12 +- src/world/Encounter/Timepoint.cpp | 37 +- src/world/Encounter/Timepoint.h | 16 +- 9 files changed, 693 insertions(+), 343 deletions(-) diff --git a/data/EncounterTimelines/IfritNormal.json b/data/EncounterTimelines/IfritNormal.json index 9c9bf713..f78401f8 100644 --- a/data/EncounterTimelines/IfritNormal.json +++ b/data/EncounterTimelines/IfritNormal.json @@ -132,29 +132,6 @@ "duration": 8000, "type": "castAction" }, - { - "data": { - "selectorName": "Eruption", - "sourceActor": "Ifrit" - }, - "description": "", - "duration": 0, - "type": "snapshot" - }, - { - "data": { - "actionId": 733, - "selectorIndex": 0, - "selectorName": "Eruption", - "snapshot": false, - "snapshotTime": 0, - "sourceActor": "Ifrit ", - "targetType": "selector" - }, - "description": "Eruption", - "duration": 8000, - "type": "castAction" - }, { "data": { "actionId": 454, @@ -217,11 +194,21 @@ "snapshot": false, "snapshotTime": 0, "sourceActor": "Ifrit", - "targetType": "selector" + "targetType": "self" }, "description": "Eruption", - "duration": 8000, + "duration": 0, "type": "castAction" + }, + { + "data": { + "conditionId": 11, + "conditionStr": "If Ifrit casts Action#455, loop Ifrit Control->Eruption", + "enabled": true + }, + "description": "", + "duration": 8000, + "type": "setCondition" } ] }, @@ -291,6 +278,14 @@ "id": 7, "name": "Hellfire", "timepoints": [ + { + "data": { + "despawnActor": "Ifrit Nail 1" + }, + "description": "Despawn nail if up", + "duration": 0, + "type": "bNpcDespawn" + }, { "data": { "conditionId": 7, @@ -388,6 +383,20 @@ "id": 6, "name": "Final Phase", "timepoints": [ + { + "data": { + "actionId": 455, + "selectorIndex": 0, + "selectorName": "", + "snapshot": false, + "snapshotTime": 0, + "sourceActor": "Ifrit", + "targetType": "self" + }, + "description": "Eruption telegraph", + "duration": 8000, + "type": "castAction" + }, { "data": { "actionId": 453, @@ -404,306 +413,27 @@ }, { "data": { - "selectorName": "Eruption", - "sourceActor": "Ifrit" - }, - "description": "Eruption snapshot", - "duration": 0, - "type": "snapshot" - }, - { - "data": { - "actionId": 733, - "selectorIndex": 0, - "selectorName": "Eruption", - "snapshot": false, - "snapshotTime": 0, - "sourceActor": "Ifrit ", - "targetType": "selector" - }, - "description": "Subactor 1 Eruption", - "duration": 8000, - "type": "castAction" - }, - { - "data": { - "actorName": "Ifrit ", - "pos": [ - -5, - 0, - 5 - ], - "rot": 0 - }, - "description": "Move plumes", - "duration": 0, - "type": "setPos" - }, - { - "data": { - "actorName": "Ifrit ", - "pos": [ - -5, - 0, - -5 - ], - "rot": 0 - }, - "description": "", - "duration": 0, - "type": "setPos" - }, - { - "data": { - "actorName": "Ifrit ", - "pos": [ - 5, - 0, - 5 - ], - "rot": 0 - }, - "description": "", - "duration": 0, - "type": "setPos" - }, - { - "data": { - "actorName": "Ifrit ", - "pos": [ - 5, - 0, - -5 - ], - "rot": 0 - }, - "description": "", - "duration": 8000, - "type": "setPos" - }, - { - "data": { - "actionId": 734, + "actionId": 456, "selectorIndex": 0, "selectorName": "", "snapshot": false, "snapshotTime": 0, - "sourceActor": "Ifrit ", + "sourceActor": "Ifrit", "targetType": "self" }, - "description": "Cast plumes", + "description": "Plumes telegraph", "duration": 0, "type": "castAction" }, { "data": { - "actionId": 734, - "selectorIndex": 0, - "selectorName": "", - "snapshot": false, - "snapshotTime": 0, - "sourceActor": "Ifrit ", - "targetType": "self" + "conditionId": 12, + "conditionStr": "If Ifrit->Final Phase is active, push Ifrit Control->Plumes Loop", + "enabled": true }, "description": "", - "duration": 0, - "type": "castAction" - }, - { - "data": { - "actionId": 734, - "selectorIndex": 0, - "selectorName": "", - "snapshot": false, - "snapshotTime": 0, - "sourceActor": "Ifrit ", - "targetType": "self" - }, - "description": "", - "duration": 0, - "type": "castAction" - }, - { - "data": { - "actionId": 734, - "selectorIndex": 0, - "selectorName": "", - "snapshot": false, - "snapshotTime": 0, - "sourceActor": "Ifrit ", - "targetType": "self" - }, - "description": "", - "duration": 5000, - "type": "castAction" - }, - { - "data": { - "actorName": "Ifrit ", - "pos": [ - 0, - 0, - -20 - ], - "rot": 0 - }, - "description": "Move plumes out", - "duration": 0, - "type": "setPos" - }, - { - "data": { - "actorName": "Ifrit ", - "pos": [ - 0, - 0, - 20 - ], - "rot": 0 - }, - "description": "", - "duration": 0, - "type": "setPos" - }, - { - "data": { - "actorName": "Ifrit ", - "pos": [ - -20, - 0, - 0 - ], - "rot": 0 - }, - "description": "", - "duration": 0, - "type": "setPos" - }, - { - "data": { - "actorName": "Ifrit ", - "pos": [ - -20, - 0, - 20 - ], - "rot": 0 - }, - "description": "", - "duration": 0, - "type": "setPos" - }, - { - "data": { - "actorName": "Ifrit ", - "pos": [ - -15, - 0, - 15 - ], - "rot": 0 - }, - "description": "", - "duration": 0, - "type": "setPos" - }, - { - "data": { - "actorName": "Ifrit ", - "pos": [ - -15, - 0, - -15 - ], - "rot": 0 - }, - "description": "", - "duration": 0, - "type": "setPos" - }, - { - "data": { - "actorName": "Ifrit ", - "pos": [ - 15, - 0, - -15 - ], - "rot": 0 - }, - "description": "", - "duration": 0, - "type": "setPos" - }, - { - "data": { - "actorName": "Ifrit ", - "pos": [ - 15, - 0, - 15 - ], - "rot": 0 - }, - "description": "", - "duration": 0, - "type": "setPos" - }, - { - "data": { - "actionId": 734, - "selectorIndex": 0, - "selectorName": "", - "snapshot": false, - "snapshotTime": 0, - "sourceActor": "Ifrit ", - "targetType": "self" - }, - "description": "Cast plumes out", - "duration": 0, - "type": "castAction" - }, - { - "data": { - "actionId": 734, - "selectorIndex": 0, - "selectorName": "", - "snapshot": false, - "snapshotTime": 0, - "sourceActor": "Ifrit ", - "targetType": "self" - }, - "description": "", - "duration": 0, - "type": "castAction" - }, - { - "data": { - "actionId": 734, - "selectorIndex": 0, - "selectorName": "", - "snapshot": false, - "snapshotTime": 0, - "sourceActor": "Ifrit ", - "targetType": "self" - }, - "description": "", - "duration": 0, - "type": "castAction" - }, - { - "data": { - "actionId": 734, - "selectorIndex": 0, - "selectorName": "", - "snapshot": false, - "snapshotTime": 0, - "sourceActor": "Ifrit ", - "targetType": "self" - }, - "description": "", - "duration": 5000, - "type": "castAction" + "duration": 16000, + "type": "setCondition" } ] }, @@ -730,16 +460,7 @@ ] } ], - "subactors": [ - "Ifrit ", - "Ifrit ", - "Ifrit ", - "Ifrit ", - "Ifrit ", - "Ifrit ", - "Ifrit ", - "Ifrit " - ], + "subactors": [], "type": "bnpc" }, { @@ -747,8 +468,404 @@ "id": 2, "layoutId": 4126284, "name": "Ifrit Control", - "phases": [], - "subactors": [], + "phases": [ + { + "description": "", + "id": 1, + "name": "Eruption", + "timepoints": [ + { + "data": { + "selectorName": "Eruption", + "sourceActor": "Ifrit Control " + }, + "description": "", + "duration": 0, + "type": "snapshot" + }, + { + "data": { + "actionId": 733, + "selectorIndex": 0, + "selectorName": "Eruption", + "snapshot": false, + "snapshotTime": 0, + "sourceActor": "Ifrit Control ", + "targetType": "selector" + }, + "description": "Subactor 1 Eruption", + "duration": 8000, + "type": "castAction" + } + ] + }, + { + "description": "", + "id": 2, + "name": "Plumes Loop", + "timepoints": [ + { + "data": { + "actorName": "Ifrit Control ", + "pos": [ + -10, + 0, + 10 + ], + "rot": 0 + }, + "description": "Move plumes", + "duration": 0, + "type": "setPos" + }, + { + "data": { + "actorName": "Ifrit Control ", + "pos": [ + -10, + 0, + -10 + ], + "rot": 0 + }, + "description": "", + "duration": 0, + "type": "setPos" + }, + { + "data": { + "actorName": "Ifrit Control ", + "pos": [ + 10, + 0, + 10 + ], + "rot": 0 + }, + "description": "", + "duration": 0, + "type": "setPos" + }, + { + "data": { + "actorName": "Ifrit Control ", + "pos": [ + 10, + 0, + -10 + ], + "rot": 0 + }, + "description": "", + "duration": 0, + "type": "setPos" + }, + { + "data": { + "actionId": 734, + "selectorIndex": 0, + "selectorName": "", + "snapshot": false, + "snapshotTime": 0, + "sourceActor": "Ifrit Control ", + "targetType": "self" + }, + "description": "Cast plumes", + "duration": 0, + "type": "castAction" + }, + { + "data": { + "actionId": 734, + "selectorIndex": 0, + "selectorName": "", + "snapshot": false, + "snapshotTime": 0, + "sourceActor": "Ifrit Control ", + "targetType": "self" + }, + "description": "", + "duration": 0, + "type": "castAction" + }, + { + "data": { + "actionId": 734, + "selectorIndex": 0, + "selectorName": "", + "snapshot": false, + "snapshotTime": 0, + "sourceActor": "Ifrit Control ", + "targetType": "self" + }, + "description": "", + "duration": 0, + "type": "castAction" + }, + { + "data": { + "actionId": 734, + "selectorIndex": 0, + "selectorName": "", + "snapshot": false, + "snapshotTime": 0, + "sourceActor": "Ifrit Control ", + "targetType": "self" + }, + "description": "", + "duration": 4000, + "type": "castAction" + }, + { + "data": { + "actorName": "Ifrit Control ", + "pos": [ + 0, + 0, + -20 + ], + "rot": 0 + }, + "description": "Move plumes out", + "duration": 0, + "type": "setPos" + }, + { + "data": { + "actorName": "Ifrit Control ", + "pos": [ + 0, + 0, + 20 + ], + "rot": 0 + }, + "description": "", + "duration": 0, + "type": "setPos" + }, + { + "data": { + "actorName": "Ifrit Control ", + "pos": [ + -20, + 0, + 0 + ], + "rot": 0 + }, + "description": "", + "duration": 0, + "type": "setPos" + }, + { + "data": { + "actorName": "Ifrit Control ", + "pos": [ + -20, + 0, + 20 + ], + "rot": 0 + }, + "description": "", + "duration": 0, + "type": "setPos" + }, + { + "data": { + "actorName": "Ifrit Control ", + "pos": [ + -15, + 0, + 15 + ], + "rot": 0 + }, + "description": "", + "duration": 0, + "type": "setPos" + }, + { + "data": { + "actorName": "Ifrit Control ", + "pos": [ + -15, + 0, + -15 + ], + "rot": 0 + }, + "description": "", + "duration": 0, + "type": "setPos" + }, + { + "data": { + "actorName": "Ifrit Control ", + "pos": [ + 15, + 0, + -15 + ], + "rot": 0 + }, + "description": "", + "duration": 0, + "type": "setPos" + }, + { + "data": { + "actorName": "Ifrit Control ", + "pos": [ + 15, + 0, + 15 + ], + "rot": 0 + }, + "description": "", + "duration": 200, + "type": "setPos" + }, + { + "data": { + "actionId": 734, + "selectorIndex": 0, + "selectorName": "", + "snapshot": false, + "snapshotTime": 0, + "sourceActor": "Ifrit Control ", + "targetType": "self" + }, + "description": "Cast plumes out", + "duration": 0, + "type": "castAction" + }, + { + "data": { + "actionId": 734, + "selectorIndex": 0, + "selectorName": "", + "snapshot": false, + "snapshotTime": 0, + "sourceActor": "Ifrit Control ", + "targetType": "self" + }, + "description": "", + "duration": 0, + "type": "castAction" + }, + { + "data": { + "actionId": 734, + "selectorIndex": 0, + "selectorName": "", + "snapshot": false, + "snapshotTime": 0, + "sourceActor": "Ifrit Control ", + "targetType": "self" + }, + "description": "", + "duration": 0, + "type": "castAction" + }, + { + "data": { + "actionId": 734, + "selectorIndex": 0, + "selectorName": "", + "snapshot": false, + "snapshotTime": 0, + "sourceActor": "Ifrit Control ", + "targetType": "self" + }, + "description": "", + "duration": 0, + "type": "castAction" + }, + { + "data": { + "actionId": 734, + "selectorIndex": 0, + "selectorName": "", + "snapshot": false, + "snapshotTime": 0, + "sourceActor": "Ifrit Control ", + "targetType": "self" + }, + "description": "", + "duration": 0, + "type": "castAction" + }, + { + "data": { + "actionId": 734, + "selectorIndex": 0, + "selectorName": "", + "snapshot": false, + "snapshotTime": 0, + "sourceActor": "Ifrit Control ", + "targetType": "self" + }, + "description": "", + "duration": 0, + "type": "castAction" + }, + { + "data": { + "actionId": 734, + "selectorIndex": 0, + "selectorName": "", + "snapshot": false, + "snapshotTime": 0, + "sourceActor": "Ifrit Control ", + "targetType": "self" + }, + "description": "", + "duration": 0, + "type": "castAction" + }, + { + "data": { + "actionId": 734, + "selectorIndex": 0, + "selectorName": "", + "snapshot": false, + "snapshotTime": 0, + "sourceActor": "Ifrit Control ", + "targetType": "self" + }, + "description": "", + "duration": 0, + "type": "castAction" + } + ] + }, + { + "description": "", + "id": 3, + "name": "Setup", + "timepoints": [ + { + "data": {}, + "description": "", + "duration": 5000, + "type": "idle" + } + ] + } + ], + "subactors": [ + "Ifrit Control ", + "Ifrit Control ", + "Ifrit Control ", + "Ifrit Control ", + "Ifrit Control ", + "Ifrit Control ", + "Ifrit Control ", + "Ifrit Control " + ], "type": "bnpc" }, { @@ -902,6 +1019,45 @@ }, "targetActor": "Ifrit", "targetPhase": "Final Phase" + }, + { + "condition": "getAction", + "description": "", + "enabled": true, + "id": 11, + "loop": true, + "paramData": { + "actionId": 455, + "sourceActor": "Ifrit" + }, + "targetActor": "Ifrit Control", + "targetPhase": "Eruption" + }, + { + "condition": "phaseActive", + "description": "", + "enabled": false, + "id": 12, + "loop": false, + "paramData": { + "phaseName": "Final Phase", + "sourceActor": "Ifrit" + }, + "targetActor": "Ifrit Control", + "targetPhase": "Plumes Loop" + }, + { + "condition": "combatState", + "description": "", + "enabled": true, + "id": 13, + "loop": false, + "paramData": { + "combatState": 1, + "sourceActor": "Ifrit" + }, + "targetActor": "Ifrit Control", + "targetPhase": "Setup" } ], "name": "Brand new timeline", diff --git a/src/world/Encounter/EncounterTimeline.cpp b/src/world/Encounter/EncounterTimeline.cpp index 2fb63564..2399c8fd 100644 --- a/src/world/Encounter/EncounterTimeline.cpp +++ b/src/world/Encounter/EncounterTimeline.cpp @@ -115,7 +115,10 @@ namespace Sapphire::Encounter { "encounterTimeElapsed", ConditionType::EncounterTimeElapsed }, { "combatState", ConditionType::CombatState }, - { "bnpcHasFlags", ConditionType::BNpcHasFlags } + { "bnpcHasFlags", ConditionType::BNpcHasFlags }, + + { "getAction", ConditionType::GetAction }, + { "phaseActive", ConditionType::PhaseActive } }; TimelinePack pack; @@ -263,6 +266,18 @@ namespace Sapphire::Encounter pCondition->from_json( pcV, phase, condition, actorNameMap ); } break; + case ConditionType::GetAction: + { + pCondition = std::make_shared< ConditionGetAction >(); + pCondition->from_json( pcV, phase, condition, actorNameMap ); + } + break; + case ConditionType::PhaseActive: + { + pCondition = std::make_shared< ConditionPhaseActive >(); + pCondition->from_json( pcV, phase, condition, actorNameMap ); + } + break; default: break; } @@ -368,6 +383,32 @@ namespace Sapphire::Encounter actor.spawnAllSubActors( pTeri ); } + bool TimelinePack::isPhaseActive( const std::string& actorName, const std::string& phaseName ) + { + for( const auto& actor : m_actors ) + if( actor.getName() == actorName ) + return actor.isPhaseActive( phaseName ); + return false; + } + + void TimelinePack::resetConditionState( uint32_t id, bool toDefault ) + { + for( auto& actor : m_actors ) + { + if( actor.resetConditionState( id, toDefault ) ) + return; + } + } + + void TimelinePack::setConditionStateEnabled( uint32_t id, bool enabled ) + { + for( auto& actor : m_actors ) + { + if( actor.setConditionStateEnabled( id, enabled ) ) + return; + } + } + bool TimelinePack::valid() { return !m_actors.empty(); diff --git a/src/world/Encounter/EncounterTimeline.h b/src/world/Encounter/EncounterTimeline.h index b302787e..3ef8e032 100644 --- a/src/world/Encounter/EncounterTimeline.h +++ b/src/world/Encounter/EncounterTimeline.h @@ -82,6 +82,12 @@ namespace Sapphire::Encounter // todo: probably just make this a Timepoint in an InitPhase void spawnSubActors( TerritoryPtr pTeri ); + bool isPhaseActive( const std::string& actorRef, const std::string& phaseName ); + + void resetConditionState( uint32_t id, bool toDefault = false ); + + void setConditionStateEnabled( uint32_t id, bool enabled ); + bool valid(); }; diff --git a/src/world/Encounter/PhaseCondition.cpp b/src/world/Encounter/PhaseCondition.cpp index 72d241a3..5a008008 100644 --- a/src/world/Encounter/PhaseCondition.cpp +++ b/src/world/Encounter/PhaseCondition.cpp @@ -4,6 +4,8 @@ #include "TimelineActor.h" #include "TimelineActorState.h" +#include + #include #include #include @@ -101,6 +103,17 @@ namespace Sapphire::Encounter return pBNpc && pBNpc->hasFlag( this->flags ); } + bool ConditionGetAction::isConditionMet( ConditionState& state, TimelinePack& pack, TerritoryPtr pTeri, uint64_t time ) const + { + auto pBNpc = pTeri->getActiveBNpcByLayoutId( this->layoutId ); + return pBNpc && pBNpc->getCurrentAction() && pBNpc->getCurrentAction()->getId() == this->actionId; + } + + bool ConditionPhaseActive::isConditionMet( ConditionState& state, TimelinePack& pack, TerritoryPtr pTeri, uint64_t time ) const + { + return pack.isPhaseActive( this->actorName, this->phaseName ); + } + void ConditionHp::from_json( nlohmann::json& json, Phase& phase, ConditionType condition, const std::unordered_map< std::string, TimelineActor >& actors ) { @@ -113,7 +126,7 @@ namespace Sapphire::Encounter 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 ) ); + throw std::runtime_error( fmt::format( std::string( "ConditionHp::from_json unable to find actor by name: %s" ), actorRef ) ); switch( condition ) { @@ -174,7 +187,7 @@ namespace Sapphire::Encounter 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 ) ); + throw std::runtime_error( fmt::format( std::string( "ConditionCombatState::from_json unable to find actor by name: %s" ), actorRef ) ); this->combatState = paramData.at( "combatState" ).get< CombatStateType >(); } @@ -201,12 +214,43 @@ namespace Sapphire::Encounter 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 ) ); + throw std::runtime_error( fmt::format( std::string( "ConditionBNpcFlags::from_json unable to find actor by name: %s" ), actorRef ) ); this->flags = json.at( "flags" ).get< uint32_t >(); // todo: BNpcHasFlags } + void ConditionGetAction::from_json( nlohmann::json& json, Phase& phase, ConditionType condition, + const std::unordered_map< std::string, TimelineActor >& actors ) + { + PhaseCondition::from_json( json, phase, condition, actors ); + + auto& paramData = json.at( "paramData" ); + auto actorRef = paramData.at( "sourceActor" ).get< std::string >(); + auto actionId = paramData.at( "actionId" ).get< uint32_t >(); + + // 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( "ConditionGetAction::from_json unable to find actor by name: %s" ), actorRef ) ); + + this->actionId = actionId; + } + + void ConditionPhaseActive::from_json( nlohmann::json& json, Phase& phase, ConditionType condition, + const std::unordered_map< std::string, TimelineActor >& actors ) + { + PhaseCondition::from_json( json, phase, condition, actors ); + + auto& paramData = json.at( "paramData" ); + auto actorRef = paramData.at( "sourceActor" ).get< std::string >(); + auto phaseName = paramData.at( "phaseName" ).get< std::string >(); + + this->actorName = actorRef; + this->phaseName = phaseName; + } + // todo: i wrote this very sleep deprived, ensure it is actually sane void Phase::execute( ConditionState& state, TimelineActor& self, TimelinePack& pack, TerritoryPtr pTeri, uint64_t time ) const @@ -274,4 +318,4 @@ namespace Sapphire::Encounter { return state.m_phaseInfo.m_lastTimepointIndex > m_timepoints.size(); } - }// namespace Sapphire::Encounter \ No newline at end of file +}// namespace Sapphire::Encounter \ No newline at end of file diff --git a/src/world/Encounter/PhaseCondition.h b/src/world/Encounter/PhaseCondition.h index 5a6b5921..2d684e31 100644 --- a/src/world/Encounter/PhaseCondition.h +++ b/src/world/Encounter/PhaseCondition.h @@ -32,7 +32,10 @@ namespace Sapphire::Encounter EncounterTimeElapsed, CombatState, - BNpcHasFlags + BNpcHasFlags, + + GetAction, + PhaseActive }; class Phase : public std::enable_shared_from_this< Phase > @@ -77,6 +80,11 @@ namespace Sapphire::Encounter this->m_id = json.at( "id" ).get< uint32_t >(); } + const std::string& getPhaseName() + { + return m_phase.m_name; + } + void execute( ConditionState& state, TimelineActor& self, TimelinePack& pack, TerritoryPtr pTeri, uint64_t time ) const { m_phase.execute( state, self, pack, pTeri, time ); @@ -213,4 +221,24 @@ namespace Sapphire::Encounter bool isConditionMet( ConditionState& state, TimelinePack& pack, TerritoryPtr pTeri, uint64_t time ) const override; }; + class ConditionGetAction : public PhaseCondition + { + public: + uint32_t layoutId; + uint32_t actionId; + + void from_json( nlohmann::json& json, Phase& phase, ConditionType condition, const std::unordered_map< std::string, TimelineActor >& actors ) override; + bool isConditionMet( ConditionState& state, TimelinePack& pack, TerritoryPtr pTeri, uint64_t time ) const override; + }; + + class ConditionPhaseActive : public PhaseCondition + { + public: + std::string actorName; + std::string phaseName; + + void from_json( nlohmann::json& json, Phase& phase, ConditionType condition, const std::unordered_map< std::string, TimelineActor >& actors ) override; + bool isConditionMet( ConditionState& state, TimelinePack& pack, TerritoryPtr pTeri, uint64_t time ) const override; + }; + }// namespace Sapphire::Encounter \ No newline at end of file diff --git a/src/world/Encounter/TimelineActor.cpp b/src/world/Encounter/TimelineActor.cpp index 5fcc2653..37e48aab 100644 --- a/src/world/Encounter/TimelineActor.cpp +++ b/src/world/Encounter/TimelineActor.cpp @@ -12,6 +12,28 @@ namespace Sapphire::Encounter { + const std::string& TimelineActor::getName() const + { + return m_name; + } + + uint32_t TimelineActor::getLayoutId() const + { + return m_layoutId; + } + + bool TimelineActor::isPhaseActive( const std::string& name ) const + { + for( const auto& condition : m_phaseConditions ) + { + const auto& pCondition = condition.second; + const auto& state = m_conditionStates.at( condition.first ); + if( pCondition->inProgress( state ) && pCondition->getPhaseName() == name ) + return true; + } + return false; + } + void TimelineActor::addPhaseCondition( PhaseConditionPtr pCondition ) { m_phaseConditions.emplace( std::make_pair( pCondition->getId(), pCondition ) ); @@ -59,22 +81,26 @@ namespace Sapphire::Encounter } } - void TimelineActor::resetConditionState( uint32_t conditionId ) + bool TimelineActor::resetConditionState( uint32_t conditionId, bool toDefault ) { if( auto it = m_phaseConditions.find( conditionId ); it != m_phaseConditions.end() ) { auto& state = m_conditionStates.at( it->first ); - it->second->reset( state ); + it->second->reset( state, toDefault ); + return true; } + return false; } - void TimelineActor::setConditionStateEnabled( uint32_t conditionId, bool enabled ) + bool 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; + return true; } + return false; } void TimelineActor::resetAllConditionStates() @@ -149,7 +175,9 @@ namespace Sapphire::Encounter if( pActor == nullptr ) { auto pParent = pTeri->getActiveBNpcByLayoutId( m_layoutId ); - pActor = pTeri->createBNpcFromLayoutId( m_layoutId, 1000, pParent->getBNpcType() ); + Common::BNpcType type = pParent ? pParent->getBNpcType() : Common::BNpcType::Enemy; + + pActor = pTeri->createBNpcFromLayoutId( m_layoutId, 1000, type ); m_subActors[ name ] = pActor; pActor->setInvincibilityType( Common::InvincibilityIgnoreDamage ); diff --git a/src/world/Encounter/TimelineActor.h b/src/world/Encounter/TimelineActor.h index 23128e1a..5e0bfc87 100644 --- a/src/world/Encounter/TimelineActor.h +++ b/src/world/Encounter/TimelineActor.h @@ -40,17 +40,23 @@ namespace Sapphire::Encounter m_conditionStates = rhs.m_conditionStates; for( const auto& state : rhs.m_phaseConditions ) - state.second->reset( m_conditionStates[ state.first ] ); + state.second->reset( m_conditionStates[ state.first ], true ); } + const std::string& getName() const; + + uint32_t getLayoutId() const; + + bool isPhaseActive( const std::string& name ) const; + void addPhaseCondition( PhaseConditionPtr pCondition ); // todo: make this sane void update( TerritoryPtr pTeri, TimelinePack& pack, uint64_t time ); - void resetConditionState( uint32_t conditionId ); + bool resetConditionState( uint32_t conditionId, bool toDefault = false ); - void setConditionStateEnabled( uint32_t conditionId, bool enabled ); + bool setConditionStateEnabled( uint32_t conditionId, bool enabled ); void resetAllConditionStates(); diff --git a/src/world/Encounter/Timepoint.cpp b/src/world/Encounter/Timepoint.cpp index cf3e3768..d4245fd8 100644 --- a/src/world/Encounter/Timepoint.cpp +++ b/src/world/Encounter/Timepoint.cpp @@ -63,6 +63,7 @@ namespace Sapphire::Encounter { "directorSeq", TimepointDataType::DirectorSeq }, { "directorFlags", TimepointDataType::DirectorFlags }, + { "bNpcDespawn", TimepointDataType::BNpcDespawn }, { "bNpcSpawn", TimepointDataType::BNpcSpawn }, { "bNpcFlags", TimepointDataType::BNpcFlags }, { "setEObjState", TimepointDataType::SetEObjState }, @@ -246,7 +247,20 @@ namespace Sapphire::Encounter } break; + case TimepointDataType::BNpcDespawn: + { + auto& dataJ = json.at( "data" ); + auto actorRef = dataJ.at( "despawnActor" ).get< std::string >(); + 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( "Timepoint::from_json: SpawnBNpc invalid actor ref: {}" ), actorRef ) ); + + m_pData = std::make_shared< TimepointDataBNpcDespawn >( layoutId ); + } + break; case TimepointDataType::BNpcSpawn: { auto& dataJ = json.at( "data" ); @@ -265,7 +279,7 @@ namespace Sapphire::Encounter else throw std::runtime_error( fmt::format( std::string( "Timepoint::from_json: SpawnBNpc invalid actor ref: {}" ), actorRef ) ); - m_pData = std::make_shared< TimepointDataSpawnBNpc >( layoutId, flags, bnpcType ); + m_pData = std::make_shared< TimepointDataBNpcSpawn >( layoutId, flags, bnpcType ); } break; case TimepointDataType::BNpcFlags: @@ -409,6 +423,7 @@ namespace Sapphire::Encounter { pAction->setInterrupted( Common::ActionInterruptType::RegularInterrupt ); pAction->interrupt(); + return false; } else { @@ -610,9 +625,23 @@ namespace Sapphire::Encounter { } break; + case TimepointDataType::BNpcDespawn: + { + auto pDespawnData = std::dynamic_pointer_cast< TimepointDataBNpcDespawn, TimepointData >( m_pData ); + auto pBNpc = pTeri->getActiveBNpcByLayoutId( pDespawnData->m_layoutId ); + + if( pBNpc ) + { + for( const auto& player : pTeri->getPlayers() ) + pBNpc->despawn( player.second ); + + pTeri->removeActor( pBNpc ); + } + } + break; case TimepointDataType::BNpcSpawn: { - auto pSpawnData = std::dynamic_pointer_cast< TimepointDataSpawnBNpc, TimepointData >( m_pData ); + auto pSpawnData = std::dynamic_pointer_cast< TimepointDataBNpcSpawn, TimepointData >( m_pData ); auto pBNpc = pTeri->getActiveBNpcByLayoutId( pSpawnData->m_layoutId ); // todo: probably have this info in the timepoint data @@ -682,8 +711,8 @@ namespace Sapphire::Encounter auto pConditionData = std::dynamic_pointer_cast< TimepointDataCondition, TimepointData >( m_pData ); // todo: dont reset so things can resume? idk - self.resetConditionState( pConditionData->m_conditionId ); - self.setConditionStateEnabled( pConditionData->m_conditionId, pConditionData->m_enabled ); + pack.resetConditionState( pConditionData->m_conditionId ); + pack.setConditionStateEnabled( pConditionData->m_conditionId, pConditionData->m_enabled ); } break; case TimepointDataType::Snapshot: diff --git a/src/world/Encounter/Timepoint.h b/src/world/Encounter/Timepoint.h index 6fab8b97..e1aa726c 100644 --- a/src/world/Encounter/Timepoint.h +++ b/src/world/Encounter/Timepoint.h @@ -31,6 +31,7 @@ namespace Sapphire::Encounter RemoveStatusEffect, BNpcSpawn, + BNpcDespawn, BNpcFlags, SetEObjState, SetBgm, @@ -186,7 +187,18 @@ namespace Sapphire::Encounter } }; - struct TimepointDataSpawnBNpc : public TimepointData + struct TimepointDataBNpcDespawn : public TimepointData + { + uint32_t m_layoutId{ 0xE0000000 }; + + TimepointDataBNpcDespawn( uint32_t layoutId ) : + TimepointData( TimepointDataType::BNpcDespawn ), + m_layoutId( layoutId ) + { + } + }; + + struct TimepointDataBNpcSpawn : public TimepointData { uint32_t m_layoutId{ 0xE0000000 }; uint32_t m_flags{ 0 }; @@ -194,7 +206,7 @@ namespace Sapphire::Encounter // todo: hate type, source - TimepointDataSpawnBNpc( uint32_t layoutId, uint32_t flags, uint32_t type ) : + TimepointDataBNpcSpawn( uint32_t layoutId, uint32_t flags, uint32_t type ) : TimepointData( TimepointDataType::BNpcSpawn ), m_layoutId( layoutId ), m_flags( flags ),