mirror of
https://github.com/liyunfan1223/azerothcore-wotlk.git
synced 2026-08-07 15:28:02 +00:00
fix(Scripts/Quest): Implement Sorlof's Booty Quest Event (#26810)
This commit is contained in:
@@ -5242,6 +5242,18 @@ void SpellMgr::LoadSpellInfoCorrections()
|
||||
spellInfo->Effects[EFFECT_0].TargetA = SpellImplicitTargetInfo(TARGET_UNIT_TARGET_ANY);
|
||||
});
|
||||
|
||||
// Boulder Assault (Sorlof's Booty)
|
||||
ApplySpellFix({ 44966 }, [](SpellInfo* spellInfo)
|
||||
{
|
||||
spellInfo->AttributesEx |= SPELL_ATTR1_NO_THREAT;
|
||||
});
|
||||
|
||||
// Cannon Assault (Sorlof's Booty)
|
||||
ApplySpellFix({ 45008 }, [](SpellInfo* spellInfo)
|
||||
{
|
||||
spellInfo->AttributesEx3 |= SPELL_ATTR3_ALWAYS_HIT;
|
||||
});
|
||||
|
||||
for (uint32 i = 0; i < GetSpellInfoStoreSize(); ++i)
|
||||
{
|
||||
SpellInfo* spellInfo = mSpellInfoMap[i];
|
||||
|
||||
@@ -15,13 +15,19 @@
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "Containers.h"
|
||||
#include "CreatureScript.h"
|
||||
#include "GameObjectAI.h"
|
||||
#include "GameObjectScript.h"
|
||||
#include "Map.h"
|
||||
#include "MotionMaster.h"
|
||||
#include "PassiveAI.h"
|
||||
#include "Player.h"
|
||||
#include "ScriptedCreature.h"
|
||||
#include "ScriptedEscortAI.h"
|
||||
#include "SpellInfo.h"
|
||||
#include "SpellScript.h"
|
||||
#include "WaypointMgr.h"
|
||||
|
||||
class npc_attracted_reef_bull : public CreatureScript
|
||||
{
|
||||
@@ -417,6 +423,263 @@ class spell_the_cleansing_on_death_cast_on_master : public SpellScript
|
||||
}
|
||||
};
|
||||
|
||||
/*######
|
||||
## Quest 11529: Sorlof's Booty
|
||||
######*/
|
||||
|
||||
enum SorlofsBooty
|
||||
{
|
||||
NPC_SORLOF = 24914,
|
||||
NPC_THE_BIG_GUN = 24992,
|
||||
|
||||
SPELL_CANNON_ASSAULT = 45008,
|
||||
SPELL_SORLOFS_BOOTY = 45070,
|
||||
|
||||
// SPELL_BOULDER_ASSAULT_AURA = 44964, // Serverside, triggers 44965 every 3s
|
||||
SPELL_BOULDER_ASSAULT_HIT = 44966,
|
||||
SPELL_BOULDER_ASSAULT_FIRE = 44967,
|
||||
|
||||
// About the maximum range for broadside
|
||||
CANNON_RANGE = 200,
|
||||
|
||||
// The ship needs to ping Sorlof from wherever she is on her lap
|
||||
SORLOF_SEARCH_RANGE = 1000,
|
||||
|
||||
SORLOF_WANDER_DISTANCE = 10,
|
||||
|
||||
DATA_SORLOF_TAKE_PATH = 1,
|
||||
POINT_SORLOF_PATH = 1000,
|
||||
|
||||
// He only knits himself back together once he has broken off and headed home
|
||||
PATH_SORLOF_RETURN = 1032785,
|
||||
|
||||
// The gun's own SmartAI announces the booty on this data set.
|
||||
DATA_SORLOF_SLAIN = 1
|
||||
};
|
||||
|
||||
// 44965 - Boulder Assault
|
||||
class spell_sorlofs_booty_boulder_assault : public SpellScript
|
||||
{
|
||||
PrepareSpellScript(spell_sorlofs_booty_boulder_assault);
|
||||
|
||||
bool Validate(SpellInfo const* /*spellInfo*/) override
|
||||
{
|
||||
return ValidateSpellInfo({ SPELL_BOULDER_ASSAULT_HIT });
|
||||
}
|
||||
|
||||
void FilterTargets(std::list<WorldObject*>& targets)
|
||||
{
|
||||
if (targets.empty())
|
||||
return;
|
||||
|
||||
// One boulder per throw from implicit targets (no limit in DBC)
|
||||
WorldObject* target = Acore::Containers::SelectRandomContainerElement(targets);
|
||||
targets.clear();
|
||||
targets.push_back(target);
|
||||
}
|
||||
|
||||
void HandleScript(SpellEffIndex /*effIndex*/)
|
||||
{
|
||||
if (Unit* caster = GetCaster())
|
||||
caster->CastSpell(GetHitUnit(), SPELL_BOULDER_ASSAULT_HIT, true);
|
||||
}
|
||||
|
||||
void Register() override
|
||||
{
|
||||
OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_sorlofs_booty_boulder_assault::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENTRY);
|
||||
OnEffectHitTarget += SpellEffectFn(spell_sorlofs_booty_boulder_assault::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
|
||||
}
|
||||
};
|
||||
|
||||
// 44966 - Boulder Assault
|
||||
class spell_sorlofs_booty_boulder_assault_hit : public SpellScript
|
||||
{
|
||||
PrepareSpellScript(spell_sorlofs_booty_boulder_assault_hit);
|
||||
|
||||
bool Validate(SpellInfo const* /*spellInfo*/) override
|
||||
{
|
||||
return ValidateSpellInfo({ SPELL_BOULDER_ASSAULT_FIRE });
|
||||
}
|
||||
|
||||
void HandleScript(SpellEffIndex /*effIndex*/)
|
||||
{
|
||||
if (Unit* target = GetHitUnit())
|
||||
target->CastSpell(target, SPELL_BOULDER_ASSAULT_FIRE, true);
|
||||
}
|
||||
|
||||
void Register() override
|
||||
{
|
||||
OnEffectHitTarget += SpellEffectFn(spell_sorlofs_booty_boulder_assault_hit::HandleScript, EFFECT_1, SPELL_EFFECT_SCRIPT_EFFECT);
|
||||
}
|
||||
};
|
||||
|
||||
uint32 GetSorlofPathForShipEvent(uint32 eventId)
|
||||
{
|
||||
switch (eventId)
|
||||
{
|
||||
// Departure events of the Sister Mercy's stops (TaxiPathNode.dbc path 778)
|
||||
case 16501: return 1032780;
|
||||
case 16502: return 1032781;
|
||||
case 16503: return 1032782;
|
||||
case 16504: return 1032783;
|
||||
case 16510: return 1032784;
|
||||
case 16511: return 1032785; // Return to spawn
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
struct npc_sorlof : public ScriptedAI
|
||||
{
|
||||
npc_sorlof(Creature* creature) : ScriptedAI(creature) { }
|
||||
|
||||
void Reset() override
|
||||
{
|
||||
_pathId = 0;
|
||||
_pathNode = 0;
|
||||
_advancePath = false;
|
||||
me->SetRegeneratingHealth(false);
|
||||
}
|
||||
|
||||
void SetData(uint32 id, uint32 value) override
|
||||
{
|
||||
if (id != DATA_SORLOF_TAKE_PATH)
|
||||
return;
|
||||
|
||||
_pathId = value;
|
||||
_pathNode = 0;
|
||||
_advancePath = true;
|
||||
me->SetRegeneratingHealth(value == PATH_SORLOF_RETURN);
|
||||
}
|
||||
|
||||
void MovementInform(uint32 type, uint32 id) override
|
||||
{
|
||||
if (type != POINT_MOTION_TYPE || id != POINT_SORLOF_PATH + _pathNode)
|
||||
return;
|
||||
|
||||
++_pathNode;
|
||||
_advancePath = true;
|
||||
}
|
||||
|
||||
void JustDied(Unit* /*killer*/) override
|
||||
{
|
||||
DoCastSelf(SPELL_SORLOFS_BOOTY, true);
|
||||
|
||||
if (Creature* gun = me->FindNearestCreature(NPC_THE_BIG_GUN, CANNON_RANGE))
|
||||
gun->AI()->SetData(DATA_SORLOF_SLAIN, DATA_SORLOF_SLAIN);
|
||||
}
|
||||
|
||||
void UpdateAI(uint32 /*diff*/) override
|
||||
{
|
||||
// Deferred out of MovementInform: that fires from the generator's DoFinalize, and
|
||||
// MotionMaster::DirectExpire then Resets the new top generator, whose DoReset stops
|
||||
// the spline it just launched. UpdateAI runs after the MotionMaster, so it sticks.
|
||||
if (_advancePath)
|
||||
{
|
||||
_advancePath = false;
|
||||
MoveToNextNode();
|
||||
}
|
||||
|
||||
if (UpdateVictim())
|
||||
DoMeleeAttackIfReady();
|
||||
}
|
||||
|
||||
private:
|
||||
void MoveToNextNode()
|
||||
{
|
||||
WaypointPath const* path = sWaypointMgr->GetPath(_pathId);
|
||||
if (!path)
|
||||
return;
|
||||
|
||||
// Each leg ends with him milling about where the ship has drawn up
|
||||
if (_pathNode >= path->Nodes.size())
|
||||
{
|
||||
me->GetMotionMaster()->MoveRandom(SORLOF_WANDER_DISTANCE);
|
||||
return;
|
||||
}
|
||||
|
||||
WaypointNode const& node = path->Nodes[_pathNode];
|
||||
me->SetWalk(node.MoveType == WAYPOINT_MOVE_TYPE_WALK);
|
||||
me->GetMotionMaster()->MovePoint(POINT_SORLOF_PATH + _pathNode, node.X, node.Y, node.Z);
|
||||
}
|
||||
|
||||
uint32 _pathId{ 0 };
|
||||
uint32 _pathNode{ 0 };
|
||||
bool _advancePath{ false };
|
||||
};
|
||||
|
||||
struct go_sister_mercy : public GameObjectAI
|
||||
{
|
||||
go_sister_mercy(GameObject* go) : GameObjectAI(go) { }
|
||||
|
||||
// Notify Sorlof the ship is departing towards the next broadside point
|
||||
void EventInform(uint32 eventId) override
|
||||
{
|
||||
uint32 pathId = GetSorlofPathForShipEvent(eventId);
|
||||
if (!pathId)
|
||||
return;
|
||||
|
||||
if (Creature* sorlof = me->FindNearestCreature(NPC_SORLOF, SORLOF_SEARCH_RANGE))
|
||||
sorlof->AI()->SetData(DATA_SORLOF_TAKE_PATH, pathId);
|
||||
}
|
||||
};
|
||||
|
||||
// 45045 - Big Cannon Assault Primer
|
||||
class spell_sorlofs_booty_cannon_primer : public SpellScript
|
||||
{
|
||||
PrepareSpellScript(spell_sorlofs_booty_cannon_primer);
|
||||
|
||||
bool Validate(SpellInfo const* spellInfo) override
|
||||
{
|
||||
return ValidateSpellInfo({ uint32(spellInfo->GetEffect(EFFECT_0).CalcValue()) });
|
||||
}
|
||||
|
||||
void HandleScript(SpellEffIndex /*effIndex*/)
|
||||
{
|
||||
// Primes the gun: the clicker performs the actual, timed Big Gun Assault cast.
|
||||
if (Unit* caster = GetCaster())
|
||||
caster->CastSpell(caster, GetSpellInfo()->Effects[EFFECT_0].CalcValue(), false);
|
||||
}
|
||||
|
||||
void Register() override
|
||||
{
|
||||
OnEffectHitTarget += SpellEffectFn(spell_sorlofs_booty_cannon_primer::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
|
||||
}
|
||||
};
|
||||
|
||||
// 45013 - Big Gun Assault
|
||||
class spell_sorlofs_booty_big_gun_assault : public SpellScript
|
||||
{
|
||||
PrepareSpellScript(spell_sorlofs_booty_big_gun_assault);
|
||||
|
||||
bool Validate(SpellInfo const* /*spellInfo*/) override
|
||||
{
|
||||
return ValidateSpellInfo({ SPELL_CANNON_ASSAULT });
|
||||
}
|
||||
|
||||
void HandleDummy(SpellEffIndex /*effIndex*/)
|
||||
{
|
||||
// Implicit target of the dummy effect, resolved to The Big Gun by conditions
|
||||
Creature* gun = GetHitCreature();
|
||||
if (!gun)
|
||||
return;
|
||||
|
||||
Creature* sorlof = gun->FindNearestCreature(NPC_SORLOF, CANNON_RANGE);
|
||||
if (!sorlof)
|
||||
return;
|
||||
|
||||
if (!gun->IsWithinLOSInMap(sorlof, VMAP::ModelIgnoreFlags::M2, LINEOFSIGHT_CHECK_VMAP))
|
||||
return;
|
||||
|
||||
// Fired by the gun rather than by the player, this is correct
|
||||
gun->CastSpell(sorlof, SPELL_CANNON_ASSAULT, true);
|
||||
}
|
||||
|
||||
void Register() override
|
||||
{
|
||||
OnEffectHitTarget += SpellEffectFn(spell_sorlofs_booty_big_gun_assault::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY);
|
||||
}
|
||||
};
|
||||
|
||||
void AddSC_howling_fjord()
|
||||
{
|
||||
new npc_attracted_reef_bull();
|
||||
@@ -428,4 +691,10 @@ void AddSC_howling_fjord()
|
||||
RegisterSpellScript(spell_the_cleansing_cleansing_soul);
|
||||
RegisterSpellScript(spell_the_cleansing_mirror_image_script_effect);
|
||||
RegisterSpellScript(spell_the_cleansing_on_death_cast_on_master);
|
||||
RegisterSpellScript(spell_sorlofs_booty_cannon_primer);
|
||||
RegisterSpellScript(spell_sorlofs_booty_big_gun_assault);
|
||||
RegisterSpellScript(spell_sorlofs_booty_boulder_assault);
|
||||
RegisterSpellScript(spell_sorlofs_booty_boulder_assault_hit);
|
||||
RegisterCreatureAI(npc_sorlof);
|
||||
RegisterGameObjectAI(go_sister_mercy);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user