#include "mod_auto_face_target.h" #include "Config.h" #include "ObjectAccessor.h" #include "ScriptMgr.h" #include "SpellDefines.h" #include "SpellInfo.h" #include "SpellMgr.h" namespace { constexpr uint32 TARGET_FLAG_LOCATION_MASK = TARGET_FLAG_SOURCE_LOCATION | TARGET_FLAG_DEST_LOCATION; } void AutoFaceTarget::LoadConfig() { _enabled = sConfigMgr->GetOption("AutoFaceTarget.Enable", true); } void AutoFaceTarget::CorrectFacing(Player* mover, uint32 spellId) const { if (!_enabled) return; SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId); if (!spellInfo) return; // Only spells that actually require the caster to face the target can fail // the in-front check; nothing to correct otherwise. if (!(spellInfo->FacingCasterFlags & SPELL_FACING_FLAG_INFRONT)) return; // Ground/location-targeted spells (Blizzard, Flare, ...) don't carry a unit // target at all, so the in-front check never applies to them - leave the // player facing wherever they clicked. if (spellInfo->Targets & TARGET_FLAG_LOCATION_MASK) return; Unit* target = ObjectAccessor::GetUnit(*mover, mover->GetTarget()); if (!target || target == mover) return; if (mover->HasInArc(static_cast(M_PI), target)) return; float angle = mover->GetAngle(target); // SetFacingTo() only takes effect once its movement spline is evaluated on // a later world tick, but the in-front check that follows this hook runs // synchronously right away - so without also setting the orientation // directly, the very first cast attempt still fails and the player has to // press again once the spline has caught up. The spline call must come // first, since it captures the *current* (pre-correction) orientation as // its turn's starting point for the client-visible animation. mover->SetFacingTo(angle); mover->SetOrientation(angle); } class AutoFaceTarget_Misc : public MiscScript { public: AutoFaceTarget_Misc() : MiscScript("AutoFaceTarget_Misc", { MISCHOOK_VALIDATE_SPELL_AT_CAST_SPELL_RESULT }) { } void ValidateSpellAtCastSpellResult(Player* /*player*/, Unit* mover, Spell* /*spell*/, uint32 /*oldSpellId*/, uint32 spellId) override { // Mirror the core in-front check, which only ever applies to player casters. if (!mover->IsPlayer()) return; sAutoFaceTarget->CorrectFacing(mover->ToPlayer(), spellId); } }; class AutoFaceTarget_World : public WorldScript { public: AutoFaceTarget_World() : WorldScript("AutoFaceTarget_World") { } void OnBeforeConfigLoad(bool /*reload*/) override { sAutoFaceTarget->LoadConfig(); } }; void AddSC_mod_auto_face_target() { new AutoFaceTarget_Misc(); new AutoFaceTarget_World(); }