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 ae70c892..3707e5db 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" #include "ItemComponent.h" Entity::Entity(const LWOOBJID& objectID, EntityInfo info, Entity* parentEntity) { @@ -77,7 +79,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; @@ -133,30 +134,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 @@ -770,7 +750,7 @@ void Entity::Initialize() { no_ghosting: - TriggerEvent("OnCreate"); + TriggerEvent(eTriggerEventType::CREATE); if (m_Character) { auto* controllablePhysicsComponent = GetComponent(); @@ -947,12 +927,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)); @@ -1312,7 +1296,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"); @@ -1346,7 +1330,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) { @@ -1394,7 +1378,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); @@ -1738,94 +1722,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 e237ccaf..18fbe19a 100644 --- a/dGame/dComponents/CMakeLists.txt +++ b/dGame/dComponents/CMakeLists.txt @@ -40,5 +40,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/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/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/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__ 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");