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