diff --git a/src/game/AI/BaseAI/UnitAI.cpp b/src/game/AI/BaseAI/UnitAI.cpp index 0b4b1aadf0..03bdb368d0 100644 --- a/src/game/AI/BaseAI/UnitAI.cpp +++ b/src/game/AI/BaseAI/UnitAI.cpp @@ -1202,6 +1202,7 @@ void UnitAI::UpdateSpellLists() // when probability is 0 for all spells, they will use priority based on positions std::vector> eligibleSpells; + std::vector> nonBlockingSpells; uint32 sum = 0; // one roll due to multiple spells @@ -1241,13 +1242,38 @@ void UnitAI::UpdateSpellLists() if (!result) continue; - eligibleSpells.emplace_back(spell.SpellId, spell.Probability, spell.ScriptId, target); - sum += spell.Probability; + if (spell.Flags & SPELL_LIST_FLAG_NON_BLOCKING) + nonBlockingSpells.emplace_back(spell.SpellId, spell.Probability, spell.ScriptId, target); + else + { + eligibleSpells.emplace_back(spell.SpellId, spell.Probability, spell.ScriptId, target); + sum += spell.Probability; + } } if (eligibleSpells.size() > 1 && sum != 0) // sum == 0 is meant to be priority based (lower position, higher priority) std::shuffle(eligibleSpells.begin(), eligibleSpells.end(), *GetRandomGenerator()); + auto executeSpell = [&](uint32 spellId, uint32 probability, uint32 scriptId, Unit* target) -> bool + { + CanCastResult castResult = DoCastSpellIfCan(target, spellId); + if (castResult == CAST_OK) + { + OnSpellCast(sSpellTemplate.LookupEntry(spellId), target); + if (scriptId) + m_unit->GetMap()->ScriptsStart(SCRIPT_TYPE_RELAY, scriptId, m_unit, target); + return true; + } + return false; + }; + + for (auto& data : nonBlockingSpells) + { + uint32 spellId; uint32 probability; uint32 scriptId; Unit* target; + std::tie(spellId, probability, scriptId, target) = data; + executeSpell(spellId, probability, scriptId, target); + } + // will hit first eligible spell when sum is 0 because roll -1 < probability 0 int32 spellRoll = sum == 0 ? -1 : irand(0, sum - 1); bool success = false; @@ -1260,15 +1286,7 @@ void UnitAI::UpdateSpellLists() std::tie(spellId, probability, scriptId, target) = *itr; if (spellRoll < int32(probability)) { - CanCastResult castResult = DoCastSpellIfCan(target, spellId); - if (castResult == CAST_OK) - { - success = true; - OnSpellCast(sSpellTemplate.LookupEntry(spellId), target); - if (scriptId) - m_unit->GetMap()->ScriptsStart(SCRIPT_TYPE_RELAY, scriptId, m_unit, target); - break; - } + success = executeSpell(spellId, probability, scriptId, target); itr = eligibleSpells.erase(itr); } else diff --git a/src/game/Entities/CreatureSpellList.h b/src/game/Entities/CreatureSpellList.h index 157f515f76..0782eafbf4 100644 --- a/src/game/Entities/CreatureSpellList.h +++ b/src/game/Entities/CreatureSpellList.h @@ -57,9 +57,10 @@ struct CreatureSpellListTargeting enum SpellListFlags { - SPELL_LIST_FLAG_SUPPORT_ACTION = 1, - SPELL_LIST_FLAG_RANGED_ACTION = 2, // previously known as main ranged spell in EAI - SPELL_LIST_FLAG_CATEGORY_COOLDOWN = 4, // imposes category cooldown instead of normal cooldown + SPELL_LIST_FLAG_SUPPORT_ACTION = 1, + SPELL_LIST_FLAG_RANGED_ACTION = 2, // previously known as main ranged spell in EAI + SPELL_LIST_FLAG_CATEGORY_COOLDOWN = 4, // imposes category cooldown instead of normal cooldown + SPELL_LIST_FLAG_NON_BLOCKING = 8, // executes spell outside of rolling for one action, should be instant TODO: check if its not identical to SPELL_ATTR_EX4_ALLOW_CAST_WHILE_CASTING }; struct CreatureSpellListSpell