From d43a98b60b4c22cb025a43df06bc2f574d8be2b4 Mon Sep 17 00:00:00 2001 From: WinterSolstice8 <60417494+wintersolstice8@users.noreply.github.com> Date: Wed, 26 Feb 2025 22:39:23 -0700 Subject: [PATCH 1/2] [core] Abort spellcasting if target is no longer eligible --- src/map/ai/states/magic_state.cpp | 35 +++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/src/map/ai/states/magic_state.cpp b/src/map/ai/states/magic_state.cpp index f05213f1f5d..dc536c15488 100644 --- a/src/map/ai/states/magic_state.cpp +++ b/src/map/ai/states/magic_state.cpp @@ -113,15 +113,38 @@ CMagicState::CMagicState(CBattleEntity* PEntity, uint16 targid, SpellID spellid, bool CMagicState::Update(time_point tick) { - if (tick > GetEntryTime() + m_castTime && !IsCompleted()) - { - auto* PTarget = m_PEntity->IsValidTarget(m_targid, m_PSpell->getValidTarget(), m_errorMsg); - MSGBASIC_ID msg = MSGBASIC_IS_INTERRUPTED; - - action_t action; + action_t action; + auto* PTarget = m_PEntity->IsValidTarget(m_targid, m_PSpell->getValidTarget(), m_errorMsg); + MSGBASIC_ID msg = MSGBASIC_IS_INTERRUPTED; + auto isTargetValid = [&]() + { + // m_PEntity->IsValidTarget checks if the target is dead and returns nullptr if so, so we don't need to duplicate it here. if (!PTarget || m_errorMsg || !CanCastSpell(PTarget, true) || (HasMoved() && (m_PEntity->objtype != TYPE_PET || static_cast(m_PEntity)->getPetType() != PET_TYPE::AUTOMATON))) + { + return false; + } + return true; + }; + + // Check if target is still valid during mid-cast (mostly to check if the target has died and to cancel.) + if (!IsCompleted()) + { + if (!isTargetValid()) + { + // guessed, but cancels correctly. + m_PEntity->OnCastInterrupted(*this, action, msg, false); + m_PEntity->loc.zone->PushPacket(m_PEntity, CHAR_INRANGE_SELF, std::make_unique(action)); + + Complete(); + return false; + } + } + + if (tick > GetEntryTime() + m_castTime && !IsCompleted()) + { + if (!isTargetValid()) { m_PEntity->OnCastInterrupted(*this, action, msg, false); m_PEntity->loc.zone->PushPacket(m_PEntity, CHAR_INRANGE_SELF, std::make_unique(action)); From dbf5d248585117287231d8755d9cbd989883fddc Mon Sep 17 00:00:00 2001 From: WinterSolstice8 <60417494+wintersolstice8@users.noreply.github.com> Date: Wed, 26 Feb 2025 23:08:49 -0700 Subject: [PATCH 2/2] [core] Abort mob casting spell if target uses Hide and isnt detectable --- src/map/ai/controllers/mob_controller.h | 2 +- src/map/ai/states/magic_state.cpp | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/map/ai/controllers/mob_controller.h b/src/map/ai/controllers/mob_controller.h index 93122eb24a9..fb25e6ddbf5 100644 --- a/src/map/ai/controllers/mob_controller.h +++ b/src/map/ai/controllers/mob_controller.h @@ -60,6 +60,7 @@ class CMobController : public CController void SetFollowTarget(CBaseEntity* PTarget, FollowType followType); bool HasFollowTarget(); void ClearFollowTarget(); + bool CheckHide(CBattleEntity* PTarget); void OnCastStopped(CMagicState& state, action_t& action); @@ -69,7 +70,6 @@ class CMobController : public CController virtual void TryLink(); bool CanDetectTarget(CBattleEntity* PTarget, bool forceSight = false); bool CanPursueTarget(CBattleEntity* PTarget); - bool CheckHide(CBattleEntity* PTarget); bool CheckLock(CBattleEntity* PTarget); bool CheckDetection(CBattleEntity* PTarget); virtual bool CanCastSpells(); diff --git a/src/map/ai/states/magic_state.cpp b/src/map/ai/states/magic_state.cpp index dc536c15488..b75073de3e8 100644 --- a/src/map/ai/states/magic_state.cpp +++ b/src/map/ai/states/magic_state.cpp @@ -22,6 +22,7 @@ #include "magic_state.h" #include "ai/ai_container.h" +#include "ai/controllers/pet_controller.h" #include "ai/states/inactive_state.h" #include "common/utils.h" #include "enmity_container.h" @@ -125,6 +126,18 @@ bool CMagicState::Update(time_point tick) { return false; } + + // Check hide if we're a mob and the target isn't ourselves + if (PTarget && PTarget->id != m_PEntity->id && m_PEntity->objtype == TYPE_MOB) + { + if (auto petController = dynamic_cast(m_PEntity->PAI->GetController())) + { + if (petController->CheckHide(PTarget)) // Returns true if cant detect target + { + return false; + } + } + } return true; };