mirror of
https://github.com/mod-playerbots/azerothcore-wotlk.git
synced 2026-01-14 01:29:07 +00:00
Fix combat movement (#18026)
* Improve combat movement - Removed a bunch of logic related to another attempt at fixing combat movement. - Removed SMART_ACTION_SET_CASTER_COMBAT_DIST and updated smarts scripts accordingly. - Cherry-picked7fb7432620- Cherry-picked63a6e1e048Co-Authored-By: Ludovic Barbier <ludovic.barbier03@gmail.com> Co-Authored-By: Giacomo Pozzoni <giacomopoz@gmail.com> * Some more cleanup + fix sql * More fixes to caster chase/combat movement + some cherry picks because why not - Fix casters always trying to chase to melee range - Fix casters another case of casters sometimes walking back instead of stopping - Cleaned up some code - Cherry pickedca25e8d019- Cherry picked96b289cadbCo-Authored-By: Giacomo Pozzoni <giacomopoz@gmail.com> * Added parentheses * Fixed caster combat movement when target is rooted - Made a few adjustments to chase range and stuff, but nothing set in stone. * convert uint to int --------- Co-authored-by: Ludovic Barbier <ludovic.barbier03@gmail.com> Co-authored-by: Giacomo Pozzoni <giacomopoz@gmail.com>
This commit is contained in:
@@ -56,15 +56,8 @@ SmartScript::SmartScript()
|
||||
mTemplate = SMARTAI_TEMPLATE_BASIC;
|
||||
mScriptType = SMART_SCRIPT_TYPE_CREATURE;
|
||||
isProcessingTimedActionList = false;
|
||||
|
||||
// Xinef: Fix Combat Movement
|
||||
mActualCombatDist = 0;
|
||||
mMaxCombatDist = 0;
|
||||
|
||||
smartCasterActualDist = 0.0f;
|
||||
smartCasterMaxDist = 0.0f;
|
||||
smartCasterPowerType = POWER_MANA;
|
||||
|
||||
mCurrentPriority = 0;
|
||||
mEventSortingRequired = false;
|
||||
_allowPhaseReset = true;
|
||||
}
|
||||
|
||||
@@ -87,14 +80,16 @@ void SmartScript::OnReset()
|
||||
InitTimer((*i));
|
||||
(*i).runOnce = false;
|
||||
}
|
||||
|
||||
if ((*i).priority != SmartScriptHolder::DEFAULT_PRIORITY)
|
||||
{
|
||||
(*i).priority = SmartScriptHolder::DEFAULT_PRIORITY;
|
||||
mEventSortingRequired = true;
|
||||
}
|
||||
}
|
||||
ProcessEventsFor(SMART_EVENT_RESET);
|
||||
mLastInvoker.Clear();
|
||||
mCounterList.clear();
|
||||
|
||||
// Xinef: Fix Combat Movement
|
||||
RestoreMaxCombatDist();
|
||||
RestoreCasterMaxDist();
|
||||
}
|
||||
|
||||
void SmartScript::ProcessEventsFor(SMART_EVENT e, Unit* unit, uint32 var0, uint32 var1, bool bvar, SpellInfo const* spell, GameObject* gob)
|
||||
@@ -118,14 +113,18 @@ void SmartScript::ProcessEventsFor(SMART_EVENT e, Unit* unit, uint32 var0, uint3
|
||||
|
||||
void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, uint32 var1, bool bvar, SpellInfo const* spell, GameObject* gob)
|
||||
{
|
||||
e.runOnce = true;//used for repeat check
|
||||
|
||||
//calc random
|
||||
if (e.event.event_chance < 100 && e.event.event_chance)
|
||||
if (e.event.event_chance < 100 && e.event.event_chance && !(e.event.event_flags & SMART_EVENT_FLAG_TEMP_IGNORE_CHANCE_ROLL))
|
||||
{
|
||||
uint32 rnd = urand(1, 100);
|
||||
if (e.event.event_chance <= rnd)
|
||||
return;
|
||||
}
|
||||
e.runOnce = true;//used for repeat check
|
||||
|
||||
// Remove SMART_EVENT_FLAG_TEMP_IGNORE_CHANCE_ROLL flag after processing roll chances as it's not needed anymore
|
||||
e.event.event_flags &= ~SMART_EVENT_FLAG_TEMP_IGNORE_CHANCE_ROLL;
|
||||
|
||||
if (unit)
|
||||
mLastInvoker = unit->GetGUID();
|
||||
@@ -589,6 +588,8 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
|
||||
if (e.action.cast.targetsLimit)
|
||||
Acore::Containers::RandomResize(targets, e.action.cast.targetsLimit);
|
||||
|
||||
bool failedSpellCast = false, successfulSpellCast = false;
|
||||
|
||||
for (WorldObject* target : targets)
|
||||
{
|
||||
// may be nullptr
|
||||
@@ -621,23 +622,40 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
|
||||
if (e.action.cast.castFlags & SMARTCAST_INTERRUPT_PREVIOUS)
|
||||
me->InterruptNonMeleeSpells(false);
|
||||
|
||||
// Flag usable only if caster has max dist set.
|
||||
if ((e.action.cast.castFlags & SMARTCAST_COMBAT_MOVE) && GetCasterMaxDist() > 0.0f && me->GetMaxPower(GetCasterPowerType()) > 0)
|
||||
{
|
||||
// Check mana case only and operate movement accordingly, LoS and range is checked in targetet movement generator.
|
||||
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(e.action.cast.spell);
|
||||
int32 currentPower = me->GetPower(GetCasterPowerType());
|
||||
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(e.action.cast.spell);
|
||||
float distanceToTarget = me->GetDistance(target->ToUnit());
|
||||
float spellMaxRange = me->GetSpellMaxRangeForTarget(target->ToUnit(), spellInfo);
|
||||
float spellMinRange = me->GetSpellMinRangeForTarget(target->ToUnit(), spellInfo);
|
||||
float meleeRange = me->GetMeleeRange(target->ToUnit());
|
||||
|
||||
if ((spellInfo && (currentPower < spellInfo->CalcPowerCost(me, spellInfo->GetSchoolMask()) || me->IsSpellProhibited(spellInfo->GetSchoolMask()))) || me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED))
|
||||
{
|
||||
SetCasterActualDist(0);
|
||||
CAST_AI(SmartAI, me->AI())->SetForcedCombatMove(0);
|
||||
}
|
||||
else if (GetCasterActualDist() == 0.0f && me->GetPowerPct(GetCasterPowerType()) > 30.0f)
|
||||
{
|
||||
RestoreCasterMaxDist();
|
||||
CAST_AI(SmartAI, me->AI())->SetForcedCombatMove(GetCasterActualDist());
|
||||
}
|
||||
bool isWithinLOSInMap = me->IsWithinLOSInMap(target->ToUnit());
|
||||
bool isWithinMeleeRange = distanceToTarget <= meleeRange;
|
||||
bool isRangedAttack = spellMaxRange > NOMINAL_MELEE_RANGE;
|
||||
bool isTargetRooted = target->ToUnit()->HasUnitState(UNIT_STATE_ROOT);
|
||||
// To prevent running back and forth when OOM, we must have more than 10% mana.
|
||||
bool canCastSpell = me->GetPowerPct(POWER_MANA) > 10.0f && spellInfo->CalcPowerCost(me, spellInfo->GetSchoolMask()) < (int32)me->GetPower(POWER_MANA) && !me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED);
|
||||
|
||||
// If target is rooted we move out of melee range before casting, but not further than spell max range.
|
||||
if (isWithinLOSInMap && isWithinMeleeRange && isRangedAttack && isTargetRooted && canCastSpell)
|
||||
{
|
||||
failedSpellCast = true; // Mark spellcast as failed so we can retry it later
|
||||
float minDistance = std::max(meleeRange, spellMinRange) - distanceToTarget + NOMINAL_MELEE_RANGE;
|
||||
CAST_AI(SmartAI, me->AI())->MoveAway(std::min(minDistance, spellMaxRange));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Let us not try to cast spell if we know it is going to fail anyway. Stick to chasing and continue.
|
||||
if (distanceToTarget > spellMaxRange && isWithinLOSInMap)
|
||||
{
|
||||
failedSpellCast = true;
|
||||
CAST_AI(SmartAI, me->AI())->SetCombatMove(true, std::max(spellMaxRange - NOMINAL_MELEE_RANGE, 0.0f));
|
||||
continue;
|
||||
}
|
||||
else if (distanceToTarget < spellMinRange || !isWithinLOSInMap)
|
||||
{
|
||||
failedSpellCast = true;
|
||||
CAST_AI(SmartAI, me->AI())->SetCombatMove(true);
|
||||
continue;
|
||||
}
|
||||
|
||||
TriggerCastFlags triggerFlags = TRIGGERED_NONE;
|
||||
@@ -649,11 +667,38 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
|
||||
triggerFlags = TRIGGERED_FULL_MASK;
|
||||
}
|
||||
|
||||
me->CastSpell(target->ToUnit(), e.action.cast.spell, triggerFlags);
|
||||
SpellCastResult result = me->CastSpell(target->ToUnit(), e.action.cast.spell, triggerFlags);
|
||||
bool spellCastFailed = (result != SPELL_CAST_OK && result != SPELL_FAILED_SPELL_IN_PROGRESS);
|
||||
|
||||
if (e.action.cast.castFlags & SMARTCAST_COMBAT_MOVE)
|
||||
{
|
||||
// If cast flag SMARTCAST_COMBAT_MOVE is set combat movement will not be allowed unless target is outside spell range, out of mana, or LOS.
|
||||
if (result == SPELL_FAILED_OUT_OF_RANGE)
|
||||
// if we are just out of range, we only chase until we are back in spell range.
|
||||
CAST_AI(SmartAI, me->AI())->SetCombatMove(true, std::max(spellMaxRange - NOMINAL_MELEE_RANGE, 0.0f));
|
||||
else
|
||||
// if spell fail for any other reason, we chase to melee range, or stay where we are if spellcast was successful.
|
||||
CAST_AI(SmartAI, me->AI())->SetCombatMove(spellCastFailed);
|
||||
}
|
||||
|
||||
if (spellCastFailed)
|
||||
failedSpellCast = true;
|
||||
else
|
||||
successfulSpellCast = true;
|
||||
|
||||
LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction:: SMART_ACTION_CAST: Unit {} casts spell {} on target {} with castflags {}",
|
||||
me->GetGUID().ToString(), e.action.cast.spell, target->GetGUID().ToString(), e.action.cast.castFlags);
|
||||
}
|
||||
}
|
||||
|
||||
// If there is at least 1 failed cast and no successful casts at all, retry again on next loop
|
||||
if (failedSpellCast && !successfulSpellCast)
|
||||
{
|
||||
RetryLater(e, true);
|
||||
// Don't execute linked events
|
||||
return;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case SMART_ACTION_SELF_CAST:
|
||||
@@ -864,15 +909,8 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
|
||||
if (!IsSmart())
|
||||
break;
|
||||
|
||||
// Xinef: Fix Combat Movement
|
||||
bool move = e.action.combatMove.move;
|
||||
if (move && GetMaxCombatDist() && e.GetEventType() == SMART_EVENT_MANA_PCT)
|
||||
{
|
||||
SetActualCombatDist(0);
|
||||
CAST_AI(SmartAI, me->AI())->SetForcedCombatMove(0);
|
||||
}
|
||||
else
|
||||
CAST_AI(SmartAI, me->AI())->SetCombatMove(move);
|
||||
CAST_AI(SmartAI, me->AI())->SetCombatMove(move);
|
||||
LOG_DEBUG("sql.sql", "SmartScript::ProcessAction:: SMART_ACTION_ALLOW_COMBAT_MOVEMENT: Creature {} bool on = {}",
|
||||
me->GetGUID().ToString(), e.action.combatMove.move);
|
||||
break;
|
||||
@@ -2425,17 +2463,6 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
|
||||
|
||||
break;
|
||||
}
|
||||
case SMART_ACTION_SET_CASTER_COMBAT_DIST:
|
||||
{
|
||||
if (e.action.casterDistance.reset)
|
||||
RestoreCasterMaxDist();
|
||||
else
|
||||
SetCasterActualDist(e.action.casterDistance.dist);
|
||||
|
||||
if (me->GetVictim() && me->GetMotionMaster()->GetCurrentMovementGeneratorType() == CHASE_MOTION_TYPE)
|
||||
me->GetMotionMaster()->MoveChase(me->GetVictim(), GetCasterActualDist());
|
||||
break;
|
||||
}
|
||||
case SMART_ACTION_SET_SIGHT_DIST:
|
||||
{
|
||||
for (WorldObject* const target : targets)
|
||||
@@ -4577,7 +4604,7 @@ void SmartScript::UpdateTimer(SmartScriptHolder& e, uint32 const diff)
|
||||
{
|
||||
if (me && me->HasUnitState(UNIT_STATE_CASTING))
|
||||
{
|
||||
e.timer = 1200;
|
||||
RaisePriority(e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -4635,6 +4662,18 @@ void SmartScript::UpdateTimer(SmartScriptHolder& e, uint32 const diff)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (e.priority != SmartScriptHolder::DEFAULT_PRIORITY)
|
||||
{
|
||||
// Reset priority to default one only if the event hasn't been rescheduled again to next loop
|
||||
if (e.timer > 1)
|
||||
{
|
||||
// Re-sort events if this was moved to the top of the queue
|
||||
mEventSortingRequired = true;
|
||||
// Reset priority to default one
|
||||
e.priority = SmartScriptHolder::DEFAULT_PRIORITY;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
e.timer -= diff;
|
||||
@@ -4663,6 +4702,12 @@ void SmartScript::OnUpdate(uint32 const diff)
|
||||
|
||||
InstallEvents();//before UpdateTimers
|
||||
|
||||
if (mEventSortingRequired)
|
||||
{
|
||||
SortEvents(mEvents);
|
||||
mEventSortingRequired = false;
|
||||
}
|
||||
|
||||
for (SmartAIEventList::iterator i = mEvents.begin(); i != mEvents.end(); ++i)
|
||||
UpdateTimer(*i, diff);
|
||||
|
||||
@@ -4718,6 +4763,33 @@ void SmartScript::OnUpdate(uint32 const diff)
|
||||
}
|
||||
}
|
||||
|
||||
void SmartScript::SortEvents(SmartAIEventList& events)
|
||||
{
|
||||
std::sort(events.begin(), events.end());
|
||||
}
|
||||
|
||||
void SmartScript::RaisePriority(SmartScriptHolder& e)
|
||||
{
|
||||
e.timer = 1200;
|
||||
// Change priority only if it's set to default, otherwise keep the current order of events
|
||||
if (e.priority == SmartScriptHolder::DEFAULT_PRIORITY)
|
||||
{
|
||||
e.priority = mCurrentPriority++;
|
||||
mEventSortingRequired = true;
|
||||
}
|
||||
}
|
||||
|
||||
void SmartScript::RetryLater(SmartScriptHolder& e, bool ignoreChanceRoll)
|
||||
{
|
||||
RaisePriority(e);
|
||||
|
||||
// This allows to retry the action later without rolling again the chance roll (which might fail and end up not executing the action)
|
||||
if (ignoreChanceRoll)
|
||||
e.event.event_flags |= SMART_EVENT_FLAG_TEMP_IGNORE_CHANCE_ROLL;
|
||||
|
||||
e.runOnce = false;
|
||||
}
|
||||
|
||||
void SmartScript::FillScript(SmartAIEventList e, WorldObject* obj, AreaTrigger const* at)
|
||||
{
|
||||
(void)at; // ensure that the variable is referenced even if extra logs are disabled in order to pass compiler checks
|
||||
@@ -4822,37 +4894,8 @@ void SmartScript::OnInitialize(WorldObject* obj, AreaTrigger const* at)
|
||||
|
||||
GetScript();//load copy of script
|
||||
|
||||
uint32 maxDisableDist = 0;
|
||||
uint32 minEnableDist = 0;
|
||||
for (SmartAIEventList::iterator i = mEvents.begin(); i != mEvents.end(); ++i)
|
||||
{
|
||||
InitTimer((*i));//calculate timers for first time use
|
||||
if (i->GetEventType() == SMART_EVENT_RANGE && i->GetActionType() == SMART_ACTION_ALLOW_COMBAT_MOVEMENT)
|
||||
{
|
||||
if (i->action.combatMove.move == 1 && i->event.minMaxRepeat.rangeMin > minEnableDist)
|
||||
minEnableDist = i->event.minMaxRepeat.rangeMin;
|
||||
else if (i->action.combatMove.move == 0 && (i->event.minMaxRepeat.rangeMax < maxDisableDist || maxDisableDist == 0))
|
||||
maxDisableDist = i->event.minMaxRepeat.rangeMax;
|
||||
}
|
||||
|
||||
// Xinef: if smartcast combat move flag is present
|
||||
if (i->GetActionType() == SMART_ACTION_CAST && (i->action.cast.castFlags & SMARTCAST_COMBAT_MOVE))
|
||||
{
|
||||
if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(i->action.cast.spell))
|
||||
{
|
||||
float maxRange = spellInfo->GetMaxRange(spellInfo->IsPositive());
|
||||
float minRange = spellInfo->GetMinRange(spellInfo->IsPositive());
|
||||
|
||||
if (maxRange > 0 && minRange <= maxRange)
|
||||
{
|
||||
smartCasterMaxDist = minRange + ((maxRange - minRange) * 0.65f);
|
||||
smartCasterPowerType = (Powers)spellInfo->PowerType;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (maxDisableDist > 0 && minEnableDist >= maxDisableDist)
|
||||
mMaxCombatDist = uint32(maxDisableDist + ((minEnableDist - maxDisableDist) / 2));
|
||||
for (SmartScriptHolder& event : mEvents)
|
||||
InitTimer(event);//calculate timers for first time use
|
||||
|
||||
ProcessEventsFor(SMART_EVENT_AI_INIT);
|
||||
InstallEvents();
|
||||
|
||||
Reference in New Issue
Block a user