diff --git a/CMakeLists.txt b/CMakeLists.txt index 0f2b1bb7..ae6455b0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,8 +4,9 @@ include(CTest) set(CMAKE_CXX_STANDARD 20) set(CXX_STANDARD_REQUIRED ON) -set(CMAKE_POLICY_DEFAULT_CMP0063 NEW) # Set CMAKE visibility policy to NEW on project and subprojects -set(CMAKE_CXX_VISIBILITY_PRESET hidden) # Set C++ symbol visibility to default to hidden +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # Export the compile commands for debugging +set(CMAKE_POLICY_DEFAULT_CMP0063 NEW) # Set CMAKE visibility policy to NEW on project and subprojects +set(CMAKE_VISIBILITY_INLINES_HIDDEN ON) # Set C and C++ symbol visibility to hide inlined functions set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") # Read variables from file diff --git a/README.md b/README.md index a0dffaad..1caa0fb0 100644 --- a/README.md +++ b/README.md @@ -73,7 +73,7 @@ sudo apt install build-essential gcc zlib1g-dev libssl-dev openssl mariadb-serve ``` #### Required CMake version -This project uses **CMake version 3.18** or higher and as such you will need to ensure you have this version installed. +This project uses **CMake version 3.25** or higher and as such you will need to ensure you have this version installed. You can check your CMake version by using the following command in a terminal. ```bash cmake --version diff --git a/dChatServer/PlayerContainer.cpp b/dChatServer/PlayerContainer.cpp index 4e4d1be5..7feff763 100644 --- a/dChatServer/PlayerContainer.cpp +++ b/dChatServer/PlayerContainer.cpp @@ -390,7 +390,7 @@ LWOOBJID PlayerContainer::GetId(const std::u16string& playerName) { } PlayerData& PlayerContainer::GetPlayerDataMutable(const LWOOBJID& playerID) { - return m_Players[playerID]; + return m_Players.contains(playerID) ? m_Players[playerID] : m_Players[LWOOBJID_EMPTY]; } PlayerData& PlayerContainer::GetPlayerDataMutable(const std::string& playerName) { diff --git a/dCommon/GeneralUtils.h b/dCommon/GeneralUtils.h index c3422b05..d502f55a 100644 --- a/dCommon/GeneralUtils.h +++ b/dCommon/GeneralUtils.h @@ -166,7 +166,7 @@ namespace GeneralUtils { return isParsed ? static_cast(result) : std::optional{}; } -#ifdef DARKFLAME_PLATFORM_MACOS +#if !(__GNUC__ >= 11 || _MSC_VER >= 1924) // MacOS floating-point parse helper function specializations namespace details { diff --git a/dGame/Entity.cpp b/dGame/Entity.cpp index 23f65540..90763b4c 100644 --- a/dGame/Entity.cpp +++ b/dGame/Entity.cpp @@ -731,15 +731,21 @@ void Entity::Initialize() { // if we have a moving platform path, then we need a moving platform component if (path->pathType == PathType::MovingPlatform) { AddComponent(pathName); - // else if we are a movement path - } /*else if (path->pathType == PathType::Movement) { - auto movementAIcomp = GetComponent(); - if (movementAIcomp){ - // TODO: set path in existing movementAIComp + } else if (path->pathType == PathType::Movement) { + auto movementAIcomponent = GetComponent(); + if (movementAIcomponent && combatAiId == 0) { + movementAIcomponent->SetPath(pathName); } else { - // TODO: create movementAIcomp and set path + MovementAIInfo moveInfo = MovementAIInfo(); + moveInfo.movementType = ""; + moveInfo.wanderChance = 0; + moveInfo.wanderRadius = 16; + moveInfo.wanderSpeed = 2.5f; + moveInfo.wanderDelayMax = 5; + moveInfo.wanderDelayMin = 2; + AddComponent(moveInfo); } - }*/ + } } else { // else we still need to setup moving platform if it has a moving platform comp but no path int32_t movingPlatformComponentId = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MOVING_PLATFORM, -1); diff --git a/dGame/dBehaviors/BasicAttackBehavior.cpp b/dGame/dBehaviors/BasicAttackBehavior.cpp index 3d45d9a7..97ceb846 100644 --- a/dGame/dBehaviors/BasicAttackBehavior.cpp +++ b/dGame/dBehaviors/BasicAttackBehavior.cpp @@ -222,7 +222,7 @@ void BasicAttackBehavior::DoBehaviorCalculation(BehaviorContext* context, RakNet if (healthDamageDealt >= 1) { successState = eBasicAttackSuccessTypes::SUCCESS; } else if (armorDamageDealt >= 1) { - successState = this->m_OnFailArmor->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY ? eBasicAttackSuccessTypes::FAILIMMUNE : eBasicAttackSuccessTypes::FAILARMOR; + successState = this->m_OnFailArmor->m_templateId == BehaviorTemplate::EMPTY ? eBasicAttackSuccessTypes::FAILIMMUNE : eBasicAttackSuccessTypes::FAILARMOR; } bitStream.Write(armorDamageDealt); diff --git a/dGame/dBehaviors/Behavior.cpp b/dGame/dBehaviors/Behavior.cpp index 36607a66..4d57a9df 100644 --- a/dGame/dBehaviors/Behavior.cpp +++ b/dGame/dBehaviors/Behavior.cpp @@ -5,7 +5,7 @@ #include "CDActivitiesTable.h" #include "Game.h" #include "Logger.h" -#include "BehaviorTemplates.h" +#include "BehaviorTemplate.h" #include "BehaviorBranchContext.h" #include @@ -110,176 +110,176 @@ Behavior* Behavior::CreateBehavior(const uint32_t behaviorId) { Behavior* behavior = nullptr; switch (templateId) { - case BehaviorTemplates::BEHAVIOR_EMPTY: break; - case BehaviorTemplates::BEHAVIOR_BASIC_ATTACK: + case BehaviorTemplate::EMPTY: break; + case BehaviorTemplate::BASIC_ATTACK: behavior = new BasicAttackBehavior(behaviorId); break; - case BehaviorTemplates::BEHAVIOR_TAC_ARC: + case BehaviorTemplate::TAC_ARC: behavior = new TacArcBehavior(behaviorId); break; - case BehaviorTemplates::BEHAVIOR_AND: + case BehaviorTemplate::AND: behavior = new AndBehavior(behaviorId); break; - case BehaviorTemplates::BEHAVIOR_PROJECTILE_ATTACK: + case BehaviorTemplate::PROJECTILE_ATTACK: behavior = new ProjectileAttackBehavior(behaviorId); break; - case BehaviorTemplates::BEHAVIOR_HEAL: + case BehaviorTemplate::HEAL: behavior = new HealBehavior(behaviorId); break; - case BehaviorTemplates::BEHAVIOR_MOVEMENT_SWITCH: + case BehaviorTemplate::MOVEMENT_SWITCH: behavior = new MovementSwitchBehavior(behaviorId); break; - case BehaviorTemplates::BEHAVIOR_AREA_OF_EFFECT: + case BehaviorTemplate::AREA_OF_EFFECT: behavior = new AreaOfEffectBehavior(behaviorId); break; - case BehaviorTemplates::BEHAVIOR_PLAY_EFFECT: + case BehaviorTemplate::PLAY_EFFECT: behavior = new PlayEffectBehavior(behaviorId); break; - case BehaviorTemplates::BEHAVIOR_IMMUNITY: + case BehaviorTemplate::IMMUNITY: behavior = new ImmunityBehavior(behaviorId); break; - case BehaviorTemplates::BEHAVIOR_DAMAGE_BUFF: break; - case BehaviorTemplates::BEHAVIOR_DAMAGE_ABSORBTION: + case BehaviorTemplate::DAMAGE_BUFF: break; + case BehaviorTemplate::DAMAGE_ABSORBTION: behavior = new DamageAbsorptionBehavior(behaviorId); break; - case BehaviorTemplates::BEHAVIOR_OVER_TIME: + case BehaviorTemplate::OVER_TIME: behavior = new OverTimeBehavior(behaviorId); break; - case BehaviorTemplates::BEHAVIOR_IMAGINATION: + case BehaviorTemplate::IMAGINATION: behavior = new ImaginationBehavior(behaviorId); break; - case BehaviorTemplates::BEHAVIOR_TARGET_CASTER: + case BehaviorTemplate::TARGET_CASTER: behavior = new TargetCasterBehavior(behaviorId); break; - case BehaviorTemplates::BEHAVIOR_STUN: + case BehaviorTemplate::STUN: behavior = new StunBehavior(behaviorId); break; - case BehaviorTemplates::BEHAVIOR_DURATION: + case BehaviorTemplate::DURATION: behavior = new DurationBehavior(behaviorId); break; - case BehaviorTemplates::BEHAVIOR_KNOCKBACK: + case BehaviorTemplate::KNOCKBACK: behavior = new KnockbackBehavior(behaviorId); break; - case BehaviorTemplates::BEHAVIOR_ATTACK_DELAY: + case BehaviorTemplate::ATTACK_DELAY: behavior = new AttackDelayBehavior(behaviorId); break; - case BehaviorTemplates::BEHAVIOR_CAR_BOOST: + case BehaviorTemplate::CAR_BOOST: behavior = new CarBoostBehavior(behaviorId); break; - case BehaviorTemplates::BEHAVIOR_FALL_SPEED: + case BehaviorTemplate::FALL_SPEED: behavior = new FallSpeedBehavior(behaviorId); break; - case BehaviorTemplates::BEHAVIOR_SHIELD: break; - case BehaviorTemplates::BEHAVIOR_REPAIR_ARMOR: + case BehaviorTemplate::SHIELD: break; + case BehaviorTemplate::REPAIR_ARMOR: behavior = new RepairBehavior(behaviorId); break; - case BehaviorTemplates::BEHAVIOR_SPEED: + case BehaviorTemplate::SPEED: behavior = new SpeedBehavior(behaviorId); break; - case BehaviorTemplates::BEHAVIOR_DARK_INSPIRATION: + case BehaviorTemplate::DARK_INSPIRATION: behavior = new DarkInspirationBehavior(behaviorId); break; - case BehaviorTemplates::BEHAVIOR_LOOT_BUFF: + case BehaviorTemplate::LOOT_BUFF: behavior = new LootBuffBehavior(behaviorId); break; - case BehaviorTemplates::BEHAVIOR_VENTURE_VISION: + case BehaviorTemplate::VENTURE_VISION: behavior = new VentureVisionBehavior(behaviorId); break; - case BehaviorTemplates::BEHAVIOR_SPAWN_OBJECT: + case BehaviorTemplate::SPAWN_OBJECT: behavior = new SpawnBehavior(behaviorId); break; - case BehaviorTemplates::BEHAVIOR_LAY_BRICK: break; - case BehaviorTemplates::BEHAVIOR_SWITCH: + case BehaviorTemplate::LAY_BRICK: break; + case BehaviorTemplate::SWITCH: behavior = new SwitchBehavior(behaviorId); break; - case BehaviorTemplates::BEHAVIOR_BUFF: + case BehaviorTemplate::BUFF: behavior = new BuffBehavior(behaviorId); break; - case BehaviorTemplates::BEHAVIOR_JETPACK: + case BehaviorTemplate::JETPACK: behavior = new JetPackBehavior(behaviorId); break; - case BehaviorTemplates::BEHAVIOR_SKILL_EVENT: + case BehaviorTemplate::SKILL_EVENT: behavior = new SkillEventBehavior(behaviorId); break; - case BehaviorTemplates::BEHAVIOR_CONSUME_ITEM: + case BehaviorTemplate::CONSUME_ITEM: behavior = new ConsumeItemBehavior(behaviorId); break; - case BehaviorTemplates::BEHAVIOR_SKILL_CAST_FAILED: + case BehaviorTemplate::SKILL_CAST_FAILED: behavior = new SkillCastFailedBehavior(behaviorId); break; - case BehaviorTemplates::BEHAVIOR_IMITATION_SKUNK_STINK: break; - case BehaviorTemplates::BEHAVIOR_CHANGE_IDLE_FLAGS: + case BehaviorTemplate::IMITATION_SKUNK_STINK: break; + case BehaviorTemplate::CHANGE_IDLE_FLAGS: behavior = new ChangeIdleFlagsBehavior(behaviorId); break; - case BehaviorTemplates::BEHAVIOR_APPLY_BUFF: + case BehaviorTemplate::APPLY_BUFF: behavior = new ApplyBuffBehavior(behaviorId); break; - case BehaviorTemplates::BEHAVIOR_CHAIN: + case BehaviorTemplate::CHAIN: behavior = new ChainBehavior(behaviorId); break; - case BehaviorTemplates::BEHAVIOR_CHANGE_ORIENTATION: + case BehaviorTemplate::CHANGE_ORIENTATION: behavior = new ChangeOrientationBehavior(behaviorId); break; - case BehaviorTemplates::BEHAVIOR_FORCE_MOVEMENT: + case BehaviorTemplate::FORCE_MOVEMENT: behavior = new ForceMovementBehavior(behaviorId); break; - case BehaviorTemplates::BEHAVIOR_INTERRUPT: + case BehaviorTemplate::INTERRUPT: behavior = new InterruptBehavior(behaviorId); break; - case BehaviorTemplates::BEHAVIOR_ALTER_COOLDOWN: break; - case BehaviorTemplates::BEHAVIOR_CHARGE_UP: + case BehaviorTemplate::ALTER_COOLDOWN: break; + case BehaviorTemplate::CHARGE_UP: behavior = new ChargeUpBehavior(behaviorId); break; - case BehaviorTemplates::BEHAVIOR_SWITCH_MULTIPLE: + case BehaviorTemplate::SWITCH_MULTIPLE: behavior = new SwitchMultipleBehavior(behaviorId); break; - case BehaviorTemplates::BEHAVIOR_START: + case BehaviorTemplate::START: behavior = new StartBehavior(behaviorId); break; - case BehaviorTemplates::BEHAVIOR_END: + case BehaviorTemplate::END: behavior = new EndBehavior(behaviorId); break; - case BehaviorTemplates::BEHAVIOR_ALTER_CHAIN_DELAY: break; - case BehaviorTemplates::BEHAVIOR_CAMERA: break; - case BehaviorTemplates::BEHAVIOR_REMOVE_BUFF: + case BehaviorTemplate::ALTER_CHAIN_DELAY: break; + case BehaviorTemplate::CAMERA: break; + case BehaviorTemplate::REMOVE_BUFF: behavior = new RemoveBuffBehavior(behaviorId); break; - case BehaviorTemplates::BEHAVIOR_GRAB: break; - case BehaviorTemplates::BEHAVIOR_MODULAR_BUILD: break; - case BehaviorTemplates::BEHAVIOR_NPC_COMBAT_SKILL: + case BehaviorTemplate::GRAB: break; + case BehaviorTemplate::MODULAR_BUILD: break; + case BehaviorTemplate::NPC_COMBAT_SKILL: behavior = new NpcCombatSkillBehavior(behaviorId); break; - case BehaviorTemplates::BEHAVIOR_BLOCK: + case BehaviorTemplate::BLOCK: behavior = new BlockBehavior(behaviorId); break; - case BehaviorTemplates::BEHAVIOR_VERIFY: + case BehaviorTemplate::VERIFY: behavior = new VerifyBehavior(behaviorId); break; - case BehaviorTemplates::BEHAVIOR_TAUNT: + case BehaviorTemplate::TAUNT: behavior = new TauntBehavior(behaviorId); break; - case BehaviorTemplates::BEHAVIOR_AIR_MOVEMENT: + case BehaviorTemplate::AIR_MOVEMENT: behavior = new AirMovementBehavior(behaviorId); break; - case BehaviorTemplates::BEHAVIOR_SPAWN_QUICKBUILD: + case BehaviorTemplate::SPAWN_QUICKBUILD: behavior = new SpawnBehavior(behaviorId); break; - case BehaviorTemplates::BEHAVIOR_PULL_TO_POINT: + case BehaviorTemplate::PULL_TO_POINT: behavior = new PullToPointBehavior(behaviorId); break; - case BehaviorTemplates::BEHAVIOR_PROPERTY_ROTATE: break; - case BehaviorTemplates::BEHAVIOR_DAMAGE_REDUCTION: + case BehaviorTemplate::PROPERTY_ROTATE: break; + case BehaviorTemplate::DAMAGE_REDUCTION: behavior = new DamageReductionBehavior(behaviorId); break; - case BehaviorTemplates::BEHAVIOR_PROPERTY_TELEPORT: + case BehaviorTemplate::PROPERTY_TELEPORT: behavior = new PropertyTeleportBehavior(behaviorId); break; - case BehaviorTemplates::BEHAVIOR_PROPERTY_CLEAR_TARGET: + case BehaviorTemplate::PROPERTY_CLEAR_TARGET: behavior = new ClearTargetBehavior(behaviorId); break; - case BehaviorTemplates::BEHAVIOR_TAKE_PICTURE: break; - case BehaviorTemplates::BEHAVIOR_MOUNT: break; - case BehaviorTemplates::BEHAVIOR_SKILL_SET: break; + case BehaviorTemplate::TAKE_PICTURE: break; + case BehaviorTemplate::MOUNT: break; + case BehaviorTemplate::SKILL_SET: break; default: //LOG("Failed to load behavior with invalid template id (%i)!", templateId); break; @@ -296,19 +296,19 @@ Behavior* Behavior::CreateBehavior(const uint32_t behaviorId) { return behavior; } -BehaviorTemplates Behavior::GetBehaviorTemplate(const uint32_t behaviorId) { +BehaviorTemplate Behavior::GetBehaviorTemplate(const uint32_t behaviorId) { auto behaviorTemplateTable = CDClientManager::GetTable(); - BehaviorTemplates templateID = BehaviorTemplates::BEHAVIOR_EMPTY; + BehaviorTemplate templateID = BehaviorTemplate::EMPTY; // Find behavior template by its behavior id. Default to 0. if (behaviorTemplateTable) { auto templateEntry = behaviorTemplateTable->GetByBehaviorID(behaviorId); if (templateEntry.behaviorID == behaviorId) { - templateID = static_cast(templateEntry.templateID); + templateID = static_cast(templateEntry.templateID); } } - if (templateID == BehaviorTemplates::BEHAVIOR_EMPTY && behaviorId != 0) { + if (templateID == BehaviorTemplate::EMPTY && behaviorId != 0) { LOG("Failed to load behavior template with id (%i)!", behaviorId); } @@ -335,26 +335,22 @@ void Behavior::PlayFx(std::u16string type, const LWOOBJID target, const LWOOBJID const auto typeString = GeneralUtils::UTF16ToWTF8(type); - if (m_effectNames == nullptr) { - m_effectNames = new std::unordered_map(); - } else { - const auto pair = m_effectNames->find(typeString); + const auto itr = m_effectNames.find(typeString); - if (type.empty()) { - type = GeneralUtils::ASCIIToUTF16(*m_effectType); - } + if (type.empty()) { + type = GeneralUtils::ASCIIToUTF16(m_effectType); + } - if (pair != m_effectNames->end()) { - if (renderComponent == nullptr) { - GameMessages::SendPlayFXEffect(targetEntity, effectId, type, pair->second, secondary, 1, 1, true); - - return; - } - - renderComponent->PlayEffect(effectId, type, pair->second, secondary); + if (itr != m_effectNames.end()) { + if (renderComponent == nullptr) { + GameMessages::SendPlayFXEffect(targetEntity, effectId, type, itr->second, secondary, 1, 1, true); return; } + + renderComponent->PlayEffect(effectId, type, itr->second, secondary); + + return; } // The SQlite result object becomes invalid if the query object leaves scope. @@ -388,12 +384,12 @@ void Behavior::PlayFx(std::u16string type, const LWOOBJID target, const LWOOBJID type = GeneralUtils::ASCIIToUTF16(typeResult); - m_effectType = new std::string(typeResult); + m_effectType = typeResult; } result.finalize(); - m_effectNames->insert_or_assign(typeString, name); + m_effectNames.insert_or_assign(typeString, name); if (renderComponent == nullptr) { GameMessages::SendPlayFXEffect(targetEntity, effectId, type, name, secondary, 1, 1, true); @@ -423,8 +419,7 @@ Behavior::Behavior(const uint32_t behaviorId) { if (behaviorId == 0) { this->m_effectId = 0; - this->m_effectHandle = nullptr; - this->m_templateId = BehaviorTemplates::BEHAVIOR_EMPTY; + this->m_templateId = BehaviorTemplate::EMPTY; } // Make sure we do not proceed if we are trying to load an invalid behavior @@ -432,17 +427,16 @@ Behavior::Behavior(const uint32_t behaviorId) { LOG("Failed to load behavior with id (%i)!", behaviorId); this->m_effectId = 0; - this->m_effectHandle = nullptr; - this->m_templateId = BehaviorTemplates::BEHAVIOR_EMPTY; + this->m_templateId = BehaviorTemplate::EMPTY; return; } - this->m_templateId = static_cast(templateInDatabase.templateID); + this->m_templateId = static_cast(templateInDatabase.templateID); this->m_effectId = templateInDatabase.effectID; - this->m_effectHandle = *templateInDatabase.effectHandle != "" ? new std::string(*templateInDatabase.effectHandle) : nullptr; + this->m_effectHandle = *templateInDatabase.effectHandle; } @@ -507,9 +501,3 @@ void Behavior::Calculate(BehaviorContext* context, RakNet::BitStream& bitStream, void Behavior::SyncCalculation(BehaviorContext* context, RakNet::BitStream& bitStream, BehaviorBranchContext branch) { } - -Behavior::~Behavior() { - delete m_effectNames; - delete m_effectType; - delete m_effectHandle; -} diff --git a/dGame/dBehaviors/Behavior.h b/dGame/dBehaviors/Behavior.h index af7fd206..f9867ffe 100644 --- a/dGame/dBehaviors/Behavior.h +++ b/dGame/dBehaviors/Behavior.h @@ -6,7 +6,7 @@ #include #include "BitStream.h" -#include "BehaviorTemplates.h" +#include "BehaviorTemplate.h" #include "dCommonVars.h" struct BehaviorContext; @@ -26,7 +26,7 @@ public: static Behavior* CreateBehavior(uint32_t behaviorId); - static BehaviorTemplates GetBehaviorTemplate(uint32_t behaviorId); + static BehaviorTemplate GetBehaviorTemplate(uint32_t behaviorId); /* * Utilities @@ -39,11 +39,11 @@ public: */ uint32_t m_behaviorId; - BehaviorTemplates m_templateId; + BehaviorTemplate m_templateId; uint32_t m_effectId; - std::string* m_effectHandle = nullptr; - std::unordered_map* m_effectNames = nullptr; - std::string* m_effectType = nullptr; + std::string m_effectHandle; + std::unordered_map m_effectNames; + std::string m_effectType; /* * Behavior parameters @@ -88,5 +88,11 @@ public: */ explicit Behavior(uint32_t behaviorId); - virtual ~Behavior(); + virtual ~Behavior() = default; + + Behavior(const Behavior& other) = default; + Behavior(Behavior&& other) = default; + + Behavior& operator=(const Behavior& other) = default; + Behavior& operator=(Behavior&& other) = default; }; diff --git a/dGame/dBehaviors/BehaviorTemplate.h b/dGame/dBehaviors/BehaviorTemplate.h new file mode 100644 index 00000000..175dce50 --- /dev/null +++ b/dGame/dBehaviors/BehaviorTemplate.h @@ -0,0 +1,70 @@ +#pragma once + +enum class BehaviorTemplate : unsigned int { + EMPTY, // Not a real behavior, indicates invalid behaviors + BASIC_ATTACK, + TAC_ARC, + AND, + PROJECTILE_ATTACK, + HEAL, + MOVEMENT_SWITCH, + AREA_OF_EFFECT, + PLAY_EFFECT, + IMMUNITY, + DAMAGE_BUFF, + DAMAGE_ABSORBTION, + OVER_TIME, + IMAGINATION, + TARGET_CASTER, + STUN, + DURATION, + KNOCKBACK, + ATTACK_DELAY, + CAR_BOOST, + FALL_SPEED, + SHIELD, + REPAIR_ARMOR, + SPEED, + DARK_INSPIRATION, + LOOT_BUFF, + VENTURE_VISION, + SPAWN_OBJECT, + LAY_BRICK, + SWITCH, + BUFF, + JETPACK, + SKILL_EVENT, + CONSUME_ITEM, + SKILL_CAST_FAILED, + IMITATION_SKUNK_STINK, + CHANGE_IDLE_FLAGS, + APPLY_BUFF, + CHAIN, + CHANGE_ORIENTATION, + FORCE_MOVEMENT, + INTERRUPT, + ALTER_COOLDOWN, + CHARGE_UP, + SWITCH_MULTIPLE, + START, + END, + ALTER_CHAIN_DELAY, + CAMERA, + REMOVE_BUFF, + GRAB, + MODULAR_BUILD, + NPC_COMBAT_SKILL, + BLOCK, + VERIFY, + TAUNT, + AIR_MOVEMENT, + SPAWN_QUICKBUILD, + PULL_TO_POINT, + PROPERTY_ROTATE, + DAMAGE_REDUCTION, + PROPERTY_TELEPORT, + PROPERTY_CLEAR_TARGET, + TAKE_PICTURE, + MOUNT, + SKILL_SET +}; diff --git a/dGame/dBehaviors/BehaviorTemplates.cpp b/dGame/dBehaviors/BehaviorTemplates.cpp deleted file mode 100644 index 8719fbe7..00000000 --- a/dGame/dBehaviors/BehaviorTemplates.cpp +++ /dev/null @@ -1 +0,0 @@ -#include "BehaviorTemplates.h" diff --git a/dGame/dBehaviors/BehaviorTemplates.h b/dGame/dBehaviors/BehaviorTemplates.h deleted file mode 100644 index 87cde694..00000000 --- a/dGame/dBehaviors/BehaviorTemplates.h +++ /dev/null @@ -1,70 +0,0 @@ -#pragma once - -enum class BehaviorTemplates : unsigned int { - BEHAVIOR_EMPTY, // Not a real behavior, indicates invalid behaviors - BEHAVIOR_BASIC_ATTACK, - BEHAVIOR_TAC_ARC, - BEHAVIOR_AND, - BEHAVIOR_PROJECTILE_ATTACK, - BEHAVIOR_HEAL, - BEHAVIOR_MOVEMENT_SWITCH, - BEHAVIOR_AREA_OF_EFFECT, - BEHAVIOR_PLAY_EFFECT, - BEHAVIOR_IMMUNITY, - BEHAVIOR_DAMAGE_BUFF, - BEHAVIOR_DAMAGE_ABSORBTION, - BEHAVIOR_OVER_TIME, - BEHAVIOR_IMAGINATION, - BEHAVIOR_TARGET_CASTER, - BEHAVIOR_STUN, - BEHAVIOR_DURATION, - BEHAVIOR_KNOCKBACK, - BEHAVIOR_ATTACK_DELAY, - BEHAVIOR_CAR_BOOST, - BEHAVIOR_FALL_SPEED, - BEHAVIOR_SHIELD, - BEHAVIOR_REPAIR_ARMOR, - BEHAVIOR_SPEED, - BEHAVIOR_DARK_INSPIRATION, - BEHAVIOR_LOOT_BUFF, - BEHAVIOR_VENTURE_VISION, - BEHAVIOR_SPAWN_OBJECT, - BEHAVIOR_LAY_BRICK, - BEHAVIOR_SWITCH, - BEHAVIOR_BUFF, - BEHAVIOR_JETPACK, - BEHAVIOR_SKILL_EVENT, - BEHAVIOR_CONSUME_ITEM, - BEHAVIOR_SKILL_CAST_FAILED, - BEHAVIOR_IMITATION_SKUNK_STINK, - BEHAVIOR_CHANGE_IDLE_FLAGS, - BEHAVIOR_APPLY_BUFF, - BEHAVIOR_CHAIN, - BEHAVIOR_CHANGE_ORIENTATION, - BEHAVIOR_FORCE_MOVEMENT, - BEHAVIOR_INTERRUPT, - BEHAVIOR_ALTER_COOLDOWN, - BEHAVIOR_CHARGE_UP, - BEHAVIOR_SWITCH_MULTIPLE, - BEHAVIOR_START, - BEHAVIOR_END, - BEHAVIOR_ALTER_CHAIN_DELAY, - BEHAVIOR_CAMERA, - BEHAVIOR_REMOVE_BUFF, - BEHAVIOR_GRAB, - BEHAVIOR_MODULAR_BUILD, - BEHAVIOR_NPC_COMBAT_SKILL, - BEHAVIOR_BLOCK, - BEHAVIOR_VERIFY, - BEHAVIOR_TAUNT, - BEHAVIOR_AIR_MOVEMENT, - BEHAVIOR_SPAWN_QUICKBUILD, - BEHAVIOR_PULL_TO_POINT, - BEHAVIOR_PROPERTY_ROTATE, - BEHAVIOR_DAMAGE_REDUCTION, - BEHAVIOR_PROPERTY_TELEPORT, - BEHAVIOR_PROPERTY_CLEAR_TARGET, - BEHAVIOR_TAKE_PICTURE, - BEHAVIOR_MOUNT, - BEHAVIOR_SKILL_SET -}; diff --git a/dGame/dBehaviors/CMakeLists.txt b/dGame/dBehaviors/CMakeLists.txt index f00ba7e2..35d8cae6 100644 --- a/dGame/dBehaviors/CMakeLists.txt +++ b/dGame/dBehaviors/CMakeLists.txt @@ -7,7 +7,6 @@ set(DGAME_DBEHAVIORS_SOURCES "AirMovementBehavior.cpp" "Behavior.cpp" "BehaviorBranchContext.cpp" "BehaviorContext.cpp" - "BehaviorTemplates.cpp" "BlockBehavior.cpp" "BuffBehavior.cpp" "CarBoostBehavior.cpp" diff --git a/dGame/dBehaviors/ForceMovementBehavior.cpp b/dGame/dBehaviors/ForceMovementBehavior.cpp index 83c6fabc..04dad715 100644 --- a/dGame/dBehaviors/ForceMovementBehavior.cpp +++ b/dGame/dBehaviors/ForceMovementBehavior.cpp @@ -7,7 +7,7 @@ #include "Logger.h" void ForceMovementBehavior::Handle(BehaviorContext* context, RakNet::BitStream& bitStream, const BehaviorBranchContext branch) { - if (this->m_hitAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY && this->m_hitEnemyAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY && this->m_hitFactionAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY) { + if (this->m_hitAction->m_templateId == BehaviorTemplate::EMPTY && this->m_hitEnemyAction->m_templateId == BehaviorTemplate::EMPTY && this->m_hitFactionAction->m_templateId == BehaviorTemplate::EMPTY) { return; } @@ -38,7 +38,7 @@ void ForceMovementBehavior::Sync(BehaviorContext* context, RakNet::BitStream& bi } void ForceMovementBehavior::Calculate(BehaviorContext* context, RakNet::BitStream& bitStream, BehaviorBranchContext branch) { - if (this->m_hitAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY && this->m_hitEnemyAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY && this->m_hitFactionAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY) { + if (this->m_hitAction->m_templateId == BehaviorTemplate::EMPTY && this->m_hitEnemyAction->m_templateId == BehaviorTemplate::EMPTY && this->m_hitFactionAction->m_templateId == BehaviorTemplate::EMPTY) { return; } diff --git a/dGame/dBehaviors/InterruptBehavior.cpp b/dGame/dBehaviors/InterruptBehavior.cpp index 0b23c34d..c2f2fe70 100644 --- a/dGame/dBehaviors/InterruptBehavior.cpp +++ b/dGame/dBehaviors/InterruptBehavior.cpp @@ -8,36 +8,50 @@ void InterruptBehavior::Handle(BehaviorContext* context, RakNet::BitStream& bitStream, BehaviorBranchContext branch) { - if (branch.target != context->originator) { - bool unknown = false; + LWOOBJID usedTarget = m_target ? branch.target : context->originator; - if (!bitStream.Read(unknown)) { - LOG("Unable to read unknown1 from bitStream, aborting Handle! %i", bitStream.GetNumberOfUnreadBits()); + if (usedTarget != context->originator) { + bool isTargetImmuneStuns = false; + if (!bitStream.Read(isTargetImmuneStuns)) { + LOG("Unable to read isTargetImmune from bitStream, aborting Handle! %i", bitStream.GetNumberOfUnreadBits()); return; }; - if (unknown) return; + if (isTargetImmuneStuns) return; } if (!this->m_interruptBlock) { - bool unknown = false; - - if (!bitStream.Read(unknown)) { - LOG("Unable to read unknown2 from bitStream, aborting Handle! %i", bitStream.GetNumberOfUnreadBits()); + bool isBlockingInterrupts = false; + if (!bitStream.Read(isBlockingInterrupts)) { + LOG("Unable to read isBlockingInterrupts from bitStream, aborting Handle! %i", bitStream.GetNumberOfUnreadBits()); return; }; - if (unknown) return; + if (isBlockingInterrupts) return; } - if (this->m_target) // Guess... - { - bool unknown = false; + bool hasInterruptedStatusEffects = false; + if (!bitStream.Read(hasInterruptedStatusEffects)) { + LOG("Unable to read hasInterruptedStatusEffects from bitStream, aborting Handle! %i", bitStream.GetNumberOfUnreadBits()); + return; + }; - if (!bitStream.Read(unknown)) { - LOG("Unable to read unknown3 from bitStream, aborting Handle! %i", bitStream.GetNumberOfUnreadBits()); - return; - }; + if (hasInterruptedStatusEffects) { + bool hasMoreInterruptedStatusEffects = false; + int32_t loopLimit = 0; + while (bitStream.Read(hasMoreInterruptedStatusEffects) && hasMoreInterruptedStatusEffects) { + int32_t statusEffectID = 0; + bitStream.Read(statusEffectID); + // nothing happens with this data yes. I have no idea why or what it was used for, but the client literally just reads it and does nothing with it. + // 0x004faca4 for a reference. it also has a hard loop limit of 100 soo, + loopLimit++; + if (loopLimit > 100) { + // if this is hit you have a problem + LOG("Loop limit reached for interrupted status effects, aborting Handle due to bad bitstream! %i", bitStream.GetNumberOfUnreadBits()); + break; + } + LOG_DEBUG("Interrupted status effect ID: %i", statusEffectID); + } } if (branch.target == context->originator) return; @@ -55,7 +69,8 @@ void InterruptBehavior::Handle(BehaviorContext* context, RakNet::BitStream& bitS void InterruptBehavior::Calculate(BehaviorContext* context, RakNet::BitStream& bitStream, BehaviorBranchContext branch) { - if (branch.target != context->originator) { + LWOOBJID usedTarget = m_target ? branch.target : context->originator; + if (usedTarget != context->originator) { bitStream.Write(false); } diff --git a/dGame/dBehaviors/MovementSwitchBehavior.cpp b/dGame/dBehaviors/MovementSwitchBehavior.cpp index cc2d7b34..7d7d3a17 100644 --- a/dGame/dBehaviors/MovementSwitchBehavior.cpp +++ b/dGame/dBehaviors/MovementSwitchBehavior.cpp @@ -6,13 +6,13 @@ void MovementSwitchBehavior::Handle(BehaviorContext* context, RakNet::BitStream& bitStream, const BehaviorBranchContext branch) { uint32_t movementType{}; if (!bitStream.Read(movementType)) { - if (this->m_groundAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY && - this->m_jumpAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY && - this->m_fallingAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY && - this->m_doubleJumpAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY && - this->m_airAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY && - this->m_jetpackAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY && - this->m_movingAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY) { + if (this->m_groundAction->m_templateId == BehaviorTemplate::EMPTY && + this->m_jumpAction->m_templateId == BehaviorTemplate::EMPTY && + this->m_fallingAction->m_templateId == BehaviorTemplate::EMPTY && + this->m_doubleJumpAction->m_templateId == BehaviorTemplate::EMPTY && + this->m_airAction->m_templateId == BehaviorTemplate::EMPTY && + this->m_jetpackAction->m_templateId == BehaviorTemplate::EMPTY && + this->m_movingAction->m_templateId == BehaviorTemplate::EMPTY) { return; } LOG("Unable to read movementType from bitStream, aborting Handle! %i", bitStream.GetNumberOfUnreadBits()); @@ -47,7 +47,7 @@ void MovementSwitchBehavior::Handle(BehaviorContext* context, RakNet::BitStream& Behavior* MovementSwitchBehavior::LoadMovementType(std::string movementType) { float actionValue = GetFloat(movementType, -1.0f); auto loadedBehavior = GetAction(actionValue != -1.0f ? actionValue : 0.0f); - if (actionValue == -1.0f && loadedBehavior->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY) { + if (actionValue == -1.0f && loadedBehavior->m_templateId == BehaviorTemplate::EMPTY) { loadedBehavior = this->m_groundAction; } return loadedBehavior; diff --git a/dGame/dBehaviors/SkillEventBehavior.cpp b/dGame/dBehaviors/SkillEventBehavior.cpp index 2de801a2..9161f5d3 100644 --- a/dGame/dBehaviors/SkillEventBehavior.cpp +++ b/dGame/dBehaviors/SkillEventBehavior.cpp @@ -9,17 +9,16 @@ void SkillEventBehavior::Handle(BehaviorContext* context, RakNet::BitStream& bit auto* target = Game::entityManager->GetEntity(branch.target); auto* caster = Game::entityManager->GetEntity(context->originator); - if (caster != nullptr && target != nullptr && this->m_effectHandle != nullptr && !this->m_effectHandle->empty()) { - target->GetScript()->OnSkillEventFired(target, caster, *this->m_effectHandle); + if (caster != nullptr && target != nullptr && !this->m_effectHandle.empty()) { + target->GetScript()->OnSkillEventFired(target, caster, this->m_effectHandle); } } -void -SkillEventBehavior::Calculate(BehaviorContext* context, RakNet::BitStream& bitStream, BehaviorBranchContext branch) { +void SkillEventBehavior::Calculate(BehaviorContext* context, RakNet::BitStream& bitStream, BehaviorBranchContext branch) { auto* target = Game::entityManager->GetEntity(branch.target); auto* caster = Game::entityManager->GetEntity(context->originator); - if (caster != nullptr && target != nullptr && this->m_effectHandle != nullptr && !this->m_effectHandle->empty()) { - target->GetScript()->OnSkillEventFired(target, caster, *this->m_effectHandle); + if (caster != nullptr && target != nullptr && !this->m_effectHandle.empty()) { + target->GetScript()->OnSkillEventFired(target, caster, this->m_effectHandle); } } diff --git a/dGame/dBehaviors/SwitchBehavior.cpp b/dGame/dBehaviors/SwitchBehavior.cpp index 88f2d85a..a53b0001 100644 --- a/dGame/dBehaviors/SwitchBehavior.cpp +++ b/dGame/dBehaviors/SwitchBehavior.cpp @@ -7,9 +7,9 @@ #include "BuffComponent.h" void SwitchBehavior::Handle(BehaviorContext* context, RakNet::BitStream& bitStream, const BehaviorBranchContext branch) { - auto state = true; + bool state = true; - if (this->m_imagination > 0 || !this->m_isEnemyFaction) { + if (m_imagination > 0 || m_targetHasBuff > 0 || m_Distance > -1.0f) { if (!bitStream.Read(state)) { LOG("Unable to read state from bitStream, aborting Handle! %i", bitStream.GetNumberOfUnreadBits()); return; @@ -18,49 +18,59 @@ void SwitchBehavior::Handle(BehaviorContext* context, RakNet::BitStream& bitStre auto* entity = Game::entityManager->GetEntity(context->originator); - if (entity == nullptr) { - return; - } + if (!entity) return; auto* destroyableComponent = entity->GetComponent(); - if (destroyableComponent == nullptr) { - return; + if (destroyableComponent) { + if (m_isEnemyFaction) { + auto* target = Game::entityManager->GetEntity(branch.target); + if (target) state = destroyableComponent->IsEnemy(target); + } + + LOG_DEBUG("[%i] State: (%d), imagination: (%i) / (%f)", entity->GetLOT(), state, destroyableComponent->GetImagination(), destroyableComponent->GetMaxImagination()); } - LOG_DEBUG("[%i] State: (%d), imagination: (%i) / (%f)", entity->GetLOT(), state, destroyableComponent->GetImagination(), destroyableComponent->GetMaxImagination()); - - if (state) { - this->m_actionTrue->Handle(context, bitStream, branch); - } else { - this->m_actionFalse->Handle(context, bitStream, branch); - } + auto* behaviorToCall = state ? m_actionTrue : m_actionFalse; + behaviorToCall->Handle(context, bitStream, branch); } void SwitchBehavior::Calculate(BehaviorContext* context, RakNet::BitStream& bitStream, BehaviorBranchContext branch) { - auto state = true; - - if (this->m_imagination > 0 || !this->m_isEnemyFaction) { + bool state = true; + if (m_imagination > 0 || m_targetHasBuff > 0 || m_Distance > -1.0f) { auto* entity = Game::entityManager->GetEntity(branch.target); state = entity != nullptr; - if (state && m_targetHasBuff != 0) { - auto* buffComponent = entity->GetComponent(); + if (state) { + if (m_targetHasBuff != 0) { + auto* buffComponent = entity->GetComponent(); - if (buffComponent != nullptr && !buffComponent->HasBuff(m_targetHasBuff)) { - state = false; + if (buffComponent != nullptr && !buffComponent->HasBuff(m_targetHasBuff)) { + state = false; + } + } else if (m_imagination > 0) { + auto* destroyableComponent = entity->GetComponent(); + + if (destroyableComponent && destroyableComponent->GetImagination() < m_imagination) { + state = false; + } + } else if (m_Distance > -1.0f) { + auto* originator = Game::entityManager->GetEntity(context->originator); + + if (originator) { + const auto distance = (originator->GetPosition() - entity->GetPosition()).Length(); + + state = distance <= m_Distance; + } } } bitStream.Write(state); } - if (state) { - this->m_actionTrue->Calculate(context, bitStream, branch); - } else { - this->m_actionFalse->Calculate(context, bitStream, branch); - } + auto* behaviorToCall = state ? m_actionTrue : m_actionFalse; + behaviorToCall->Calculate(context, bitStream, branch); } void SwitchBehavior::Load() { @@ -72,5 +82,7 @@ void SwitchBehavior::Load() { this->m_isEnemyFaction = GetBoolean("isEnemyFaction"); - this->m_targetHasBuff = GetInt("target_has_buff"); + this->m_targetHasBuff = GetInt("target_has_buff", -1); + + this->m_Distance = GetFloat("distance", -1.0f); } diff --git a/dGame/dBehaviors/SwitchBehavior.h b/dGame/dBehaviors/SwitchBehavior.h index b7fcb653..1a8ed3a8 100644 --- a/dGame/dBehaviors/SwitchBehavior.h +++ b/dGame/dBehaviors/SwitchBehavior.h @@ -14,6 +14,8 @@ public: int32_t m_targetHasBuff; + float m_Distance; + /* * Inherited */ diff --git a/dGame/dComponents/BaseCombatAIComponent.cpp b/dGame/dComponents/BaseCombatAIComponent.cpp index d40cf73e..2bd2b6f1 100644 --- a/dGame/dComponents/BaseCombatAIComponent.cpp +++ b/dGame/dComponents/BaseCombatAIComponent.cpp @@ -162,7 +162,6 @@ void BaseCombatAIComponent::Update(const float deltaTime) { // Check if we should stop the tether effect if (m_TetherEffectActive) { m_TetherTime -= deltaTime; - const auto& info = m_MovementAI->GetInfo(); if (m_Target != LWOOBJID_EMPTY || (NiPoint3::DistanceSquared( m_StartPosition, m_Parent->GetPosition()) < 20 * 20 && m_TetherTime <= 0) diff --git a/dGame/dComponents/MovementAIComponent.cpp b/dGame/dComponents/MovementAIComponent.cpp index 8377031a..b6a16803 100644 --- a/dGame/dComponents/MovementAIComponent.cpp +++ b/dGame/dComponents/MovementAIComponent.cpp @@ -50,9 +50,44 @@ MovementAIComponent::MovementAIComponent(Entity* parent, MovementAIInfo info) : m_CurrentSpeed = 0; m_MaxSpeed = 0; m_LockRotation = false; + m_Path = nullptr; + m_SourcePosition = m_Parent->GetPosition(); + m_Paused = false; + m_SavedVelocity = NiPoint3Constant::ZERO; + + if (!m_Parent->GetComponent()) SetPath(m_Parent->GetVarAsString(u"attached_path")); +} + +void MovementAIComponent::SetPath(const std::string pathName) { + m_Path = Game::zoneManager->GetZone()->GetPath(pathName); + if (!pathName.empty()) LOG("WARNING: %s path %s", m_Path ? "Found" : "Failed to find", pathName.c_str()); + if (!m_Path) return; + SetMaxSpeed(1); + SetCurrentSpeed(m_BaseSpeed); + SetPath(m_Path->pathWaypoints); +} + +void MovementAIComponent::Pause() { + if (m_Paused) return; + m_Paused = true; + SetPosition(ApproximateLocation()); + m_SavedVelocity = GetVelocity(); + SetVelocity(NiPoint3Constant::ZERO); + Game::entityManager->SerializeEntity(m_Parent); +} + +void MovementAIComponent::Resume() { + if (!m_Paused) return; + m_Paused = false; + SetVelocity(m_SavedVelocity); + m_SavedVelocity = NiPoint3Constant::ZERO; + SetRotation(NiQuaternion::LookAt(m_Parent->GetPosition(), m_NextWaypoint)); + Game::entityManager->SerializeEntity(m_Parent); } void MovementAIComponent::Update(const float deltaTime) { + if (m_Paused) return; + if (m_PullingToPoint) { const auto source = GetCurrentWaypoint(); @@ -81,64 +116,65 @@ void MovementAIComponent::Update(const float deltaTime) { } m_TimeTravelled += deltaTime; + + SetPosition(ApproximateLocation()); + if (m_TimeTravelled < m_TimeToTravel) return; m_TimeTravelled = 0.0f; const auto source = GetCurrentWaypoint(); SetPosition(source); - - NiPoint3 velocity = NiPoint3Constant::ZERO; + m_SourcePosition = source; if (m_Acceleration > 0 && m_BaseSpeed > 0 && AdvanceWaypointIndex()) // Do we have another waypoint to seek? { m_NextWaypoint = GetCurrentWaypoint(); - if (m_NextWaypoint == source) { m_TimeToTravel = 0.0f; + } else { + m_CurrentSpeed = std::min(m_CurrentSpeed + m_Acceleration, m_MaxSpeed); - goto nextAction; + const auto speed = m_CurrentSpeed * m_BaseSpeed; // scale speed based on base speed + + const auto delta = m_NextWaypoint - source; + + // Normalize the vector + const auto length = delta.Length(); + if (length > 0.0f) { + SetVelocity((delta / length) * speed); + } + + // Calclute the time it will take to reach the next waypoint with the current speed + m_TimeTravelled = 0.0f; + m_TimeToTravel = length / speed; + + SetRotation(NiQuaternion::LookAt(source, m_NextWaypoint)); } - - if (m_CurrentSpeed < m_MaxSpeed) { - m_CurrentSpeed += m_Acceleration; - } - - if (m_CurrentSpeed > m_MaxSpeed) { - m_CurrentSpeed = m_MaxSpeed; - } - - const auto speed = m_CurrentSpeed * m_BaseSpeed; // scale speed based on base speed - - const auto delta = m_NextWaypoint - source; - - // Normalize the vector - const auto length = delta.Length(); - if (length > 0) { - velocity = (delta / length) * speed; - } - - // Calclute the time it will take to reach the next waypoint with the current speed - m_TimeTravelled = 0.0f; - m_TimeToTravel = length / speed; - - SetRotation(NiQuaternion::LookAt(source, m_NextWaypoint)); } else { // Check if there are more waypoints in the queue, if so set our next destination to the next waypoint if (m_CurrentPath.empty()) { - Stop(); - - return; + if (m_Path) { + if (m_Path->pathBehavior == PathBehavior::Loop) { + SetPath(m_Path->pathWaypoints); + } else if (m_Path->pathBehavior == PathBehavior::Bounce) { + std::vector waypoints = m_Path->pathWaypoints; + std::reverse(waypoints.begin(), waypoints.end()); + SetPath(waypoints); + } else if (m_Path->pathBehavior == PathBehavior::Once) { + Stop(); + return; + } + } else { + Stop(); + return; + } } - SetDestination(m_CurrentPath.top()); + SetDestination(m_CurrentPath.top().position); m_CurrentPath.pop(); } -nextAction: - - SetVelocity(velocity); - Game::entityManager->SerializeEntity(m_Parent); } @@ -161,7 +197,7 @@ NiPoint3 MovementAIComponent::GetCurrentWaypoint() const { } NiPoint3 MovementAIComponent::ApproximateLocation() const { - auto source = m_Parent->GetPosition(); + auto source = m_SourcePosition; if (AtFinalWaypoint()) return source; @@ -227,13 +263,13 @@ void MovementAIComponent::PullToPoint(const NiPoint3& point) { m_PullPoint = point; } -void MovementAIComponent::SetPath(std::vector path) { +void MovementAIComponent::SetPath(std::vector path) { if (path.empty()) return; - std::for_each(path.rbegin(), path.rend() - 1, [this](const NiPoint3& point) { + std::for_each(path.rbegin(), path.rend() - 1, [this](const PathWaypoint& point) { this->m_CurrentPath.push(point); }); - SetDestination(path.front()); + SetDestination(path.front().position); } float MovementAIComponent::GetBaseSpeed(LOT lot) { @@ -278,6 +314,23 @@ void MovementAIComponent::SetRotation(const NiQuaternion& value) { if (!m_LockRotation) m_Parent->SetRotation(value); } +NiPoint3 MovementAIComponent::GetVelocity() const { + auto* controllablePhysicsComponent = m_Parent->GetComponent(); + + if (controllablePhysicsComponent != nullptr) { + return controllablePhysicsComponent->GetVelocity(); + } + + auto* simplePhysicsComponent = m_Parent->GetComponent(); + + if (simplePhysicsComponent != nullptr) { + return simplePhysicsComponent->GetVelocity(); + } + + return NiPoint3Constant::ZERO; + +} + void MovementAIComponent::SetVelocity(const NiPoint3& value) { auto* controllablePhysicsComponent = m_Parent->GetComponent(); @@ -294,7 +347,7 @@ void MovementAIComponent::SetVelocity(const NiPoint3& value) { } } -void MovementAIComponent::SetDestination(const NiPoint3& destination) { +void MovementAIComponent::SetDestination(const NiPoint3 destination) { if (m_PullingToPoint) return; const auto location = ApproximateLocation(); @@ -303,6 +356,8 @@ void MovementAIComponent::SetDestination(const NiPoint3& destination) { SetPosition(location); } + m_SourcePosition = location; + std::vector computedPath; if (dpWorld::IsLoaded()) { computedPath = dpWorld::GetNavMesh()->GetPath(m_Parent->GetPosition(), destination, m_Info.wanderSpeed); @@ -319,8 +374,7 @@ void MovementAIComponent::SetDestination(const NiPoint3& destination) { auto step = delta / 10.0f; for (int i = 0; i < 10; i++) { - // TODO: Replace this with += when the NiPoint3::operator+= is fixed - start = start + step; + start += step; computedPath.push_back(start); } diff --git a/dGame/dComponents/MovementAIComponent.h b/dGame/dComponents/MovementAIComponent.h index 852f7001..15b5aaed 100644 --- a/dGame/dComponents/MovementAIComponent.h +++ b/dGame/dComponents/MovementAIComponent.h @@ -14,11 +14,14 @@ #include "Logger.h" #include "Component.h" #include "eReplicaComponentType.h" +#include "Zone.h" #include class ControllablePhysicsComponent; class BaseCombatAIComponent; +struct Path; + /** * Information that describes the different variables used to make an entity move around */ @@ -61,6 +64,8 @@ public: MovementAIComponent(Entity* parentEntity, MovementAIInfo info); + void SetPath(const std::string pathName); + void Update(float deltaTime) override; /** @@ -73,7 +78,7 @@ public: * Set a destination point for the entity to move towards * @param value the destination point to move towards */ - void SetDestination(const NiPoint3& value); + void SetDestination(const NiPoint3 value); /** * Returns the current rotation this entity is moving towards @@ -189,7 +194,13 @@ public: * Sets a path to follow for the AI * @param path the path to follow */ - void SetPath(std::vector path); + void SetPath(std::vector path); + + void Pause(); + + void Resume(); + + NiPoint3 GetVelocity() const; /** * Returns the base speed from the DB for a given LOT @@ -301,7 +312,15 @@ private: /** * The path from the current position to the destination. */ - std::stack m_CurrentPath; + std::stack m_CurrentPath; + + const Path* m_Path = nullptr; + + NiPoint3 m_SourcePosition; + + bool m_Paused; + + NiPoint3 m_SavedVelocity; }; #endif // MOVEMENTAICOMPONENT_H diff --git a/dGame/dComponents/ProximityMonitorComponent.cpp b/dGame/dComponents/ProximityMonitorComponent.cpp index fbac8ddb..9ab3f1db 100644 --- a/dGame/dComponents/ProximityMonitorComponent.cpp +++ b/dGame/dComponents/ProximityMonitorComponent.cpp @@ -64,6 +64,7 @@ void ProximityMonitorComponent::Update(float deltaTime) { for (const auto& prox : m_ProximitiesData) { if (!prox.second) continue; + prox.second->SetPosition(m_Parent->GetPosition()); //Process enter events for (auto* en : prox.second->GetNewObjects()) { m_Parent->OnCollisionProximity(en->GetObjectID(), prox.first, "ENTER"); diff --git a/dScripts/02_server/Map/AM/WanderingVendor.cpp b/dScripts/02_server/Map/AM/WanderingVendor.cpp index ae49aa94..742741d3 100644 --- a/dScripts/02_server/Map/AM/WanderingVendor.cpp +++ b/dScripts/02_server/Map/AM/WanderingVendor.cpp @@ -1,11 +1,11 @@ #include "WanderingVendor.h" #include "MovementAIComponent.h" #include "ProximityMonitorComponent.h" +#include void WanderingVendor::OnStartup(Entity* self) { auto movementAIComponent = self->GetComponent(); if (!movementAIComponent) return; - // movementAIComponent->Resume(); self->SetProximityRadius(10, "playermonitor"); } @@ -13,14 +13,23 @@ void WanderingVendor::OnProximityUpdate(Entity* self, Entity* entering, std::str if (status == "ENTER" && entering->IsPlayer()) { auto movementAIComponent = self->GetComponent(); if (!movementAIComponent) return; - // movementAIComponent->Pause(); + movementAIComponent->Pause(); self->CancelTimer("startWalking"); } else if (status == "LEAVE") { auto* proximityMonitorComponent = self->GetComponent(); if (!proximityMonitorComponent) self->AddComponent(); const auto proxObjs = proximityMonitorComponent->GetProximityObjects("playermonitor"); - if (proxObjs.empty()) self->AddTimer("startWalking", 1.5); + bool foundPlayer = false; + for (const auto id : proxObjs | std::views::keys) { + auto* entity = Game::entityManager->GetEntity(id); + if (entity && entity->IsPlayer()) { + foundPlayer = true; + break; + } + } + + if (!foundPlayer) self->AddTimer("startWalking", 1.5); } } @@ -28,6 +37,6 @@ void WanderingVendor::OnTimerDone(Entity* self, std::string timerName) { if (timerName == "startWalking") { auto movementAIComponent = self->GetComponent(); if (!movementAIComponent) return; - // movementAIComponent->Resume(); + movementAIComponent->Resume(); } } diff --git a/dScripts/ai/MINIGAME/SG_GF/SERVER/SGCannon.cpp b/dScripts/ai/MINIGAME/SG_GF/SERVER/SGCannon.cpp index 1952831a..56f5b257 100644 --- a/dScripts/ai/MINIGAME/SG_GF/SERVER/SGCannon.cpp +++ b/dScripts/ai/MINIGAME/SG_GF/SERVER/SGCannon.cpp @@ -307,11 +307,7 @@ void SGCannon::DoSpawnTimerFunc(Entity* self, const std::string& name) { movementAI->SetCurrentSpeed(toSpawn.initialSpeed); movementAI->SetHaltDistance(0.0f); - std::vector pathWaypoints; - - for (const auto& waypoint : path->pathWaypoints) { - pathWaypoints.push_back(waypoint.position); - } + std::vector pathWaypoints = path->pathWaypoints; if (GeneralUtils::GenerateRandomNumber(0, 1) < 0.5f) { std::reverse(pathWaypoints.begin(), pathWaypoints.end()); diff --git a/dScripts/zone/LUPs/RobotCity_Intro/WblRobotCitizen.cpp b/dScripts/zone/LUPs/RobotCity_Intro/WblRobotCitizen.cpp index 93bf2576..8ad303a6 100644 --- a/dScripts/zone/LUPs/RobotCity_Intro/WblRobotCitizen.cpp +++ b/dScripts/zone/LUPs/RobotCity_Intro/WblRobotCitizen.cpp @@ -5,20 +5,19 @@ void WblRobotCitizen::OnStartup(Entity* self) { auto movementAIComponent = self->GetComponent(); if (!movementAIComponent) return; - // movementAIComponent->Resume(); } void WblRobotCitizen::OnUse(Entity* self, Entity* user) { - // auto movementAIComponent = self->GetComponent(); - // if (!movementAIComponent) movementAIComponent->Pause(); + auto movementAIComponent = self->GetComponent(); + if (movementAIComponent) movementAIComponent->Pause(); auto face = NiQuaternion::LookAt(self->GetPosition(), user->GetPosition()); self->SetRotation(face); - auto timer = RenderComponent::PlayAnimation(self, "wave"); + auto timer = RenderComponent::PlayAnimation(self, "wave", 0.4f); self->AddTimer("animation time", timer); } void WblRobotCitizen::OnTimerDone(Entity* self, std::string timerName) { auto movementAIComponent = self->GetComponent(); if (!movementAIComponent) return; - // movementAIComponent->Resume(); + movementAIComponent->Resume(); } diff --git a/dZoneManager/Zone.h b/dZoneManager/Zone.h index c5bac6a6..299d675b 100644 --- a/dZoneManager/Zone.h +++ b/dZoneManager/Zone.h @@ -16,57 +16,57 @@ class Level; enum class eWaypointCommandType : uint32_t; struct WaypointCommand { - eWaypointCommandType command; + eWaypointCommandType command{}; std::string data; }; struct SceneRef { std::string filename; - uint32_t id; - uint32_t sceneType; //0 = general, 1 = audio? + uint32_t id{}; + uint32_t sceneType{}; //0 = general, 1 = audio? std::string name; NiPoint3 unknown1; - float unknown2; - uint8_t color_r; - uint8_t color_g; - uint8_t color_b; + float unknown2{}; + uint8_t color_r{}; + uint8_t color_g{}; + uint8_t color_b{}; Level* level; std::map triggers; }; struct SceneTransitionInfo { - uint64_t sceneID; //id of the scene being transitioned to. + uint64_t sceneID{}; //id of the scene being transitioned to. NiPoint3 position; }; struct SceneTransition { std::string name; std::vector points; - float width; + float width{}; }; struct MovingPlatformPathWaypoint { - uint8_t lockPlayer; - float wait; + uint8_t lockPlayer{}; + float wait{}; std::string departSound; std::string arriveSound; }; struct CameraPathWaypoint { - float time; - float fov; - float tension; - float continuity; - float bias; + float time{}; + float fov{}; + float tension{}; + float continuity{}; + float bias{}; }; struct RacingPathWaypoint { - uint8_t isResetNode; - uint8_t isNonHorizontalCamera; - float planeWidth; - float planeHeight; - float shortestDistanceToEnd; + uint8_t isResetNode{}; + uint8_t isNonHorizontalCamera{}; + float planeWidth{}; + float planeHeight{}; + float shortestDistanceToEnd{}; }; struct PathWaypoint { @@ -75,7 +75,7 @@ struct PathWaypoint { MovingPlatformPathWaypoint movingPlatform; CameraPathWaypoint camera; RacingPathWaypoint racing; - float speed; + float speed{}; std::vector config; std::vector commands; }; @@ -137,49 +137,49 @@ enum class PropertyAchievmentRequired : uint32_t { struct MovingPlatformPath { std::string platformTravelSound; - uint8_t timeBasedMovement; + uint8_t timeBasedMovement{}; }; struct PropertyPath { - PropertyPathType pathType; - int32_t price; - uint32_t rentalTime; - uint64_t associatedZone; + PropertyPathType pathType{}; + int32_t price{}; + uint32_t rentalTime{}; + uint64_t associatedZone{}; std::string displayName; std::string displayDesc; - PropertyType type; - uint32_t cloneLimit; - float repMultiplier; - PropertyRentalPeriod rentalPeriod; - PropertyAchievmentRequired achievementRequired; + PropertyType type{}; + uint32_t cloneLimit{}; + float repMultiplier{}; + PropertyRentalPeriod rentalPeriod{}; + PropertyAchievmentRequired achievementRequired{}; // Player respawn coordinates in the main zone (not the property zone) NiPoint3 playerZoneCoords; - float maxBuildHeight; + float maxBuildHeight{}; }; struct CameraPath { std::string nextPath; - uint8_t rotatePlayer; + uint8_t rotatePlayer{}; }; struct SpawnerPath { - LOT spawnedLOT; - uint32_t respawnTime; - int32_t maxToSpawn; - uint32_t amountMaintained; + LOT spawnedLOT{}; + uint32_t respawnTime{}; + int32_t maxToSpawn{}; + uint32_t amountMaintained{}; LWOOBJID spawnerObjID; - uint8_t spawnerNetActive; + uint8_t spawnerNetActive{}; }; struct Path { - uint32_t pathVersion; + uint32_t pathVersion{}; PathType pathType; std::string pathName; - uint32_t flags; + uint32_t flags{}; PathBehavior pathBehavior; - uint32_t waypointCount; + uint32_t waypointCount{}; std::vector pathWaypoints; SpawnerPath spawner; MovingPlatformPath movingPlatform; diff --git a/migrations/dlu/9_Update_Leaderboard_Storage.sql b/migrations/dlu/9_Update_Leaderboard_Storage.sql index c87e3501..9b5098eb 100644 --- a/migrations/dlu/9_Update_Leaderboard_Storage.sql +++ b/migrations/dlu/9_Update_Leaderboard_Storage.sql @@ -11,7 +11,7 @@ ALTER TABLE leaderboard CHANGE time secondaryScore FLOAT NOT NULL DEFAULT 0 AFTE /* A bit messy, but better than going through a bunch of code fixes all to be run once. */ UPDATE leaderboard SET primaryScore = secondaryScore, - secondaryScore = 0 WHERE game_id IN (1, 44, 46, 47, 48, 49, 53, 103, 104, 108, 1901); + secondaryScore = 0 WHERE game_id IN (1, 44, 46, 47, 48, 49, 53, 103, 104, 108, 1901) AND secondaryScore > 0; /* Do this last so we dont update entry times erroneously */ ALTER TABLE leaderboard