mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-06-20 13:44:21 +00:00
feat: enemies now use weights on their attacks (#2004)
* feat: enemies now use weights on their attacks tested that 8 times out of 10, in close range, spiders did a web attack instead of a melee attack, vs the prior behavior of always following a pattern fixes #2002 * feedback
This commit is contained in:
@@ -38,3 +38,11 @@ std::vector<CDObjectSkills> CDObjectSkillsTable::Query(std::function<bool(CDObje
|
|||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<CDObjectSkills> CDObjectSkillsTable::Get(const LOT lot) const {
|
||||||
|
std::vector<CDObjectSkills> toReturn;
|
||||||
|
for (const auto& entry : GetEntries()) {
|
||||||
|
if (entry.objectTemplate == lot) toReturn.push_back(entry);
|
||||||
|
}
|
||||||
|
return toReturn;
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,12 +4,13 @@
|
|||||||
#include "CDTable.h"
|
#include "CDTable.h"
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
struct CDObjectSkills {
|
struct CDObjectSkills {
|
||||||
uint32_t objectTemplate; //!< The LOT of the item
|
uint32_t objectTemplate; //!< The LOT of the item
|
||||||
uint32_t skillID; //!< The Skill ID of the object
|
uint32_t skillID; //!< The Skill ID of the object
|
||||||
uint32_t castOnType; //!< ???
|
uint32_t castOnType; //!< ???
|
||||||
uint32_t AICombatWeight; //!< ???
|
int32_t AICombatWeight; //!< ???
|
||||||
};
|
};
|
||||||
|
|
||||||
class CDObjectSkillsTable : public CDTable<CDObjectSkillsTable, std::vector<CDObjectSkills>> {
|
class CDObjectSkillsTable : public CDTable<CDObjectSkillsTable, std::vector<CDObjectSkills>> {
|
||||||
@@ -17,5 +18,6 @@ public:
|
|||||||
void LoadValuesFromDatabase();
|
void LoadValuesFromDatabase();
|
||||||
// Queries the table with a custom "where" clause
|
// Queries the table with a custom "where" clause
|
||||||
std::vector<CDObjectSkills> Query(std::function<bool(CDObjectSkills)> predicate);
|
std::vector<CDObjectSkills> Query(std::function<bool(CDObjectSkills)> predicate);
|
||||||
|
std::vector<CDObjectSkills> Get(const LOT lot) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,8 @@
|
|||||||
|
|
||||||
#include "CDClientDatabase.h"
|
#include "CDClientDatabase.h"
|
||||||
#include "CDClientManager.h"
|
#include "CDClientManager.h"
|
||||||
|
#include "CDObjectSkillsTable.h"
|
||||||
|
#include "CDSkillBehaviorTable.h"
|
||||||
#include "DestroyableComponent.h"
|
#include "DestroyableComponent.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
@@ -43,7 +45,7 @@ BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const int32_t compo
|
|||||||
|
|
||||||
//Grab the aggro information from BaseCombatAI:
|
//Grab the aggro information from BaseCombatAI:
|
||||||
auto componentQuery = CDClientDatabase::CreatePreppedStmt(
|
auto componentQuery = CDClientDatabase::CreatePreppedStmt(
|
||||||
"SELECT aggroRadius, tetherSpeed, pursuitSpeed, softTetherRadius, hardTetherRadius FROM BaseCombatAIComponent WHERE id = ?;");
|
"SELECT aggroRadius, tetherSpeed, pursuitSpeed, softTetherRadius, hardTetherRadius, minRoundLength, maxRoundLength, combatRoundLength FROM BaseCombatAIComponent WHERE id = ?;");
|
||||||
componentQuery.bind(1, static_cast<int>(componentID));
|
componentQuery.bind(1, static_cast<int>(componentID));
|
||||||
|
|
||||||
auto componentResult = componentQuery.execQuery();
|
auto componentResult = componentQuery.execQuery();
|
||||||
@@ -63,44 +65,37 @@ BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const int32_t compo
|
|||||||
|
|
||||||
if (!componentResult.fieldIsNull("hardTetherRadius"))
|
if (!componentResult.fieldIsNull("hardTetherRadius"))
|
||||||
m_HardTetherRadius = componentResult.getFloatField("hardTetherRadius");
|
m_HardTetherRadius = componentResult.getFloatField("hardTetherRadius");
|
||||||
|
|
||||||
|
m_MinRoundLength = componentResult.getFloatField("minRoundLength");
|
||||||
|
m_MaxRoundLength = componentResult.getFloatField("maxRoundLength");
|
||||||
|
m_CombatRoundLength = componentResult.getFloatField("combatRoundLength");
|
||||||
}
|
}
|
||||||
|
|
||||||
componentResult.finalize();
|
|
||||||
|
|
||||||
// Get aggro and tether radius from settings and use this if it is present. Only overwrite the
|
// Get aggro and tether radius from settings and use this if it is present. Only overwrite the
|
||||||
// radii if it is greater than the one in the database.
|
// radii if it is greater than the one in the database.
|
||||||
if (m_Parent) {
|
m_AggroRadius = m_Parent->HasVar(u"aggroRadius") ? m_Parent->GetVar<float>(u"aggroRadius") : m_AggroRadius;
|
||||||
auto aggroRadius = m_Parent->GetVar<float>(u"aggroRadius");
|
m_HardTetherRadius = m_Parent->HasVar(u"tetherRadius") ? m_Parent->GetVar<float>(u"tetherRadius") : m_HardTetherRadius;
|
||||||
m_AggroRadius = aggroRadius != 0 ? aggroRadius : m_AggroRadius;
|
|
||||||
auto tetherRadius = m_Parent->GetVar<float>(u"tetherRadius");
|
|
||||||
m_HardTetherRadius = tetherRadius != 0 ? tetherRadius : m_HardTetherRadius;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Find skills
|
* Find skills
|
||||||
*/
|
*/
|
||||||
auto skillQuery = CDClientDatabase::CreatePreppedStmt(
|
for (const auto objectSkill : CDClientManager::GetTable<CDObjectSkillsTable>()->Get(parent->GetLOT())) {
|
||||||
"SELECT skillID, cooldown, behaviorID FROM SkillBehavior WHERE skillID IN (SELECT skillID FROM ObjectSkills WHERE objectTemplate = ?);");
|
const auto skillBehavior = CDClientManager::GetTable<CDSkillBehaviorTable>()->GetSkillByID(objectSkill.skillID);
|
||||||
skillQuery.bind(1, static_cast<int>(parent->GetLOT()));
|
if (skillBehavior.skillID == objectSkill.skillID) {
|
||||||
|
const auto skillId = skillBehavior.skillID;
|
||||||
|
|
||||||
auto result = skillQuery.execQuery();
|
const auto abilityCooldown = skillBehavior.cooldown;
|
||||||
|
|
||||||
while (!result.eof()) {
|
const auto behaviorId = skillBehavior.behaviorID;
|
||||||
const auto skillId = static_cast<uint32_t>(result.getIntField("skillID"));
|
|
||||||
|
|
||||||
const auto abilityCooldown = static_cast<float>(result.getFloatField("cooldown"));
|
const auto combatWeight = objectSkill.AICombatWeight;
|
||||||
|
|
||||||
const auto behaviorId = static_cast<uint32_t>(result.getIntField("behaviorID"));
|
auto* behavior = Behavior::CreateBehavior(behaviorId);
|
||||||
|
|
||||||
auto* behavior = Behavior::CreateBehavior(behaviorId);
|
AiSkillEntry entry = { .skillId = skillId, .cooldown = 0.0f, .abilityCooldown = abilityCooldown, .behavior = behavior, .combatWeight = combatWeight };
|
||||||
|
|
||||||
std::stringstream behaviorQuery;
|
m_SkillEntries.push_back(entry);
|
||||||
|
}
|
||||||
AiSkillEntry entry = { skillId, 0, abilityCooldown, behavior };
|
|
||||||
|
|
||||||
m_SkillEntries.push_back(entry);
|
|
||||||
|
|
||||||
result.nextRow();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Stun(1.0f);
|
Stun(1.0f);
|
||||||
@@ -248,10 +243,12 @@ void BaseCombatAIComponent::Update(const float deltaTime) {
|
|||||||
|
|
||||||
void BaseCombatAIComponent::CalculateCombat(const float deltaTime) {
|
void BaseCombatAIComponent::CalculateCombat(const float deltaTime) {
|
||||||
bool hasSkillToCast = false;
|
bool hasSkillToCast = false;
|
||||||
|
int32_t maxSkillWeights = 0;
|
||||||
for (auto& entry : m_SkillEntries) {
|
for (auto& entry : m_SkillEntries) {
|
||||||
if (entry.cooldown > 0.0f) {
|
if (entry.cooldown > 0.0f) {
|
||||||
entry.cooldown -= deltaTime;
|
entry.cooldown -= deltaTime;
|
||||||
} else {
|
} else {
|
||||||
|
maxSkillWeights += entry.combatWeight;
|
||||||
hasSkillToCast = true;
|
hasSkillToCast = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -337,10 +334,19 @@ void BaseCombatAIComponent::CalculateCombat(const float deltaTime) {
|
|||||||
LookAt(target->GetPosition());
|
LookAt(target->GetPosition());
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto i = 0; i < m_SkillEntries.size(); ++i) {
|
// Roll to find which skill we'll try to cast
|
||||||
auto entry = m_SkillEntries.at(i);
|
auto randomizedWeight = GeneralUtils::GenerateRandomNumber<int32_t>(0, maxSkillWeights);
|
||||||
|
|
||||||
if (entry.cooldown > 0) {
|
for (auto& entry : m_SkillEntries) {
|
||||||
|
// Skill isn't cooled off yet
|
||||||
|
if (entry.cooldown > 0.0f) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
randomizedWeight -= entry.combatWeight;
|
||||||
|
|
||||||
|
// if the weight is still greater than 0 continue to the next rolled skill
|
||||||
|
if (randomizedWeight > 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -359,8 +365,6 @@ void BaseCombatAIComponent::CalculateCombat(const float deltaTime) {
|
|||||||
|
|
||||||
entry.cooldown = entry.abilityCooldown + m_SkillTime;
|
entry.cooldown = entry.abilityCooldown + m_SkillTime;
|
||||||
|
|
||||||
m_SkillEntries[i] = entry;
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -914,8 +918,16 @@ bool BaseCombatAIComponent::MsgGetObjectReportInfo(GameMessages::GetObjectReport
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto& ignoredThreats = cmptType.PushDebug("Temp Ignored Threats");
|
auto& ignoredThreats = cmptType.PushDebug("Temp Ignored Threats");
|
||||||
for (const auto& [id, threat] : m_ThreatEntries) {
|
for (const auto& [id, threat] : m_RemovedThreatList) {
|
||||||
ignoredThreats.PushDebug<AMFDoubleValue>(std::to_string(id) + " - Time") = threat;
|
ignoredThreats.PushDebug<AMFDoubleValue>(std::to_string(id) + " - Time") = threat;
|
||||||
}
|
}
|
||||||
|
auto& skillInfo = cmptType.PushDebug("Skill Info");
|
||||||
|
for (const auto& skill : m_SkillEntries) {
|
||||||
|
auto& skillDebug = skillInfo.PushDebug("Skill ID " + std::to_string(skill.skillId));
|
||||||
|
skillDebug.PushDebug<AMFDoubleValue>("Cooldown") = skill.cooldown;
|
||||||
|
skillDebug.PushDebug<AMFDoubleValue>("Ability Cooldown") = skill.abilityCooldown;
|
||||||
|
skillDebug.PushDebug<AMFIntValue>("AI Combat Weight") = skill.combatWeight;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,13 +33,15 @@ enum class AiState : uint32_t {
|
|||||||
*/
|
*/
|
||||||
struct AiSkillEntry
|
struct AiSkillEntry
|
||||||
{
|
{
|
||||||
uint32_t skillId;
|
uint32_t skillId{};
|
||||||
|
|
||||||
float cooldown;
|
float cooldown{};
|
||||||
|
|
||||||
float abilityCooldown;
|
float abilityCooldown{};
|
||||||
|
|
||||||
Behavior* behavior;
|
Behavior* behavior{};
|
||||||
|
|
||||||
|
int32_t combatWeight{};
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -396,9 +398,17 @@ private:
|
|||||||
*/
|
*/
|
||||||
bool m_DirtyStateOrTarget = false;
|
bool m_DirtyStateOrTarget = false;
|
||||||
|
|
||||||
|
// Min amount of time to remain as in combat after casting a skill
|
||||||
|
float m_MinRoundLength = 0.0f;
|
||||||
|
|
||||||
|
// max amount of time to remain as in combat after casting a skill
|
||||||
|
float m_MaxRoundLength = 0.0f;
|
||||||
|
|
||||||
// The amount of time the entity will be forced to tether for
|
// The amount of time the entity will be forced to tether for
|
||||||
float m_ForcedTetherTime = 0.0f;
|
float m_ForcedTetherTime = 0.0f;
|
||||||
|
|
||||||
|
float m_CombatRoundLength = 0.0f;
|
||||||
|
|
||||||
// The amount of time a removed threat will be ignored for.
|
// The amount of time a removed threat will be ignored for.
|
||||||
std::map<LWOOBJID, float> m_RemovedThreatList;
|
std::map<LWOOBJID, float> m_RemovedThreatList;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user