1
Fork 0
mirror of https://github.com/SapphireServer/Sapphire.git synced 2025-04-28 15:17:46 +00:00

wip: reorganise some timeline stuff

This commit is contained in:
Tahir 2024-05-06 20:13:53 +01:00
parent 6c5df82306
commit 05f9feb6ed
2 changed files with 253 additions and 82 deletions

View file

@ -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;

View file

@ -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;
};