From fee9709b3e96f0ddf04276ba853983363c9ba9e9 Mon Sep 17 00:00:00 2001 From: Andrew <47818697+Nyeriah@users.noreply.github.com> Date: Sun, 5 Jul 2026 20:43:21 -0300 Subject: [PATCH] feat(Core/AI): allow instance creatures to teleport to unreachable targets (#26459) Co-authored-by: Claude Fable 5 --- .../apps/worldserver/worldserver.conf.dist | 10 +++++++++ src/server/game/AI/CoreAI/UnitAI.cpp | 21 +++++++++++++++++++ src/server/game/World/WorldConfig.cpp | 1 + src/server/game/World/WorldConfig.h | 1 + 4 files changed, 33 insertions(+) diff --git a/src/server/apps/worldserver/worldserver.conf.dist b/src/server/apps/worldserver/worldserver.conf.dist index 7b16a07cbe..dd6fe04a3c 100644 --- a/src/server/apps/worldserver/worldserver.conf.dist +++ b/src/server/apps/worldserver/worldserver.conf.dist @@ -3184,6 +3184,16 @@ WaypointMovementStopTimeForPlayer = 120 NpcRegenHPIfTargetIsUnreachable = 1 +# Creature.Instance.TeleportToUnreachableTarget +# Description: In dungeons and raids, non-boss creatures that cannot path to their +# target teleport to it instead of evading or regenerating in place +# (retail-like behaviour). Bosses and player-controlled creatures are +# not affected. +# Default: 0 - (Disabled) +# 1 - (Enabled) + +Creature.Instance.TeleportToUnreachableTarget = 0 + # Creatures.CustomIDs # Description: The list of custom creatures with gossip dialogues hardcoded in core, # divided by "," without spaces. diff --git a/src/server/game/AI/CoreAI/UnitAI.cpp b/src/server/game/AI/CoreAI/UnitAI.cpp index dad8adebe4..d08958a94e 100644 --- a/src/server/game/AI/CoreAI/UnitAI.cpp +++ b/src/server/game/AI/CoreAI/UnitAI.cpp @@ -24,6 +24,7 @@ #include "SpellAuraEffects.h" #include "SpellInfo.h" #include "SpellMgr.h" +#include "World.h" void UnitAI::AttackStart(Unit* victim) { @@ -414,6 +415,26 @@ void UnitAI::EvadeTimerExpired() } } + // Retail-like: instance trash teleports to its unreachable target instead of evading + if (sWorld->getBoolConfig(CONFIG_CREATURE_INSTANCE_TELEPORT_TO_UNREACHABLE_TARGET) + && creature->GetMap()->IsDungeon() + && !creature->IsDungeonBoss() && !creature->isWorldBoss() + && !creature->IsControlledByPlayer()) + { + if (ObjectGuid targetGuid = creature->GetCannotReachTarget()) + { + if (Unit* target = ObjectAccessor::GetUnit(*creature, targetGuid)) + { + if (target->IsAlive() && creature->IsEngagedBy(target)) + { + creature->NearTeleportTo(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), target->GetOrientation()); + creature->SetCannotReachTarget(); + return; + } + } + } + } + if (creature->GetMap()->IsRaid()) { creature->GetCombatManager().ContinueEvadeRegen(); diff --git a/src/server/game/World/WorldConfig.cpp b/src/server/game/World/WorldConfig.cpp index 1cc9cd9975..5233d6e485 100644 --- a/src/server/game/World/WorldConfig.cpp +++ b/src/server/game/World/WorldConfig.cpp @@ -477,6 +477,7 @@ void WorldConfig::BuildConfigCache() SetConfigValue(CONFIG_OFFHAND_CHECK_AT_SPELL_UNLEARN, "OffhandCheckAtSpellUnlearn", true); SetConfigValue(CONFIG_CREATURE_REPOSITION_AGAINST_NPCS, "Creature.RepositionAgainstNpcs", true); + SetConfigValue(CONFIG_CREATURE_INSTANCE_TELEPORT_TO_UNREACHABLE_TARGET, "Creature.Instance.TeleportToUnreachableTarget", false); SetConfigValue(CONFIG_CREATURE_STOP_FOR_PLAYER, "Creature.MovingStopTimeForPlayer", 180000); SetConfigValue(CONFIG_WATER_BREATH_TIMER, "WaterBreath.Timer", 180000, ConfigValueCache::Reloadable::Yes, [](uint32 const& value) { return value > 0; }, "> 0"); diff --git a/src/server/game/World/WorldConfig.h b/src/server/game/World/WorldConfig.h index dd0707fcef..7e3ef13193 100644 --- a/src/server/game/World/WorldConfig.h +++ b/src/server/game/World/WorldConfig.h @@ -75,6 +75,7 @@ enum ServerConfigs CONFIG_ARENA_QUEUE_ANNOUNCER_PLAYERONLY, CONFIG_OFFHAND_CHECK_AT_SPELL_UNLEARN, CONFIG_CREATURE_REPOSITION_AGAINST_NPCS, + CONFIG_CREATURE_INSTANCE_TELEPORT_TO_UNREACHABLE_TARGET, CONFIG_VMAP_INDOOR_CHECK, CONFIG_VMAP_ENABLE_LOS, CONFIG_VMAP_ENABLE_HEIGHT,