Merge remote-tracking branch 'upstream/main' into PetFixes

This commit is contained in:
jadebenn 2024-03-31 21:55:44 -05:00
commit 0b4d7b6d92
27 changed files with 461 additions and 357 deletions

View File

@ -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

View File

@ -73,7 +73,7 @@ sudo apt install build-essential gcc zlib1g-dev libssl-dev openssl mariadb-serve
```
#### Required CMake version
This project uses <font size="4">**CMake version 3.18**</font> or higher and as such you will need to ensure you have this version installed.
This project uses <font size="4">**CMake version 3.25**</font> 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

View File

@ -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) {

View File

@ -166,7 +166,7 @@ namespace GeneralUtils {
return isParsed ? static_cast<T>(result) : std::optional<T>{};
}
#ifdef DARKFLAME_PLATFORM_MACOS
#if !(__GNUC__ >= 11 || _MSC_VER >= 1924)
// MacOS floating-point parse helper function specializations
namespace details {

View File

@ -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<MovingPlatformComponent>(pathName);
// else if we are a movement path
} /*else if (path->pathType == PathType::Movement) {
auto movementAIcomp = GetComponent<MovementAIComponent>();
if (movementAIcomp){
// TODO: set path in existing movementAIComp
} else if (path->pathType == PathType::Movement) {
auto movementAIcomponent = GetComponent<MovementAIComponent>();
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<MovementAIComponent>(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);

View File

@ -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);

View File

@ -5,7 +5,7 @@
#include "CDActivitiesTable.h"
#include "Game.h"
#include "Logger.h"
#include "BehaviorTemplates.h"
#include "BehaviorTemplate.h"
#include "BehaviorBranchContext.h"
#include <unordered_map>
@ -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<CDBehaviorTemplateTable>();
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<BehaviorTemplates>(templateEntry.templateID);
templateID = static_cast<BehaviorTemplate>(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<std::string, std::string>();
} 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<BehaviorTemplates>(templateInDatabase.templateID);
this->m_templateId = static_cast<BehaviorTemplate>(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;
}

View File

@ -6,7 +6,7 @@
#include <unordered_map>
#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<std::string, std::string>* m_effectNames = nullptr;
std::string* m_effectType = nullptr;
std::string m_effectHandle;
std::unordered_map<std::string, std::string> 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;
};

View File

@ -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
};

View File

@ -1 +0,0 @@
#include "BehaviorTemplates.h"

View File

@ -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
};

View File

@ -7,7 +7,6 @@ set(DGAME_DBEHAVIORS_SOURCES "AirMovementBehavior.cpp"
"Behavior.cpp"
"BehaviorBranchContext.cpp"
"BehaviorContext.cpp"
"BehaviorTemplates.cpp"
"BlockBehavior.cpp"
"BuffBehavior.cpp"
"CarBoostBehavior.cpp"

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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<DestroyableComponent>();
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<BuffComponent>();
if (state) {
if (m_targetHasBuff != 0) {
auto* buffComponent = entity->GetComponent<BuffComponent>();
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<DestroyableComponent>();
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);
}

View File

@ -14,6 +14,8 @@ public:
int32_t m_targetHasBuff;
float m_Distance;
/*
* Inherited
*/

View File

@ -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)

View File

@ -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<BaseCombatAIComponent>()) 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<PathWaypoint> 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<NiPoint3> path) {
void MovementAIComponent::SetPath(std::vector<PathWaypoint> 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<ControllablePhysicsComponent>();
if (controllablePhysicsComponent != nullptr) {
return controllablePhysicsComponent->GetVelocity();
}
auto* simplePhysicsComponent = m_Parent->GetComponent<SimplePhysicsComponent>();
if (simplePhysicsComponent != nullptr) {
return simplePhysicsComponent->GetVelocity();
}
return NiPoint3Constant::ZERO;
}
void MovementAIComponent::SetVelocity(const NiPoint3& value) {
auto* controllablePhysicsComponent = m_Parent->GetComponent<ControllablePhysicsComponent>();
@ -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<NiPoint3> 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);
}

View File

@ -14,11 +14,14 @@
#include "Logger.h"
#include "Component.h"
#include "eReplicaComponentType.h"
#include "Zone.h"
#include <vector>
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<NiPoint3> path);
void SetPath(std::vector<PathWaypoint> 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<NiPoint3> m_CurrentPath;
std::stack<PathWaypoint> m_CurrentPath;
const Path* m_Path = nullptr;
NiPoint3 m_SourcePosition;
bool m_Paused;
NiPoint3 m_SavedVelocity;
};
#endif // MOVEMENTAICOMPONENT_H

View File

@ -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");

View File

@ -1,11 +1,11 @@
#include "WanderingVendor.h"
#include "MovementAIComponent.h"
#include "ProximityMonitorComponent.h"
#include <ranges>
void WanderingVendor::OnStartup(Entity* self) {
auto movementAIComponent = self->GetComponent<MovementAIComponent>();
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<MovementAIComponent>();
if (!movementAIComponent) return;
// movementAIComponent->Pause();
movementAIComponent->Pause();
self->CancelTimer("startWalking");
} else if (status == "LEAVE") {
auto* proximityMonitorComponent = self->GetComponent<ProximityMonitorComponent>();
if (!proximityMonitorComponent) self->AddComponent<ProximityMonitorComponent>();
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<MovementAIComponent>();
if (!movementAIComponent) return;
// movementAIComponent->Resume();
movementAIComponent->Resume();
}
}

View File

@ -307,11 +307,7 @@ void SGCannon::DoSpawnTimerFunc(Entity* self, const std::string& name) {
movementAI->SetCurrentSpeed(toSpawn.initialSpeed);
movementAI->SetHaltDistance(0.0f);
std::vector<NiPoint3> pathWaypoints;
for (const auto& waypoint : path->pathWaypoints) {
pathWaypoints.push_back(waypoint.position);
}
std::vector<PathWaypoint> pathWaypoints = path->pathWaypoints;
if (GeneralUtils::GenerateRandomNumber<float_t>(0, 1) < 0.5f) {
std::reverse(pathWaypoints.begin(), pathWaypoints.end());

View File

@ -5,20 +5,19 @@
void WblRobotCitizen::OnStartup(Entity* self) {
auto movementAIComponent = self->GetComponent<MovementAIComponent>();
if (!movementAIComponent) return;
// movementAIComponent->Resume();
}
void WblRobotCitizen::OnUse(Entity* self, Entity* user) {
// auto movementAIComponent = self->GetComponent<MovementAIComponent>();
// if (!movementAIComponent) movementAIComponent->Pause();
auto movementAIComponent = self->GetComponent<MovementAIComponent>();
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<MovementAIComponent>();
if (!movementAIComponent) return;
// movementAIComponent->Resume();
movementAIComponent->Resume();
}

View File

@ -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<uint32_t, LUTriggers::Trigger*> 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<SceneTransitionInfo> 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<LDFBaseData*> config;
std::vector<WaypointCommand> 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<PathWaypoint> pathWaypoints;
SpawnerPath spawner;
MovingPlatformPath movingPlatform;

View File

@ -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