From 91c0c1fcfb682ff68ded75d71b5b211e4e630b75 Mon Sep 17 00:00:00 2001 From: Aaron Kimbrell Date: Fri, 10 Feb 2023 02:29:53 -0600 Subject: [PATCH 1/2] Split out LUTriggers into it's own component (#986) * Split out LUTriggers into it's own component * some cleanup * fix debug log * use emplace and tryParse * slight refactor to make the work on startup rather than at runtime Also TODO's for getting targets via all the possible methods * address feedback --- dCommon/dEnums/dCommonVars.h | 1 + dCommon/dEnums/eTriggerCommandType.h | 119 +++++++++++++++ dCommon/dEnums/eTriggerEventType.h | 53 +++++++ dGame/Entity.cpp | 137 +++-------------- dGame/Entity.h | 11 +- dGame/EntityManager.cpp | 3 +- dGame/dComponents/CMakeLists.txt | 1 + dGame/dComponents/SwitchComponent.cpp | 5 +- dGame/dComponents/TriggerComponent.cpp | 187 +++++++++++++++++++++++ dGame/dComponents/TriggerComponent.h | 34 +++++ dGame/dUtilities/SlashCommandHandler.cpp | 9 +- dZoneManager/LUTriggers.h | 7 +- dZoneManager/Zone.cpp | 7 +- 13 files changed, 437 insertions(+), 137 deletions(-) create mode 100644 dCommon/dEnums/eTriggerCommandType.h create mode 100644 dCommon/dEnums/eTriggerEventType.h create mode 100644 dGame/dComponents/TriggerComponent.cpp create mode 100644 dGame/dComponents/TriggerComponent.h diff --git a/dCommon/dEnums/dCommonVars.h b/dCommon/dEnums/dCommonVars.h index 9bf824e0..c90fd8e6 100644 --- a/dCommon/dEnums/dCommonVars.h +++ b/dCommon/dEnums/dCommonVars.h @@ -414,6 +414,7 @@ enum eReplicaComponentType : int32_t { COMPONENT_TYPE_MODULE_ASSEMBLY = 61, //!< The ModuleAssembly Component COMPONENT_TYPE_PROPERTY_VENDOR = 65, //!< The PropertyVendor Component COMPONENT_TYPE_ROCKET_LAUNCH = 67, //!< The RocketLaunch Component + COMPONENT_TYPE_TRIGGER = 69, COMPONENT_TYPE_RACING_CONTROL = 71, //!< The RacingControl Component COMPONENT_TYPE_MISSION_OFFER = 73, //!< The MissionOffer Component COMPONENT_TYPE_EXHIBIT = 75, //!< The Exhibit Component diff --git a/dCommon/dEnums/eTriggerCommandType.h b/dCommon/dEnums/eTriggerCommandType.h new file mode 100644 index 00000000..fadfafb8 --- /dev/null +++ b/dCommon/dEnums/eTriggerCommandType.h @@ -0,0 +1,119 @@ +#ifndef __ETRIGGERCOMMANDTYPE__H__ +#define __ETRIGGERCOMMANDTYPE__H__ + +// For info about Trigger Command see: +// https://docs.lu-dev.net/en/latest/file-structures/lutriggers.html?highlight=trigger#possible-values-commands + +enum class eTriggerCommandType { + INVALID, + ZONE_PLAYER, + FIRE_EVENT, + DESTROY_OBJ, + TOGGLE_TRIGGER, + RESET_REBUILD, + SET_PATH, + SET_PICK_TYPE, + MOVE_OBJECT, + ROTATE_OBJECT, + PUSH_OBJECT, + REPEL_OBJECT, + SET_TIMER, + CANCEL_TIMER, + PLAY_CINEMATIC, + TOGGLE_BBB, + UPDATE_MISSION, + SET_BOUNCER_STATE, + BOUNCE_ALL_ON_BOUNCER, + TURN_AROUND_ON_PATH, + GO_FORWARD_ON_PATH, + GO_BACKWARD_ON_PATH, + STOP_PATHING, + START_PATHING, + LOCK_OR_UNLOCK_CONTROLS, + PLAY_EFFECT, + STOP_EFFECT, + ACTIVATE_MUSIC_CUE, + DEACTIVATE_MUSIC_CUE, + FLASH_MUSIC_CUE, + SET_MUSIC_PARAMETER, + PLAY_2D_AMBIENT_SOUND, + STOP_2D_AMBIENT_SOUND, + PLAY_3D_AMBIENT_SOUND, + STOP_3D_AMBIENT_SOUND, + ACTIVATE_MIXER_PROGRAM, + DEACTIVATE_MIXER_PROGRAM, + CAST_SKILL, + DISPLAY_ZONE_SUMMARY, + SET_PHYSICS_VOLUME_EFFECT, + SET_PHYSICS_VOLUME_STATUS, + SET_MODEL_TO_BUILD, + SPAWN_MODEL_BRICKS, + ACTIVATE_SPAWNER_NETWORK, + DEACTIVATE_SPAWNER_NETWORK, + RESET_SPAWNER_NETWORK, + DESTROY_SPAWNER_NETWORK_OBJECTS, + GO_TO_WAYPOINT, + ACTIVATE_PHYSICS +}; + + +class TriggerCommandType { +public: + static eTriggerCommandType StringToTriggerCommandType(std::string commandString) { + const std::map TriggerCommandMap = { + { "zonePlayer", eTriggerCommandType::ZONE_PLAYER}, + { "fireEvent", eTriggerCommandType::FIRE_EVENT}, + { "destroyObj", eTriggerCommandType::DESTROY_OBJ}, + { "toggleTrigger", eTriggerCommandType::TOGGLE_TRIGGER}, + { "resetRebuild", eTriggerCommandType::RESET_REBUILD}, + { "setPath", eTriggerCommandType::SET_PATH}, + { "setPickType", eTriggerCommandType::SET_PICK_TYPE}, + { "moveObject", eTriggerCommandType::MOVE_OBJECT}, + { "rotateObject", eTriggerCommandType::ROTATE_OBJECT}, + { "pushObject", eTriggerCommandType::PUSH_OBJECT}, + { "repelObject", eTriggerCommandType::REPEL_OBJECT}, + { "setTimer", eTriggerCommandType::SET_TIMER}, + { "cancelTimer", eTriggerCommandType::CANCEL_TIMER}, + { "playCinematic", eTriggerCommandType::PLAY_CINEMATIC}, + { "toggleBBB", eTriggerCommandType::TOGGLE_BBB}, + { "updateMission", eTriggerCommandType::UPDATE_MISSION}, + { "setBouncerState", eTriggerCommandType::SET_BOUNCER_STATE}, + { "bounceAllOnBouncer", eTriggerCommandType::BOUNCE_ALL_ON_BOUNCER}, + { "turnAroundOnPath", eTriggerCommandType::TURN_AROUND_ON_PATH}, + { "goForwardOnPath", eTriggerCommandType::GO_FORWARD_ON_PATH}, + { "goBackwardOnPath", eTriggerCommandType::GO_BACKWARD_ON_PATH}, + { "stopPathing", eTriggerCommandType::STOP_PATHING}, + { "startPathing", eTriggerCommandType::START_PATHING}, + { "LockOrUnlockControls", eTriggerCommandType::LOCK_OR_UNLOCK_CONTROLS}, + { "PlayEffect", eTriggerCommandType::PLAY_EFFECT}, + { "StopEffect", eTriggerCommandType::STOP_EFFECT}, + { "activateMusicCue", eTriggerCommandType::ACTIVATE_MUSIC_CUE}, + { "deactivateMusicCue", eTriggerCommandType::DEACTIVATE_MUSIC_CUE}, + { "flashMusicCue", eTriggerCommandType::FLASH_MUSIC_CUE}, + { "setMusicParameter", eTriggerCommandType::SET_MUSIC_PARAMETER}, + { "play2DAmbientSound", eTriggerCommandType::PLAY_2D_AMBIENT_SOUND}, + { "stop2DAmbientSound", eTriggerCommandType::STOP_2D_AMBIENT_SOUND}, + { "play3DAmbientSound", eTriggerCommandType::PLAY_3D_AMBIENT_SOUND}, + { "stop3DAmbientSound", eTriggerCommandType::STOP_3D_AMBIENT_SOUND}, + { "activateMixerProgram", eTriggerCommandType::ACTIVATE_MIXER_PROGRAM}, + { "deactivateMixerProgram", eTriggerCommandType::DEACTIVATE_MIXER_PROGRAM}, + { "CastSkill", eTriggerCommandType::CAST_SKILL}, + { "displayZoneSummary", eTriggerCommandType::DISPLAY_ZONE_SUMMARY}, + { "SetPhysicsVolumeEffect", eTriggerCommandType::SET_PHYSICS_VOLUME_EFFECT}, + { "SetPhysicsVolumeStatus", eTriggerCommandType::SET_PHYSICS_VOLUME_STATUS}, + { "setModelToBuild", eTriggerCommandType::SET_MODEL_TO_BUILD}, + { "spawnModelBricks", eTriggerCommandType::SPAWN_MODEL_BRICKS}, + { "ActivateSpawnerNetwork", eTriggerCommandType::ACTIVATE_SPAWNER_NETWORK}, + { "DeactivateSpawnerNetwork", eTriggerCommandType::DEACTIVATE_SPAWNER_NETWORK}, + { "ResetSpawnerNetwork", eTriggerCommandType::RESET_SPAWNER_NETWORK}, + { "DestroySpawnerNetworkObjects", eTriggerCommandType::DESTROY_SPAWNER_NETWORK_OBJECTS}, + { "Go_To_Waypoint", eTriggerCommandType::GO_TO_WAYPOINT}, + { "ActivatePhysics", eTriggerCommandType::ACTIVATE_PHYSICS} + }; + + auto intermed = TriggerCommandMap.find(commandString); + return (intermed != TriggerCommandMap.end()) ? intermed->second : eTriggerCommandType::INVALID; + }; +}; + +#endif //!__ETRIGGERCOMMANDTYPE__H__ diff --git a/dCommon/dEnums/eTriggerEventType.h b/dCommon/dEnums/eTriggerEventType.h new file mode 100644 index 00000000..1705ce22 --- /dev/null +++ b/dCommon/dEnums/eTriggerEventType.h @@ -0,0 +1,53 @@ +#ifndef __ETRIGGEREVENTTYPE__H__ +#define __ETRIGGEREVENTTYPE__H__ + +enum class eTriggerEventType { + INVALID, + DESTROY, + CUSTOM_EVENT, + ENTER, + EXIT, + CREATE, + HIT, + TIMER_DONE, + REBUILD_COMPLETE, + ACTIVATED, + DEACTIVATED, + ARRIVED, + ARRIVED_AT_END_OF_PATH, + ZONE_SUMMARY_DISMISSED, + ARRIVED_AT_DESIRED_WAYPOINT, + PET_ON_SWITCH, + PET_OFF_SWITCH, + INTERACT +}; + +class TriggerEventType { +public: + static eTriggerEventType StringToTriggerEventType(std::string commandString) { + const std::map TriggerEventMap = { + {"OnDestroy", eTriggerEventType::DESTROY}, + {"OnCustomEvent", eTriggerEventType::CUSTOM_EVENT}, + {"OnEnter", eTriggerEventType::ENTER}, + {"OnExit", eTriggerEventType::EXIT}, + {"OnCreate", eTriggerEventType::CREATE}, + {"OnHit", eTriggerEventType::HIT}, + {"OnTimerDone", eTriggerEventType::TIMER_DONE}, + {"OnRebuildComplete", eTriggerEventType::REBUILD_COMPLETE}, + {"OnActivated", eTriggerEventType::ACTIVATED}, + {"OnDeactivated", eTriggerEventType::DEACTIVATED}, + {"OnArrived", eTriggerEventType::ARRIVED}, + {"OnArrivedAtEndOfPath", eTriggerEventType::ARRIVED_AT_END_OF_PATH}, + {"OnZoneSummaryDismissed", eTriggerEventType::ZONE_SUMMARY_DISMISSED}, + {"OnArrivedAtDesiredWaypoint", eTriggerEventType::ARRIVED_AT_DESIRED_WAYPOINT}, + {"OnPetOnSwitch", eTriggerEventType::PET_ON_SWITCH}, + {"OnPetOffSwitch", eTriggerEventType::PET_OFF_SWITCH}, + {"OnInteract", eTriggerEventType::INTERACT}, + }; + + auto intermed = TriggerEventMap.find(commandString); + return (intermed != TriggerEventMap.end()) ? intermed->second : eTriggerEventType::INVALID; + }; +}; + +#endif //!__ETRIGGEREVENTTYPE__H__ diff --git a/dGame/Entity.cpp b/dGame/Entity.cpp index eefa5107..94693385 100644 --- a/dGame/Entity.cpp +++ b/dGame/Entity.cpp @@ -23,6 +23,7 @@ #include "EntityCallbackTimer.h" #include "Loot.h" #include "eMissionTaskType.h" +#include "eTriggerEventType.h" //Component includes: #include "Component.h" @@ -68,6 +69,7 @@ #include "ShootingGalleryComponent.h" #include "RailActivatorComponent.h" #include "LUPExhibitComponent.h" +#include "TriggerComponent.h" Entity::Entity(const LWOOBJID& objectID, EntityInfo info, Entity* parentEntity) { m_ObjectID = objectID; @@ -76,7 +78,6 @@ Entity::Entity(const LWOOBJID& objectID, EntityInfo info, Entity* parentEntity) m_Character = nullptr; m_GMLevel = 0; m_CollectibleID = 0; - m_Trigger = nullptr; //new LUTriggers::Trigger(); m_NetworkID = 0; m_Groups = {}; m_OwnerOverride = LWOOBJID_EMPTY; @@ -132,30 +133,9 @@ void Entity::Initialize() { * Setup trigger */ - const auto triggerName = GetVarAsString(u"trigger_id"); + const auto triggerInfo = GetVarAsString(u"trigger_id"); - if (!triggerName.empty()) { - std::stringstream ss(triggerName); - std::vector tokens; - std::string token; - while (std::getline(ss, token, ':')) { - tokens.push_back(token); - } - - uint32_t sceneID = std::stoi(tokens[0]); - uint32_t triggerID = std::stoi(tokens[1]); - - if (m_Trigger != nullptr) { - delete m_Trigger; - m_Trigger = nullptr; - } - - m_Trigger = dZoneManager::Instance()->GetZone()->GetTrigger(sceneID, triggerID); - - if (m_Trigger == nullptr) { - m_Trigger = new LUTriggers::Trigger(); - } - } + if (!triggerInfo.empty()) m_Components.emplace(COMPONENT_TYPE_TRIGGER, new TriggerComponent(this, triggerInfo)); /** * Setup groups @@ -769,7 +749,7 @@ void Entity::Initialize() { no_ghosting: - TriggerEvent("OnCreate"); + TriggerEvent(eTriggerEventType::CREATE); if (m_Character) { auto* controllablePhysicsComponent = GetComponent(); @@ -946,12 +926,16 @@ void Entity::WriteBaseReplicaData(RakNet::BitStream* outBitStream, eReplicaPacke outBitStream->Write0(); //No ldf data } - if (m_Trigger != nullptr && m_Trigger->events.size() > 0) { - outBitStream->Write1(); - } else { + TriggerComponent* triggerComponent; + if (TryGetComponent(COMPONENT_TYPE_TRIGGER, triggerComponent)) { + // has trigger component, check to see if we have events to handle + auto* trigger = triggerComponent->GetTrigger(); + outBitStream->Write(trigger && trigger->events.size() > 0); + } else { // no trigger componenet, so definitely no triggers outBitStream->Write0(); } + if (m_ParentEntity != nullptr || m_SpawnerID != 0) { outBitStream->Write1(); if (m_ParentEntity != nullptr) outBitStream->Write(GeneralUtils::SetBit(m_ParentEntity->GetObjectID(), OBJECT_BIT_CLIENT)); @@ -1310,7 +1294,7 @@ void Entity::OnCollisionPhantom(const LWOOBJID otherEntity) { switchComp->EntityEnter(other); } - TriggerEvent("OnEnter", other); + TriggerEvent(eTriggerEventType::ENTER, other); // POI system const auto& poi = GetVar(u"POI"); @@ -1344,7 +1328,7 @@ void Entity::OnCollisionLeavePhantom(const LWOOBJID otherEntity) { auto* other = EntityManager::Instance()->GetEntity(otherEntity); if (!other) return; - TriggerEvent("OnLeave", other); + TriggerEvent(eTriggerEventType::EXIT, other); SwitchComponent* switchComp = GetComponent(); if (switchComp) { @@ -1392,7 +1376,7 @@ void Entity::OnEmoteReceived(const int32_t emote, Entity* target) { } void Entity::OnUse(Entity* originator) { - TriggerEvent("OnInteract"); + TriggerEvent(eTriggerEventType::INTERACT); for (CppScripts::Script* script : CppScripts::GetEntityScripts(this)) { script->OnUse(this, originator); @@ -1736,94 +1720,9 @@ bool Entity::IsPlayer() const { return m_TemplateID == 1 && GetSystemAddress() != UNASSIGNED_SYSTEM_ADDRESS; } -void Entity::TriggerEvent(std::string eventID, Entity* optionalTarget) { - if (m_Trigger != nullptr && m_Trigger->enabled) { - for (LUTriggers::Event* triggerEvent : m_Trigger->events) { - if (triggerEvent->eventID == eventID) { - for (LUTriggers::Command* cmd : triggerEvent->commands) { - HandleTriggerCommand(cmd->id, cmd->target, cmd->targetName, cmd->args, optionalTarget); - } - } - } - } -} - -// This should probably get it's own triggers class at some point... -void Entity::HandleTriggerCommand(std::string id, std::string target, std::string targetName, std::string args, Entity* optionalTarget) { - std::vector argArray; - // Parse args - std::stringstream ssData(args); - std::string token; - char deliminator = ','; - - while (std::getline(ssData, token, deliminator)) { - std::string lowerToken; - for (char character : token) { - lowerToken.push_back(std::tolower(character)); // make lowercase to ensure it works - } - argArray.push_back(lowerToken); - } - - std::vector targetEntities; - if (target == "self") targetEntities.push_back(this); - if (target == "objGroup") targetEntities = EntityManager::Instance()->GetEntitiesInGroup(targetName); - if (optionalTarget) targetEntities.push_back(optionalTarget); - if (targetEntities.size() == 0) return; - for (Entity* targetEntity : targetEntities) { - if (!targetEntity) continue; - - if (id == "SetPhysicsVolumeEffect") { - PhantomPhysicsComponent* phanPhys = GetComponent(); - if (!phanPhys) return; - - phanPhys->SetPhysicsEffectActive(true); - uint32_t effectType = 0; - if (argArray[0] == "push") effectType = 0; - else if (argArray[0] == "attract") effectType = 1; - else if (argArray[0] == "repulse") effectType = 2; - else if (argArray[0] == "gravity") effectType = 3; - else if (argArray[0] == "friction") effectType = 4; - - phanPhys->SetEffectType(effectType); - phanPhys->SetDirectionalMultiplier(std::stof(argArray[1])); - if (argArray.size() > 4) { - NiPoint3 direction = NiPoint3::ZERO; - GeneralUtils::TryParse(argArray[2], direction.x); - GeneralUtils::TryParse(argArray[3], direction.y); - GeneralUtils::TryParse(argArray[4], direction.z); - phanPhys->SetDirection(direction); - } - if (argArray.size() > 5) { - phanPhys->SetMin(std::stoi(argArray[6])); - phanPhys->SetMax(std::stoi(argArray[7])); - } - - if (target == "self") { - EntityManager::Instance()->ConstructEntity(this); - } - } else if (id == "updateMission") { - CDMissionTasksTable* missionTasksTable = CDClientManager::Instance()->GetTable("MissionTasks"); - std::vector missionTasks = missionTasksTable->Query([=](CDMissionTasks entry) { - std::string lowerTargetGroup; - for (char character : entry.targetGroup) { - lowerTargetGroup.push_back(std::tolower(character)); // make lowercase to ensure it works - } - - return (lowerTargetGroup == argArray[4]); - }); - - for (const CDMissionTasks& task : missionTasks) { - MissionComponent* missionComponent = targetEntity->GetComponent(); - if (!missionComponent) continue; - - missionComponent->ForceProgress(task.id, task.uid, std::stoi(argArray[2])); - } - } else if (id == "fireEvent") { - for (CppScripts::Script* script : CppScripts::GetEntityScripts(targetEntity)) { - script->OnFireEventServerSide(targetEntity, this, args, 0, 0, 0); - } - } - } +void Entity::TriggerEvent(eTriggerEventType event, Entity* optionalTarget) { + auto* triggerComponent = GetComponent(); + if (triggerComponent) triggerComponent->TriggerEvent(event, optionalTarget); } Entity* Entity::GetOwner() const { diff --git a/dGame/Entity.h b/dGame/Entity.h index 248018c0..0bde01e2 100644 --- a/dGame/Entity.h +++ b/dGame/Entity.h @@ -18,9 +18,6 @@ namespace Loot { namespace tinyxml2 { class XMLDocument; }; -namespace LUTriggers { - struct Trigger; -}; class Player; class EntityInfo; @@ -33,6 +30,7 @@ class Component; class Item; class Character; class EntityCallbackTimer; +enum class eTriggerEventType; namespace CppScripts { class Script; @@ -67,8 +65,6 @@ public: Entity* GetParentEntity() const { return m_ParentEntity; } - LUTriggers::Trigger* GetTrigger() const { return m_Trigger; } - std::vector& GetGroups() { return m_Groups; }; Spawner* GetSpawner() const { return m_Spawner; } @@ -221,9 +217,8 @@ public: void RegisterCoinDrop(uint64_t count); void ScheduleKillAfterUpdate(Entity* murderer = nullptr); - void TriggerEvent(std::string eveneventtID, Entity* optionalTarget = nullptr); + void TriggerEvent(eTriggerEventType event, Entity* optionalTarget = nullptr); void ScheduleDestructionAfterUpdate() { m_ShouldDestroyAfterUpdate = true; } - void HandleTriggerCommand(std::string id, std::string target, std::string targetName, std::string args, Entity* optionalTarget); virtual NiPoint3 GetRespawnPosition() const { return NiPoint3::ZERO; } virtual NiQuaternion GetRespawnRotation() const { return NiQuaternion::IDENTITY; } @@ -308,8 +303,6 @@ protected: bool m_HasSpawnerNodeID; uint32_t m_SpawnerNodeID; - LUTriggers::Trigger* m_Trigger; - Character* m_Character; Entity* m_ParentEntity; //For spawners and the like diff --git a/dGame/EntityManager.cpp b/dGame/EntityManager.cpp index 22c31291..dae27af6 100644 --- a/dGame/EntityManager.cpp +++ b/dGame/EntityManager.cpp @@ -19,6 +19,7 @@ #include "dLogger.h" #include "MessageIdentifiers.h" #include "dConfig.h" +#include "eTriggerEventType.h" EntityManager* EntityManager::m_Address = nullptr; @@ -585,7 +586,7 @@ void EntityManager::ScheduleForKill(Entity* entity) { SwitchComponent* switchComp = entity->GetComponent(); if (switchComp) { - entity->TriggerEvent("OnDectivated"); + entity->TriggerEvent(eTriggerEventType::DEACTIVATED); } const auto objectId = entity->GetObjectID(); diff --git a/dGame/dComponents/CMakeLists.txt b/dGame/dComponents/CMakeLists.txt index 0995428b..b396829a 100644 --- a/dGame/dComponents/CMakeLists.txt +++ b/dGame/dComponents/CMakeLists.txt @@ -38,5 +38,6 @@ set(DGAME_DCOMPONENTS_SOURCES "BaseCombatAIComponent.cpp" "SkillComponent.cpp" "SoundTriggerComponent.cpp" "SwitchComponent.cpp" + "TriggerComponent.cpp" "VehiclePhysicsComponent.cpp" "VendorComponent.cpp" PARENT_SCOPE) diff --git a/dGame/dComponents/SwitchComponent.cpp b/dGame/dComponents/SwitchComponent.cpp index 2263a866..dbdf37a9 100644 --- a/dGame/dComponents/SwitchComponent.cpp +++ b/dGame/dComponents/SwitchComponent.cpp @@ -1,5 +1,6 @@ #include "SwitchComponent.h" #include "EntityManager.h" +#include "eTriggerEventType.h" std::vector SwitchComponent::petSwitches; @@ -42,7 +43,7 @@ void SwitchComponent::EntityEnter(Entity* entity) { } m_Active = true; if (!m_Parent) return; - m_Parent->TriggerEvent("OnActivated"); + m_Parent->TriggerEvent(eTriggerEventType::ACTIVATED); const auto grpName = m_Parent->GetVarAsString(u"grp_name"); @@ -78,7 +79,7 @@ void SwitchComponent::Update(float deltaTime) { if (m_Timer <= 0.0f) { m_Active = false; if (!m_Parent) return; - m_Parent->TriggerEvent("OnDectivated"); + m_Parent->TriggerEvent(eTriggerEventType::DEACTIVATED); const auto grpName = m_Parent->GetVarAsString(u"grp_name"); diff --git a/dGame/dComponents/TriggerComponent.cpp b/dGame/dComponents/TriggerComponent.cpp new file mode 100644 index 00000000..4ed95a15 --- /dev/null +++ b/dGame/dComponents/TriggerComponent.cpp @@ -0,0 +1,187 @@ +#include "TriggerComponent.h" +#include "dZoneManager.h" +#include "LUTriggers.h" +#include "eTriggerCommandType.h" +#include "MissionComponent.h" +#include "PhantomPhysicsComponent.h" +#include "CDMissionTasksTable.h" + +TriggerComponent::TriggerComponent(Entity* parent, const std::string triggerInfo): Component(parent) { + m_Parent = parent; + m_Trigger = nullptr; + + std::vector tokens = GeneralUtils::SplitString(triggerInfo, ':'); + + uint32_t sceneID; + GeneralUtils::TryParse(tokens.at(0), sceneID); + uint32_t triggerID; + GeneralUtils::TryParse(tokens.at(1), triggerID); + + m_Trigger = dZoneManager::Instance()->GetZone()->GetTrigger(sceneID, triggerID); + + if (!m_Trigger) m_Trigger = new LUTriggers::Trigger(); +} + +void TriggerComponent::TriggerEvent(eTriggerEventType event, Entity* optionalTarget) { + if (m_Trigger && m_Trigger->enabled) { + for (LUTriggers::Event* triggerEvent : m_Trigger->events) { + if (triggerEvent->id == event) { + for (LUTriggers::Command* command : triggerEvent->commands) { + HandleTriggerCommand(command, optionalTarget); + } + } + } + } +} + +void TriggerComponent::HandleTriggerCommand(LUTriggers::Command* command, Entity* optionalTarget) { + auto argArray = GeneralUtils::SplitString(command->args, ','); + + // determine targets + std::vector targetEntities = GatherTargets(command, optionalTarget); + + // if we have no targets, then we are done + if (targetEntities.empty()) return; + + for (Entity* targetEntity : targetEntities) { + if (!targetEntity) continue; + + switch (command->id) { + case eTriggerCommandType::ZONE_PLAYER: break; + case eTriggerCommandType::FIRE_EVENT: + HandleFireEvent(targetEntity, command->args); + break; + case eTriggerCommandType::DESTROY_OBJ: break; + case eTriggerCommandType::TOGGLE_TRIGGER: break; + case eTriggerCommandType::RESET_REBUILD: break; + case eTriggerCommandType::SET_PATH: break; + case eTriggerCommandType::SET_PICK_TYPE: break; + case eTriggerCommandType::MOVE_OBJECT: break; + case eTriggerCommandType::ROTATE_OBJECT: break; + case eTriggerCommandType::PUSH_OBJECT: break; + case eTriggerCommandType::REPEL_OBJECT: break; + case eTriggerCommandType::SET_TIMER: break; + case eTriggerCommandType::CANCEL_TIMER: break; + case eTriggerCommandType::PLAY_CINEMATIC: break; + case eTriggerCommandType::TOGGLE_BBB: break; + case eTriggerCommandType::UPDATE_MISSION: + HandleUpdateMission(targetEntity, argArray); + break; + case eTriggerCommandType::SET_BOUNCER_STATE: break; + case eTriggerCommandType::BOUNCE_ALL_ON_BOUNCER: break; + case eTriggerCommandType::TURN_AROUND_ON_PATH: break; + case eTriggerCommandType::GO_FORWARD_ON_PATH: break; + case eTriggerCommandType::GO_BACKWARD_ON_PATH: break; + case eTriggerCommandType::STOP_PATHING: break; + case eTriggerCommandType::START_PATHING: break; + case eTriggerCommandType::LOCK_OR_UNLOCK_CONTROLS: break; + case eTriggerCommandType::PLAY_EFFECT: break; + case eTriggerCommandType::STOP_EFFECT: break; + case eTriggerCommandType::ACTIVATE_MUSIC_CUE: break; + case eTriggerCommandType::DEACTIVATE_MUSIC_CUE: break; + case eTriggerCommandType::FLASH_MUSIC_CUE: break; + case eTriggerCommandType::SET_MUSIC_PARAMETER: break; + case eTriggerCommandType::PLAY_2D_AMBIENT_SOUND: break; + case eTriggerCommandType::STOP_2D_AMBIENT_SOUND: break; + case eTriggerCommandType::PLAY_3D_AMBIENT_SOUND: break; + case eTriggerCommandType::STOP_3D_AMBIENT_SOUND: break; + case eTriggerCommandType::ACTIVATE_MIXER_PROGRAM: break; + case eTriggerCommandType::DEACTIVATE_MIXER_PROGRAM: break; + case eTriggerCommandType::CAST_SKILL: break; + case eTriggerCommandType::DISPLAY_ZONE_SUMMARY: break; + case eTriggerCommandType::SET_PHYSICS_VOLUME_EFFECT: + HandleSetPhysicsVolume(targetEntity, argArray, command->target); + break; + case eTriggerCommandType::SET_PHYSICS_VOLUME_STATUS: break; + case eTriggerCommandType::SET_MODEL_TO_BUILD: break; + case eTriggerCommandType::SPAWN_MODEL_BRICKS: break; + case eTriggerCommandType::ACTIVATE_SPAWNER_NETWORK: break; + case eTriggerCommandType::DEACTIVATE_SPAWNER_NETWORK: break; + case eTriggerCommandType::RESET_SPAWNER_NETWORK: break; + case eTriggerCommandType::DESTROY_SPAWNER_NETWORK_OBJECTS: break; + case eTriggerCommandType::GO_TO_WAYPOINT: break; + case eTriggerCommandType::ACTIVATE_PHYSICS: break; + default: + Game::logger->LogDebug("TriggerComponent", "Event %i was not handled!", command->id); + break; + } + } +} + +std::vector TriggerComponent::GatherTargets(LUTriggers::Command* command, Entity* optionalTarget) { + std::vector entities = {}; + + if (command->target == "self") entities.push_back(m_Parent); + else if (command->target == "zone") { /*TODO*/ } + else if (command->target == "target") { /*TODO*/ } + else if (command->target == "targetTeam") { /*TODO*/ } + else if (command->target == "objGroup") entities = EntityManager::Instance()->GetEntitiesInGroup(command->targetName); + else if (command->target == "allPlayers") { /*TODO*/ } + else if (command->target == "allNPCs") { /*TODO*/ } + + if (optionalTarget) entities.push_back(optionalTarget); + + return entities; +} + +void TriggerComponent::HandleSetPhysicsVolume(Entity* targetEntity, std::vector argArray, std::string target) { + PhantomPhysicsComponent* phanPhys = m_Parent->GetComponent(); + if (!phanPhys) return; + + phanPhys->SetPhysicsEffectActive(true); + uint32_t effectType = 0; + std::transform(argArray.at(0).begin(), argArray.at(0).end(), argArray.at(0).begin(), ::tolower); //Transform to lowercase + if (argArray.at(0) == "push") effectType = 0; + else if (argArray.at(0) == "attract") effectType = 1; + else if (argArray.at(0) == "repulse") effectType = 2; + else if (argArray.at(0) == "gravity") effectType = 3; + else if (argArray.at(0) == "friction") effectType = 4; + + phanPhys->SetEffectType(effectType); + phanPhys->SetDirectionalMultiplier(std::stof(argArray.at(1))); + if (argArray.size() > 4) { + NiPoint3 direction = NiPoint3::ZERO; + GeneralUtils::TryParse(argArray.at(2), direction.x); + GeneralUtils::TryParse(argArray.at(3), direction.y); + GeneralUtils::TryParse(argArray.at(4), direction.z); + phanPhys->SetDirection(direction); + } + if (argArray.size() > 5) { + uint32_t min; + GeneralUtils::TryParse(argArray.at(6), min); + phanPhys->SetMin(min); + + uint32_t max; + GeneralUtils::TryParse(argArray.at(7), max); + phanPhys->SetMax(max); + } + + // TODO: why is this contruct and not serialize? + if (target == "self") EntityManager::Instance()->ConstructEntity(m_Parent); +} + +void TriggerComponent::HandleUpdateMission(Entity* targetEntity, std::vector argArray) { + CDMissionTasksTable* missionTasksTable = CDClientManager::Instance()->GetTable("MissionTasks"); + std::vector missionTasks = missionTasksTable->Query([=](CDMissionTasks entry) { + std::string lowerTargetGroup; + for (char character : entry.targetGroup) { + lowerTargetGroup.push_back(std::tolower(character)); // make lowercase to ensure it works + } + + return (lowerTargetGroup == argArray[4]); + }); + + for (const CDMissionTasks& task : missionTasks) { + MissionComponent* missionComponent = targetEntity->GetComponent(); + if (!missionComponent) continue; + + missionComponent->ForceProgress(task.id, task.uid, std::stoi(argArray[2])); + } +} + +void TriggerComponent::HandleFireEvent(Entity* targetEntity, std::string args) { + for (CppScripts::Script* script : CppScripts::GetEntityScripts(targetEntity)) { + script->OnFireEventServerSide(targetEntity, m_Parent, args, 0, 0, 0); + } +} + diff --git a/dGame/dComponents/TriggerComponent.h b/dGame/dComponents/TriggerComponent.h new file mode 100644 index 00000000..d7711696 --- /dev/null +++ b/dGame/dComponents/TriggerComponent.h @@ -0,0 +1,34 @@ +#ifndef __TRIGGERCOMPONENT__H__ +#define __TRIGGERCOMPONENT__H__ + +#include "Component.h" + +namespace LUTriggers { + struct Trigger; + struct Command; +}; + +class TriggerComponent : public Component { +public: + static const uint32_t ComponentType = COMPONENT_TYPE_TRIGGER; + + explicit TriggerComponent(Entity* parent, const std::string triggerInfo); + + void TriggerEvent(eTriggerEventType event, Entity* optionalTarget = nullptr); + LUTriggers::Trigger* GetTrigger() const { return m_Trigger; } + +private: + + void HandleTriggerCommand(LUTriggers::Command* command, Entity* optionalTarget); + std::vector ParseArgs(std::string args); + std::vector GatherTargets(LUTriggers::Command* command, Entity* optionalTarget); + + // Trigger Event Handlers + void HandleSetPhysicsVolume(Entity* targetEntity, std::vector argArray, std::string target); + void HandleUpdateMission(Entity* targetEntity, std::vector argArray); + void HandleFireEvent(Entity* targetEntity, std::string args); + void HandleCastSkill(Entity* targetEntity, uint32_t skillID); + + LUTriggers::Trigger* m_Trigger; +}; +#endif //!__TRIGGERCOMPONENT__H__ diff --git a/dGame/dUtilities/SlashCommandHandler.cpp b/dGame/dUtilities/SlashCommandHandler.cpp index d8be44c6..e558e19c 100644 --- a/dGame/dUtilities/SlashCommandHandler.cpp +++ b/dGame/dUtilities/SlashCommandHandler.cpp @@ -73,6 +73,7 @@ #include "MovingPlatformComponent.h" #include "dMessageIdentifiers.h" #include "eMissionState.h" +#include "TriggerComponent.h" void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entity* entity, const SystemAddress& sysAddr) { std::string chatCommand; @@ -1984,8 +1985,12 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit ChatPackets::SendSystemMessage(sysAddr, u"Active: " + (GeneralUtils::to_u16string(phantomPhysicsComponent->GetPhysicsEffectActive()))); } - if (closest->GetTrigger() != nullptr) { - ChatPackets::SendSystemMessage(sysAddr, u"Trigger: " + (GeneralUtils::to_u16string(closest->GetTrigger()->id))); + auto* triggerComponent = closest->GetComponent(); + if (triggerComponent){ + auto trigger = triggerComponent->GetTrigger(); + if (trigger) { + ChatPackets::SendSystemMessage(sysAddr, u"Trigger: " + (GeneralUtils::to_u16string(trigger->id))); + } } } } diff --git a/dZoneManager/LUTriggers.h b/dZoneManager/LUTriggers.h index 1869b4c3..a93cd67d 100644 --- a/dZoneManager/LUTriggers.h +++ b/dZoneManager/LUTriggers.h @@ -6,17 +6,20 @@ class Command; class Event; +enum class eTriggerCommandType; +enum class eTriggerEventType; + namespace LUTriggers { struct Command { - std::string id; + eTriggerCommandType id; std::string target; std::string targetName; std::string args; }; struct Event { - std::string eventID; + eTriggerEventType id; std::vector commands; }; diff --git a/dZoneManager/Zone.cpp b/dZoneManager/Zone.cpp index 79d940af..c32f8447 100644 --- a/dZoneManager/Zone.cpp +++ b/dZoneManager/Zone.cpp @@ -14,6 +14,9 @@ #include "Spawner.h" #include "dZoneManager.h" +#include "eTriggerCommandType.h" +#include "eTriggerEventType.h" + Zone::Zone(const LWOMAPID& mapID, const LWOINSTANCEID& instanceID, const LWOCLONEID& cloneID) : m_ZoneID(mapID, instanceID, cloneID) { m_NumberOfScenesLoaded = 0; @@ -296,11 +299,11 @@ std::vector Zone::LoadLUTriggers(std::string triggerFile, auto currentEvent = currentTrigger->FirstChildElement("event"); while (currentEvent) { LUTriggers::Event* newEvent = new LUTriggers::Event(); - newEvent->eventID = currentEvent->Attribute("id"); + newEvent->id = TriggerEventType::StringToTriggerEventType(currentEvent->Attribute("id")); auto currentCommand = currentEvent->FirstChildElement("command"); while (currentCommand) { LUTriggers::Command* newCommand = new LUTriggers::Command(); - newCommand->id = currentCommand->Attribute("id"); + newCommand->id = TriggerCommandType::StringToTriggerCommandType(currentCommand->Attribute("id")); newCommand->target = currentCommand->Attribute("target"); if (currentCommand->Attribute("targetName") != NULL) { newCommand->targetName = currentCommand->Attribute("targetName"); From 3cd0d1ec3df09b1cc741cd333760dd94a11c276b Mon Sep 17 00:00:00 2001 From: Aaron Kimbrell Date: Fri, 10 Feb 2023 02:30:17 -0600 Subject: [PATCH 2/2] Make wrapper for casting skills (#987) * Make wrapper for casting skills this is to reduce magic numbers in the code base Only updated one use of this to demo that this works. Will be do more in a sepearate PR. Also, inadvertantly fix damage stacking and self-damage in the teslapack * add skill<->behavior caching * explicit by reference * address emo's feedback --- dGame/dComponents/SkillComponent.cpp | 26 +++++++++++++++++++ dGame/dComponents/SkillComponent.h | 14 ++++++++++ .../EquipmentTriggers/CoilBackpackBase.cpp | 4 +-- dScripts/EquipmentTriggers/CoilBackpackBase.h | 4 +-- dScripts/EquipmentTriggers/GemPack.h | 3 +-- dScripts/EquipmentTriggers/ShardArmor.h | 3 +-- dScripts/EquipmentTriggers/TeslaPack.h | 3 +-- 7 files changed, 46 insertions(+), 11 deletions(-) diff --git a/dGame/dComponents/SkillComponent.cpp b/dGame/dComponents/SkillComponent.cpp index 562c284a..2391dc3b 100644 --- a/dGame/dComponents/SkillComponent.cpp +++ b/dGame/dComponents/SkillComponent.cpp @@ -22,10 +22,13 @@ #include "EchoStartSkill.h" #include "dMessageIdentifiers.h" #include "DoClientProjectileImpact.h" +#include "CDClientManager.h" ProjectileSyncEntry::ProjectileSyncEntry() { } +std::unordered_map SkillComponent::m_skillBehaviorCache = {}; + bool SkillComponent::CastPlayerSkill(const uint32_t behaviorId, const uint32_t skillUid, RakNet::BitStream* bitStream, const LWOOBJID target, uint32_t skillID) { auto* context = new BehaviorContext(this->m_Parent->GetObjectID()); @@ -210,6 +213,29 @@ void SkillComponent::RegisterCalculatedProjectile(const LWOOBJID projectileId, B this->m_managedProjectiles.push_back(entry); } +bool SkillComponent::CastSkill(const uint32_t skillId, LWOOBJID target, const LWOOBJID optionalOriginatorID){ + uint32_t behaviorId = -1; + // try to find it via the cache + const auto& pair = m_skillBehaviorCache.find(skillId); + + // if it's not in the cache look it up and cache it + if (pair == m_skillBehaviorCache.end()) { + auto skillTable = CDClientManager::Instance()->GetTable("SkillBehavior"); + behaviorId = skillTable->GetSkillByID(skillId).behaviorID; + m_skillBehaviorCache.insert_or_assign(skillId, behaviorId); + } else { + behaviorId = pair->second; + } + + // check to see if we got back a valid behavior + if (behaviorId == -1) { + Game::logger->LogDebug("SkillComponent", "Tried to cast skill %i but found no behavior", skillId); + return false; + } + + return CalculateBehavior(skillId, behaviorId, target, false, false, optionalOriginatorID).success; +} + SkillExecutionResult SkillComponent::CalculateBehavior(const uint32_t skillId, const uint32_t behaviorId, const LWOOBJID target, const bool ignoreTarget, const bool clientInitalized, const LWOOBJID originatorOverride) { auto* bitStream = new RakNet::BitStream(); diff --git a/dGame/dComponents/SkillComponent.h b/dGame/dComponents/SkillComponent.h index f43276f1..2bdcb88f 100644 --- a/dGame/dComponents/SkillComponent.h +++ b/dGame/dComponents/SkillComponent.h @@ -119,6 +119,15 @@ public: */ void RegisterPlayerProjectile(LWOOBJID projectileId, BehaviorContext* context, const BehaviorBranchContext& branch, LOT lot); + /** + * Wrapper for CalculateBehavior that mimics the call structure in scripts and helps reduce magic numbers + * @param skillId the skill to cast + * @param target the target of the skill + * @param optionalOriginatorID change the originator of the skill + * @return if the case succeeded + */ + bool CastSkill(const uint32_t skillId, LWOOBJID target = LWOOBJID_EMPTY, const LWOOBJID optionalOriginatorID = LWOOBJID_EMPTY); + /** * Initializes a server-side skill calculation. * @param skillId the skill ID @@ -190,6 +199,11 @@ private: */ uint32_t m_skillUid; + /** + * Cache for looking up a behavior id via a skill ID + */ + static std::unordered_map m_skillBehaviorCache; + /** * Sync a server-side projectile calculation. * @param entry the projectile information diff --git a/dScripts/EquipmentTriggers/CoilBackpackBase.cpp b/dScripts/EquipmentTriggers/CoilBackpackBase.cpp index d3102e0e..4e323a08 100644 --- a/dScripts/EquipmentTriggers/CoilBackpackBase.cpp +++ b/dScripts/EquipmentTriggers/CoilBackpackBase.cpp @@ -14,10 +14,10 @@ void CoilBackpackBase::NotifyHitOrHealResult(Entity* self, Entity* attacker, int if (self->GetVar(u"coilCount") > 4) { auto* skillComponent = self->GetComponent(); if (!skillComponent) return; - skillComponent->CalculateBehavior(m_SkillId, m_BehaviorId, self->GetObjectID()); + skillComponent->CastSkill(m_SkillId); self->SetVar(u"coilCount", 0); } - } + } } void CoilBackpackBase::OnFactionTriggerItemUnequipped(Entity* itemOwner, LWOOBJID itemObjId) { diff --git a/dScripts/EquipmentTriggers/CoilBackpackBase.h b/dScripts/EquipmentTriggers/CoilBackpackBase.h index 290c6c0f..2d641346 100644 --- a/dScripts/EquipmentTriggers/CoilBackpackBase.h +++ b/dScripts/EquipmentTriggers/CoilBackpackBase.h @@ -5,9 +5,8 @@ class CoilBackpackBase: public CppScripts::Script { public: - CoilBackpackBase(uint32_t skillId, uint32_t behaviorId) { + CoilBackpackBase(uint32_t skillId) { m_SkillId = skillId; - m_BehaviorId = behaviorId; }; void OnFactionTriggerItemEquipped(Entity* itemOwner, LWOOBJID itemObjId) override; @@ -15,7 +14,6 @@ public: void OnFactionTriggerItemUnequipped(Entity* itemOwner, LWOOBJID itemObjId) override; private: uint32_t m_SkillId = 0; - uint32_t m_BehaviorId = 0; }; #endif //!__GemPackBase__H__ diff --git a/dScripts/EquipmentTriggers/GemPack.h b/dScripts/EquipmentTriggers/GemPack.h index 13bf7b0b..d71181ab 100644 --- a/dScripts/EquipmentTriggers/GemPack.h +++ b/dScripts/EquipmentTriggers/GemPack.h @@ -5,10 +5,9 @@ class GemPack : public CoilBackpackBase { public: - GemPack() : CoilBackpackBase(skillId, behaviorId) {}; + GemPack() : CoilBackpackBase(skillId) {}; private: static const uint32_t skillId = 1488; - static const uint32_t behaviorId = 36779; }; #endif //!__GEMPACK__H__ diff --git a/dScripts/EquipmentTriggers/ShardArmor.h b/dScripts/EquipmentTriggers/ShardArmor.h index 01d2fe33..5486db54 100644 --- a/dScripts/EquipmentTriggers/ShardArmor.h +++ b/dScripts/EquipmentTriggers/ShardArmor.h @@ -5,10 +5,9 @@ class ShardArmor : public CoilBackpackBase { public: - ShardArmor() : CoilBackpackBase(skillId, behaviorId) {}; + ShardArmor() : CoilBackpackBase(skillId) {}; private: static const uint32_t skillId = 1249; - static const uint32_t behaviorId = 29086; }; #endif //!__SHARDARMOR__H__ diff --git a/dScripts/EquipmentTriggers/TeslaPack.h b/dScripts/EquipmentTriggers/TeslaPack.h index 6e8bc9a4..3ba09f5c 100644 --- a/dScripts/EquipmentTriggers/TeslaPack.h +++ b/dScripts/EquipmentTriggers/TeslaPack.h @@ -5,10 +5,9 @@ class TeslaPack : public CoilBackpackBase { public: - TeslaPack() : CoilBackpackBase(skillId, behaviorId) {}; + TeslaPack() : CoilBackpackBase(skillId) {}; private: static const uint32_t skillId = 1001; - static const uint32_t behaviorId = 20917; }; #endif //!__TESLAPACK__H__