diff options
author | Giacomo Pozzoni <giacomopoz@gmail.com> | 2019-01-13 20:25:02 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-01-13 20:25:02 +0100 |
commit | 8f9654d8176646c3076482477fc4b91011fb83cc (patch) | |
tree | cd985399d4c8de1381198f84a5898d875ea6010f /src | |
parent | a8558c4361a7e3560c094886ef3072d46c64de44 (diff) |
3.3.5 UnitAI (#22911)
* Core/AI: Revamp how UnitAI changes are applied
Revamp how UnitAI changes are applied by storing current AI in a variable and all previous AIs plus current in a stack.
The callers can push/pop AIs on the stack that will take effect only in next Unit::Update() call.
The current AI will be a valid object for the whole duration of Unit::Update() and until next Unit::Update() call.
* Core/AI: Apply new AI change code
* Core/AI: Fix build
* Core/AI: Fix crash on Creature::AIM_Create()
* Core/AI: Fix crash
* Core/AI: Restore ASSERT
* Core/AI: Fix UnitAI not being popped properly when restoring a charmed AI
Diffstat (limited to 'src')
-rw-r--r-- | src/server/game/Entities/Creature/Creature.cpp | 3 | ||||
-rw-r--r-- | src/server/game/Entities/Unit/Unit.cpp | 63 | ||||
-rw-r--r-- | src/server/game/Entities/Unit/Unit.h | 15 | ||||
-rw-r--r-- | src/server/scripts/Northrend/FrozenHalls/PitOfSaron/boss_scourgelord_tyrannus.cpp | 4 | ||||
-rw-r--r-- | src/server/scripts/Spells/spell_dk.cpp | 4 |
5 files changed, 60 insertions, 29 deletions
diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 7da8902842c..371b588e5e8 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -949,7 +949,8 @@ void Creature::DoFleeToGetAssistance() bool Creature::AIM_Destroy() { - SetAI(nullptr); + PopAI(); + RefreshAI(); return true; } diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index d424210000f..366b330d507 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -63,6 +63,7 @@ #include "TemporarySummon.h" #include "Transport.h" #include "Totem.h" +#include "UnitAI.h" #include "UpdateFieldFlags.h" #include "Util.h" #include "Vehicle.h" @@ -451,8 +452,9 @@ void Unit::Update(uint32 p_time) UpdateSplineMovement(p_time); i_motionMaster->Update(p_time); - if (!i_AI && (GetTypeId() != TYPEID_PLAYER || IsCharmed())) + if (!GetAI() && (GetTypeId() != TYPEID_PLAYER || IsCharmed())) UpdateCharmAI(); + RefreshAI(); } bool Unit::haveOffhandWeapon() const @@ -9409,37 +9411,56 @@ void Unit::AIUpdateTick(uint32 diff) } } +void Unit::PushAI(UnitAI* newAI) +{ + i_AIs.emplace(newAI); +} + void Unit::SetAI(UnitAI* newAI) { - ASSERT(!m_aiLocked, "Attempt to replace AI during AI update tick"); - i_AI.reset(newAI); + PushAI(newAI); + RefreshAI(); +} + +bool Unit::PopAI() +{ + if (!i_AIs.empty()) + { + i_AIs.pop(); + return true; + } + else + return false; +} + +void Unit::RefreshAI() +{ + ASSERT(!m_aiLocked, "Tried to change current AI during UpdateAI()"); + if (i_AIs.empty()) + i_AI = nullptr; + else + i_AI = i_AIs.top(); } void Unit::ScheduleAIChange() { bool const charmed = IsCharmed(); - // if charm is applied, we can't have disabled AI already, and vice versa - if (charmed) - ASSERT(!i_disabledAI, "Attempt to schedule charm AI change on unit that already has disabled AI"); - else if (GetTypeId() != TYPEID_PLAYER) - ASSERT(i_disabledAI, "Attempt to schedule charm ID change on unit that doesn't have disabled AI"); if (charmed) - i_disabledAI = std::move(i_AI); - else if (m_aiLocked) + PushAI(nullptr); + else { - ASSERT(!i_lockedAILifetimeExtension, "Attempt to schedule multiple charm AI changes during one update"); - i_lockedAILifetimeExtension = std::move(i_AI); // AI needs to live just a bit longer to finish its UpdateAI + RestoreDisabledAI(); + PushAI(nullptr); //This could actually be PopAI() to get the previous AI but it's required atm to trigger UpdateCharmAI() } - else - i_AI.reset(); } void Unit::RestoreDisabledAI() { - ASSERT((GetTypeId() == TYPEID_PLAYER) || i_disabledAI, "Attempt to restore disabled AI on creature without disabled AI"); - i_AI = std::move(i_disabledAI); - i_lockedAILifetimeExtension.reset(); + // Keep popping the stack until we either reach the bottom or find a valid AI + while (PopAI()) + if (GetTopAI()) + return; } void Unit::AddToWorld() @@ -9559,14 +9580,16 @@ void Unit::UpdateCharmAI() } ASSERT(newAI); - i_AI.reset(newAI); + SetAI(newAI); newAI->OnCharmed(true); } else { RestoreDisabledAI(); - if (i_AI) - i_AI->OnCharmed(true); + // Hack: this is required because we want to call OnCharmed(true) on the restored AI + RefreshAI(); + if (UnitAI* ai = GetAI()) + ai->OnCharmed(true); } } diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index f8832dff52a..0a787f04de5 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -29,6 +29,7 @@ #include "Util.h" #include <map> #include <memory> +#include <stack> #define VISUAL_WAYPOINT 1 // Creature Entry ID used for waypoints show, visible only for GMs #define WORLD_TRIGGER 12999 @@ -767,8 +768,14 @@ class TC_GAME_API Unit : public WorldObject bool IsAIEnabled() const { return (i_AI != nullptr); } void AIUpdateTick(uint32 diff); UnitAI* GetAI() const { return i_AI.get(); } - void SetAI(UnitAI* newAI); void ScheduleAIChange(); + void PushAI(UnitAI* newAI); + bool PopAI(); + protected: + void SetAI(UnitAI* newAI); + UnitAI* GetTopAI() const { return i_AIs.empty() ? nullptr : i_AIs.top().get(); } + void RefreshAI(); + public: void AddToWorld() override; void RemoveFromWorld() override; @@ -1791,9 +1798,9 @@ class TC_GAME_API Unit : public WorldObject void UpdateCharmAI(); void RestoreDisabledAI(); - std::unique_ptr<UnitAI> i_AI; - std::unique_ptr<UnitAI> i_disabledAI; - std::unique_ptr<UnitAI> i_lockedAILifetimeExtension; // yes, this lifetime extension is terrible + typedef std::stack<std::shared_ptr<UnitAI>> UnitAIStack; + UnitAIStack i_AIs; + std::shared_ptr<UnitAI> i_AI; bool m_aiLocked; std::unordered_set<AbstractFollower*> m_followingMe; diff --git a/src/server/scripts/Northrend/FrozenHalls/PitOfSaron/boss_scourgelord_tyrannus.cpp b/src/server/scripts/Northrend/FrozenHalls/PitOfSaron/boss_scourgelord_tyrannus.cpp index a813c84994c..f76da2e1c95 100644 --- a/src/server/scripts/Northrend/FrozenHalls/PitOfSaron/boss_scourgelord_tyrannus.cpp +++ b/src/server/scripts/Northrend/FrozenHalls/PitOfSaron/boss_scourgelord_tyrannus.cpp @@ -447,7 +447,7 @@ class spell_tyrannus_overlord_brand : public SpellScriptLoader return; Player* pTarget = GetTarget()->ToPlayer(); - GetTarget()->SetAI(new player_overlord_brandAI(pTarget, GetCasterGUID())); + GetTarget()->PushAI(new player_overlord_brandAI(pTarget, GetCasterGUID())); } void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) @@ -455,7 +455,7 @@ class spell_tyrannus_overlord_brand : public SpellScriptLoader if (GetTarget()->GetTypeId() != TYPEID_PLAYER) return; - GetTarget()->SetAI(nullptr); + GetTarget()->PopAI(); } void Register() override diff --git a/src/server/scripts/Spells/spell_dk.cpp b/src/server/scripts/Spells/spell_dk.cpp index 43020beaa9b..5739c27ee33 100644 --- a/src/server/scripts/Spells/spell_dk.cpp +++ b/src/server/scripts/Spells/spell_dk.cpp @@ -2914,14 +2914,14 @@ public: if (ghoulGuid.IsEmpty()) return; - player->SetAI(new player_ghoulAI(player, ghoulGuid)); + player->PushAI(new player_ghoulAI(player, ghoulGuid)); } void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) { Player* player = GetTarget()->ToPlayer(); - player->SetAI(nullptr); + player->PopAI(); // Dismiss ghoul if necessary if (Creature* ghoul = ObjectAccessor::GetCreature(*player, ghoulGuid)) |