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

wip: timeline shit

- add BattleTalk handling including packets
- add LogMessage handling
- add SetCondition handling (allow conditions to toggle others on/off)
- handle Director ops
- add basic CastAction (prolly doesnt work)
- add SpawnBNpc
This commit is contained in:
Tahir 2024-05-23 18:40:37 +01:00
parent a4d712f32c
commit e4024a677b
6 changed files with 467 additions and 122 deletions

View file

@ -1,9 +1,17 @@
#include "EncounterFight.h"
#include "EncounterTimeline.h"
#include <Action/Action.h>
#include <Actor/BNpc.h>
#include <Actor/Chara.h>
#include <Actor/EventObject.h>
#include <Actor/Player.h>
#include <Manager/ActionMgr.h>
#include <Manager/PlayerMgr.h>
#include <Service.h>
#include <Util/UtilMath.h>
@ -91,9 +99,14 @@ namespace Sapphire
return pBNpc && pBNpc->hasFlag( this->flags );
}
void EncounterTimeline::Timepoint::update( TimepointState& state, InstanceContentPtr pInstance, uint64_t time ) const
void EncounterTimeline::Timepoint::update( TimepointState& state, TimelineActor& self, InstanceContentPtr pInstance, uint64_t time ) const
{
state.m_lastTick = time;
// todo: separate execute and update?
if( state.m_finished )
return;
switch( m_type )
{
case TimepointDataType::Idle:
@ -104,15 +117,25 @@ namespace Sapphire
if( pBNpc )
{
// todo: idle
}
}
break;
case TimepointDataType::CastAction:
{
auto pActionData = std::dynamic_pointer_cast< TimepointDataAction, TimepointData >( getData() );
auto pBNpc = pInstance->getActiveBNpcByLayoutId( pActionData->m_layoutId );
// todo: filter the correct target
// todo: tie to mechanic script?
// todo: mechanic should probably just be an Action::onTick, with instance/director passed to it
if( pBNpc)
{
auto actionMgr = Common::Service< Sapphire::World::Manager::ActionMgr >::ref();
// todo: this is probably wrong
if( pBNpc->getCurrentAction() && pBNpc->getCurrentAction()->getId() != pActionData->m_actionId )
actionMgr.handleTargetedAction( *pBNpc.get(), pActionData->m_actionId, pBNpc->getTargetId(), 0 );
}
}
break;
case TimepointDataType::MoveTo:
@ -144,49 +167,105 @@ namespace Sapphire
break;
case TimepointDataType::LogMessage:
{
// todo: LogMessage
auto pLogMessage = std::dynamic_pointer_cast< TimepointDataLogMessage, TimepointData >( getData() );
auto params = pLogMessage->m_params;
// todo: probably should use ContentDirector
if( pInstance )
{
auto& playerMgr = Common::Service< Sapphire::World::Manager::PlayerMgr >::ref();
for( uint32_t id : pInstance->getSpawnedPlayerIds() )
{
auto pPlayer = playerMgr.getPlayer( id );
if( pPlayer )
playerMgr.sendLogMessage( *pPlayer.get(), pLogMessage->m_messageId,
params[ 0 ], params[ 1 ], params[ 2 ], params[ 3 ], params[ 4 ] );
}
}
}
break;
case TimepointDataType::BattleTalk:
{
// todo: BattleTalk
// auto pBattleTalkData = std::dynamic_pointer_cast< TimepointDataBattleTalk, TimepointData >();
auto pBtData = std::dynamic_pointer_cast< TimepointDataBattleTalk, TimepointData >( getData() );
auto params = pBtData->m_params;
if( pInstance )
{
auto& playerMgr = Common::Service< Sapphire::World::Manager::PlayerMgr >::ref();
for( uint32_t id : pInstance->getSpawnedPlayerIds() )
{
auto pPlayer = playerMgr.getPlayer( id );
if( pPlayer )
playerMgr.sendBattleTalk( *pPlayer.get(), pBtData->m_battleTalkId, pBtData->m_handlerId,
pBtData->m_kind, pBtData->m_nameId, pBtData->m_talkerId,
params[ 0 ], params[ 1 ], params[ 2 ], params[ 3 ],
params[ 4 ], params[ 5 ], params[ 6 ], params[ 7 ] );
}
}
}
break;
case TimepointDataType::SetDirectorSeq:
case TimepointDataType::SetDirectorVar:
case TimepointDataType::SetDirectorVarLR:
case TimepointDataType::SetDirectorFlag:
case TimepointDataType::DirectorSeq:
case TimepointDataType::DirectorVar:
case TimepointDataType::DirectorVarLR:
case TimepointDataType::DirectorFlags:
{
auto pDirectorData = std::dynamic_pointer_cast< TimepointDataDirector, TimepointData >( getData() );
uint32_t val = 0;
uint32_t param = 0;
// todo: this should never not be set?
// todo: probably should use ContentDirector
if( pInstance )
{
switch( m_type )
{
case TimepointDataType::DirectorVar:
val = pInstance->getDirectorVar( pDirectorData->m_data.index );
param = pDirectorData->m_data.value.val;
break;
case TimepointDataType::DirectorFlags:
val = pInstance->getFlags();
param = pDirectorData->m_data.flags;
break;
case TimepointDataType::DirectorSeq:
val = pInstance->getSequence();
param = pDirectorData->m_data.seq;
break;
default:
break;
}
switch( pDirectorData->m_directorOp )
{
case DirectorOpId::SetDirectorVar:
pInstance->setDirectorVar( pDirectorData->m_data.index, pDirectorData->m_data.value.val );
break;
case DirectorOpId::SetDirectorVarLR:
pInstance->setDirectorVar( pDirectorData->m_data.index, pDirectorData->m_data.value.left, pDirectorData->m_data.value.right );
break;
case DirectorOpId::SetDirectorFlag:
pInstance->setDirectorFlags( pDirectorData->m_data.flags );
break;
case DirectorOpId::SetDirectorSeq:
pInstance->setDirectorSequence( pDirectorData->m_data.seq );
break;
case DirectorOpId::ClearDirector:
{
for( auto playerId : pInstance->getSpawnedPlayerIds() )
{
// todo: get all players, clear director vars/flags to default(?)
}
}
break;
case DirectorOpId::Set: val = param; break;
case DirectorOpId::Add: val += param; break;
case DirectorOpId::Sub: val -= param; break;
case DirectorOpId::Mul: val *= param; break;
case DirectorOpId::Div: val /= param; break;
case DirectorOpId::Mod: val %= param; break;
case DirectorOpId::Sll: val = val << param; break;
case DirectorOpId::Srl: val = val >> param; break;
case DirectorOpId::Or: val |= param; break;
case DirectorOpId::Xor: val ^= param; break;
case DirectorOpId::Nor: val = ~( val | param ); break;
case DirectorOpId::And: val &= param; break;
default: break;
}
switch( m_type )
{
case TimepointDataType::DirectorVar:
pInstance->setVar( pDirectorData->m_data.index, val );
break;
case TimepointDataType::DirectorFlags:
pInstance->setFlags( val );
break;
case TimepointDataType::DirectorSeq:
pInstance->setSequence( val );
break;
default:
// probably throw an error here
break;
}
}
@ -200,6 +279,20 @@ namespace Sapphire
case TimepointDataType::RemoveStatusEffect:
{
}
break;
case TimepointDataType::SpawnBNpc:
{
auto pSpawnData = std::dynamic_pointer_cast< TimepointDataSpawnBNpc, TimepointData >( getData() );
auto pBNpc = pInstance->getActiveBNpcByLayoutId( pSpawnData->m_layoutId );
if( pBNpc )
{
pBNpc->clearFlags();
pBNpc->setFlag( pSpawnData->m_flags );
// todo: pBNpc->hateListAdd();
pInstance->pushActor( pBNpc );
}
}
break;
case TimepointDataType::SetBNpcFlags:
@ -211,6 +304,7 @@ namespace Sapphire
{
pBNpc->clearFlags();
pBNpc->setFlag( pBNpcFlagData->m_flags );
// todo: resend some bnpc packet/actrl?
}
}
break;
@ -219,9 +313,11 @@ namespace Sapphire
auto pEObjData = std::dynamic_pointer_cast< TimepointDataEObjState, TimepointData >( getData() );
auto pEObj = pInstance->getEObjById( pEObjData->m_eobjId );
// todo: SetEObjAnimationFlag?
if( pEObj )
{
pEObj->setState( pEObjData->m_state );
// todo: resend the eobj spawn packet?
}
}
break;
@ -231,7 +327,19 @@ namespace Sapphire
pInstance->setCurrentBGM( pBgmData->m_bgmId );
}
break;
case TimepointDataType::SetCondition:
{
auto pConditionData = std::dynamic_pointer_cast< TimepointDataCondition, TimepointData >( getData() );
// todo: dont reset so things can resume? idk
self.resetConditionState( pConditionData->m_index );
self.setConditionStateEnabled( pConditionData->m_index, pConditionData->m_enabled );
}
}
if( m_type != TimepointDataType::MoveTo && m_type != TimepointDataType::CastAction )
state.m_finished = true;
state.m_finished = state.m_finished || state.m_startTime + m_duration <= time;
}
@ -297,10 +405,10 @@ namespace Sapphire
}
*/
void EncounterTimeline::Timepoint::execute( TimepointState& state, InstanceContentPtr pInstance, uint64_t time ) const
void EncounterTimeline::Timepoint::execute( TimepointState& state, TimelineActor& self, InstanceContentPtr pInstance, uint64_t time ) const
{
state.m_startTime = time;
update( state, pInstance, time );
update( state, self, pInstance, time );
}
//
@ -346,8 +454,8 @@ namespace Sapphire
case ConditionId::DirectorVarEquals:
case ConditionId::DirectorVarGreaterThan:
{
param.index = paramData.at( "index" ).get< uint32_t >();
param.value = paramData.at( "value" ).get< uint32_t >();
param.index = paramData.at( "idx" ).get< uint32_t >();
param.value = paramData.at( "val" ).get< uint32_t >();
}
break;
case ConditionId::DirectorFlagsEquals:
@ -398,6 +506,15 @@ namespace Sapphire
const std::unordered_map< std::string, TimelineActor >& actors )
{
PhaseCondition::from_json( json, phase, conditionId );
auto actorRef = json.at( "actor" ).get< std::string >();
// 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( "EncounterTimeline::ConditionBNpcFlags::from_json unable to find actor by name: %s" ), actorRef ) );
this->flags = json.at( "flags" ).get< uint32_t >();
// todo: BNpcHasFlags
}
@ -410,13 +527,15 @@ namespace Sapphire
{ "moveTo", TimepointDataType::MoveTo },
{ "logMessage", TimepointDataType::LogMessage },
{ "battleTalk", TimepointDataType::BattleTalk },
{ "setDirectorVar", TimepointDataType::SetDirectorVar },
{ "setDirectorSeq", TimepointDataType::SetDirectorSeq },
{ "setDirectorFlags", TimepointDataType::SetDirectorFlag },
{ "directorVar", TimepointDataType::DirectorVar },
{ "directorSeq", TimepointDataType::DirectorSeq },
{ "directorFlags", TimepointDataType::DirectorFlags },
{ "addStatusEffect", TimepointDataType::AddStatusEffect },
{ "removeStatusEffect", TimepointDataType::RemoveStatusEffect },
{ "setBNpcFlags", TimepointDataType::SetBNpcFlags },
{ "setEObjState", TimepointDataType::SetEObjState }
{ "spawnBNpc", TimepointDataType::SpawnBNpc },
{ "bNpcFlags", TimepointDataType::SetBNpcFlags },
{ "setEObjState", TimepointDataType::SetEObjState },
{ "setCondition", TimepointDataType::SetCondition }
};
const static std::unordered_map< std::string, TimepointOverrideFlags > overrideFlagMap =
@ -445,6 +564,22 @@ namespace Sapphire
{ "onActionExecute", TimepointCallbackType::OnActionExecute },
};
const static std::unordered_map< std::string, DirectorOpId > directorOpMap =
{
{ "set", DirectorOpId::Set },
{ "add", DirectorOpId::Add },
{ "sub", DirectorOpId::Sub },
{ "mul", DirectorOpId::Mul },
{ "div", DirectorOpId::Div },
{ "mod", DirectorOpId::Mod },
{ "sll", DirectorOpId::Sll },
{ "srl", DirectorOpId::Srl },
{ "or", DirectorOpId::Or },
{ "xor", DirectorOpId::Xor },
{ "nor", DirectorOpId::Nor },
{ "and", DirectorOpId::And }
};
TimepointDataType tpType{ 0 };
auto typeStr = json.at( "type" ).get< std::string >();
@ -470,20 +605,26 @@ namespace Sapphire
// todo: CastAction
// todo: parse and build callback funcs
auto dataJ = json.at( "data" );
auto actorRef = json.at( "sourceActor" ).get< std::string >();
auto actionId = json.at( "actionId" ).get< uint32_t >();
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( "EncounterTimeline::Timepoint::from_json: CastAction invalid actor ref: %s", actorRef ) );
m_pData = std::make_shared< TimepointDataAction >( layoutId, actionId );
}
break;
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 pos = dataJ.at( "pos" ).get< std::vector< float > >();
auto rot = dataJ.at( "rot" ).get< float >();
auto pathReq = dataJ.at( "pathRequested" ).get< bool >() ? MoveType::WalkPath : MoveType::Teleport;
m_pData = std::make_shared< TimepointDataMoveTo >( selfLayoutId, pathReq, x, y, z, rot );
m_pData = std::make_shared< TimepointDataMoveTo >( selfLayoutId, pathReq, pos[ 0 ], pos[ 1 ], pos[ 2 ], rot );
}
break;
case TimepointDataType::LogMessage:
@ -498,37 +639,48 @@ namespace Sapphire
case TimepointDataType::BattleTalk:
{
auto dataJ = json.at( "data" );
auto params = dataJ.at( "params" ).get< std::vector< uint32_t > >();
// todo: BattleTalk
auto pBattleTalkData = std::make_shared< TimepointDataBattleTalk >( params );
pBattleTalkData->m_battleTalkId = dataJ.at( "battleTalkId" ).get< uint32_t >();
pBattleTalkData->m_handlerId = dataJ.at( "handlerId" ).get< uint32_t >();
pBattleTalkData->m_kind = dataJ.at( "kind" ).get< uint32_t >();
pBattleTalkData->m_nameId = dataJ.at( "nameId" ).get< uint32_t >();
pBattleTalkData->m_talkerId = dataJ.at( "talkerId" ).get< uint32_t >();
m_pData = pBattleTalkData;
}
break;
//
// Directors
//
case TimepointDataType::SetDirectorVar:
case TimepointDataType::DirectorVar:
{
auto dataJ = json.at( "data" );
auto index = dataJ.at( "index" ).get< uint32_t >();
auto val = dataJ.at( "value" ).get< uint32_t >();
auto index = dataJ.at( "idx" ).get< uint32_t >();
auto val = dataJ.at( "val" ).get< uint32_t >();
auto opStr = dataJ.at( "opc" ).get< std::string >();
DirectorOpId op = directorOpMap.find( opStr )->second;
auto pDirectorData = std::make_shared< TimepointDataDirector >( tpType );
pDirectorData->m_directorOp = DirectorOpId::SetDirectorVar;
auto pDirectorData = std::make_shared< TimepointDataDirector >( tpType, op );
pDirectorData->m_data.index = index;
pDirectorData->m_data.value.val = val;
m_pData = pDirectorData;
}
break;
case TimepointDataType::SetDirectorVarLR:
case TimepointDataType::DirectorVarLR:
{
auto dataJ = json.at( "data" );
auto index = dataJ.at( "index" ).get< uint32_t >();
auto index = dataJ.at( "idx" ).get< uint32_t >();
auto left = dataJ.at( "left" ).get< uint32_t >();
auto right = dataJ.at( "right" ).get< uint32_t >();
auto opStr = dataJ.at( "opc" ).get< std::string >();
DirectorOpId op = directorOpMap.find( opStr )->second;
auto pDirectorData = std::make_shared< TimepointDataDirector >( tpType );
pDirectorData->m_directorOp = DirectorOpId::SetDirectorVarLR;
auto pDirectorData = std::make_shared< TimepointDataDirector >( tpType, op );
pDirectorData->m_data.index = index;
pDirectorData->m_data.value.left = left;
pDirectorData->m_data.value.right = right;
@ -536,25 +688,27 @@ namespace Sapphire
m_pData = pDirectorData;
}
break;
case TimepointDataType::SetDirectorSeq:
case TimepointDataType::DirectorSeq:
{
auto dataJ = json.at( "data" );
auto seq = dataJ.at( "seq" ).get< uint32_t >();
auto seq = dataJ.at( "val" ).get< uint32_t >();
auto opStr = dataJ.at( "opc" ).get< std::string >();
DirectorOpId op = directorOpMap.find( opStr )->second;
auto pDirectorData = std::make_shared< TimepointDataDirector >( tpType );
pDirectorData->m_directorOp = DirectorOpId::SetDirectorSeq;
auto pDirectorData = std::make_shared< TimepointDataDirector >( tpType, op );
pDirectorData->m_data.seq = seq;
m_pData = pDirectorData;
}
break;
case TimepointDataType::SetDirectorFlag:
case TimepointDataType::DirectorFlags:
{
auto dataJ = json.at( "data" );
auto flags = dataJ.at( "flags" ).get< uint32_t >();
auto flags = dataJ.at( "val" ).get< uint32_t >();
auto opStr = dataJ.at( "opc" ).get< std::string >();
DirectorOpId op = directorOpMap.find( opStr )->second;
auto pDirectorData = std::make_shared< TimepointDataDirector >( tpType );
pDirectorData->m_directorOp = DirectorOpId::SetDirectorFlag;
auto pDirectorData = std::make_shared< TimepointDataDirector >( tpType, op );
pDirectorData->m_data.flags = flags;
m_pData = pDirectorData;
@ -572,10 +726,39 @@ namespace Sapphire
break;
case TimepointDataType::SpawnBNpc:
{
auto dataJ = json.at( "data" );
auto hateSrcJ = dataJ.at( "hateSrc" );
auto actorRef = dataJ.at( "spawnActor" );
auto flags = dataJ.at( "flags" ).get< uint32_t >();
// todo: hateSrc
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( "EncounterTimeline::Timepoint::from_json: SpawnBNpc invalid actor ref: %s", actorRef ) );
m_pData = std::make_shared< TimepointDataSpawnBNpc >( layoutId, flags );
}
break;
case TimepointDataType::SetBNpcFlags:
{
auto dataJ = json.at( "data" );
auto actorRef = dataJ.at( "spawnActor" );
auto flags = dataJ.at( "flags" ).get< uint32_t >();
// todo: hateSrc
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( "EncounterTimeline::Timepoint::from_json: SetBNpcFlags invalid actor ref: %s", actorRef ) );
m_pData = std::make_shared< TimepointDataBNpcFlags >( layoutId, flags );
// todo: SetBNpcFlags
}
break;
@ -586,6 +769,18 @@ namespace Sapphire
// todo: SetEObjState
}
break;
case TimepointDataType::SetCondition:
{
auto dataJ = json.at( "data" );
auto index = dataJ.at( "conditionId" ).get< uint32_t >();
auto enabled = dataJ.at( "enabled" ).get< bool >();
m_pData = std::make_shared< TimepointDataCondition >( index, enabled );
}
break;
default:
break;
}
@ -683,7 +878,7 @@ namespace Sapphire
}
// build the condition list
for( const auto& pcJ : json.at( "phaseConditions" ).items() )
for( const auto& pcJ : json.at( "conditions" ).items() )
{
auto pcV = pcJ.value();
auto conditionName = pcV.at( "condition" ).get< std::string >();

View file

@ -21,6 +21,11 @@ namespace Sapphire
class TimelineActor;
class TimelinePack;
//
// State tracking objects (per actor)
//
// todo: move ConditionState/TimepointState to Chara::GambitState?
struct TimepointState
{
uint64_t m_startTime{ 0 };
@ -33,6 +38,7 @@ namespace Sapphire
uint64_t m_startTime{ 0 };
bool m_loop{ false };
bool m_completed{ false };
bool m_enabled{ false };
struct
{
@ -44,6 +50,9 @@ namespace Sapphire
} m_phaseInfo;
};
//
// Enums
//
// EncounterFight::OnTick() { switch EncounterTimepointConditionId }
enum class ConditionId : uint32_t
{
@ -64,11 +73,18 @@ namespace Sapphire
enum class DirectorOpId
{
SetDirectorVar,
SetDirectorVarLR,
SetDirectorSeq,
SetDirectorFlag,
ClearDirector
Set, // idx = val
Add, // idx += val
Sub, // idx -= val
Mul, // idx *= val
Div, // idx /= val
Mod, // idx %= val
Sll, // idx << val
Srl, // idx >> val
Or, // idx |= val
Xor, // idx ^= val
Nor, // idx ~= val
And // idx &= val
};
// TODO: what should this do?
@ -87,17 +103,20 @@ namespace Sapphire
LogMessage,
BattleTalk,
SetDirectorVar,
SetDirectorVarLR,
SetDirectorSeq,
SetDirectorFlag,
DirectorVar,
DirectorVarLR,
DirectorSeq,
DirectorFlags,
AddStatusEffect,
RemoveStatusEffect,
SpawnBNpc,
SetBNpcFlags,
SetEObjState,
SetBgm
SetBgm,
SetCondition
};
enum class TimepointCallbackType : uint32_t
@ -155,7 +174,9 @@ namespace Sapphire
TargetSelectFilterId m_flags;
};
//
// Timepoint.m_pData objects
//
using TimepointCallbackFunc = std::function< void( InstanceContentPtr, uint64_t ) >;
// Timepoint Data Objects
struct TimepointCallbackData :
@ -179,7 +200,7 @@ namespace Sapphire
struct TimepointDataIdle : public TimepointData
{
uint32_t m_layoutId;
uint32_t m_layoutId{ 0xE0000000 };
uint64_t m_durationMs;
TimepointDataIdle( uint32_t layoutId, uint64_t durationMs ) :
@ -220,22 +241,22 @@ namespace Sapphire
struct TimepointDataAction : public TimepointData
{
uint32_t m_layoutId;
uint32_t m_layoutId{ 0xE0000000 };
uint32_t m_actionId;
TimepointCallbacks m_callbacks;
//TimepointCallbacks m_callbacks;
TimepointDataAction( uint32_t layoutId, uint32_t actionId, TimepointCallbacks callbacks ) :
TimepointDataAction( uint32_t layoutId, uint32_t actionId ) :
TimepointData( TimepointDataType::CastAction ),
m_layoutId( layoutId ),
m_actionId( actionId ),
m_callbacks( callbacks )
m_actionId( actionId )
//m_callbacks( callbacks )
{
}
};
struct TimepointDataMoveTo : public TimepointData
{
uint32_t m_layoutId;
uint32_t m_layoutId{ 0xE0000000 };
MoveType m_moveType;
float m_x, m_y, m_z, m_rot;
@ -251,13 +272,31 @@ namespace Sapphire
struct TimepointDataLogMessage : public TimepointData
{
uint32_t m_messageId;
uint32_t m_params[ 6 ]{ 0 };
uint32_t m_params[ 5 ]{ 0 };
TimepointDataLogMessage( uint32_t messageId, std::vector< uint32_t > params ) :
TimepointDataLogMessage( uint32_t messageId, const std::vector< uint32_t >& params ) :
TimepointData( TimepointDataType::LogMessage ),
m_messageId( messageId )
{
for( auto i = 0; i < params.size(); ++i )
for( auto i = 0; i < params.size() && i < 5; ++i )
m_params[i] = params[i];
}
};
struct TimepointDataBattleTalk : public TimepointData
{
uint32_t m_battleTalkId;
uint32_t m_handlerId;
uint32_t m_kind;
uint32_t m_nameId;
uint32_t m_talkerId;
uint32_t m_params[ 8 ]{ 0 };
TimepointDataBattleTalk( const std::vector< uint32_t >& params ) :
TimepointData( TimepointDataType::BattleTalk )
{
for( auto i = 0; i < params.size() && i < 8; ++i )
m_params[i] = params[i];
}
};
@ -283,31 +322,32 @@ namespace Sapphire
uint8_t flags;
} m_data{ 0 };
TimepointDataDirector( TimepointDataType type ) :
TimepointData( type )
TimepointDataDirector( TimepointDataType type, DirectorOpId op ) :
TimepointData( type ),
m_directorOp( op )
{
}
};
struct TimepointDataSpawnBNpc : public TimepointData
{
uint32_t m_layoutId{ 0xE0000000 };
uint32_t m_flags{ 0 };
// todo: hate type, source
TimepointDataSpawnBNpc( uint32_t layoutId, uint32_t flags ) :
TimepointData( TimepointDataType::SpawnBNpc ),
m_layoutId( layoutId ),
m_flags( flags)
{
switch( type )
{
case TimepointDataType::SetDirectorVar:
m_directorOp = DirectorOpId::SetDirectorVar;
break;
case TimepointDataType::SetDirectorVarLR:
m_directorOp = DirectorOpId::SetDirectorVarLR;
break;
case TimepointDataType::SetDirectorFlag:
m_directorOp = DirectorOpId::SetDirectorFlag;
break;
case TimepointDataType::SetDirectorSeq:
m_directorOp = DirectorOpId::SetDirectorSeq;
break;
}
}
};
struct TimepointDataBNpcFlags : public TimepointData
{
uint32_t m_layoutId;
uint32_t m_flags;
uint32_t m_layoutId{ 0xE0000000 };
uint32_t m_flags{ 0 };
TimepointDataBNpcFlags( uint32_t layoutId, uint32_t flags ) :
TimepointData( TimepointDataType::SetBNpcFlags ),
@ -341,13 +381,27 @@ namespace Sapphire
}
};
struct TimepointDataCondition : public TimepointData
{
// todo: rng?
uint32_t m_index;
bool m_enabled;
TimepointDataCondition( uint32_t index, bool enabled ) :
TimepointData( TimepointDataType::SetCondition ),
m_index( index ),
m_enabled( enabled )
{
}
};
// todo: refactor all this to allow solo actor to use
class Timepoint :
public std::enable_shared_from_this< Timepoint >
{
public:
TimepointDataType m_type;
uint64_t m_duration{ 0 };
uint64_t m_duration{ 0 }; // milliseconds
TimepointOverrideFlags m_overrideFlags;
TimepointDataPtr m_pData;
std::string m_description;
@ -359,14 +413,19 @@ namespace Sapphire
return m_pData;
}
bool canExecute( TimepointState& state, uint64_t elapsed ) const
bool canExecute( const TimepointState& state, uint64_t elapsed ) const
{
return state.m_startTime == 0; // & &m_duration <= elapsed;
}
bool finished( TimepointState& state, uint64_t time ) const
bool durationElapsed( uint64_t elapsed ) const
{
return state.m_startTime + m_duration <= time || state.m_finished;
return m_duration < elapsed;
}
bool finished( const TimepointState& state, uint64_t elapsed ) const
{
return durationElapsed( elapsed ) || state.m_finished;
}
void reset( TimepointState& state ) const
@ -378,8 +437,8 @@ namespace Sapphire
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( TimepointState& state, InstanceContentPtr pInstance, uint64_t time ) const;
void execute( TimepointState& state, InstanceContentPtr pInstance, uint64_t time ) const;
void update( TimepointState& state, TimelineActor& self, InstanceContentPtr pInstance, uint64_t time ) const;
void execute( TimepointState& state, TimelineActor& self, InstanceContentPtr pInstance, uint64_t time ) const;
};
class Phase :
@ -387,13 +446,13 @@ namespace Sapphire
{
public:
// todo: respect looping phases, allow callbacks to push timepoints
// todo: allow callbacks to push timepoints
std::string m_name;
std::vector< Timepoint > m_timepoints;
// todo: i wrote this very sleep deprived, ensure it is actually sane
void execute( ConditionState& state, InstanceContentPtr pInstance, uint64_t time ) const
void execute( ConditionState& state, TimelineActor& self, InstanceContentPtr pInstance, uint64_t time ) const
{
if( state.m_startTime == 0 )
state.m_startTime = time;
@ -414,17 +473,18 @@ namespace Sapphire
auto& tpState = state.m_phaseInfo.m_timepointStates[ i ];
auto& timepoint = m_timepoints[ i ];
if( timepoint.canExecute( tpState, timepointElapsed ) )
{
timepoint.execute( tpState, pInstance, time );
timepoint.execute( tpState, self, pInstance, time );
state.m_phaseInfo.m_lastTimepointTime = time;
}
else if( !timepoint.finished( tpState, timepointElapsed ) )
{
timepoint.update( tpState, pInstance, time );
timepoint.update( tpState, self, pInstance, time );
}
if( timepoint.finished( tpState, timepointElapsed ) )
if( timepoint.durationElapsed( timepointElapsed ) && timepoint.finished( tpState, timepointElapsed ) )
{
timepoint.reset( tpState );
// make sure this timepoint isnt run again unless phase loops
@ -451,7 +511,7 @@ namespace Sapphire
state.m_phaseInfo.m_timepointStates.resize( m_timepoints.size() );
}
bool completed( ConditionState& state ) const
bool completed( const ConditionState& state ) const
{
return state.m_phaseInfo.m_lastTimepointIndex > m_timepoints.size();
}
@ -467,6 +527,8 @@ namespace Sapphire
std::string m_description;
uint32_t m_cooldown{ 0 };
bool m_loop{ false };
bool m_enabled{ true };
public:
PhaseCondition() {}
~PhaseCondition() {}
@ -478,31 +540,49 @@ namespace Sapphire
//this->m_cooldown = json.at( "cooldown" ).get< uint32_t >();
this->m_phase = phase;
this->m_description = json.at( "description" ).get< std::string >();
this->m_enabled = json.at( "enabled" ).get< bool >();
}
void execute( ConditionState& state, InstanceContentPtr pInstance, TimelinePack& pack, uint64_t time ) const
void execute( ConditionState& state, TimelineActor& self, InstanceContentPtr pInstance, TimelinePack& pack, uint64_t time ) const
{
state.m_startTime = time;
m_phase.execute( state, pInstance, time );
m_phase.execute( state, self, pInstance, time );
};
void update( ConditionState& state, InstanceContentPtr pInstance, TimelinePack& pack, uint64_t time ) const
void update( ConditionState& state, TimelineActor& self, InstanceContentPtr pInstance, TimelinePack& pack, uint64_t time ) const
{
m_phase.execute( state, pInstance, time );
m_phase.execute( state, self, pInstance, time );
}
void setEnabled( ConditionState& state, bool enabled )
{
state.m_enabled = enabled;
}
void reset( ConditionState& state ) const
{
state.m_startTime = 0;
state.m_enabled = isDefaultEnabled();
m_phase.reset( state );
}
bool inProgress( ConditionState& state ) const
bool inProgress( const ConditionState& state ) const
{
return state.m_startTime != 0;
}
bool completed( ConditionState& state ) const
// todo: better naming
bool isStateEnabled( const ConditionState& state ) const
{
return state.m_enabled;
}
bool isDefaultEnabled() const
{
return m_enabled;
}
bool completed( const ConditionState& state ) const
{
return m_phase.completed( state );
}
@ -551,6 +631,7 @@ namespace Sapphire
{
m_phaseConditions.push_back( pCondition );
m_conditionStates.push_back( {} );
m_conditionStates[ m_conditionStates.size() - 1 ].m_enabled = pCondition->isDefaultEnabled();
}
// todo: make this sane and pass info down
@ -563,6 +644,10 @@ namespace Sapphire
const auto& pCondition = m_phaseConditions[ i ];
auto& state = m_conditionStates[ i ];
// ignore if not enabled, unless overriden to enable
if( !pCondition->isDefaultEnabled() && !pCondition->isStateEnabled( state ) )
continue;
if( pCondition->completed( state ) )
{
if( pCondition->isLoopable() )
@ -573,15 +658,36 @@ namespace Sapphire
}
else if( pCondition->inProgress( state ) )
{
pCondition->update( state, pInstance, pack, time );
pCondition->update( state, *this, pInstance, pack, time );
}
else if( pCondition->isConditionMet( state, pInstance, pack, time ) )
{
pCondition->execute( state, pInstance, pack, time );
pCondition->execute( state, *this, pInstance, pack, time );
m_phaseHistory.push( pCondition );
if( pack.getStartTime() == 0 )
pack.setStartTime( state.m_startTime );
}
}
}
void resetConditionState( uint32_t conditionIdx )
{
assert( conditionIdx < m_phaseConditions.size() );
const auto& pCondition = m_phaseConditions[ conditionIdx ];
auto& state = m_conditionStates[ conditionIdx ];
pCondition->reset( state );
}
void setConditionStateEnabled( uint32_t conditionIdx, bool enabled )
{
assert( conditionIdx < m_conditionStates.size() );
auto& state = m_conditionStates[ conditionIdx ];
state.m_enabled = enabled;
}
};
// todo: actually handle solo stuff properly (or tie to zone director/content director at least)

