diff --git a/src/world/Encounter/EncounterTimeline.cpp b/src/world/Encounter/EncounterTimeline.cpp index ed896e18..ca44c548 100644 --- a/src/world/Encounter/EncounterTimeline.cpp +++ b/src/world/Encounter/EncounterTimeline.cpp @@ -1,9 +1,17 @@ #include "EncounterFight.h" #include "EncounterTimeline.h" +#include + #include #include #include +#include + + +#include +#include +#include #include @@ -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 >(); diff --git a/src/world/Encounter/EncounterTimeline.h b/src/world/Encounter/EncounterTimeline.h index 1d07acf2..0a05185f 100644 --- a/src/world/Encounter/EncounterTimeline.h +++ b/src/world/Encounter/EncounterTimeline.h @@ -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) diff --git a/src/world/Manager/PlayerMgr.cpp b/src/world/Manager/PlayerMgr.cpp index f04a9d18..675bf99d 100644 --- a/src/world/Manager/PlayerMgr.cpp +++ b/src/world/Manager/PlayerMgr.cpp @@ -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 ); +} diff --git a/src/world/Manager/PlayerMgr.h b/src/world/Manager/PlayerMgr.h index 4777f699..b9f4193f 100644 --- a/src/world/Manager/PlayerMgr.h +++ b/src/world/Manager/PlayerMgr.h @@ -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; diff --git a/src/world/Network/Util/PacketUtil.cpp b/src/world/Network/Util/PacketUtil.cpp index 842fa03c..154dd18b 100644 --- a/src/world/Network/Util/PacketUtil.cpp +++ b/src/world/Network/Util/PacketUtil.cpp @@ -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() ); diff --git a/src/world/Network/Util/PacketUtil.h b/src/world/Network/Util/PacketUtil.h index 81d855af..0aa7739b 100644 --- a/src/world/Network/Util/PacketUtil.h +++ b/src/world/Network/Util/PacketUtil.h @@ -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 ); } \ No newline at end of file