#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();
}