mirror of
https://github.com/SapphireServer/Sapphire.git
synced 2025-05-07 11:17:46 +00:00
wip: encounter timeline parsing
- todo: parse the json into usable data objects
This commit is contained in:
parent
fd768eb18f
commit
9331fa3dd1
2 changed files with 499 additions and 0 deletions
275
src/world/Encounter/EncounterTimeline.cpp
Normal file
275
src/world/Encounter/EncounterTimeline.cpp
Normal file
|
@ -0,0 +1,275 @@
|
||||||
|
#include "EncounterFight.h"
|
||||||
|
#include "EncounterTimeline.h"
|
||||||
|
|
||||||
|
#include "../Actor/BNpc.h"
|
||||||
|
#include "../Actor/Chara.h"
|
||||||
|
|
||||||
|
namespace Sapphire
|
||||||
|
{
|
||||||
|
void EncounterTimeline::EncounterConditionHp::from_json( nlohmann::json& json, EncounterPhasePtr pPhase, EncounterConditionId conditionId )
|
||||||
|
{
|
||||||
|
EncounterTimepointCondition::from_json( json, pPhase, conditionId );
|
||||||
|
|
||||||
|
auto params = json.at( "params" ).get< std::vector< uint32_t > >();
|
||||||
|
|
||||||
|
this->actorId = params[ 0 ];
|
||||||
|
|
||||||
|
if( conditionId == EncounterConditionId::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 )
|
||||||
|
{
|
||||||
|
auto pBNpc = pFight->getBNpc( actorId );
|
||||||
|
if( !pBNpc )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// todo: check time elapsed
|
||||||
|
|
||||||
|
switch( m_conditionId )
|
||||||
|
{
|
||||||
|
case EncounterConditionId::HpPctLessThan:
|
||||||
|
return pBNpc->getHpPercent() < hp.val;
|
||||||
|
case EncounterConditionId::HpPctBetween:
|
||||||
|
{
|
||||||
|
auto hpPct = pBNpc->getHpPercent();
|
||||||
|
return hpPct >= hp.min && hpPct <= hp.max;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
void EncounterTimeline::EncounterConditionDirectorVar::from_json( nlohmann::json& json, EncounterPhasePtr pPhase, EncounterConditionId conditionId )
|
||||||
|
{
|
||||||
|
EncounterTimepointCondition::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::EncounterConditionDirectorVar::canExecute( EncounterFightPtr pFight, uint64_t time )
|
||||||
|
{
|
||||||
|
switch( m_conditionId )
|
||||||
|
{
|
||||||
|
case EncounterConditionId::DirectorVarEquals:
|
||||||
|
return false; // pFight->getDirectorVar( directorVar ) == value;
|
||||||
|
case EncounterConditionId::DirectorVarGreaterThan:
|
||||||
|
return false; // pFight->getDirectorVar( directorVar ) > value;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
EncounterTimeline::EncounterTimelineInfo EncounterTimeline::buildEncounterTimeline( uint32_t encounterId, bool reload )
|
||||||
|
{
|
||||||
|
static std::map< uint32_t, EncounterTimelineInfo > cache = {};
|
||||||
|
const static std::map< std::string, EncounterTimepointDataType > timepointTypeMap =
|
||||||
|
{
|
||||||
|
{ "idle", EncounterTimepointDataType::Idle },
|
||||||
|
{ "castAction", EncounterTimepointDataType::CastAction },
|
||||||
|
{ "moveTo", EncounterTimepointDataType::MoveTo },
|
||||||
|
{ "logMessage", EncounterTimepointDataType::LogMessage },
|
||||||
|
{ "setDirectorVar", EncounterTimepointDataType::SetDirectorVar },
|
||||||
|
{ "addStatusEffect", EncounterTimepointDataType::AddStatusEffect },
|
||||||
|
{ "removeStatusEffect", EncounterTimepointDataType::RemoveStatusEffect }
|
||||||
|
};
|
||||||
|
|
||||||
|
const static std::map< std::string, EncounterTimepointCallbackType > callbackTypeMap =
|
||||||
|
{
|
||||||
|
{ "onActionInit", EncounterTimepointCallbackType::OnActionInit },
|
||||||
|
{ "onActionStart", EncounterTimepointCallbackType::OnActionStart },
|
||||||
|
{ "onActionInterrupt", EncounterTimepointCallbackType::OnActionInterrupt },
|
||||||
|
{ "onActionExecute", EncounterTimepointCallbackType::OnActionExecute },
|
||||||
|
};
|
||||||
|
|
||||||
|
const static std::map< std::string, EncounterConditionId > conditionIdMap =
|
||||||
|
{
|
||||||
|
{ "hpPctLessThan", EncounterConditionId::HpPctLessThan },
|
||||||
|
{ "hpPctBetween", EncounterConditionId::HpPctBetween },
|
||||||
|
{ "directorVarEquals", EncounterConditionId::DirectorVarEquals },
|
||||||
|
{ "directorVarGreaterThan", EncounterConditionId::DirectorVarGreaterThan },
|
||||||
|
};
|
||||||
|
|
||||||
|
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
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Main encounter script
|
||||||
|
EncounterFight::update()
|
||||||
|
{
|
||||||
|
auto pStateCondition = m_stateConditions.pop();
|
||||||
|
|
||||||
|
switch( pStateCondition->getType() )
|
||||||
|
{
|
||||||
|
case EncounterTimepointConditionId::HpBetweenPct:
|
||||||
|
if (((HpPercentCondition*)pStateCondition)->isConditionMet( bossHpPct ) )
|
||||||
|
pStateCondition->execute( someDutyInstanceInfoHere );
|
||||||
|
else if( !pStateCondition->hasExecuted() || pStateCondition->canLoop() )
|
||||||
|
m_stateConditions.push( pStateCondition );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
std::string encounter_name( fmt::format( std::string( "data/EncounterTimelines/EncounterTimeline%u.json" ), encounterId ) );
|
||||||
|
|
||||||
|
std::fstream f( encounter_name );
|
||||||
|
|
||||||
|
if( !f.is_open() )
|
||||||
|
return {};
|
||||||
|
|
||||||
|
auto json = nlohmann::json::parse( f );
|
||||||
|
|
||||||
|
std::map< std::string, EncounterPhasePtr > phaseNameMap;
|
||||||
|
|
||||||
|
|
||||||
|
for( const auto& phaseJ : json.at( "phases" ).items() )
|
||||||
|
{
|
||||||
|
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" );
|
||||||
|
|
||||||
|
EncounterPhasePtr pPhase = std::make_shared< EncounterPhase >();
|
||||||
|
|
||||||
|
for( const auto& timepoint : timepoints.items() )
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
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 ) );
|
||||||
|
}
|
||||||
|
for( const auto& pcJ : json.at( "phaseConditions" ).items() )
|
||||||
|
{
|
||||||
|
auto pcV = pcJ.value();
|
||||||
|
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( "phase" ).get< std::string >();
|
||||||
|
|
||||||
|
EncounterPhasePtr pPhase;
|
||||||
|
EncounterConditionId conditionId;
|
||||||
|
|
||||||
|
// make sure condition exists
|
||||||
|
if( auto it = conditionIdMap.find( conditionName ); it != conditionIdMap.end() )
|
||||||
|
conditionId = it->second;
|
||||||
|
else
|
||||||
|
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
|
||||||
|
EncounterTimepointConditionPtr pCondition;
|
||||||
|
switch( conditionId )
|
||||||
|
{
|
||||||
|
case EncounterConditionId::HpPctLessThan:
|
||||||
|
case EncounterConditionId::HpPctBetween:
|
||||||
|
{
|
||||||
|
auto pHpCondition = std::make_shared< EncounterConditionHp >();
|
||||||
|
pHpCondition->from_json( pcV, pPhase, conditionId );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case EncounterConditionId::DirectorVarEquals:
|
||||||
|
case EncounterConditionId::DirectorVarGreaterThan:
|
||||||
|
{
|
||||||
|
auto pDirectorCondition = std::make_shared< EncounterConditionDirectorVar >();
|
||||||
|
pDirectorCondition->from_json( pcV, pPhase, conditionId );
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
info.push( pCondition );
|
||||||
|
}
|
||||||
|
if( reload )
|
||||||
|
cache[ encounterId ] = info;
|
||||||
|
else
|
||||||
|
cache.emplace( std::make_pair( encounterId, info ) );
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
}// namespace Sapphire
|
224
src/world/Encounter/EncounterTimeline.h
Normal file
224
src/world/Encounter/EncounterTimeline.h
Normal file
|
@ -0,0 +1,224 @@
|
||||||
|
#include <fstream>
|
||||||
|
#include <memory>
|
||||||
|
#include <map>
|
||||||
|
#include <optional>
|
||||||
|
#include <stack>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
namespace Sapphire
|
||||||
|
{
|
||||||
|
static class EncounterTimeline
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// EncounterFight::OnTick() { switch EncounterTimepointConditionId }
|
||||||
|
enum class EncounterConditionId : uint32_t
|
||||||
|
{
|
||||||
|
HpPctLessThan,
|
||||||
|
HpPctBetween,
|
||||||
|
DirectorVarEquals,
|
||||||
|
DirectorVarGreaterThan
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: what should this do?
|
||||||
|
enum class EncounterTimepointOverrideFlags
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
Invulnerable
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class EncounterTimepointDataType : uint32_t
|
||||||
|
{
|
||||||
|
Idle,
|
||||||
|
CastAction,
|
||||||
|
MoveTo,
|
||||||
|
LogMessage,
|
||||||
|
BattleTalk,
|
||||||
|
SetDirectorVar,
|
||||||
|
AddStatusEffect,
|
||||||
|
RemoveStatusEffect
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class EncounterTimepointCallbackType : uint32_t
|
||||||
|
{
|
||||||
|
OnActionInit,
|
||||||
|
OnActionStart,
|
||||||
|
OnActionInterrupt,
|
||||||
|
OnActionExecute
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class TargetSelectFilterIds
|
||||||
|
{
|
||||||
|
Self,
|
||||||
|
Tank,
|
||||||
|
Healer,
|
||||||
|
Dps,
|
||||||
|
DpsMelee,
|
||||||
|
DpsRanged,
|
||||||
|
Furthest,
|
||||||
|
|
||||||
|
Aggro1,
|
||||||
|
Aggro2
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class MoveType
|
||||||
|
{
|
||||||
|
WalkPath,
|
||||||
|
Teleport
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TargetSelectFilter
|
||||||
|
{
|
||||||
|
TargetSelectFilterIds m_flags;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Generated Structures
|
||||||
|
|
||||||
|
// Generated Callback Structure
|
||||||
|
struct EncounterTimepointCallbackData :
|
||||||
|
public std::enable_shared_from_this< EncounterTimepointCallbackData >
|
||||||
|
{
|
||||||
|
EncounterTimepointCallbackType m_type;
|
||||||
|
};
|
||||||
|
using EncounterTimepointCallbackDataPtr = std::shared_ptr< EncounterTimepointCallbackData >;
|
||||||
|
using EncounterTimepointCallbacks = std::map< EncounterTimepointCallbackType, EncounterTimepointCallbackDataPtr >;
|
||||||
|
|
||||||
|
|
||||||
|
// Generated State Objects
|
||||||
|
struct EncounterTimepointData :
|
||||||
|
public std::enable_shared_from_this< EncounterTimepointData >
|
||||||
|
{
|
||||||
|
EncounterTimepointDataType m_type;
|
||||||
|
};
|
||||||
|
using EncounterTimepointDataPtr = std::shared_ptr< EncounterTimepointData >;
|
||||||
|
|
||||||
|
|
||||||
|
// Generated State Data Objects
|
||||||
|
struct EncounterTimepointDataStatusEffect : EncounterTimepointData
|
||||||
|
{
|
||||||
|
uint32_t m_statusEffectId;
|
||||||
|
TargetSelectFilter m_targetFilter;
|
||||||
|
uint32_t m_durationMs;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct EncounterTimepointDataAction : EncounterTimepointData
|
||||||
|
{
|
||||||
|
uint32_t m_actionId;
|
||||||
|
EncounterTimepointCallbacks m_callbacks;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct EncounterTimepointDataMoveTo : EncounterTimepointData
|
||||||
|
{
|
||||||
|
float x, y, z, rot;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct EncounterTimepoint :
|
||||||
|
public std::enable_shared_from_this< EncounterTimepoint >
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
EncounterTimepointDataType m_type;
|
||||||
|
uint32_t m_duration;
|
||||||
|
EncounterTimepointOverrideFlags m_overrideFlags;
|
||||||
|
EncounterTimepointDataPtr m_pData;
|
||||||
|
std::string m_description;
|
||||||
|
|
||||||
|
// switch( m_type )
|
||||||
|
virtual void execute( EncounterFightPtr pFight, uint64_t time );
|
||||||
|
};
|
||||||
|
using EncounterTimepointPtr = std::shared_ptr< EncounterTimepoint >;
|
||||||
|
|
||||||
|
class EncounterPhase :
|
||||||
|
public std::enable_shared_from_this< EncounterPhase >
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
std::string m_name;
|
||||||
|
std::map< std::string, EncounterTimepointPtr > m_timepoints;
|
||||||
|
uint64_t m_startTime{ 0 };
|
||||||
|
uint64_t m_currTime{ 0 };
|
||||||
|
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;
|
||||||
|
|
||||||
|
m_currTime = time;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
using EncounterPhasePtr = std::shared_ptr< EncounterPhase >;
|
||||||
|
|
||||||
|
class EncounterTimepointCondition :
|
||||||
|
public std::enable_shared_from_this< EncounterTimepointCondition >
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
EncounterConditionId m_conditionId{ 0 };
|
||||||
|
EncounterPhasePtr m_pPhase{ nullptr };
|
||||||
|
bool m_loop{ false };
|
||||||
|
uint64_t m_startTime{ 0 };
|
||||||
|
uint32_t m_cooldown{ 0 };
|
||||||
|
|
||||||
|
EncounterTimepointCondition() {}
|
||||||
|
~EncounterTimepointCondition() {}
|
||||||
|
|
||||||
|
virtual void from_json( nlohmann::json& json, EncounterPhasePtr pPhase, EncounterConditionId 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
void execute( EncounterFightPtr pFight, uint64_t time )
|
||||||
|
{
|
||||||
|
m_startTime = time;
|
||||||
|
m_pPhase->execute( pFight, time );
|
||||||
|
};
|
||||||
|
|
||||||
|
virtual bool canExecute( EncounterFightPtr pFight, uint64_t time )
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
using EncounterTimepointConditionPtr = std::shared_ptr< EncounterTimepointCondition >;
|
||||||
|
|
||||||
|
class EncounterConditionHp : EncounterTimepointCondition
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
uint32_t actorId;
|
||||||
|
union
|
||||||
|
{
|
||||||
|
uint8_t val;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
uint8_t min, max;
|
||||||
|
};
|
||||||
|
} hp;
|
||||||
|
|
||||||
|
void from_json( nlohmann::json& json, EncounterPhasePtr pPhase, EncounterConditionId conditionId );
|
||||||
|
bool canExecute( EncounterFightPtr pFight, uint64_t time ) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class EncounterConditionDirectorVar : EncounterTimepointCondition
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
uint32_t directorVar;
|
||||||
|
uint32_t value;
|
||||||
|
|
||||||
|
void from_json( nlohmann::json& json, EncounterPhasePtr pPhase, EncounterConditionId conditionId );
|
||||||
|
bool canExecute( EncounterFightPtr pFight, uint64_t time ) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
using EncounterTimelineInfo = std::stack< EncounterTimepointConditionPtr >;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
EncounterTimelineInfo buildEncounterTimeline( uint32_t encounterId, bool reload = false );
|
||||||
|
};
|
||||||
|
}// namespace Sapphire
|
Loading…
Add table
Reference in a new issue