View file

@ -362,3 +362,12 @@ void PlayerMgr::sendLogMessage( Entity::Player& player, uint32_t messageId, uint
{
Network::Util::Packet::sendActorControlTarget( player, player.getId(), LogMsg, messageId, param2, param3, param4, param5, param6 );
}
void PlayerMgr::sendBattleTalk( Sapphire::Entity::Player& player, uint32_t battleTalkId, uint32_t handlerId,
uint32_t kind, uint32_t nameId, uint32_t talkerId,
uint32_t param1, uint32_t param2, uint32_t param3, uint32_t param4,
uint32_t param5, uint32_t param6, uint32_t param7, uint32_t param8 )
{
Network::Util::Packet::sendBattleTalk( player, battleTalkId, handlerId, kind, nameId, talkerId,
param1, param2, param3, param4, param5, param6, param7, param8 );
}

View file

@ -61,6 +61,11 @@ namespace Sapphire::World::Manager
static void sendLogMessage( Sapphire::Entity::Player& player, uint32_t messageId, uint32_t param2 = 0, uint32_t param3 = 0,
uint32_t param4 = 0, uint32_t param5 = 0, uint32_t param6 = 0 );
static void sendBattleTalk( Sapphire::Entity::Player& player, uint32_t battleTalkId, uint32_t handlerId, uint32_t kind,
uint32_t nameId, uint32_t talkerId,
uint32_t param1 = 0, uint32_t param2 = 0, uint32_t param3 = 0, uint32_t param4 = 0,
uint32_t param5 = 0, uint32_t param6 = 0, uint32_t param7 = 0, uint32_t param8 = 0 );
private:
std::map< uint32_t, Entity::PlayerPtr > m_playerMapById;
std::map< uint64_t, Entity::PlayerPtr > m_playerMapByCharacterId;

View file

@ -167,6 +167,31 @@ void Util::Packet::sendActorControlTarget( const std::set< uint64_t >& character
server().queueForPlayers( characterIds, makeActorControlTarget( srcId, category, param1, param2, param3, param4, param5, param6 ) );
}
void Sapphire::Network::Util::Packet::sendBattleTalk( Sapphire::Entity::Player& player, uint32_t battleTalkId, uint32_t handlerId,
uint32_t kind, uint32_t nameId, uint32_t talkerId,
uint32_t param1, uint32_t param2, uint32_t param3, uint32_t param4,
uint32_t param5, uint32_t param6, uint32_t param7, uint32_t param8 )
{
auto battleTalkPacket = makeZonePacket< FFXIVIpcBattleTalk8 >( talkerId, player.getId() );
auto& data = battleTalkPacket->data();
data.battleTalkId = battleTalkId;
data.handlerId = handlerId;
data.nameId = nameId;
data.kind = kind;
data.talkerId = talkerId;
data.args[ 0 ] = param1;
data.args[ 1 ] = param2;
data.args[ 2 ] = param3;
data.args[ 3 ] = param4;
data.args[ 4 ] = param5;
data.args[ 5 ] = param6;
data.args[ 6 ] = param7;
data.args[ 7 ] = param8;
server().queueForPlayer( player.getCharacterId(), battleTalkPacket );
}
void Util::Packet::sendTitleList( Entity::Player& player )
{
auto titleListPacket = makeZonePacket< FFXIVIpcTitleList >( player.getId() );

View file

@ -68,4 +68,9 @@ namespace Sapphire::Network::Util::Packet
void sendActorControlTarget( const std::set< uint64_t >& characterIds, uint32_t srcId, uint16_t category, uint32_t param1 = 0,
uint32_t param2 = 0, uint32_t param3 = 0, uint32_t param4 = 0, uint32_t param5 = 0, uint32_t param6 = 0 );
void sendBattleTalk( Sapphire::Entity::Player& player, uint32_t battleTalkId, uint32_t handlerId,
uint32_t kind, uint32_t nameId, uint32_t talkerId,
uint32_t param1 = 0, uint32_t param2= 0, uint32_t param3 = 0, uint32_t param4 = 0,
uint32_t param5 = 0, uint32_t param6= 0, uint32_t param7 = 0, uint32_t param8 = 0 );
}