mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-06-20 21:54:23 +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;
|
||||
}
|
||||
|
||||
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 <cstdint>
|
||||
#include <vector>
|
||||
|
||||
struct CDObjectSkills {
|
||||
uint32_t objectTemplate; //!< The LOT of the item
|
||||
uint32_t skillID; //!< The Skill ID of the object
|
||||
uint32_t castOnType; //!< ???
|
||||
uint32_t AICombatWeight; //!< ???
|
||||
int32_t AICombatWeight; //!< ???
|
||||
};
|
||||
|
||||
class CDObjectSkillsTable : public CDTable<CDObjectSkillsTable, std::vector<CDObjectSkills>> {
|
||||
@@ -17,5 +18,6 @@ public:
|
||||
void LoadValuesFromDatabase();
|
||||
// Queries the table with a custom "where" clause
|
||||
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 "CDClientManager.h"
|
||||
#include "CDObjectSkillsTable.h"
|
||||
#include "CDSkillBehaviorTable.h"
|
||||
#include "DestroyableComponent.h"
|
||||
|
||||
#include <algorithm>
|
||||
@@ -43,7 +45,7 @@ BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const int32_t compo
|
||||
|
||||
//Grab the aggro information from BaseCombatAI:
|
||||
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));
|
||||
|
||||
auto componentResult = componentQuery.execQuery();
|
||||
@@ -63,44 +65,37 @@ BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const int32_t compo
|
||||
|
||||
if (!componentResult.fieldIsNull("hardTetherRadius"))
|
||||
m_HardTetherRadius = componentResult.getFloatField("hardTetherRadius");
|
||||
}
|
||||
|
||||
componentResult.finalize();
|
||||
m_MinRoundLength = componentResult.getFloatField("minRoundLength");
|
||||
m_MaxRoundLength = componentResult.getFloatField("maxRoundLength");
|
||||
m_CombatRoundLength = componentResult.getFloatField("combatRoundLength");
|
||||
}
|
||||
|
||||
// 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.
|
||||
if (m_Parent) {
|
||||
auto aggroRadius = m_Parent->GetVar<float>(u"aggroRadius");
|
||||
m_AggroRadius = aggroRadius != 0 ? aggroRadius : m_AggroRadius;
|
||||
auto tetherRadius = m_Parent->GetVar<float>(u"tetherRadius");
|
||||
m_HardTetherRadius = tetherRadius != 0 ? tetherRadius : m_HardTetherRadius;
|
||||
}
|
||||
m_AggroRadius = m_Parent->HasVar(u"aggroRadius") ? m_Parent->GetVar<float>(u"aggroRadius") : m_AggroRadius;
|
||||
m_HardTetherRadius = m_Parent->HasVar(u"tetherRadius") ? m_Parent->GetVar<float>(u"tetherRadius") : m_HardTetherRadius;
|
||||
|
||||
/*
|
||||
* Find skills
|
||||
*/
|
||||
auto skillQuery = CDClientDatabase::CreatePreppedStmt(
|
||||
"SELECT skillID, cooldown, behaviorID FROM SkillBehavior WHERE skillID IN (SELECT skillID FROM ObjectSkills WHERE objectTemplate = ?);");
|
||||
skillQuery.bind(1, static_cast<int>(parent->GetLOT()));
|
||||
for (const auto objectSkill : CDClientManager::GetTable<CDObjectSkillsTable>()->Get(parent->GetLOT())) {
|
||||
const auto skillBehavior = CDClientManager::GetTable<CDSkillBehaviorTable>()->GetSkillByID(objectSkill.skillID);
|
||||
if (skillBehavior.skillID == objectSkill.skillID) {
|
||||
const auto skillId = skillBehavior.skillID;
|
||||
|
||||
auto result = skillQuery.execQuery();
|
||||
const auto abilityCooldown = skillBehavior.cooldown;
|
||||
|
||||
while (!result.eof()) {
|
||||
const auto skillId = static_cast<uint32_t>(result.getIntField("skillID"));
|
||||
const auto behaviorId = skillBehavior.behaviorID;
|
||||
|
||||
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;
|
||||
|
||||
AiSkillEntry entry = { skillId, 0, abilityCooldown, behavior };
|
||||
|
||||
m_SkillEntries.push_back(entry);
|
||||
|
||||
result.nextRow();
|
||||
m_SkillEntries.push_back(entry);
|
||||
}
|
||||
}
|
||||
|
||||
Stun(1.0f);
|
||||
@@ -248,10 +243,12 @@ void BaseCombatAIComponent::Update(const float deltaTime) {
|
||||
|
||||
void BaseCombatAIComponent::CalculateCombat(const float deltaTime) {
|
||||
bool hasSkillToCast = false;
|
||||
int32_t maxSkillWeights = 0;
|
||||
for (auto& entry : m_SkillEntries) {
|
||||
if (entry.cooldown > 0.0f) {
|
||||
entry.cooldown -= deltaTime;
|
||||
} else {
|
||||
maxSkillWeights += entry.combatWeight;
|
||||
hasSkillToCast = true;
|
||||
}
|
||||
}
|
||||
@@ -337,10 +334,19 @@ void BaseCombatAIComponent::CalculateCombat(const float deltaTime) {
|
||||
LookAt(target->GetPosition());
|
||||
}
|
||||
|
||||
for (auto i = 0; i < m_SkillEntries.size(); ++i) {
|
||||
auto entry = m_SkillEntries.at(i);
|
||||
// Roll to find which skill we'll try to cast
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -359,8 +365,6 @@ void BaseCombatAIComponent::CalculateCombat(const float deltaTime) {
|
||||
|
||||
entry.cooldown = entry.abilityCooldown + m_SkillTime;
|
||||
|
||||
m_SkillEntries[i] = entry;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -914,8 +918,16 @@ bool BaseCombatAIComponent::MsgGetObjectReportInfo(GameMessages::GetObjectReport
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -33,13 +33,15 @@ enum class AiState : uint32_t {
|
||||
*/
|
||||
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;
|
||||
|
||||
// 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
|
||||
float m_ForcedTetherTime = 0.0f;
|
||||
|
||||
float m_CombatRoundLength = 0.0f;
|
||||
|
||||
// The amount of time a removed threat will be ignored for.
|
||||
std::map<LWOOBJID, float> m_RemovedThreatList;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user