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-picked https://github.com/TrinityCore/TrinityCore/commit/7fb74326205acbc5c0fa6b2aa9731d891e788d64
- Cherry-picked https://github.com/TrinityCore/TrinityCore/commit/63a6e1e0480df659e773a3df5e1acc2027ebcf15

Co-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 picked https://github.com/TrinityCore/TrinityCore/commit/ca25e8d0199730c0976ebc37317e9407aceccc34
- Cherry picked https://github.com/TrinityCore/TrinityCore/commit/96b289cadbf304e427ddbd281d1a6f592af612a7

Co-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:
AG
2024-01-03 09:56:24 +01:00
committed by GitHub
parent 623ee56509
commit 8f127f9e21
8 changed files with 224 additions and 142 deletions
@@ -0,0 +1,32 @@
# Lord Cobrahn
DELETE FROM `smart_scripts` WHERE `entryorguid`=3669 AND `source_type`=0 AND `id`=6 AND `link`=7;
UPDATE `smart_scripts` SET `id`=6 WHERE `entryorguid`=3669 AND `source_type`=0 AND `id`=7 AND `link`=0;
UPDATE `smart_scripts` SET `id`=7 WHERE `entryorguid`=3669 AND `source_type`=0 AND `id`=8 AND `link`=0;
# Druid of the Fang
DELETE FROM `smart_scripts` WHERE `entryorguid`=3840 AND `source_type`=0 AND `id`=4 AND `link`=0;
UPDATE `smart_scripts` SET `link`=0 WHERE `entryorguid`=3840 AND `source_type`=0 AND `id`=3 AND `link`=4;
UPDATE `smart_scripts` SET `id`=4, `link`=5 WHERE `entryorguid`=3840 AND `source_type`=0 AND `id`=5 AND `link`=6;
UPDATE `smart_scripts` SET `id`=5 WHERE `entryorguid`=3840 AND `source_type`=0 AND `id`=6 AND `link`=0;
UPDATE `smart_scripts` SET `id`=6 WHERE `entryorguid`=3840 AND `source_type`=0 AND `id`=7 AND `link`=0;
# Phantom Guest
DELETE FROM `smart_scripts` WHERE `entryorguid`=16409 AND `source_type`=0 AND `id`=6 AND `link`=0;
UPDATE `smart_scripts` SET `id`=6 WHERE `entryorguid`=16409 AND `source_type`=0 AND `id`=7 AND `link`=0;
UPDATE `smart_scripts` SET `id`=7 WHERE `entryorguid`=16409 AND `source_type`=0 AND `id`=8 AND `link`=0;
UPDATE `smart_scripts` SET `id`=8 WHERE `entryorguid`=16409 AND `source_type`=0 AND `id`=9 AND `link`=0;
UPDATE `smart_scripts` SET `id`=9 WHERE `entryorguid`=16409 AND `source_type`=0 AND `id`=10 AND `link`=0;
UPDATE `smart_scripts` SET `id`=10 WHERE `entryorguid`=16409 AND `source_type`=0 AND `id`=11 AND `link`=0;
UPDATE `smart_scripts` SET `id`=11 WHERE `entryorguid`=16409 AND `source_type`=0 AND `id`=12 AND `link`=0;
UPDATE `smart_scripts` SET `id`=12 WHERE `entryorguid`=16409 AND `source_type`=0 AND `id`=13 AND `link`=0;
UPDATE `smart_scripts` SET `id`=13 WHERE `entryorguid`=16409 AND `source_type`=0 AND `id`=14 AND `link`=0;
UPDATE `smart_scripts` SET `id`=14 WHERE `entryorguid`=16409 AND `source_type`=0 AND `id`=15 AND `link`=0;
UPDATE `smart_scripts` SET `id`=15 WHERE `entryorguid`=16409 AND `source_type`=0 AND `id`=20 AND `link`=0;
UPDATE `smart_scripts` SET `id`=16 WHERE `entryorguid`=16409 AND `source_type`=0 AND `id`=21 AND `link`=0;
UPDATE `smart_scripts` SET `id`=17 WHERE `entryorguid`=16409 AND `source_type`=0 AND `id`=22 AND `link`=0;
# Maiev Shadowsong
DELETE FROM `smart_scripts` WHERE `entryorguid`=23197 AND `source_type`=0 AND `id`=7 AND `link`=0;
DELETE FROM `smart_scripts` WHERE `entryorguid`=23197 AND `source_type`=0 AND `id`=8 AND `link`=0;
UPDATE `smart_scripts` SET `id`=7 WHERE `entryorguid`=23197 AND `source_type`=0 AND `id`=9 AND `link`=0;
UPDATE `smart_scripts` SET `id`=8 WHERE `entryorguid`=23197 AND `source_type`=0 AND `id`=10 AND `link`=0;
+35 -21
View File
@@ -500,6 +500,7 @@ void SmartAI::CheckConditions(const uint32 diff)
void SmartAI::UpdateAI(uint32 diff) void SmartAI::UpdateAI(uint32 diff)
{ {
bool hasVictim = UpdateVictim();
CheckConditions(diff); CheckConditions(diff);
GetScript()->OnUpdate(diff); GetScript()->OnUpdate(diff);
UpdatePath(diff); UpdatePath(diff);
@@ -535,7 +536,7 @@ void SmartAI::UpdateAI(uint32 diff)
return; return;
} }
if (!UpdateVictim()) if (!hasVictim)
return; return;
if (mCanAutoAttack) if (mCanAutoAttack)
@@ -827,14 +828,14 @@ void SmartAI::AttackStart(Unit* who)
// xinef: dont allow charmed npcs to act on their own // xinef: dont allow charmed npcs to act on their own
if (me->HasUnitFlag(UNIT_FLAG_POSSESSED)) if (me->HasUnitFlag(UNIT_FLAG_POSSESSED))
{ {
if (who && mCanAutoAttack) if (who)
me->Attack(who, true); me->Attack(who, mCanAutoAttack);
return; return;
} }
if (who && me->Attack(who, me->IsWithinMeleeRange(who))) if (who && me->Attack(who, me->IsWithinMeleeRange(who)))
{ {
if (mCanCombatMove || GetScript()->GetMaxCombatDist()) if (mCanCombatMove)
{ {
SetRun(mRun); SetRun(mRun);
MovementGeneratorType type = me->GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_ACTIVE); MovementGeneratorType type = me->GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_ACTIVE);
@@ -843,8 +844,8 @@ void SmartAI::AttackStart(Unit* who)
me->GetMotionMaster()->MovementExpired(); me->GetMotionMaster()->MovementExpired();
me->StopMoving(); me->StopMoving();
} }
float range = GetScript()->GetCasterActualDist() > 0.f ? GetScript()->GetCasterActualDist() : GetScript()->GetActualCombatDist();
me->GetMotionMaster()->MoveChase(who, range > 0.f ? ChaseRange(range) : std::optional<ChaseRange>()); me->GetMotionMaster()->MoveChase(who);
} }
} }
} }
@@ -1019,21 +1020,8 @@ void SmartAI::sQuestReward(Player* player, Quest const* quest, uint32 opt)
GetScript()->ProcessEventsFor(SMART_EVENT_REWARD_QUEST, player, quest->GetQuestId(), opt); GetScript()->ProcessEventsFor(SMART_EVENT_REWARD_QUEST, player, quest->GetQuestId(), opt);
} }
void SmartAI::SetForcedCombatMove(float dist) void SmartAI::SetCombatMove(bool on, float chaseRange)
{ {
if (!me->GetVictim())
return;
SetRun(mRun);
me->GetMotionMaster()->MoveChase(me->GetVictim(), dist);
}
void SmartAI::SetCombatMove(bool on)
{
// Xinef: Fix Combat Movement
if (GetScript()->GetMaxCombatDist()/* || GetScript()->GetCasterMaxDist()*/) // Xinef: we only need this hack for old caster movement system
return;
if (mCanCombatMove == on) if (mCanCombatMove == on)
return; return;
@@ -1049,7 +1037,7 @@ void SmartAI::SetCombatMove(bool on)
if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == IDLE_MOTION_TYPE) if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == IDLE_MOTION_TYPE)
{ {
SetRun(mRun); SetRun(mRun);
me->GetMotionMaster()->MoveChase(me->GetVictim()); me->GetMotionMaster()->MoveChase(me->GetVictim(), chaseRange);
me->CastStop(); me->CastStop();
} }
} }
@@ -1115,6 +1103,32 @@ void SmartAI::StopFollow(bool complete)
GetScript()->ProcessEventsFor(SMART_EVENT_FOLLOW_COMPLETED, player); GetScript()->ProcessEventsFor(SMART_EVENT_FOLLOW_COMPLETED, player);
} }
void SmartAI::MoveAway(float distance)
{
if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == POINT_MOTION_TYPE)
return;
mCanCombatMove = false;
if (!IsAIControlled())
return;
if (!HasEscortState(SMART_ESCORT_ESCORTING))
{
if (me->GetVictim())
{
me->StopMoving();
if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == CHASE_MOTION_TYPE)
me->GetMotionMaster()->Clear(false);
float x, y, z;
me->GetClosePoint(x, y, z, me->GetObjectSize(), distance, M_PI);
if (me->GetVictim()->IsWithinLOS(x, y, z))
me->GetMotionMaster()->MovePoint(SMART_RANDOM_POINT, x, y, z);
}
}
}
void SmartAI::SetScript9(SmartScriptHolder& e, uint32 entry, Unit* invoker) void SmartAI::SetScript9(SmartScriptHolder& e, uint32 entry, Unit* invoker)
{ {
if (invoker) if (invoker)
+2 -2
View File
@@ -64,10 +64,11 @@ public:
bool IsEscorted() override { return (mEscortState & SMART_ESCORT_ESCORTING); } bool IsEscorted() override { return (mEscortState & SMART_ESCORT_ESCORTING); }
void RemoveEscortState(uint32 uiEscortState) { mEscortState &= ~uiEscortState; } void RemoveEscortState(uint32 uiEscortState) { mEscortState &= ~uiEscortState; }
void SetAutoAttack(bool on) { mCanAutoAttack = on; } void SetAutoAttack(bool on) { mCanAutoAttack = on; }
void SetCombatMove(bool on); void SetCombatMove(bool on, float chaseRange = 0.0f);
bool CanCombatMove() { return mCanCombatMove; } bool CanCombatMove() { return mCanCombatMove; }
void SetFollow(Unit* target, float dist = 0.0f, float angle = 0.0f, uint32 credit = 0, uint32 end = 0, uint32 creditType = 0, bool aliveState = true); void SetFollow(Unit* target, float dist = 0.0f, float angle = 0.0f, uint32 credit = 0, uint32 end = 0, uint32 creditType = 0, bool aliveState = true);
void StopFollow(bool complete); void StopFollow(bool complete);
void MoveAway(float distance);
void SetScript9(SmartScriptHolder& e, uint32 entry, Unit* invoker); void SetScript9(SmartScriptHolder& e, uint32 entry, Unit* invoker);
SmartScript* GetScript() { return &mScript; } SmartScript* GetScript() { return &mScript; }
@@ -205,7 +206,6 @@ public:
// Xinef // Xinef
void SetWPPauseTimer(uint32 time) { mWPPauseTimer = time; } void SetWPPauseTimer(uint32 time) { mWPPauseTimer = time; }
void SetForcedCombatMove(float dist);
private: private:
bool mIsCharmed; bool mIsCharmed;
+120 -77
View File
@@ -56,15 +56,8 @@ SmartScript::SmartScript()
mTemplate = SMARTAI_TEMPLATE_BASIC; mTemplate = SMARTAI_TEMPLATE_BASIC;
mScriptType = SMART_SCRIPT_TYPE_CREATURE; mScriptType = SMART_SCRIPT_TYPE_CREATURE;
isProcessingTimedActionList = false; isProcessingTimedActionList = false;
mCurrentPriority = 0;
// Xinef: Fix Combat Movement mEventSortingRequired = false;
mActualCombatDist = 0;
mMaxCombatDist = 0;
smartCasterActualDist = 0.0f;
smartCasterMaxDist = 0.0f;
smartCasterPowerType = POWER_MANA;
_allowPhaseReset = true; _allowPhaseReset = true;
} }
@@ -87,14 +80,16 @@ void SmartScript::OnReset()
InitTimer((*i)); InitTimer((*i));
(*i).runOnce = false; (*i).runOnce = false;
} }
if ((*i).priority != SmartScriptHolder::DEFAULT_PRIORITY)
{
(*i).priority = SmartScriptHolder::DEFAULT_PRIORITY;
mEventSortingRequired = true;
}
} }
ProcessEventsFor(SMART_EVENT_RESET); ProcessEventsFor(SMART_EVENT_RESET);
mLastInvoker.Clear(); mLastInvoker.Clear();
mCounterList.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) 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) 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 //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); uint32 rnd = urand(1, 100);
if (e.event.event_chance <= rnd) if (e.event.event_chance <= rnd)
return; 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) if (unit)
mLastInvoker = unit->GetGUID(); mLastInvoker = unit->GetGUID();
@@ -589,6 +588,8 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
if (e.action.cast.targetsLimit) if (e.action.cast.targetsLimit)
Acore::Containers::RandomResize(targets, e.action.cast.targetsLimit); Acore::Containers::RandomResize(targets, e.action.cast.targetsLimit);
bool failedSpellCast = false, successfulSpellCast = false;
for (WorldObject* target : targets) for (WorldObject* target : targets)
{ {
// may be nullptr // 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) if (e.action.cast.castFlags & SMARTCAST_INTERRUPT_PREVIOUS)
me->InterruptNonMeleeSpells(false); 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); SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(e.action.cast.spell);
int32 currentPower = me->GetPower(GetCasterPowerType()); 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)) 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)
{ {
SetCasterActualDist(0); failedSpellCast = true; // Mark spellcast as failed so we can retry it later
CAST_AI(SmartAI, me->AI())->SetForcedCombatMove(0); float minDistance = std::max(meleeRange, spellMinRange) - distanceToTarget + NOMINAL_MELEE_RANGE;
CAST_AI(SmartAI, me->AI())->MoveAway(std::min(minDistance, spellMaxRange));
continue;
} }
else if (GetCasterActualDist() == 0.0f && me->GetPowerPct(GetCasterPowerType()) > 30.0f)
// 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)
{ {
RestoreCasterMaxDist(); failedSpellCast = true;
CAST_AI(SmartAI, me->AI())->SetForcedCombatMove(GetCasterActualDist()); 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; TriggerCastFlags triggerFlags = TRIGGERED_NONE;
@@ -649,11 +667,38 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
triggerFlags = TRIGGERED_FULL_MASK; 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 {}", 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); 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; break;
} }
case SMART_ACTION_SELF_CAST: case SMART_ACTION_SELF_CAST:
@@ -864,14 +909,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
if (!IsSmart()) if (!IsSmart())
break; break;
// Xinef: Fix Combat Movement
bool move = e.action.combatMove.move; 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 = {}", LOG_DEBUG("sql.sql", "SmartScript::ProcessAction:: SMART_ACTION_ALLOW_COMBAT_MOVEMENT: Creature {} bool on = {}",
me->GetGUID().ToString(), e.action.combatMove.move); me->GetGUID().ToString(), e.action.combatMove.move);
@@ -2425,17 +2463,6 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
break; 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: case SMART_ACTION_SET_SIGHT_DIST:
{ {
for (WorldObject* const target : targets) for (WorldObject* const target : targets)
@@ -4577,7 +4604,7 @@ void SmartScript::UpdateTimer(SmartScriptHolder& e, uint32 const diff)
{ {
if (me && me->HasUnitState(UNIT_STATE_CASTING)) if (me && me->HasUnitState(UNIT_STATE_CASTING))
{ {
e.timer = 1200; RaisePriority(e);
return; return;
} }
} }
@@ -4635,6 +4662,18 @@ void SmartScript::UpdateTimer(SmartScriptHolder& e, uint32 const diff)
break; 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 else
e.timer -= diff; e.timer -= diff;
@@ -4663,6 +4702,12 @@ void SmartScript::OnUpdate(uint32 const diff)
InstallEvents();//before UpdateTimers InstallEvents();//before UpdateTimers
if (mEventSortingRequired)
{
SortEvents(mEvents);
mEventSortingRequired = false;
}
for (SmartAIEventList::iterator i = mEvents.begin(); i != mEvents.end(); ++i) for (SmartAIEventList::iterator i = mEvents.begin(); i != mEvents.end(); ++i)
UpdateTimer(*i, diff); 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 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 (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 GetScript();//load copy of script
uint32 maxDisableDist = 0; for (SmartScriptHolder& event : mEvents)
uint32 minEnableDist = 0; InitTimer(event);//calculate timers for first time use
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));
ProcessEventsFor(SMART_EVENT_AI_INIT); ProcessEventsFor(SMART_EVENT_AI_INIT);
InstallEvents(); InstallEvents();
+6 -22
View File
@@ -216,19 +216,6 @@ public:
typedef std::unordered_map<uint32, uint32> CounterMap; typedef std::unordered_map<uint32, uint32> CounterMap;
CounterMap mCounterList; CounterMap mCounterList;
// Xinef: Fix Combat Movement
void SetActualCombatDist(uint32 dist) { mActualCombatDist = dist; }
void RestoreMaxCombatDist() { mActualCombatDist = mMaxCombatDist; }
uint32 GetActualCombatDist() const { return mActualCombatDist; }
uint32 GetMaxCombatDist() const { return mMaxCombatDist; }
// Xinef: SmartCasterAI, replace above
void SetCasterActualDist(float dist) { smartCasterActualDist = dist; }
void RestoreCasterMaxDist() { smartCasterActualDist = smartCasterMaxDist; }
Powers GetCasterPowerType() const { return smartCasterPowerType; }
float GetCasterActualDist() const { return smartCasterActualDist; }
float GetCasterMaxDist() const { return smartCasterMaxDist; }
bool AllowPhaseReset() const { return _allowPhaseReset; } bool AllowPhaseReset() const { return _allowPhaseReset; }
void SetPhaseReset(bool allow) { _allowPhaseReset = allow; } void SetPhaseReset(bool allow) { _allowPhaseReset = allow; }
@@ -241,6 +228,10 @@ private:
void SetPhase(uint32 p); void SetPhase(uint32 p);
bool IsInPhase(uint32 p) const; bool IsInPhase(uint32 p) const;
void SortEvents(SmartAIEventList& events);
void RaisePriority(SmartScriptHolder& e);
void RetryLater(SmartScriptHolder& e, bool ignoreChanceRoll = false);
SmartAIEventList mEvents; SmartAIEventList mEvents;
SmartAIEventList mInstallEvents; SmartAIEventList mInstallEvents;
SmartAIEventList mTimedActionList; SmartAIEventList mTimedActionList;
@@ -262,15 +253,8 @@ private:
uint32 mLastTextID; uint32 mLastTextID;
uint32 mTalkerEntry; uint32 mTalkerEntry;
bool mUseTextTimer; bool mUseTextTimer;
uint32 mCurrentPriority;
// Xinef: Fix Combat Movement bool mEventSortingRequired;
uint32 mActualCombatDist;
uint32 mMaxCombatDist;
// Xinef: SmartCasterAI, replace above in future
uint32 smartCasterActualDist;
uint32 smartCasterMaxDist;
Powers smartCasterPowerType;
// Xinef: misc // Xinef: misc
bool _allowPhaseReset; bool _allowPhaseReset;
@@ -755,7 +755,6 @@ bool SmartAIMgr::CheckUnusedActionParams(SmartScriptHolder const& e)
case SMART_ACTION_EXIT_VEHICLE: return NO_PARAMS; case SMART_ACTION_EXIT_VEHICLE: return NO_PARAMS;
case SMART_ACTION_SET_UNIT_MOVEMENT_FLAGS: return sizeof(SmartAction::movementFlag); case SMART_ACTION_SET_UNIT_MOVEMENT_FLAGS: return sizeof(SmartAction::movementFlag);
case SMART_ACTION_SET_COMBAT_DISTANCE: return sizeof(SmartAction::combatDistance); case SMART_ACTION_SET_COMBAT_DISTANCE: return sizeof(SmartAction::combatDistance);
case SMART_ACTION_SET_CASTER_COMBAT_DIST: return sizeof(SmartAction::casterDistance);
case SMART_ACTION_FALL: return NO_PARAMS; case SMART_ACTION_FALL: return NO_PARAMS;
case SMART_ACTION_SET_EVENT_FLAG_RESET: return sizeof(SmartAction::setActive); case SMART_ACTION_SET_EVENT_FLAG_RESET: return sizeof(SmartAction::setActive);
case SMART_ACTION_STOP_MOTION: return sizeof(SmartAction::stopMotion); case SMART_ACTION_STOP_MOTION: return sizeof(SmartAction::stopMotion);
@@ -1918,7 +1917,6 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
case SMART_ACTION_EXIT_VEHICLE: case SMART_ACTION_EXIT_VEHICLE:
case SMART_ACTION_SET_UNIT_MOVEMENT_FLAGS: case SMART_ACTION_SET_UNIT_MOVEMENT_FLAGS:
case SMART_ACTION_SET_COMBAT_DISTANCE: case SMART_ACTION_SET_COMBAT_DISTANCE:
case SMART_ACTION_SET_CASTER_COMBAT_DIST:
case SMART_ACTION_SET_SIGHT_DIST: case SMART_ACTION_SET_SIGHT_DIST:
case SMART_ACTION_FLEE: case SMART_ACTION_FLEE:
case SMART_ACTION_ADD_THREAT: case SMART_ACTION_ADD_THREAT:
@@ -26,6 +26,7 @@
#include "Spell.h" #include "Spell.h"
#include "SpellMgr.h" #include "SpellMgr.h"
#include "Unit.h" #include "Unit.h"
#include <limits>
typedef uint32 SAIBool; typedef uint32 SAIBool;
@@ -686,7 +687,7 @@ enum SMART_ACTION
SMART_ACTION_EXIT_VEHICLE = 203, // none SMART_ACTION_EXIT_VEHICLE = 203, // none
SMART_ACTION_SET_UNIT_MOVEMENT_FLAGS = 204, // flags SMART_ACTION_SET_UNIT_MOVEMENT_FLAGS = 204, // flags
SMART_ACTION_SET_COMBAT_DISTANCE = 205, // combatDistance SMART_ACTION_SET_COMBAT_DISTANCE = 205, // combatDistance
SMART_ACTION_SET_CASTER_COMBAT_DIST = 206, // followDistance, resetToMax // UNUSED = 206,
SMART_ACTION_SET_HOVER = 207, // 0/1 SMART_ACTION_SET_HOVER = 207, // 0/1
SMART_ACTION_ADD_IMMUNITY = 208, // type, id, value SMART_ACTION_ADD_IMMUNITY = 208, // type, id, value
SMART_ACTION_REMOVE_IMMUNITY = 209, // type, id, value SMART_ACTION_REMOVE_IMMUNITY = 209, // type, id, value
@@ -1859,7 +1860,10 @@ enum SmartEventFlags
SMART_EVENT_FLAG_WHILE_CHARMED = 0x200, // Event occurs even if AI owner is charmed SMART_EVENT_FLAG_WHILE_CHARMED = 0x200, // Event occurs even if AI owner is charmed
SMART_EVENT_FLAG_DIFFICULTY_ALL = (SMART_EVENT_FLAG_DIFFICULTY_0 | SMART_EVENT_FLAG_DIFFICULTY_1 | SMART_EVENT_FLAG_DIFFICULTY_2 | SMART_EVENT_FLAG_DIFFICULTY_3), SMART_EVENT_FLAG_DIFFICULTY_ALL = (SMART_EVENT_FLAG_DIFFICULTY_0 | SMART_EVENT_FLAG_DIFFICULTY_1 | SMART_EVENT_FLAG_DIFFICULTY_2 | SMART_EVENT_FLAG_DIFFICULTY_3),
SMART_EVENT_FLAGS_ALL = (SMART_EVENT_FLAG_NOT_REPEATABLE | SMART_EVENT_FLAG_DIFFICULTY_ALL | SMART_EVENT_FLAG_RESERVED_5 | SMART_EVENT_FLAG_RESERVED_6 | SMART_EVENT_FLAG_DEBUG_ONLY | SMART_EVENT_FLAG_DONT_RESET | SMART_EVENT_FLAG_WHILE_CHARMED) SMART_EVENT_FLAGS_ALL = (SMART_EVENT_FLAG_NOT_REPEATABLE | SMART_EVENT_FLAG_DIFFICULTY_ALL | SMART_EVENT_FLAG_RESERVED_5 | SMART_EVENT_FLAG_RESERVED_6 | SMART_EVENT_FLAG_DEBUG_ONLY | SMART_EVENT_FLAG_DONT_RESET | SMART_EVENT_FLAG_WHILE_CHARMED),
// Temp flags, used only at runtime, never stored in DB
SMART_EVENT_FLAG_TEMP_IGNORE_CHANCE_ROLL = 0x40000000, //Event occurs no matter what roll_chance_i(e.event.event_chance) returns.
}; };
enum SmartCastFlags enum SmartCastFlags
@@ -1889,7 +1893,7 @@ enum SmartFollowType
struct SmartScriptHolder struct SmartScriptHolder
{ {
SmartScriptHolder() : entryOrGuid(0), source_type(SMART_SCRIPT_TYPE_CREATURE) SmartScriptHolder() : entryOrGuid(0), source_type(SMART_SCRIPT_TYPE_CREATURE)
, event_id(0), link(0), event(), action(), target(), timer(0), active(false), runOnce(false) , event_id(0), link(0), event(), action(), target(), timer(0), priority(DEFAULT_PRIORITY), active(false), runOnce(false)
, enableTimed(false) {} , enableTimed(false) {}
int32 entryOrGuid; int32 entryOrGuid;
@@ -1908,9 +1912,18 @@ public:
uint32 GetTargetType() const { return (uint32)target.type; } uint32 GetTargetType() const { return (uint32)target.type; }
uint32 timer; uint32 timer;
uint32 priority;
bool active; bool active;
bool runOnce; bool runOnce;
bool enableTimed; bool enableTimed;
// Default comparision operator using priority field as first ordering field
bool operator<(SmartScriptHolder const& other) const
{
return std::tie(priority, entryOrGuid, source_type, event_id, link) < std::tie(other.priority, other.entryOrGuid, other.source_type, other.event_id, other.link);
}
static constexpr uint32 DEFAULT_PRIORITY = std::numeric_limits<uint32>::max();
}; };
typedef std::unordered_map<uint32, WayPoint*> WPPath; typedef std::unordered_map<uint32, WayPoint*> WPPath;
@@ -107,7 +107,7 @@ bool ChaseMovementGenerator<T>::DoUpdate(T* owner, uint32 time_diff)
if (i_recalculateTravel && PositionOkay(owner, target, _movingTowards ? maxTarget : Optional<float>(), angle)) if (i_recalculateTravel && PositionOkay(owner, target, _movingTowards ? maxTarget : Optional<float>(), angle))
{ {
if (owner->HasUnitState(UNIT_STATE_CHASE_MOVE) && !target->isMoving() && !mutualChase) if ((owner->HasUnitState(UNIT_STATE_CHASE_MOVE) && !target->isMoving() && !mutualChase) || _range)
{ {
i_recalculateTravel = false; i_recalculateTravel = false;
i_path = nullptr; i_path = nullptr;
@@ -134,11 +134,11 @@ bool ChaseMovementGenerator<T>::DoUpdate(T* owner, uint32 time_diff)
} }
// if the target moved, we have to consider whether to adjust // if the target moved, we have to consider whether to adjust
if (!_lastTargetPosition || target->GetPosition() != _lastTargetPosition.value() || mutualChase != _mutualChase) if (!_lastTargetPosition || target->GetPosition() != _lastTargetPosition.value() || mutualChase != _mutualChase || !owner->IsWithinLOSInMap(target))
{ {
_lastTargetPosition = target->GetPosition(); _lastTargetPosition = target->GetPosition();
_mutualChase = mutualChase; _mutualChase = mutualChase;
if (owner->HasUnitState(UNIT_STATE_CHASE_MOVE) || !PositionOkay(owner, target, target->isMoving() ? maxTarget : maxRange, angle)) if (owner->HasUnitState(UNIT_STATE_CHASE_MOVE) || !PositionOkay(owner, target, maxTarget, angle))
{ {
// can we get to the target? // can we get to the target?
if (cOwner && !target->isInAccessiblePlaceFor(cOwner)) if (cOwner && !target->isInAccessiblePlaceFor(cOwner))
@@ -150,10 +150,10 @@ bool ChaseMovementGenerator<T>::DoUpdate(T* owner, uint32 time_diff)
} }
// figure out which way we want to move // figure out which way we want to move
float tarX, tarY, tarZ; float x, y, z;
target->GetPosition(tarX, tarY, tarZ); target->GetPosition(x, y, z);
bool withinRange = owner->IsInDist(target, maxRange); bool withinRange = owner->IsInDist(target, maxRange);
bool withinLOS = owner->IsWithinLOS(tarX, tarY, tarZ); bool withinLOS = owner->IsWithinLOS(x, y, z);
bool moveToward = !(withinRange && withinLOS); bool moveToward = !(withinRange && withinLOS);
// make a new path if we have to... // make a new path if we have to...
@@ -181,14 +181,12 @@ bool ChaseMovementGenerator<T>::DoUpdate(T* owner, uint32 time_diff)
additionalRange = owner->GetExactDistSq(target) < G3D::square(speed) ? 0 : speed; additionalRange = owner->GetExactDistSq(target) < G3D::square(speed) ? 0 : speed;
} }
float x, y, z;
bool shortenPath; bool shortenPath;
// if we want to move toward the target and there's no fixed angle... // if we want to move toward the target and there's no fixed angle...
if (moveToward && !angle) if (moveToward && !angle)
{ {
// ...we'll pathfind to the center, then shorten the path // ...we'll pathfind to the center, then shorten the path
target->GetPosition(x, y, z);
shortenPath = true; shortenPath = true;
} }
else else
@@ -214,7 +212,7 @@ bool ChaseMovementGenerator<T>::DoUpdate(T* owner, uint32 time_diff)
} }
if (shortenPath) if (shortenPath)
i_path->ShortenPathUntilDist(G3D::Vector3(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ()), maxTarget); i_path->ShortenPathUntilDist(G3D::Vector3(x, y, z), maxTarget);
if (cOwner) if (cOwner)
{ {