From c5aa1ea06f45450e257712da5c32cd38a5cf788e Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 31 Jan 2025 15:30:25 -0600 Subject: [PATCH] Add rules (Bots, AICastSpellTypeDelay, Bots, AICastSpellTypeHeldDelay) to prevent spamming of failed spell type AI casts --- common/ruletypes.h | 2 ++ zone/bot.cpp | 4 ++++ zone/bot.h | 2 ++ zone/bot_structs.h | 1 + zone/botspellsai.cpp | 21 +++++++++++++++++++++ 5 files changed, 30 insertions(+) diff --git a/common/ruletypes.h b/common/ruletypes.h index 06f49edad..99abf1c14 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -898,6 +898,8 @@ RULE_STRING(Bots, ZonesWithSpawnLimits, "", "Comma-delimited list of zones where RULE_STRING(Bots, ZoneSpawnLimits, "", "Comma-delimited list of spawn limits for zones.") RULE_STRING(Bots, ZonesWithForcedSpawnLimits, "", "Comma-delimited list of zones where bot spawn limits are forced. This will take priority over any other type of spawn limits.") RULE_STRING(Bots, ZoneForcedSpawnLimits, "", "Comma-delimited list of forced spawn limits for zones.") +RULE_INT(Bots, AICastSpellTypeDelay, 100, "Delay in milliseconds between AI cast attempts for each spell type. Default 100ms") +RULE_INT(Bots, AICastSpellTypeHeldDelay, 2500, "Delay in milliseconds between AI cast attempts for each spell type that is held or disabled. Default 2500ms (2.5s)") RULE_CATEGORY_END() RULE_CATEGORY(Chat) diff --git a/zone/bot.cpp b/zone/bot.cpp index 7af8f4e5a..22f18f774 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -9434,6 +9434,9 @@ bool Bot::PrecastChecks(Mob* tar, uint16 spell_type) { LogBotSpellChecksDetail("{} says, 'Running [{}] PreChecks on [{}].'", GetCleanName(), GetSpellTypeNameByID(spell_type), tar->GetCleanName()); if (GetUltimateSpellHold(spell_type, tar)) { + if (!IsCommandedSpell()) { + SetSpellTypeAITimer(spell_type, RuleI(Bots, AICastSpellTypeHeldDelay)); + } LogBotSpellChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetUltimateSpellHold.'", GetCleanName(), GetSpellTypeNameByID(spell_type), tar->GetCleanName()); return false; } @@ -10521,6 +10524,7 @@ void Bot::LoadDefaultBotSettings() { t.ae_or_group_target_count = GetDefaultSpellTypeAEOrGroupTargetCount(i, bot_stance); t.announce_cast = GetDefaultSpellTypeAnnounceCast(i, bot_stance); t.recast_timer.Start(); + t.ai_delay.Start(); m_bot_spell_settings.push_back(t); diff --git a/zone/bot.h b/zone/bot.h index 87824774a..92ee7bc41 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -637,6 +637,8 @@ public: inline void SetSpellMaxThreshold(uint16 spell_type, uint8 threshold_value) { m_bot_spell_settings[spell_type].max_threshold = threshold_value; } inline bool SpellTypeRecastCheck(uint16 spellType) { return !m_bot_spell_settings[spellType].recast_timer.GetRemainingTime(); } void SetSpellTypeRecastTimer(uint16 spell_type, uint32 recast_time) { m_bot_spell_settings[spell_type].recast_timer.Start(recast_time); } + inline bool SpellTypeAIDelayCheck(uint16 spellType) { return !m_bot_spell_settings[spellType].ai_delay.GetRemainingTime(); } + void SetSpellTypeAITimer(uint16 spell_type, uint32 recast_time) { m_bot_spell_settings[spell_type].ai_delay.Start(recast_time); } uint16 GetDefaultSpellDelay(uint16 spell_type, uint8 stance = Stance::Balanced); uint8 GetDefaultSpellMinThreshold(uint16 spell_type, uint8 stance = Stance::Balanced); uint8 GetDefaultSpellMaxThreshold(uint16 spell_type, uint8 stance = Stance::Balanced); diff --git a/zone/bot_structs.h b/zone/bot_structs.h index 47ac30455..baa1aa73c 100644 --- a/zone/bot_structs.h +++ b/zone/bot_structs.h @@ -131,6 +131,7 @@ struct BotSpellSettings { uint16 ae_or_group_target_count; // require target count to cast an AE or Group spell type uint16 announce_cast; // announce when casting a certain spell type Timer recast_timer; // recast timer based off delay + Timer ai_delay; // spell timer based off delay }; struct BotSpellTypeOrder { diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index 33709ef2b..f08d78b7e 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -663,6 +663,7 @@ bool Bot::AI_PursueCastCheck() { for (auto& current_cast : cast_order) { if (current_cast.priority == 0) { + SetSpellTypeAITimer(current_cast.spellType, RuleI(Bots, AICastSpellTypeHeldDelay)); LogBotSpellChecksDetail("{} says, '[{}] is priority 0, skipping.'", GetCleanName(), GetSpellTypeNameByID(current_cast.spellType)); continue; } @@ -679,12 +680,18 @@ bool Bot::AI_PursueCastCheck() { continue; } + if (!SpellTypeAIDelayCheck(current_cast.spellType)) { + continue; + } + result = AttemptAICastSpell(current_cast.spellType, nullptr); if (!result && IsBotSpellTypeBeneficial(current_cast.spellType)) { result = AttemptCloseBeneficialSpells(current_cast.spellType); } + SetSpellTypeAITimer(current_cast.spellType, RuleI(Bots, AICastSpellTypeDelay)); + if (result) { break; } @@ -731,6 +738,7 @@ bool Bot::AI_IdleCastCheck() { for (auto& current_cast : cast_order) { if (current_cast.priority == 0) { + SetSpellTypeAITimer(current_cast.spellType, RuleI(Bots, AICastSpellTypeHeldDelay)); LogBotSpellChecksDetail("{} says, '[{}] is priority 0, skipping.'", GetCleanName(), GetSpellTypeNameByID(current_cast.spellType)); continue; } @@ -755,6 +763,10 @@ bool Bot::AI_IdleCastCheck() { continue; } + if (!SpellTypeAIDelayCheck(current_cast.spellType)) { + continue; + } + result = AttemptAICastSpell(current_cast.spellType, nullptr); if (result) { @@ -763,6 +775,8 @@ bool Bot::AI_IdleCastCheck() { result = AttemptCloseBeneficialSpells(current_cast.spellType); + SetSpellTypeAITimer(current_cast.spellType, RuleI(Bots, AICastSpellTypeDelay)); + if (result) { break; } @@ -799,6 +813,7 @@ bool Bot::AI_EngagedCastCheck() { for (auto& current_cast : cast_order) { if (current_cast.priority == 0) { + SetSpellTypeAITimer(current_cast.spellType, RuleI(Bots, AICastSpellTypeHeldDelay)); LogBotSpellChecksDetail("{} says, '[{}] is priority 0, skipping.'", GetCleanName(), GetSpellTypeNameByID(current_cast.spellType)); continue; } @@ -815,8 +830,14 @@ bool Bot::AI_EngagedCastCheck() { continue; } + if (!SpellTypeAIDelayCheck(current_cast.spellType)) { + continue; + } + result = AttemptAICastSpell(current_cast.spellType, nullptr); + SetSpellTypeAITimer(current_cast.spellType, RuleI(Bots, AICastSpellTypeDelay)); + if (!result && IsBotSpellTypeBeneficial(current_cast.spellType)) { result = AttemptCloseBeneficialSpells(current_cast.spellType); }