diff --git a/sql/sylvania/dire_beast_ai_20260705.sql b/sql/sylvania/dire_beast_ai_20260705.sql new file mode 100644 index 0000000..5a68aed --- /dev/null +++ b/sql/sylvania/dire_beast_ai_20260705.sql @@ -0,0 +1,15 @@ +-- Dire Beast (spell 120679 "Bête féroce") — the summoned "Beast" guardians had no AI/ScriptName +-- and never engaged the hunter's target (they idled next to the owner). +-- Bind them to the new C++ AI npc_pet_hun_dire_beast (added to src/server/scripts/Pet/pet_hunter.cpp), +-- which orders the summon to attack the summoner's target on spawn. +-- Targets the 108 inert level-1 friendly "Beast" summon templates (one per zone/expansion). +-- 2026-07-05 +UPDATE `creature_template` +SET `ScriptName` = 'npc_pet_hun_dire_beast' +WHERE `name` = 'Beast' + AND `type` = 1 + AND `faction` = 35 + AND `minlevel` = 1 + AND `maxlevel` = 1 + AND (`AIName` IS NULL OR `AIName` = '') + AND (`ScriptName` IS NULL OR `ScriptName` = ''); diff --git a/src/server/scripts/Pet/pet_hunter.cpp b/src/server/scripts/Pet/pet_hunter.cpp index a96dc94..d0acdc7 100644 --- a/src/server/scripts/Pet/pet_hunter.cpp +++ b/src/server/scripts/Pet/pet_hunter.cpp @@ -181,8 +181,65 @@ public: } }; +// Dire Beast (120679) summoned "Beast" - order the summoned beast to attack the hunter's target. +// Same proven pattern as npc_pet_hunter_snake_trap, without the snake-specific health/poison +// (dire beasts are level 1 -> the snake health formula would underflow). +class npc_pet_hun_dire_beast : public CreatureScript +{ + public: + npc_pet_hun_dire_beast() : CreatureScript("npc_pet_hun_dire_beast") { } + + struct npc_pet_hun_dire_beastAI : public ScriptedAI + { + npc_pet_hun_dire_beastAI(Creature* creature) : ScriptedAI(creature) { } + + void EnterCombat(Unit* /*who*/) override { } + + void Reset() override + { + // On spawn, engage the summoner's current target (or its attacker). + if (!me->GetVictim() && me->IsSummon()) + if (Unit* owner = me->ToTempSummon()->GetSummoner()) + { + if (Unit* target = owner->GetVictim()) + AttackStart(target); + else if (Unit* attacker = owner->getAttackerForHelper()) + AttackStart(attacker); + } + } + + // Pick up a closer target if the owner switches while the beast is out. + void MoveInLineOfSight(Unit* who) override + { + if (!me->GetVictim() && me->CanCreatureAttack(who)) + { + if (me->GetDistanceZ(who) > CREATURE_Z_ATTACK_RANGE) + return; + + float attackRadius = me->GetAttackDistance(who); + if (me->IsWithinDistInMap(who, attackRadius) && me->IsWithinLOSInMap(who)) + AttackStart(who); + } + } + + void UpdateAI(uint32 /*diff*/) override + { + if (!UpdateVictim() || !me->GetVictim()) + return; + + DoMeleeAttackIfReady(); + } + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return new npc_pet_hun_dire_beastAI(creature); + } +}; + void AddSC_hunter_pet_scripts() { new npc_pet_hunter_snake_trap(); new npc_pet_hunter_hati(); + new npc_pet_hun_dire_beast(); }