diff --git a/common/ruletypes.h b/common/ruletypes.h index 97a235e55..170c62e96 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -908,6 +908,7 @@ RULE_STRING(Bots, ZoneForcedSpawnLimits, "", "Comma-delimited list of forced spa 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_BOOL(Bots, BotsRequireLoS, true, "Whether or not bots require line of sight to be told to attack their target") +RULE_INT(Bots, BlindMoveChance, 5, "Chance for a bot to run to its target if it is blinded. Default 5.") RULE_CATEGORY_END() RULE_CATEGORY(Chat) diff --git a/zone/bot.cpp b/zone/bot.cpp index ed3f47f45..d05b85254 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -3432,6 +3432,10 @@ bool Bot::CheckIfIncapacitated() { } if (currently_fleeing) { + if (!FindType(SpellEffect::Fear)) { // Blinded + return BotProcessBlind(); + } + if (RuleB(Combat, EnableFearPathing) && AI_movement_timer->Check()) { // Check if we have reached the last fear point if (DistanceNoZ(glm::vec3(GetX(), GetY(), GetZ()), m_FearWalkTarget) <= 5.0f) { @@ -3453,6 +3457,23 @@ bool Bot::CheckIfIncapacitated() { return false; } +bool Bot::BotProcessBlind() { + if (m_combat_jitter_timer.Check() && zone->random.Int(1, 100) <= RuleI(Bots, BlindMoveChance)) { + Mob* tar = GetTarget() ? GetTarget() : GetBotOwner(); + + if (tar) { + glm::vec3 Goal = tar->GetPosition(); + + RunTo(Goal.x, Goal.y, Goal.z); + SetCombatJitter(); + } + + return true; + } + + return false; +} + void Bot::SetBerserkState() {// Berserk updates should occur if primary AI criteria are met if (GetClass() == Class::Warrior || GetClass() == Class::Berserker) { if (!berserk && GetHPRatio() < RuleI(Combat, BerserkerFrenzyStart)) { diff --git a/zone/bot.h b/zone/bot.h index 9511cf9ee..64d0a2a3a 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -542,7 +542,7 @@ public: bool IsValidMezTarget(Mob* owner, Mob* npc, uint16 spell_id); // Cast checks - bool PrecastChecks(Mob* tar, uint16 spell_type); + bool PrecastChecks(Mob* tar, uint16 spell_type); bool CastChecks(uint16 spell_id, Mob* tar, uint16 spell_type, bool prechecks = false, bool ae_check = false); bool IsImmuneToBotSpell(uint16 spell_id, Mob* caster); bool CanCastSpellType(uint16 spell_type, uint16 spell_id, Mob* tar); @@ -1055,6 +1055,7 @@ public: bool BotCastCure(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spell_type); bool CheckIfIncapacitated(); + bool BotProcessBlind(); bool IsAIProcessValid(const Client* bot_owner, const Group* bot_group, const Raid* raid); Client* SetLeashOwner(Client* bot_owner, Group* bot_group, Raid* raid, uint32 r_group) const; diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 4930f1154..9c0cb8d05 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -1362,6 +1362,10 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove } } } + else if (IsBot()) { + currently_fleeing = true; + CastToBot()->BotProcessBlind(); + } else if (!IsClient()) { CalculateNewFearpoint(); }