1627 lines
52 KiB
C++
1627 lines
52 KiB
C++
/*
|
|
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the
|
|
* Free Software Foundation; either version 2 of the License, or (at your
|
|
* option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
* more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "Player.h"
|
|
#include "ScriptMgr.h"
|
|
#include "SpellScript.h"
|
|
#include "CombatAI.h"
|
|
#include "CreatureAIImpl.h"
|
|
#include "MotionMaster.h"
|
|
#include "ObjectAccessor.h"
|
|
#include "ScriptedCreature.h"
|
|
#include "ScriptedGossip.h"
|
|
#include "SpellAuraEffects.h"
|
|
#include "SpellInfo.h"
|
|
#include "SharedDefines.h"
|
|
#include "TemporarySummon.h"
|
|
#include "ObjectMgr.h"
|
|
#include "Group.h"
|
|
|
|
enum WestfallQuest
|
|
{
|
|
QUEST_MURDER_WAS_THE_CASE_THAT_THEY_GAVE_ME = 26209,
|
|
QUEST_LOUS_PARTING_THOUGHTS = 26232
|
|
};
|
|
|
|
enum WestfallCreature
|
|
{
|
|
NPC_HOMELESS_STORMWIND_CITIZEN_1 = 42386,
|
|
NPC_HOMELESS_STORMWIND_CITIZEN_2 = 42384,
|
|
NPC_WEST_PLAINS_DRIFTER = 42391,
|
|
NPC_TRANSIENT = 42383,
|
|
NPC_THUG = 42387,
|
|
MPC_LOUS_PARTING_THOUGHTS_TRIGGER = 42562,
|
|
NPC_SMALL_TIME_HUSTLER = 42390,
|
|
NPC_MERCENARY = 42656,
|
|
NPC_AGENT_KEARNEN = 7024
|
|
};
|
|
|
|
enum WestfallSpell
|
|
{
|
|
SPELL_HOBO_INFORMATION_1 = 79181,
|
|
SPELL_HOBO_INFORMATION_2 = 79182,
|
|
SPELL_HOBO_INFORMATION_3 = 79183,
|
|
SPELL_HOBO_INFORMATION_4 = 79184,
|
|
SPELL_SUMMON_RAGAMUFFIN_LOOTER = 79169,
|
|
SPELL_SUMMON_RAGAMUFFIN_LOOTER_1 = 79170,
|
|
SPELL_SUMMON_RAGAMUFFIN_LOOTER_2 = 79171,
|
|
SPELL_SUMMON_RAGAMUFFIN_LOOTER_3 = 79172,
|
|
SPELL_SUMMON_RAGAMUFFIN_LOOTER_4 = 79173,
|
|
SPELL_AGGRO_HOBO = 79168,
|
|
SPELL_HOBO_INFORMATION = 79184,
|
|
SPELL_KILL_SHOT_TRIGGERED = 79525
|
|
};
|
|
|
|
class spell_westfall_unbound_energy : public SpellScript
|
|
{
|
|
void FilterTargets(std::list<WorldObject*>& targets)
|
|
{
|
|
if (targets.empty())
|
|
return;
|
|
|
|
Unit* caster = GetCaster();
|
|
targets.remove_if([caster](WorldObject const* target)->bool
|
|
{
|
|
return caster == target;
|
|
});
|
|
|
|
if (targets.size() > 1)
|
|
Trinity::Containers::RandomResize(targets, 1);
|
|
}
|
|
|
|
void Register() override
|
|
{
|
|
OnObjectAreaTargetSelect.Register(&spell_westfall_unbound_energy::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENTRY);
|
|
}
|
|
};
|
|
|
|
enum ItsAlive
|
|
{
|
|
// Events
|
|
EVENT_CHECK_AREA = 1,
|
|
EVENT_DESPAWN_HARVESTER = 2,
|
|
|
|
// Texts
|
|
SAY_ANNOUNCE_OUT_OF_AREA = 0,
|
|
|
|
// Area Ids
|
|
AREA_ID_THE_MOEST_FARM = 918,
|
|
|
|
// Creatures
|
|
NPC_ENERGIZED_HARVEST_REAPER = 42342,
|
|
NPC_OVERLOADED_HARVEST_GOLEM = 42601
|
|
};
|
|
|
|
struct npc_westfall_overloaded_harvest_golem : public VehicleAI
|
|
{
|
|
npc_westfall_overloaded_harvest_golem(Creature* creature) : VehicleAI(creature) { }
|
|
|
|
void JustAppeared() override
|
|
{
|
|
_events.ScheduleEvent(EVENT_CHECK_AREA, 1s);
|
|
}
|
|
|
|
void UpdateAI(uint32 diff) override
|
|
{
|
|
_events.Update(diff);
|
|
while (uint32 eventId = _events.ExecuteEvent())
|
|
{
|
|
switch (eventId)
|
|
{
|
|
case EVENT_CHECK_AREA:
|
|
if (me->GetAreaId() != AREA_ID_THE_MOEST_FARM)
|
|
{
|
|
if (Unit* owner = me->GetCharmerOrOwner())
|
|
Talk(SAY_ANNOUNCE_OUT_OF_AREA, owner);
|
|
_events.ScheduleEvent(EVENT_DESPAWN_HARVESTER, 8s);
|
|
}
|
|
else
|
|
_events.Repeat(1s);
|
|
break;
|
|
case EVENT_DESPAWN_HARVESTER:
|
|
if (me->GetAreaId() != AREA_ID_THE_MOEST_FARM)
|
|
me->DespawnOrUnsummon();
|
|
else
|
|
_events.ScheduleEvent(EVENT_CHECK_AREA, 1s);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
private:
|
|
EventMap _events;
|
|
};
|
|
|
|
class spell_westfall_reaping_blows : public AuraScript
|
|
{
|
|
void HandlePeriodic(AuraEffect const* /*aurEff*/)
|
|
{
|
|
PreventDefaultAction();
|
|
if (Creature* reaper = GetTarget()->FindNearestCreature(NPC_ENERGIZED_HARVEST_REAPER, 5.f, true))
|
|
GetTarget()->CastSpell(reaper, GetSpellInfo()->Effects[EFFECT_1].TriggerSpell, true);
|
|
}
|
|
|
|
void Register() override
|
|
{
|
|
OnEffectPeriodic.Register(&spell_westfall_reaping_blows::HandlePeriodic, EFFECT_1, SPELL_AURA_PERIODIC_TRIGGER_SPELL);
|
|
}
|
|
};
|
|
|
|
class spell_westfall_wake_harvest_golem : public SpellScript
|
|
{
|
|
SpellCastResult CheckTarget()
|
|
{
|
|
Unit* target = GetExplTargetUnit();
|
|
if (!target || !target->IsCreature())
|
|
return SPELL_FAILED_BAD_TARGETS;
|
|
|
|
return SPELL_CAST_OK;
|
|
}
|
|
|
|
void HandleScriptEffect(SpellEffIndex /*effIndex*/)
|
|
{
|
|
Unit* caster = GetCaster();
|
|
if (!caster || !caster->IsPlayer())
|
|
return;
|
|
|
|
if (Creature* target = GetHitCreature())
|
|
{
|
|
caster->ToPlayer()->KilledMonsterCredit(NPC_OVERLOADED_HARVEST_GOLEM);
|
|
target->DespawnOrUnsummon(100ms);
|
|
}
|
|
}
|
|
|
|
void Register() override
|
|
{
|
|
OnCheckCast.Register(&spell_westfall_wake_harvest_golem::CheckTarget);
|
|
OnEffectHitTarget.Register(&spell_westfall_wake_harvest_golem::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
|
|
}
|
|
};
|
|
|
|
class spell_westfall_quest_credit_jangolode_event : public SpellScript
|
|
{
|
|
void HandleScriptEffect(SpellEffIndex /*effIndex*/)
|
|
{
|
|
GetHitUnit()->ExitVehicle();
|
|
}
|
|
|
|
void Register() override
|
|
{
|
|
OnEffectHitTarget.Register(&spell_westfall_quest_credit_jangolode_event::HandleScriptEffect, EFFECT_1, SPELL_EFFECT_SCRIPT_EFFECT);
|
|
}
|
|
};
|
|
|
|
enum HoboGossip
|
|
{
|
|
GOSSIP_MENU_ID_HOBO = 11635
|
|
};
|
|
|
|
enum HoboGossipOption
|
|
{
|
|
OPTION_QUESTION = 0,
|
|
OPTION_PAY = 1,
|
|
};
|
|
|
|
enum HoboQuestObjective
|
|
{
|
|
CLUE1 = 0,
|
|
CLUE2 = 1,
|
|
CLUE3 = 2,
|
|
CLUE4 = 3
|
|
};
|
|
|
|
enum HoboText
|
|
{
|
|
SAY_CLUE1 = 0,
|
|
SAY_CLUE2 = 1,
|
|
SAY_CLUE3 = 2,
|
|
SAY_CLUE4 = 3,
|
|
SAY_AGGRO_BRIBE = 4,
|
|
SAY_AGGRO_CONVINCE = 5,
|
|
SAY_EVENT = 6,
|
|
SAY_JACKPOT_INTRO = 7,
|
|
SAY_JACKPOT_END = 8,
|
|
SAY_PROPERTY_RAGE = 9,
|
|
SAY_FLEE = 10
|
|
};
|
|
|
|
enum HoboEvent
|
|
{
|
|
EVENT_TALK = 1,
|
|
EVENT_JACKPOT_INTRO = 2,
|
|
EVENT_CRY = 3,
|
|
EVENT_PROPERTY_RAGE = 4,
|
|
EVENT_JACKPOT_MIDDLE = 5,
|
|
EVENT_JACKPOT_END = 6,
|
|
EVENT_RESUME_MOVE = 7,
|
|
EVENT_GROUP_OOC = 1
|
|
};
|
|
|
|
enum HoboAction
|
|
{
|
|
ACTION_AGGRO_HOBO = 1,
|
|
ACTION_AGGRO_HOBO_DONE = 2
|
|
};
|
|
|
|
struct npc_westfall_hobo_witness : public ScriptedAI
|
|
{
|
|
npc_westfall_hobo_witness(Creature* creature) : ScriptedAI(creature), _bribeFailed(false), _hoboRage(false), _flee(false) { }
|
|
|
|
bool GossipSelect(Player* player, uint32 /*menuId*/, uint32 gossipListId) override
|
|
{
|
|
uint8 clueGainChance = 0;
|
|
switch (gossipListId)
|
|
{
|
|
case OPTION_QUESTION:
|
|
{
|
|
clueGainChance = 25;
|
|
break;
|
|
}
|
|
case OPTION_PAY:
|
|
{
|
|
clueGainChance = 75;
|
|
_bribeFailed = true;
|
|
break;
|
|
}
|
|
default:
|
|
return true;
|
|
}
|
|
|
|
me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP);
|
|
CloseGossipMenuFor(player);
|
|
if (roll_chance_i(clueGainChance))
|
|
GiveClue(player);
|
|
else
|
|
{
|
|
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC);
|
|
AttackStart(player);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void GiveClue(Player* player)
|
|
{
|
|
if (!player)
|
|
return;
|
|
|
|
_events.CancelEventGroup(EVENT_GROUP_OOC);
|
|
me->SetFacingToObject(player);
|
|
|
|
uint16 slot = player->FindQuestSlot(QUEST_MURDER_WAS_THE_CASE_THAT_THEY_GAVE_ME);
|
|
|
|
if (player->GetQuestSlotCounter(slot, CLUE1) == 0)
|
|
{
|
|
player->CastSpell(player, SPELL_HOBO_INFORMATION_1);
|
|
Talk(SAY_CLUE1, player);
|
|
me->DespawnOrUnsummon(12s);
|
|
return;
|
|
}
|
|
|
|
if (player->GetQuestSlotCounter(slot, CLUE2) == 0)
|
|
{
|
|
player->CastSpell(player, SPELL_HOBO_INFORMATION_2);
|
|
Talk(SAY_CLUE2, player);
|
|
me->DespawnOrUnsummon(12s);
|
|
return;
|
|
}
|
|
|
|
if (player->GetQuestSlotCounter(slot, CLUE3) == 0)
|
|
{
|
|
player->CastSpell(player, SPELL_HOBO_INFORMATION_3);
|
|
Talk(SAY_CLUE3, player);
|
|
me->DespawnOrUnsummon(12s);
|
|
return;
|
|
}
|
|
|
|
if (player->GetQuestSlotCounter(slot, CLUE4) == 0)
|
|
{
|
|
player->CastSpell(player, SPELL_HOBO_INFORMATION_4);
|
|
Talk(SAY_CLUE4, player);
|
|
me->DespawnOrUnsummon(12s);
|
|
return;
|
|
}
|
|
}
|
|
|
|
void JustEngagedWith(Unit* who) override
|
|
{
|
|
me->SetAIAnimKitId(0);
|
|
_events.CancelEventGroup(EVENT_GROUP_OOC);
|
|
|
|
if (!who->IsPlayer())
|
|
return;
|
|
|
|
Talk(_bribeFailed ? SAY_AGGRO_BRIBE : SAY_AGGRO_CONVINCE, who);
|
|
}
|
|
|
|
void DamageTaken(Unit* /*attacker*/, uint32& damage) override
|
|
{
|
|
if (!_flee && me->HealthBelowPctDamaged(20, damage))
|
|
{
|
|
_flee = true;
|
|
me->DoFleeToGetAssistance();
|
|
Talk(SAY_FLEE);
|
|
}
|
|
}
|
|
|
|
void JustDied(Unit* who) override
|
|
{
|
|
if (who->IsCreature() && who->ToCreature()->IsAIEnabled())
|
|
who->ToCreature()->AI()->DoAction(ACTION_AGGRO_HOBO_DONE);
|
|
me->CastSpell(me, SPELL_SUMMON_RAGAMUFFIN_LOOTER);
|
|
}
|
|
|
|
void Reset() override
|
|
{
|
|
ScriptedAI::Reset();
|
|
_events.Reset();
|
|
_bribeFailed = false;
|
|
_hoboRage = false;
|
|
_flee = false;
|
|
me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP);
|
|
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC);
|
|
_events.ScheduleEvent(EVENT_RESUME_MOVE, 0, EVENT_GROUP_OOC);
|
|
}
|
|
|
|
void DoAction(int32 action) override
|
|
{
|
|
switch (action)
|
|
{
|
|
case ACTION_AGGRO_HOBO:
|
|
if (Unit* target = ObjectAccessor::GetUnit(*me, _targetGUID))
|
|
{
|
|
_hoboRage = true;
|
|
me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP);
|
|
me->PauseMovement();
|
|
me->SetFacingToObject(target);
|
|
AttackStart(target);
|
|
}
|
|
break;
|
|
case ACTION_AGGRO_HOBO_DONE:
|
|
_hoboRage = false;
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
}
|
|
|
|
void SetGUID(ObjectGuid const& guid, int32 /*id*/) override
|
|
{
|
|
_targetGUID = guid;
|
|
DoAction(ACTION_AGGRO_HOBO);
|
|
}
|
|
|
|
void UpdateAI(uint32 diff) override
|
|
{
|
|
_events.Update(diff);
|
|
while (uint32 eventId = _events.ExecuteEvent())
|
|
{
|
|
switch (eventId)
|
|
{
|
|
case EVENT_TALK:
|
|
me->PauseMovement();
|
|
Talk(SAY_EVENT);
|
|
_events.ScheduleEvent(EVENT_RESUME_MOVE, 6s, EVENT_GROUP_OOC);
|
|
break;
|
|
case EVENT_JACKPOT_INTRO:
|
|
me->PauseMovement();
|
|
Talk(SAY_JACKPOT_INTRO);
|
|
_events.ScheduleEvent(EVENT_JACKPOT_MIDDLE, 2s + 500ms, EVENT_GROUP_OOC);
|
|
break;
|
|
case EVENT_JACKPOT_MIDDLE:
|
|
me->SetAIAnimKitId(648);
|
|
Talk(SAY_JACKPOT_END);
|
|
_events.ScheduleEvent(EVENT_JACKPOT_END, 6s, EVENT_GROUP_OOC);
|
|
break;
|
|
case EVENT_JACKPOT_END:
|
|
me->SetAIAnimKitId(0);
|
|
_events.ScheduleEvent(EVENT_RESUME_MOVE, 4s, EVENT_GROUP_OOC);
|
|
break;
|
|
case EVENT_CRY:
|
|
me->HandleEmoteCommand(EMOTE_ONESHOT_CRY);
|
|
me->PauseMovement();
|
|
_events.ScheduleEvent(EVENT_RESUME_MOVE, 2s, EVENT_GROUP_OOC);
|
|
break;
|
|
case EVENT_PROPERTY_RAGE:
|
|
{
|
|
uint32 creatureId = RAND(NPC_HOMELESS_STORMWIND_CITIZEN_1, NPC_HOMELESS_STORMWIND_CITIZEN_2, NPC_WEST_PLAINS_DRIFTER, NPC_TRANSIENT);
|
|
if (Creature* creature = GetClosestCreatureWithEntry(me, creatureId, 25.0f))
|
|
{
|
|
if (!creature->IsAlive() || creature->IsInCombat())
|
|
{
|
|
_events.ScheduleEvent(EVENT_RESUME_MOVE, 0, EVENT_GROUP_OOC);
|
|
return;
|
|
}
|
|
_hoboRage = true;
|
|
Talk(SAY_PROPERTY_RAGE);
|
|
me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP);
|
|
me->PauseMovement();
|
|
me->CastSpell(creature, SPELL_AGGRO_HOBO, true);
|
|
me->SetFacingToObject(creature);
|
|
}
|
|
else
|
|
_events.ScheduleEvent(EVENT_RESUME_MOVE, 0, EVENT_GROUP_OOC);
|
|
break;
|
|
}
|
|
case EVENT_RESUME_MOVE:
|
|
me->ResumeMovement();
|
|
if (roll_chance_i(50)) // prevent spam
|
|
{
|
|
if (roll_chance_i(70)) // chance to trigger simple talk event
|
|
_events.ScheduleEvent(EVENT_TALK, 30s, 80s, EVENT_GROUP_OOC);
|
|
else
|
|
_events.ScheduleEvent(urand(EVENT_JACKPOT_INTRO, EVENT_PROPERTY_RAGE), 30s, 80s, EVENT_GROUP_OOC);
|
|
}
|
|
else
|
|
_events.Repeat(30s, 80s);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!_hoboRage)
|
|
if (!UpdateVictim())
|
|
return;
|
|
|
|
DoMeleeAttackIfReady();
|
|
}
|
|
|
|
private:
|
|
EventMap _events;
|
|
ObjectGuid _targetGUID;
|
|
bool _bribeFailed;
|
|
bool _hoboRage;
|
|
bool _flee;
|
|
};
|
|
|
|
class spell_westfall_summon_ragamuffin_looter : public SpellScript
|
|
{
|
|
bool Validate(SpellInfo const* /*spellInfo*/) override
|
|
{
|
|
return ValidateSpellInfo(
|
|
{
|
|
SPELL_SUMMON_RAGAMUFFIN_LOOTER_1,
|
|
SPELL_SUMMON_RAGAMUFFIN_LOOTER_2,
|
|
SPELL_SUMMON_RAGAMUFFIN_LOOTER_3,
|
|
SPELL_SUMMON_RAGAMUFFIN_LOOTER_4
|
|
});
|
|
}
|
|
|
|
void HandleScript(SpellEffIndex /*effIndex*/)
|
|
{
|
|
Unit* caster = GetCaster();
|
|
caster->CastSpell(caster, SPELL_SUMMON_RAGAMUFFIN_LOOTER_1, true);
|
|
caster->CastSpell(caster, SPELL_SUMMON_RAGAMUFFIN_LOOTER_2, true);
|
|
caster->CastSpell(caster, SPELL_SUMMON_RAGAMUFFIN_LOOTER_3, true);
|
|
caster->CastSpell(caster, SPELL_SUMMON_RAGAMUFFIN_LOOTER_4, true);
|
|
}
|
|
|
|
void Register() override
|
|
{
|
|
OnEffectLaunch.Register(&spell_westfall_summon_ragamuffin_looter::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
|
|
}
|
|
};
|
|
|
|
class spell_westfall_aggro_hobo : public SpellScript
|
|
{
|
|
void HandleDummy(SpellEffIndex /*effIndex*/)
|
|
{
|
|
if (Creature* creature = GetHitCreature())
|
|
if (creature->IsAIEnabled())
|
|
creature->AI()->SetGUID(GetCaster()->GetGUID(), 0);
|
|
}
|
|
|
|
void Register() override
|
|
{
|
|
OnEffectHitTarget.Register(&spell_westfall_aggro_hobo::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY);
|
|
}
|
|
};
|
|
|
|
enum BridgeRefugee
|
|
{
|
|
// Events
|
|
EVENT_START_MOVE = 1,
|
|
EVENT_CRIME_SCENE_DRIFTER_TALK_0,
|
|
EVENT_CRIME_SCENE_TALK_12,
|
|
EVENT_CRIME_SCENE_DRIFTER_TALK_1,
|
|
EVENT_CRIME_SCENE_TALK_13,
|
|
EVENT_CRIME_SCENE_DRIFTER_TALK_2,
|
|
EVENT_SENTINEL_HILL_GUARD1_TALK_1,
|
|
EVENT_SENTINEL_HILL_TALK_14,
|
|
EVENT_SENTINEL_HILL_TALK_3,
|
|
EVENT_SENTINEL_HILL_GUARD2_TALK_2,
|
|
EVENT_SENTINEL_HILL_GUARD2_TALK_3,
|
|
EVENT_SENTINEL_HILL_TALK_4,
|
|
EVENT_SENTINEL_HILL_TALK_5,
|
|
EVENT_SENTINEL_HILL_TALK_15,
|
|
EVENT_SENTINEL_HILL_TALK_16,
|
|
EVENT_CANCEL,
|
|
|
|
// Action
|
|
ACTION_CRIME_SCENE = 1,
|
|
ACTION_SENTINEL_HILL_GATE_1 = 2,
|
|
ACTION_SENTINEL_HILL_GATE_2 = 3,
|
|
ACTION_SENTINEL_HILL_TALK_3 = 4,
|
|
ACTION_SENTINEL_HILL_TALK_4 = 5,
|
|
ACTION_SENTINEL_HILL_TALK_5 = 6,
|
|
|
|
// MovePoints
|
|
POINT_CRIME_SCENE = 0,
|
|
POINT_SENTINEL_HILL_GATE = 1,
|
|
POINT_DESPAWN = 2,
|
|
|
|
// Text
|
|
SAY_TRANSIENT_11 = 11,
|
|
SAY_TRANSIENT_12 = 12,
|
|
SAY_TRANSIENT_13 = 13,
|
|
SAY_TRANSIENT_14 = 14,
|
|
SAY_TRANSIENT_15 = 15,
|
|
SAY_TRANSIENT_16 = 16,
|
|
SAY_WEST_PLAINS_DRIFTER_0 = 0,
|
|
SAY_WEST_PLAINS_DRIFTER_1 = 1,
|
|
SAY_WEST_PLAINS_DRIFTER_2 = 2,
|
|
SAY_WEST_PLAINS_DRIFTER_3 = 3,
|
|
SAY_WEST_PLAINS_DRIFTER_4 = 4,
|
|
SAY_WEST_PLAINS_DRIFTER_5 = 5,
|
|
SAY_SENTINEL_HILL_GUARD_0 = 0,
|
|
SAY_SENTINEL_HILL_GUARD_1 = 1,
|
|
SAY_SENTINEL_HILL_GUARD_2 = 2,
|
|
SAY_SENTINEL_HILL_GUARD_3 = 3,
|
|
|
|
// DisplayID
|
|
DISPLAY_HOBO_CART = 32849,
|
|
|
|
// CreatureID
|
|
NPC_HOBO_CART = 42399,
|
|
NPC_WEST_PLAINS_DRIFTER_FOLLOWER = 42400,
|
|
NPC_SENTINEL_HILL_GUARD = 42407
|
|
};
|
|
|
|
Position const SentinelHillGuard1Pos = { -10341.4f, 978.075f, 31.2339f };
|
|
Position const SentinelHillGuard2Pos = { -10341.3f, 981.691f, 31.4328f };
|
|
|
|
struct npc_westfall_refugee_bridge_to_sentinelhill : public ScriptedAI
|
|
{
|
|
npc_westfall_refugee_bridge_to_sentinelhill(Creature* creature) : ScriptedAI(creature) { }
|
|
|
|
void JustAppeared() override
|
|
{
|
|
_events.ScheduleEvent(EVENT_START_MOVE, 4s);
|
|
}
|
|
|
|
void MovementInform(uint32 type, uint32 pointId) override
|
|
{
|
|
if (type != WAYPOINT_MOTION_TYPE)
|
|
return;
|
|
|
|
switch (pointId)
|
|
{
|
|
case POINT_CRIME_SCENE:
|
|
Talk(SAY_TRANSIENT_11);
|
|
_events.ScheduleEvent(EVENT_CRIME_SCENE_DRIFTER_TALK_0, 6s);
|
|
break;
|
|
case POINT_SENTINEL_HILL_GATE:
|
|
{
|
|
std::vector<Creature*> guardList;
|
|
Creature* guard1 = nullptr;
|
|
Creature* guard2 = nullptr;
|
|
me->GetCreatureListWithEntryInGrid(guardList, NPC_SENTINEL_HILL_GUARD, 8.f);
|
|
for (Creature* guard : guardList)
|
|
{
|
|
if (guard->GetHomePosition().GetExactDist2d(SentinelHillGuard1Pos) < 0.1f)
|
|
guard1 = guard;
|
|
|
|
if (guard->GetHomePosition().GetExactDist2d(SentinelHillGuard2Pos) < 0.1f)
|
|
guard2 = guard;
|
|
|
|
if (guard1 && guard2)
|
|
break;
|
|
}
|
|
|
|
if (guard1 && guard2 && !guard1->IsInCombat() && !guard2->IsInCombat())
|
|
{
|
|
if (guard1->IsAIEnabled())
|
|
{
|
|
guard1->AI()->DoAction(ACTION_SENTINEL_HILL_GATE_1);
|
|
guard1->AI()->SetGUID(me->GetGUID(), 0);
|
|
}
|
|
|
|
if (guard2->IsAIEnabled())
|
|
{
|
|
guard2->AI()->DoAction(ACTION_SENTINEL_HILL_GATE_2);
|
|
guard2->AI()->SetGUID(me->GetGUID(), 0);
|
|
}
|
|
|
|
Creature* creature = ObjectAccessor::GetCreature(*me, _westPlainsDrifter);
|
|
|
|
if (roll_chance_i(50))
|
|
_events.ScheduleEvent(EVENT_SENTINEL_HILL_TALK_14, 10s);
|
|
else
|
|
{
|
|
if (creature)
|
|
if (creature->IsAIEnabled())
|
|
creature->AI()->DoAction(ACTION_SENTINEL_HILL_TALK_3);
|
|
}
|
|
|
|
if (roll_chance_i(50))
|
|
_events.ScheduleEvent(EVENT_SENTINEL_HILL_TALK_15, 32s + 500ms);
|
|
else
|
|
{
|
|
if (creature)
|
|
if (creature->IsAIEnabled())
|
|
creature->AI()->DoAction(ACTION_SENTINEL_HILL_TALK_4);
|
|
}
|
|
|
|
if (roll_chance_i(50))
|
|
_events.ScheduleEvent(EVENT_SENTINEL_HILL_TALK_15, 38s + 500ms);
|
|
else
|
|
{
|
|
if (creature)
|
|
if (creature->IsAIEnabled())
|
|
creature->AI()->DoAction(ACTION_SENTINEL_HILL_TALK_5);
|
|
}
|
|
}
|
|
else
|
|
_events.ScheduleEvent(EVENT_CANCEL, 5min);
|
|
break;
|
|
}
|
|
case POINT_DESPAWN:
|
|
_events.ScheduleEvent(EVENT_CANCEL, 0);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void UpdateAI(uint32 diff) override
|
|
{
|
|
_events.Update(diff);
|
|
while (uint32 eventId = _events.ExecuteEvent())
|
|
{
|
|
switch (eventId)
|
|
{
|
|
case EVENT_START_MOVE:
|
|
me->GetMotionMaster()->MovePath(me->GetEntry() * 100, false);
|
|
break;
|
|
case EVENT_CRIME_SCENE_DRIFTER_TALK_0:
|
|
if (Creature* creature = me->FindNearestCreature(NPC_WEST_PLAINS_DRIFTER_FOLLOWER, 5.f))
|
|
{
|
|
if (creature->IsAIEnabled())
|
|
creature->AI()->DoAction(ACTION_CRIME_SCENE);
|
|
_westPlainsDrifter = creature->GetGUID();
|
|
}
|
|
_events.ScheduleEvent(EVENT_CRIME_SCENE_TALK_12, 6s);
|
|
break;
|
|
case EVENT_CRIME_SCENE_TALK_12:
|
|
Talk(SAY_TRANSIENT_12);
|
|
_events.ScheduleEvent(EVENT_CRIME_SCENE_TALK_13, 12s);
|
|
break;
|
|
case EVENT_CRIME_SCENE_TALK_13:
|
|
Talk(SAY_TRANSIENT_13);
|
|
break;
|
|
case EVENT_SENTINEL_HILL_TALK_14:
|
|
Talk(SAY_TRANSIENT_14);
|
|
break;
|
|
case EVENT_SENTINEL_HILL_TALK_15:
|
|
Talk(SAY_TRANSIENT_15);
|
|
break;
|
|
case EVENT_SENTINEL_HILL_TALK_16:
|
|
Talk(SAY_TRANSIENT_16);
|
|
break;
|
|
case EVENT_CANCEL:
|
|
if (Creature* creature = me->FindNearestCreature(NPC_WEST_PLAINS_DRIFTER_FOLLOWER, 5.f))
|
|
creature->DespawnOrUnsummon();
|
|
me->DespawnOrUnsummon();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
private:
|
|
EventMap _events;
|
|
ObjectGuid _westPlainsDrifter;
|
|
};
|
|
|
|
struct npc_westfall_west_plains_drifter_walk_to_sentinel : public ScriptedAI
|
|
{
|
|
npc_westfall_west_plains_drifter_walk_to_sentinel(Creature* creature) : ScriptedAI(creature) { }
|
|
|
|
void JustSummoned(Creature* summon) override
|
|
{
|
|
if (summon->GetEntry() == NPC_HOBO_CART)
|
|
summon->SetDisplayId(DISPLAY_HOBO_CART);
|
|
}
|
|
|
|
void DoAction(int32 action) override
|
|
{
|
|
switch (action)
|
|
{
|
|
case ACTION_CRIME_SCENE:
|
|
Talk(SAY_WEST_PLAINS_DRIFTER_0);
|
|
_events.ScheduleEvent(EVENT_CRIME_SCENE_DRIFTER_TALK_1, 12s);
|
|
break;
|
|
case ACTION_SENTINEL_HILL_TALK_3:
|
|
_events.ScheduleEvent(EVENT_SENTINEL_HILL_TALK_3, 10s);
|
|
break;
|
|
case ACTION_SENTINEL_HILL_TALK_4:
|
|
_events.ScheduleEvent(EVENT_SENTINEL_HILL_TALK_4, 32s + 500ms);
|
|
break;
|
|
case ACTION_SENTINEL_HILL_TALK_5:
|
|
_events.ScheduleEvent(EVENT_SENTINEL_HILL_TALK_5, 38s + 500ms);
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
}
|
|
|
|
void UpdateAI(uint32 diff) override
|
|
{
|
|
_events.Update(diff);
|
|
while (uint32 eventId = _events.ExecuteEvent())
|
|
{
|
|
switch (eventId)
|
|
{
|
|
case EVENT_CRIME_SCENE_DRIFTER_TALK_1:
|
|
Talk(SAY_WEST_PLAINS_DRIFTER_1);
|
|
_events.ScheduleEvent(EVENT_CRIME_SCENE_DRIFTER_TALK_2, 11s);
|
|
break;
|
|
case EVENT_CRIME_SCENE_DRIFTER_TALK_2:
|
|
Talk(SAY_WEST_PLAINS_DRIFTER_2);
|
|
break;
|
|
case EVENT_SENTINEL_HILL_TALK_3:
|
|
Talk(SAY_WEST_PLAINS_DRIFTER_3);
|
|
break;
|
|
case EVENT_SENTINEL_HILL_TALK_4:
|
|
Talk(SAY_WEST_PLAINS_DRIFTER_4);
|
|
break;
|
|
case EVENT_SENTINEL_HILL_TALK_5:
|
|
Talk(SAY_WEST_PLAINS_DRIFTER_5);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
private:
|
|
EventMap _events;
|
|
};
|
|
|
|
struct npc_westfall_sentinel_hill_guard : public ScriptedAI
|
|
{
|
|
npc_westfall_sentinel_hill_guard(Creature* creature) : ScriptedAI(creature) { }
|
|
|
|
void DoAction(int32 action) override
|
|
{
|
|
switch (action)
|
|
{
|
|
case ACTION_SENTINEL_HILL_GATE_1:
|
|
Talk(SAY_SENTINEL_HILL_GUARD_0);
|
|
_events.ScheduleEvent(EVENT_SENTINEL_HILL_GUARD1_TALK_1, 4s);
|
|
break;
|
|
case ACTION_SENTINEL_HILL_GATE_2:
|
|
_events.ScheduleEvent(EVENT_SENTINEL_HILL_GUARD2_TALK_2, 17s);
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
}
|
|
|
|
void SetGUID(ObjectGuid const& guid, int32 /*id*/) override
|
|
{
|
|
_transientGuid = guid;
|
|
}
|
|
|
|
void UpdateAI(uint32 diff) override
|
|
{
|
|
_events.Update(diff);
|
|
while (uint32 eventId = _events.ExecuteEvent())
|
|
{
|
|
switch (eventId)
|
|
{
|
|
case EVENT_SENTINEL_HILL_GUARD1_TALK_1:
|
|
Talk(SAY_SENTINEL_HILL_GUARD_1);
|
|
break;
|
|
case EVENT_SENTINEL_HILL_GUARD2_TALK_2:
|
|
Talk(SAY_SENTINEL_HILL_GUARD_2);
|
|
_events.ScheduleEvent(EVENT_SENTINEL_HILL_GUARD2_TALK_3, 6s);
|
|
break;
|
|
case EVENT_SENTINEL_HILL_GUARD2_TALK_3:
|
|
Talk(SAY_SENTINEL_HILL_GUARD_3);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!UpdateVictim())
|
|
return;
|
|
|
|
DoMeleeAttackIfReady();
|
|
}
|
|
|
|
private:
|
|
EventMap _events;
|
|
ObjectGuid _transientGuid;
|
|
};
|
|
|
|
enum ThugText
|
|
{
|
|
SAY_THUG_0 = 0,
|
|
SAY_THUG_1 = 1,
|
|
SAY_THUG_2 = 2,
|
|
SAY_THUG_3 = 3,
|
|
SAY_THUG_4 = 4,
|
|
SAY_THUG_5 = 5,
|
|
SAY_THUG_6 = 6,
|
|
SAY_WARNING = 0
|
|
};
|
|
|
|
enum ThugEvent
|
|
{
|
|
EVENT_SUMMON_THUGS = 1,
|
|
EVENT_THUG1_SAY_0 = 2,
|
|
EVENT_THUG2_SAY_1 = 3,
|
|
EVENT_THUG2_SAY_2 = 4,
|
|
EVENT_THUG3_SAY_3 = 5,
|
|
EVENT_THUG1_SAY_4 = 6,
|
|
EVENT_THUG1_SAY_5 = 7,
|
|
EVENT_THUG1_SAY_6 = 8,
|
|
EVENT_THUG_CREDIT = 9,
|
|
EVENT_THUG_RESET = 10,
|
|
EVENT_THUG_SHOOT_1 = 11,
|
|
EVENT_THUG_SHOOT_2 = 12,
|
|
EVENT_THUG_SCREAM = 13,
|
|
EVENT_THUG_WARNING = 14
|
|
};
|
|
|
|
enum ThugAction
|
|
{
|
|
ACTION_THUG_RESET = 1
|
|
};
|
|
|
|
enum ThugData
|
|
{
|
|
DATA_THUG_DEATH = 1
|
|
};
|
|
|
|
enum ThugMisc
|
|
{
|
|
OBJECT_SOUND_SHOOTING = 15071,
|
|
OBJECT_SOUND_SCREAM = 17852
|
|
};
|
|
|
|
class at_westfall_two_shoed_lou_thugs : public AreaTriggerScript
|
|
{
|
|
public:
|
|
at_westfall_two_shoed_lou_thugs() : AreaTriggerScript("at_westfall_two_shoed_lou_thugs") { }
|
|
|
|
bool OnTrigger(Player* player, AreaTriggerEntry const* /*at*/) override
|
|
{
|
|
if (player->IsAlive())
|
|
if (player->GetQuestStatus(QUEST_LOUS_PARTING_THOUGHTS) == QUEST_STATUS_INCOMPLETE)
|
|
if (Creature* lousPartingThoughtsTrigger = player->FindNearestCreature(MPC_LOUS_PARTING_THOUGHTS_TRIGGER, 50.0f, true))
|
|
if (lousPartingThoughtsTrigger->IsAIEnabled())
|
|
lousPartingThoughtsTrigger->AI()->SetGUID(player->GetGUID());
|
|
|
|
return true;
|
|
}
|
|
};
|
|
|
|
struct npc_westfall_thug : public ScriptedAI
|
|
{
|
|
npc_westfall_thug(Creature* creature) : ScriptedAI(creature) { }
|
|
|
|
void Reset() override
|
|
{
|
|
ScriptedAI::Reset();
|
|
_events.Reset();
|
|
}
|
|
|
|
void EnterEvadeMode(EvadeReason /*why*/) override
|
|
{
|
|
if (Unit* summon = me->ToTempSummon()->GetSummoner())
|
|
if (Creature* creature = summon->ToCreature())
|
|
if (creature->IsAIEnabled())
|
|
creature->AI()->DoAction(ACTION_THUG_RESET);
|
|
|
|
me->DespawnOrUnsummon();
|
|
}
|
|
|
|
void JustDied(Unit* /*who*/) override
|
|
{
|
|
if (Unit* summon = me->ToTempSummon()->GetSummoner())
|
|
if (Creature* creature = summon->ToCreature())
|
|
if (creature->IsAIEnabled())
|
|
creature->AI()->SetData(0, DATA_THUG_DEATH);
|
|
}
|
|
|
|
void UpdateAI(uint32 /*diff*/) override
|
|
{
|
|
if (!UpdateVictim())
|
|
return;
|
|
|
|
DoMeleeAttackIfReady();
|
|
}
|
|
|
|
private:
|
|
EventMap _events;
|
|
};
|
|
|
|
Position const ThugPos[4] =
|
|
{
|
|
{ -9859.36f, 1332.42f, 41.9859f, 2.49582f },
|
|
{ -9862.52f, 1332.08f, 41.9859f, 0.855211f },
|
|
{ -9863.49f, 1335.49f, 41.9859f, 5.63741f },
|
|
{ -9860.43f, 1335.46f, 41.9859f, 4.11898f },
|
|
};
|
|
|
|
struct npc_westfall_lous_parting_thoughts_trigger : public ScriptedAI
|
|
{
|
|
npc_westfall_lous_parting_thoughts_trigger(Creature* creature) : ScriptedAI(creature), _summons(), _thugDeathCount(0) { }
|
|
|
|
void SetGUID(ObjectGuid const& guid, int32 /*id*/) override
|
|
{
|
|
if (!_eventInvokerGUID.IsEmpty())
|
|
return;
|
|
|
|
_eventInvokerGUID = guid;
|
|
|
|
_events.ScheduleEvent(EVENT_THUG1_SAY_0, 0s);
|
|
}
|
|
|
|
void SetData(uint32 /*type*/, uint32 data) override
|
|
{
|
|
if (data == DATA_THUG_DEATH)
|
|
{
|
|
_thugDeathCount++;
|
|
|
|
if (_thugDeathCount >= 4)
|
|
{
|
|
_events.ScheduleEvent(EVENT_THUG_CREDIT, 0s);
|
|
DoAction(ACTION_THUG_RESET);
|
|
}
|
|
}
|
|
}
|
|
|
|
void DoAction(int32 action) override
|
|
{
|
|
switch (action)
|
|
{
|
|
case ACTION_THUG_RESET:
|
|
_events.ScheduleEvent(EVENT_SUMMON_THUGS, 60s);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void JustAppeared() override
|
|
{
|
|
_events.ScheduleEvent(EVENT_SUMMON_THUGS, 0s);
|
|
}
|
|
|
|
void UpdateAI(uint32 diff) override
|
|
{
|
|
_events.Update(diff);
|
|
while (uint32 eventId = _events.ExecuteEvent())
|
|
{
|
|
switch (eventId)
|
|
{
|
|
case EVENT_SUMMON_THUGS:
|
|
_summons[0] = me->SummonCreature(NPC_THUG, ThugPos[0], TEMPSUMMON_CORPSE_TIMED_DESPAWN, 60s)->GetGUID();
|
|
_summons[1] = me->SummonCreature(NPC_THUG, ThugPos[1], TEMPSUMMON_CORPSE_TIMED_DESPAWN, 60s)->GetGUID();
|
|
_summons[2] = me->SummonCreature(NPC_THUG, ThugPos[2], TEMPSUMMON_CORPSE_TIMED_DESPAWN, 60s)->GetGUID();
|
|
_summons[3] = me->SummonCreature(NPC_THUG, ThugPos[3], TEMPSUMMON_CORPSE_TIMED_DESPAWN, 60s)->GetGUID();
|
|
_eventInvokerGUID = ObjectGuid::Empty;
|
|
_thugDeathCount = 0;
|
|
break;
|
|
case EVENT_THUG1_SAY_0:
|
|
{
|
|
Creature* thug1 = ObjectAccessor::GetCreature(*me, _summons[0]);
|
|
Player* invoker = ObjectAccessor::GetPlayer(*me, _eventInvokerGUID);
|
|
if (invoker && thug1)
|
|
if (thug1->IsAIEnabled())
|
|
thug1->AI()->Talk(SAY_THUG_0, invoker);
|
|
_events.ScheduleEvent(EVENT_THUG2_SAY_1, 5s);
|
|
break;
|
|
}
|
|
case EVENT_THUG2_SAY_1:
|
|
{
|
|
Creature* thug2 = ObjectAccessor::GetCreature(*me, _summons[1]);
|
|
Player* invoker = ObjectAccessor::GetPlayer(*me, _eventInvokerGUID);
|
|
if (invoker && thug2)
|
|
if (thug2->IsAIEnabled())
|
|
thug2->AI()->Talk(SAY_THUG_1, invoker);
|
|
_events.ScheduleEvent(EVENT_THUG2_SAY_2, 5s);
|
|
break;
|
|
}
|
|
case EVENT_THUG2_SAY_2:
|
|
{
|
|
Creature* thug2 = ObjectAccessor::GetCreature(*me, _summons[1]);
|
|
Player* invoker = ObjectAccessor::GetPlayer(*me, _eventInvokerGUID);
|
|
if (invoker && thug2)
|
|
if (thug2->IsAIEnabled())
|
|
thug2->AI()->Talk(SAY_THUG_2, invoker);
|
|
_events.ScheduleEvent(EVENT_THUG3_SAY_3, 8s + 500ms);
|
|
break;
|
|
}
|
|
case EVENT_THUG3_SAY_3:
|
|
{
|
|
Creature* thug3 = ObjectAccessor::GetCreature(*me, _summons[2]);
|
|
Player* invoker = ObjectAccessor::GetPlayer(*me, _eventInvokerGUID);
|
|
if (invoker && thug3)
|
|
if (thug3->IsAIEnabled())
|
|
thug3->AI()->Talk(SAY_THUG_3, invoker);
|
|
_events.ScheduleEvent(EVENT_THUG1_SAY_4, 5s);
|
|
break;
|
|
}
|
|
case EVENT_THUG1_SAY_4:
|
|
{
|
|
Creature* thug1 = ObjectAccessor::GetCreature(*me, _summons[0]);
|
|
Player* invoker = ObjectAccessor::GetPlayer(*me, _eventInvokerGUID);
|
|
if (invoker && thug1)
|
|
{
|
|
if (thug1->IsAIEnabled())
|
|
thug1->AI()->Talk(SAY_THUG_4, invoker);
|
|
|
|
for (ObjectGuid const& guid : _summons)
|
|
if (Creature* thug = ObjectAccessor::GetCreature(*me, guid))
|
|
thug->SetFacingToObject(invoker);
|
|
}
|
|
|
|
_events.ScheduleEvent(EVENT_THUG1_SAY_5, 8s + 500ms);
|
|
break;
|
|
}
|
|
case EVENT_THUG1_SAY_5:
|
|
{
|
|
Creature* thug1 = ObjectAccessor::GetCreature(*me, _summons[0]);
|
|
Player* invoker = ObjectAccessor::GetPlayer(*me, _eventInvokerGUID);
|
|
if (invoker && thug1)
|
|
if (thug1->IsAIEnabled())
|
|
thug1->AI()->Talk(SAY_THUG_5, invoker);
|
|
|
|
|
|
_events.ScheduleEvent(EVENT_THUG1_SAY_6, 5s);
|
|
break;
|
|
}
|
|
case EVENT_THUG1_SAY_6:
|
|
{
|
|
Creature* thug1 = ObjectAccessor::GetCreature(*me, _summons[0]);
|
|
Player* invoker = ObjectAccessor::GetPlayer(*me, _eventInvokerGUID);
|
|
if (invoker && thug1)
|
|
{
|
|
if (thug1->IsAIEnabled())
|
|
thug1->AI()->Talk(SAY_THUG_6, invoker);
|
|
|
|
for (ObjectGuid const& guid : _summons)
|
|
if (Creature* thug = ObjectAccessor::GetCreature(*me, guid))
|
|
{
|
|
thug->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC);
|
|
if (thug->IsAIEnabled())
|
|
thug->AI()->AttackStart(invoker);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case EVENT_THUG_CREDIT:
|
|
{
|
|
Player* invoker = ObjectAccessor::GetPlayer(*me, _eventInvokerGUID);
|
|
if (invoker)
|
|
{
|
|
invoker->CastSpell(invoker, SPELL_HOBO_INFORMATION, TriggerCastFlags(TRIGGERED_FULL_MASK));
|
|
|
|
if (Group* group = invoker->GetGroup())
|
|
{
|
|
for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next())
|
|
{
|
|
Player* groupMember = itr->GetSource();
|
|
if (groupMember && groupMember->IsInMap(invoker) && groupMember->GetQuestStatus(QUEST_LOUS_PARTING_THOUGHTS) == QUEST_STATUS_INCOMPLETE && groupMember->GetDistance(me) <= 75.f)
|
|
{
|
|
groupMember->CastSpell(groupMember, SPELL_HOBO_INFORMATION, TriggerCastFlags(TRIGGERED_FULL_MASK));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
_events.ScheduleEvent(EVENT_THUG_SHOOT_1, 1s + 500ms);
|
|
break;
|
|
}
|
|
case EVENT_THUG_SHOOT_1:
|
|
{
|
|
Player* invoker = ObjectAccessor::GetPlayer(*me, _eventInvokerGUID);
|
|
if (invoker)
|
|
me->PlayDistanceSound(OBJECT_SOUND_SHOOTING, invoker);
|
|
|
|
_events.ScheduleEvent(EVENT_THUG_SHOOT_2, 1s + 200ms);
|
|
break;
|
|
}
|
|
case EVENT_THUG_SHOOT_2:
|
|
{
|
|
Player* invoker = ObjectAccessor::GetPlayer(*me, _eventInvokerGUID);
|
|
if (invoker)
|
|
me->PlayDistanceSound(OBJECT_SOUND_SHOOTING, invoker);
|
|
|
|
_events.ScheduleEvent(EVENT_THUG_SCREAM, 1s + 200ms);
|
|
break;
|
|
}
|
|
case EVENT_THUG_SCREAM:
|
|
{
|
|
Player* invoker = ObjectAccessor::GetPlayer(*me, _eventInvokerGUID);
|
|
if (invoker)
|
|
me->PlayDistanceSound(OBJECT_SOUND_SCREAM, invoker);
|
|
|
|
_events.ScheduleEvent(EVENT_THUG_WARNING, 2s + 500ms);
|
|
break;
|
|
}
|
|
case EVENT_THUG_WARNING:
|
|
{
|
|
Player* invoker = ObjectAccessor::GetPlayer(*me, _eventInvokerGUID);
|
|
if (invoker)
|
|
Talk(SAY_WARNING, invoker);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
private:
|
|
EventMap _events;
|
|
ObjectGuid _eventInvokerGUID;
|
|
std::array<ObjectGuid, 4> _summons;
|
|
uint32 _thugDeathCount;
|
|
};
|
|
|
|
enum SmallTimeEvent
|
|
{
|
|
EVENT_TALK_0 = 1,
|
|
EVENT_TALK_1 = 2,
|
|
EVENT_RESET = 3
|
|
};
|
|
|
|
enum SmallTimeSay
|
|
{
|
|
SAY_SMALL_TIME_0 = 0,
|
|
SAY_SMALL_TIME_1 = 1
|
|
};
|
|
|
|
class at_westfall_small_time_hustler : public AreaTriggerScript
|
|
{
|
|
public:
|
|
at_westfall_small_time_hustler() : AreaTriggerScript("at_westfall_small_time_hustler") { }
|
|
|
|
bool OnTrigger(Player* player, AreaTriggerEntry const* /*at*/) override
|
|
{
|
|
if (player->IsAlive())
|
|
if (Creature* lousPartingThoughtsTrigger = player->FindNearestCreature(NPC_SMALL_TIME_HUSTLER, 10.0f, true))
|
|
if (lousPartingThoughtsTrigger->IsAIEnabled())
|
|
lousPartingThoughtsTrigger->AI()->SetGUID(player->GetGUID());
|
|
|
|
return true;
|
|
}
|
|
};
|
|
|
|
struct npc_westfall_small_time_hustler : public ScriptedAI
|
|
{
|
|
npc_westfall_small_time_hustler(Creature* creature) : ScriptedAI(creature) { }
|
|
|
|
void SetGUID(ObjectGuid const& guid, int32 /*id*/) override
|
|
{
|
|
if (!_eventInvokerGUID.IsEmpty())
|
|
return;
|
|
|
|
_eventInvokerGUID = guid;
|
|
|
|
_events.ScheduleEvent(EVENT_TALK_0, 0s);
|
|
}
|
|
|
|
void UpdateAI(uint32 diff) override
|
|
{
|
|
_events.Update(diff);
|
|
while (uint32 eventId = _events.ExecuteEvent())
|
|
{
|
|
switch (eventId)
|
|
{
|
|
case EVENT_TALK_0:
|
|
{
|
|
Player* invoker = ObjectAccessor::GetPlayer(*me, _eventInvokerGUID);
|
|
if (invoker)
|
|
{
|
|
me->SetFacingToObject(invoker);
|
|
Talk(SAY_SMALL_TIME_0, invoker);
|
|
_events.ScheduleEvent(EVENT_TALK_1, 4s);
|
|
}
|
|
break;
|
|
}
|
|
case EVENT_TALK_1:
|
|
{
|
|
Player* invoker = ObjectAccessor::GetPlayer(*me, _eventInvokerGUID);
|
|
if (invoker)
|
|
{
|
|
Talk(SAY_SMALL_TIME_1, invoker);
|
|
_events.ScheduleEvent(EVENT_RESET, 60s);
|
|
}
|
|
break;
|
|
}
|
|
case EVENT_RESET:
|
|
{
|
|
_eventInvokerGUID = ObjectGuid::Empty;
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
private:
|
|
EventMap _events;
|
|
ObjectGuid _eventInvokerGUID;
|
|
};
|
|
|
|
enum WestFallStewNpc
|
|
{
|
|
NPC_WESTFALL_STEW = 42617
|
|
};
|
|
|
|
enum WestFallStewSpell
|
|
{
|
|
SPELL_FULL_BELLY = 79451,
|
|
SPELL_COSMETIC_SLEEP = 78677
|
|
};
|
|
|
|
enum WestFallStewEvent
|
|
{
|
|
EVENT_START = 1,
|
|
EVENT_MOVE_TO_STEW,
|
|
EVENT_RETURN_TO_HOME
|
|
};
|
|
|
|
enum WestFallText
|
|
{
|
|
SAY_NPC_HOMELESS_STORMWIND_CITIZEN_1 = 11,
|
|
SAY_NPC_HOMELESS_STORMWIND_CITIZEN_2 = 12,
|
|
SAY_NPC_TRANSIENT = 17,
|
|
SAY_NPC_WEST_PLAINS_DRIFTER = 11,
|
|
SAY_NPC_WEST_PLAINS_DRIFTER_FOLLOWER = 6
|
|
};
|
|
|
|
struct npc_westfall_westfall_stew: public ScriptedAI
|
|
{
|
|
npc_westfall_westfall_stew(Creature* creature) : ScriptedAI(creature) { }
|
|
|
|
void IsSummonedBy(Unit* summoner) override
|
|
{
|
|
if (!summoner)
|
|
return;
|
|
|
|
std::vector<Creature*> homelessCitizens;
|
|
me->GetCreatureListWithEntryInGrid(homelessCitizens, NPC_HOMELESS_STORMWIND_CITIZEN_1, 8.f);
|
|
me->GetCreatureListWithEntryInGrid(homelessCitizens, NPC_HOMELESS_STORMWIND_CITIZEN_2, 8.f);
|
|
me->GetCreatureListWithEntryInGrid(homelessCitizens, NPC_TRANSIENT, 8.f);
|
|
me->GetCreatureListWithEntryInGrid(homelessCitizens, NPC_WEST_PLAINS_DRIFTER, 8.f);
|
|
me->GetCreatureListWithEntryInGrid(homelessCitizens, NPC_WEST_PLAINS_DRIFTER_FOLLOWER, 8.f);
|
|
for (Creature* homelessCitizen : homelessCitizens)
|
|
{
|
|
if (homelessCitizen->HasAura(SPELL_FULL_BELLY))
|
|
continue;
|
|
|
|
homelessCitizen->AI()->SetGUID(me->GetGUID());
|
|
}
|
|
}
|
|
|
|
private:
|
|
EventMap _events;
|
|
};
|
|
|
|
struct npc_westfall_homless_stormwind_citizen : public ScriptedAI
|
|
{
|
|
npc_westfall_homless_stormwind_citizen(Creature* creature) : ScriptedAI(creature) { }
|
|
|
|
void SetGUID(ObjectGuid const& guid, int32 /*id*/) override
|
|
{
|
|
if (!_westfallStewGUID.IsEmpty())
|
|
return;
|
|
|
|
_westfallStewGUID = guid;
|
|
|
|
_events.ScheduleEvent(EVENT_START, 0);
|
|
}
|
|
|
|
void MovementInform(uint32 MotionType, uint32 PointId) override
|
|
{
|
|
if ((MotionType == POINT_MOTION_TYPE) && (PointId == 1))
|
|
{
|
|
me->CastSpell(me, SPELL_FULL_BELLY);
|
|
|
|
if (Creature* stew = ObjectAccessor::GetCreature(*me, _westfallStewGUID))
|
|
{
|
|
if (Unit* summoner = stew->ToTempSummon()->GetSummoner())
|
|
if (Player* player = summoner->ToPlayer())
|
|
player->KilledMonsterCredit(NPC_WESTFALL_STEW);
|
|
|
|
if (roll_chance_i(50))
|
|
{
|
|
switch (me->GetEntry())
|
|
{
|
|
case NPC_HOMELESS_STORMWIND_CITIZEN_1:
|
|
Talk(SAY_NPC_HOMELESS_STORMWIND_CITIZEN_1, stew);
|
|
break;
|
|
case NPC_HOMELESS_STORMWIND_CITIZEN_2:
|
|
Talk(SAY_NPC_HOMELESS_STORMWIND_CITIZEN_2, stew);
|
|
break;
|
|
case NPC_TRANSIENT:
|
|
Talk(SAY_NPC_TRANSIENT, stew);
|
|
break;
|
|
case NPC_WEST_PLAINS_DRIFTER:
|
|
Talk(SAY_NPC_WEST_PLAINS_DRIFTER, stew);
|
|
break;
|
|
case NPC_WEST_PLAINS_DRIFTER_FOLLOWER:
|
|
Talk(SAY_NPC_WEST_PLAINS_DRIFTER_FOLLOWER, stew);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
_events.ScheduleEvent(EVENT_RETURN_TO_HOME, 5s + 500ms);
|
|
}
|
|
}
|
|
|
|
void UpdateAI(uint32 diff) override
|
|
{
|
|
_events.Update(diff);
|
|
while (uint32 eventId = _events.ExecuteEvent())
|
|
{
|
|
switch (eventId)
|
|
{
|
|
case EVENT_START:
|
|
{
|
|
me->SetStandState(UNIT_STAND_STATE_STAND);
|
|
|
|
if (me->HasAura(SPELL_COSMETIC_SLEEP))
|
|
me->RemoveAurasDueToSpell(SPELL_COSMETIC_SLEEP);
|
|
|
|
_events.ScheduleEvent(EVENT_MOVE_TO_STEW, 2s);
|
|
break;
|
|
}
|
|
case EVENT_MOVE_TO_STEW:
|
|
{
|
|
Creature* stew = ObjectAccessor::GetCreature(*me, _westfallStewGUID);
|
|
if (stew)
|
|
{
|
|
float x, y, z;
|
|
stew->GetContactPoint(me, x, y, z, 0.2f);
|
|
me->GetMotionMaster()->MovePoint(1, x, y, z);
|
|
}
|
|
else
|
|
me->GetMotionMaster()->MoveTargetedHome();
|
|
break;
|
|
}
|
|
case EVENT_RETURN_TO_HOME:
|
|
{
|
|
me->GetMotionMaster()->MoveTargetedHome();
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!UpdateVictim())
|
|
return;
|
|
|
|
DoMeleeAttackIfReady();
|
|
}
|
|
|
|
private:
|
|
EventMap _events;
|
|
ObjectGuid _westfallStewGUID;
|
|
};
|
|
|
|
struct npc_westfall_west_plains_drifter : public ScriptedAI
|
|
{
|
|
npc_westfall_west_plains_drifter(Creature* creature) : ScriptedAI(creature) { }
|
|
|
|
void JustSummoned(Creature* summon) override
|
|
{
|
|
if (summon->GetEntry() == NPC_HOBO_CART)
|
|
summon->SetDisplayId(DISPLAY_HOBO_CART);
|
|
}
|
|
|
|
void SetGUID(ObjectGuid const& guid, int32 /*id*/) override
|
|
{
|
|
if (!_westfallStewGUID.IsEmpty())
|
|
return;
|
|
|
|
_westfallStewGUID = guid;
|
|
_events.ScheduleEvent(EVENT_START, 0);
|
|
}
|
|
|
|
void MovementInform(uint32 motionType, uint32 pointId) override
|
|
{
|
|
if (motionType != POINT_MOTION_TYPE || pointId != 1)
|
|
return;
|
|
|
|
me->CastSpell(me, SPELL_FULL_BELLY);
|
|
|
|
if (Creature* stew = ObjectAccessor::GetCreature(*me, _westfallStewGUID))
|
|
{
|
|
// Should never happen, unless the stew has been spawned via db which indicates dirty db data
|
|
if (!stew->IsSummon())
|
|
return;
|
|
|
|
if (Unit* summoner = stew->ToTempSummon()->GetSummoner())
|
|
if (Player* player = summoner->ToPlayer())
|
|
player->KilledMonsterCredit(NPC_WESTFALL_STEW);
|
|
|
|
if (roll_chance_i(50))
|
|
{
|
|
switch (me->GetEntry())
|
|
{
|
|
case NPC_HOMELESS_STORMWIND_CITIZEN_1:
|
|
Talk(SAY_NPC_HOMELESS_STORMWIND_CITIZEN_1, stew);
|
|
break;
|
|
case NPC_HOMELESS_STORMWIND_CITIZEN_2:
|
|
Talk(SAY_NPC_HOMELESS_STORMWIND_CITIZEN_2, stew);
|
|
break;
|
|
case NPC_TRANSIENT:
|
|
Talk(SAY_NPC_TRANSIENT, stew);
|
|
break;
|
|
case NPC_WEST_PLAINS_DRIFTER:
|
|
Talk(SAY_NPC_WEST_PLAINS_DRIFTER, stew);
|
|
break;
|
|
case NPC_WEST_PLAINS_DRIFTER_FOLLOWER:
|
|
Talk(SAY_NPC_WEST_PLAINS_DRIFTER_FOLLOWER, stew);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
_events.ScheduleEvent(EVENT_RETURN_TO_HOME, 5s + 500ms);
|
|
}
|
|
|
|
void UpdateAI(uint32 diff) override
|
|
{
|
|
_events.Update(diff);
|
|
while (uint32 eventId = _events.ExecuteEvent())
|
|
{
|
|
switch (eventId)
|
|
{
|
|
case EVENT_START:
|
|
me->SetStandState(UNIT_STAND_STATE_STAND);
|
|
|
|
if (me->HasAura(SPELL_COSMETIC_SLEEP))
|
|
me->RemoveAurasDueToSpell(SPELL_COSMETIC_SLEEP);
|
|
|
|
_events.ScheduleEvent(EVENT_MOVE_TO_STEW, 2s);
|
|
break;
|
|
case EVENT_MOVE_TO_STEW:
|
|
if (Creature* stew = ObjectAccessor::GetCreature(*me, _westfallStewGUID))
|
|
{
|
|
float x, y, z;
|
|
stew->GetContactPoint(me, x, y, z, 0.2f);
|
|
me->GetMotionMaster()->MovePoint(1, x, y, z);
|
|
}
|
|
else
|
|
me->GetMotionMaster()->MoveTargetedHome();
|
|
break;
|
|
case EVENT_RETURN_TO_HOME:
|
|
me->GetMotionMaster()->MoveTargetedHome();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!UpdateVictim())
|
|
return;
|
|
|
|
DoMeleeAttackIfReady();
|
|
}
|
|
|
|
private:
|
|
EventMap _events;
|
|
ObjectGuid _westfallStewGUID;
|
|
};
|
|
|
|
class spell_westfall_sniper_fire_proc : public AuraScript
|
|
{
|
|
bool CheckProc(ProcEventInfo& eventInfo)
|
|
{
|
|
_target = (eventInfo.GetProcTarget() == GetTarget() ? eventInfo.GetActor() : eventInfo.GetProcTarget());
|
|
return _target && _target->IsCreature() && _target->GetEntry() == NPC_MERCENARY;
|
|
}
|
|
|
|
void HandleProc(AuraEffect const* aurEff, ProcEventInfo& /*eventInfo*/)
|
|
{
|
|
PreventDefaultAction();
|
|
GetTarget()->CastSpell(_target, GetSpellInfo()->Effects[aurEff->GetEffIndex()].TriggerSpell);
|
|
|
|
}
|
|
|
|
void Register() override
|
|
{
|
|
DoCheckProc.Register(&spell_westfall_sniper_fire_proc::CheckProc);
|
|
OnEffectProc.Register(&spell_westfall_sniper_fire_proc::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL);
|
|
}
|
|
|
|
private:
|
|
Unit* _target = nullptr;
|
|
};
|
|
|
|
enum AgentKearnenText
|
|
{
|
|
SAY_0 = 0
|
|
};
|
|
|
|
class spell_westfall_sniper_fire : public SpellScript
|
|
{
|
|
bool Validate(SpellInfo const* /*spellInfo*/) override
|
|
{
|
|
return ValidateSpellInfo({ SPELL_KILL_SHOT_TRIGGERED });
|
|
}
|
|
|
|
void HandleDummyEffect(SpellEffIndex /*effIndex*/)
|
|
{
|
|
Unit* target = GetHitUnit();
|
|
Unit* caster = GetCaster();
|
|
if (caster && target)
|
|
if (Creature* agent = caster->FindNearestCreature(NPC_AGENT_KEARNEN, 100.f))
|
|
{
|
|
agent->CastSpell(target, SPELL_KILL_SHOT_TRIGGERED); // 79525
|
|
if (agent->IsAIEnabled())
|
|
agent->AI()->Talk(SAY_0, caster);
|
|
}
|
|
}
|
|
|
|
void Register() override
|
|
{
|
|
OnEffectHitTarget.Register(&spell_westfall_sniper_fire::HandleDummyEffect, EFFECT_0, SPELL_EFFECT_DUMMY);
|
|
}
|
|
};
|
|
|
|
void AddSC_westfall()
|
|
{
|
|
RegisterSpellScript(spell_westfall_unbound_energy);
|
|
RegisterCreatureAI(npc_westfall_overloaded_harvest_golem);
|
|
RegisterSpellScript(spell_westfall_reaping_blows);
|
|
RegisterSpellScript(spell_westfall_wake_harvest_golem);
|
|
RegisterSpellScript(spell_westfall_quest_credit_jangolode_event);
|
|
RegisterCreatureAI(npc_westfall_hobo_witness);
|
|
RegisterSpellScript(spell_westfall_summon_ragamuffin_looter);
|
|
RegisterSpellScript(spell_westfall_aggro_hobo);
|
|
RegisterCreatureAI(npc_westfall_refugee_bridge_to_sentinelhill);
|
|
RegisterCreatureAI(npc_westfall_west_plains_drifter_walk_to_sentinel);
|
|
RegisterCreatureAI(npc_westfall_sentinel_hill_guard);
|
|
new at_westfall_two_shoed_lou_thugs();
|
|
RegisterCreatureAI(npc_westfall_thug);
|
|
RegisterCreatureAI(npc_westfall_lous_parting_thoughts_trigger);
|
|
new at_westfall_small_time_hustler();
|
|
RegisterCreatureAI(npc_westfall_small_time_hustler);
|
|
RegisterCreatureAI(npc_westfall_westfall_stew);
|
|
RegisterCreatureAI(npc_westfall_homless_stormwind_citizen);
|
|
RegisterCreatureAI(npc_westfall_west_plains_drifter);
|
|
RegisterSpellScript(spell_westfall_sniper_fire_proc);
|
|
RegisterSpellScript(spell_westfall_sniper_fire);
|
|
}
|