diff --git a/data/sql/updates/pending_db_world/2023_12_14_00.sql b/data/sql/updates/pending_db_world/2023_12_14_00.sql new file mode 100644 index 000000000..56dc8d506 --- /dev/null +++ b/data/sql/updates/pending_db_world/2023_12_14_00.sql @@ -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; diff --git a/src/server/game/AI/SmartScripts/SmartAI.cpp b/src/server/game/AI/SmartScripts/SmartAI.cpp index c1210c836..84b726ab5 100644 --- a/src/server/game/AI/SmartScripts/SmartAI.cpp +++ b/src/server/game/AI/SmartScripts/SmartAI.cpp @@ -500,6 +500,7 @@ void SmartAI::CheckConditions(const uint32 diff) void SmartAI::UpdateAI(uint32 diff) { + bool hasVictim = UpdateVictim(); CheckConditions(diff); GetScript()->OnUpdate(diff); UpdatePath(diff); @@ -535,7 +536,7 @@ void SmartAI::UpdateAI(uint32 diff) return; } - if (!UpdateVictim()) + if (!hasVictim) return; if (mCanAutoAttack) @@ -827,14 +828,14 @@ void SmartAI::AttackStart(Unit* who) // xinef: dont allow charmed npcs to act on their own if (me->HasUnitFlag(UNIT_FLAG_POSSESSED)) { - if (who && mCanAutoAttack) - me->Attack(who, true); + if (who) + me->Attack(who, mCanAutoAttack); return; } if (who && me->Attack(who, me->IsWithinMeleeRange(who))) { - if (mCanCombatMove || GetScript()->GetMaxCombatDist()) + if (mCanCombatMove) { SetRun(mRun); MovementGeneratorType type = me->GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_ACTIVE); @@ -843,8 +844,8 @@ void SmartAI::AttackStart(Unit* who) me->GetMotionMaster()->MovementExpired(); me->StopMoving(); } - float range = GetScript()->GetCasterActualDist() > 0.f ? GetScript()->GetCasterActualDist() : GetScript()->GetActualCombatDist(); - me->GetMotionMaster()->MoveChase(who, range > 0.f ? ChaseRange(range) : std::optional()); + + 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); } -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) return; @@ -1049,7 +1037,7 @@ void SmartAI::SetCombatMove(bool on) if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == IDLE_MOTION_TYPE) { SetRun(mRun); - me->GetMotionMaster()->MoveChase(me->GetVictim()); + me->GetMotionMaster()->MoveChase(me->GetVictim(), chaseRange); me->CastStop(); } } @@ -1115,6 +1103,32 @@ void SmartAI::StopFollow(bool complete) 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) { if (invoker) diff --git a/src/server/game/AI/SmartScripts/SmartAI.h b/src/server/game/AI/SmartScripts/SmartAI.h index 5723822f5..79d72c807 100644 --- a/src/server/game/AI/SmartScripts/SmartAI.h +++ b/src/server/game/AI/SmartScripts/SmartAI.h @@ -64,10 +64,11 @@ public: bool IsEscorted() override { return (mEscortState & SMART_ESCORT_ESCORTING); } void RemoveEscortState(uint32 uiEscortState) { mEscortState &= ~uiEscortState; } void SetAutoAttack(bool on) { mCanAutoAttack = on; } - void SetCombatMove(bool on); + void SetCombatMove(bool on, float chaseRange = 0.0f); 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 StopFollow(bool complete); + void MoveAway(float distance); void SetScript9(SmartScriptHolder& e, uint32 entry, Unit* invoker); SmartScript* GetScript() { return &mScript; } @@ -205,7 +206,6 @@ public: // Xinef void SetWPPauseTimer(uint32 time) { mWPPauseTimer = time; } - void SetForcedCombatMove(float dist); private: bool mIsCharmed; diff --git a/src/server/game/AI/SmartScripts/SmartScript.cpp b/src/server/game/AI/SmartScripts/SmartScript.cpp index 41e3f9ce3..8ccdf9830 100644 --- a/src/server/game/AI/SmartScripts/SmartScript.cpp +++ b/src/server/game/AI/SmartScripts/SmartScript.cpp @@ -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(); diff --git a/src/server/game/AI/SmartScripts/SmartScript.h b/src/server/game/AI/SmartScripts/SmartScript.h index 61a1d7efa..b9ab57327 100644 --- a/src/server/game/AI/SmartScripts/SmartScript.h +++ b/src/server/game/AI/SmartScripts/SmartScript.h @@ -216,19 +216,6 @@ public: typedef std::unordered_map CounterMap; 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; } void SetPhaseReset(bool allow) { _allowPhaseReset = allow; } @@ -241,6 +228,10 @@ private: void SetPhase(uint32 p); bool IsInPhase(uint32 p) const; + void SortEvents(SmartAIEventList& events); + void RaisePriority(SmartScriptHolder& e); + void RetryLater(SmartScriptHolder& e, bool ignoreChanceRoll = false); + SmartAIEventList mEvents; SmartAIEventList mInstallEvents; SmartAIEventList mTimedActionList; @@ -262,15 +253,8 @@ private: uint32 mLastTextID; uint32 mTalkerEntry; bool mUseTextTimer; - - // Xinef: Fix Combat Movement - uint32 mActualCombatDist; - uint32 mMaxCombatDist; - - // Xinef: SmartCasterAI, replace above in future - uint32 smartCasterActualDist; - uint32 smartCasterMaxDist; - Powers smartCasterPowerType; + uint32 mCurrentPriority; + bool mEventSortingRequired; // Xinef: misc bool _allowPhaseReset; diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp index 2ce60090a..7a7bf270f 100644 --- a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp +++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp @@ -755,7 +755,6 @@ bool SmartAIMgr::CheckUnusedActionParams(SmartScriptHolder const& e) case SMART_ACTION_EXIT_VEHICLE: return NO_PARAMS; 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_CASTER_COMBAT_DIST: return sizeof(SmartAction::casterDistance); case SMART_ACTION_FALL: return NO_PARAMS; case SMART_ACTION_SET_EVENT_FLAG_RESET: return sizeof(SmartAction::setActive); 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_SET_UNIT_MOVEMENT_FLAGS: case SMART_ACTION_SET_COMBAT_DISTANCE: - case SMART_ACTION_SET_CASTER_COMBAT_DIST: case SMART_ACTION_SET_SIGHT_DIST: case SMART_ACTION_FLEE: case SMART_ACTION_ADD_THREAT: diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.h b/src/server/game/AI/SmartScripts/SmartScriptMgr.h index a9530daed..73f5a8973 100644 --- a/src/server/game/AI/SmartScripts/SmartScriptMgr.h +++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.h @@ -26,6 +26,7 @@ #include "Spell.h" #include "SpellMgr.h" #include "Unit.h" +#include typedef uint32 SAIBool; @@ -686,7 +687,7 @@ enum SMART_ACTION SMART_ACTION_EXIT_VEHICLE = 203, // none SMART_ACTION_SET_UNIT_MOVEMENT_FLAGS = 204, // flags 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_ADD_IMMUNITY = 208, // 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_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 @@ -1889,7 +1893,7 @@ enum SmartFollowType struct SmartScriptHolder { 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) {} int32 entryOrGuid; @@ -1908,9 +1912,18 @@ public: uint32 GetTargetType() const { return (uint32)target.type; } uint32 timer; + uint32 priority; bool active; bool runOnce; 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::max(); }; typedef std::unordered_map WPPath; diff --git a/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp index a864ac507..568079503 100644 --- a/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp @@ -107,7 +107,7 @@ bool ChaseMovementGenerator::DoUpdate(T* owner, uint32 time_diff) if (i_recalculateTravel && PositionOkay(owner, target, _movingTowards ? maxTarget : Optional(), 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_path = nullptr; @@ -134,11 +134,11 @@ bool ChaseMovementGenerator::DoUpdate(T* owner, uint32 time_diff) } // 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(); _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? if (cOwner && !target->isInAccessiblePlaceFor(cOwner)) @@ -150,10 +150,10 @@ bool ChaseMovementGenerator::DoUpdate(T* owner, uint32 time_diff) } // figure out which way we want to move - float tarX, tarY, tarZ; - target->GetPosition(tarX, tarY, tarZ); + float x, y, z; + target->GetPosition(x, y, z); bool withinRange = owner->IsInDist(target, maxRange); - bool withinLOS = owner->IsWithinLOS(tarX, tarY, tarZ); + bool withinLOS = owner->IsWithinLOS(x, y, z); bool moveToward = !(withinRange && withinLOS); // make a new path if we have to... @@ -181,14 +181,12 @@ bool ChaseMovementGenerator::DoUpdate(T* owner, uint32 time_diff) additionalRange = owner->GetExactDistSq(target) < G3D::square(speed) ? 0 : speed; } - float x, y, z; bool shortenPath; // if we want to move toward the target and there's no fixed angle... if (moveToward && !angle) { // ...we'll pathfind to the center, then shorten the path - target->GetPosition(x, y, z); shortenPath = true; } else @@ -214,7 +212,7 @@ bool ChaseMovementGenerator::DoUpdate(T* owner, uint32 time_diff) } 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) {