Core/Scripts: implement boss Apothecary Hummel ("Love is in the Air" event boss) (#19076)

Closes #5114
This commit is contained in:
Gustavo
2017-03-13 10:30:32 -03:00
committed by Aokromes
parent b2e2861118
commit 4ec7b63de1
4 changed files with 847 additions and 1 deletions

View File

@@ -0,0 +1,598 @@
/*
* Copyright (C) 2008-2017 TrinityCore <http://www.trinitycore.org/>
*
* 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 "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "ScriptedGossip.h"
#include "LFGMgr.h"
#include "Player.h"
#include "Group.h"
#include "SpellScript.h"
#include "SpellAuraEffects.h"
#include "shadowfang_keep.h"
#include "GridNotifiersImpl.h"
enum ApothecarySpells
{
SPELL_ALLURING_PERFUME = 68589,
SPELL_PERFUME_SPRAY = 68607,
SPELL_CHAIN_REACTION = 68821,
SPELL_SUMMON_TABLE = 69218,
SPELL_PERMANENT_FEIGN_DEATH = 29266,
SPELL_QUIET_SUICIDE = 3617,
SPELL_COLOGNE_SPRAY = 68948,
SPELL_VALIDATE_AREA = 68644,
SPELL_THROW_COLOGNE = 68841,
SPELL_BUNNY_LOCKDOWN = 69039,
SPELL_THROW_PERFUME = 68799,
SPELL_PERFUME_SPILL = 68798,
SPELL_COLOGNE_SPILL = 68614,
SPELL_PERFUME_SPILL_DAMAGE = 68927,
SPELL_COLOGNE_SPILL_DAMAGE = 68934
};
enum ApothecarySays
{
SAY_INTRO_0 = 0,
SAY_INTRO_1 = 1,
SAY_INTRO_2 = 2,
SAY_CALL_BAXTER = 3,
SAY_CALL_FRYE = 4,
SAY_HUMMEL_DEATH = 5,
SAY_BAXTER_DEATH = 0,
SAY_FRYE_DEATH = 0
};
enum ApothecaryEvents
{
EVENT_HUMMEL_SAY_0 = 1,
EVENT_HUMMEL_SAY_1,
EVENT_HUMMEL_SAY_2,
EVENT_START_FIGHT,
EVENT_CHAIN_REACTION,
EVENT_PERFUME_SPRAY,
EVENT_COLOGNE_SPRAY,
EVENT_CALL_BAXTER,
EVENT_CALL_FRYE
};
enum ApothecaryMisc
{
ACTION_START_EVENT = 1,
ACTION_START_FIGHT = 2,
FACTION_APOTHECARY_HOSTILE = 14,
FACTION_APOTHECARY_FRIENDLY = 35,
GOSSIP_OPTION_START = 0,
GOSSIP_MENU_HUMMEL = 10847,
QUEST_YOUVE_BEEN_SERVED = 14488,
NPC_APOTHECARY_FRYE = 36272,
NPC_APOTHECARY_BAXTER = 36565,
NPC_VIAL_BUNNY = 36530,
NPC_CROWN_APOTHECARY = 36885,
PHASE_ALL = 0,
PHASE_INTRO = 1
};
Position const BaxterMovePos = { -221.4115f, 2206.825f, 79.93151f, 0.0f };
Position const FryeMovePos = { -196.2483f, 2197.224f, 79.9315f, 0.0f };
class boss_apothecary_hummel : public CreatureScript
{
public:
boss_apothecary_hummel() : CreatureScript("boss_apothecary_hummel") { }
struct boss_apothecary_hummelAI : public BossAI
{
boss_apothecary_hummelAI(Creature* creature) : BossAI(creature, DATA_APOTHECARY_HUMMEL), _deadCount(0), _isDead(false) { }
void sGossipSelect(Player* player, uint32 menuId, uint32 gossipListId) override
{
if (menuId == GOSSIP_MENU_HUMMEL && gossipListId == GOSSIP_OPTION_START)
{
me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP);
CloseGossipMenuFor(player);
DoAction(ACTION_START_EVENT);
}
}
void Reset() override
{
_Reset();
_deadCount = 0;
_isDead = false;
events.SetPhase(PHASE_ALL);
me->setFaction(FACTION_APOTHECARY_FRIENDLY);
me->SummonCreatureGroup(1);
}
void EnterEvadeMode(EvadeReason /*why*/) override
{
summons.DespawnAll();
_EnterEvadeMode();
_DespawnAtEvade(Seconds(10));
}
void DoAction(int32 action) override
{
if (action == ACTION_START_EVENT && events.IsInPhase(PHASE_ALL))
{
events.SetPhase(PHASE_INTRO);
events.ScheduleEvent(EVENT_HUMMEL_SAY_0, Milliseconds(1));
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC);
me->setFaction(FACTION_APOTHECARY_HOSTILE);
DummyEntryCheckPredicate pred;
summons.DoAction(ACTION_START_EVENT, pred);
}
}
void DamageTaken(Unit* /*attacker*/, uint32& damage) override
{
if (damage >= me->GetHealth())
if (_deadCount < 2)
{
damage = me->GetHealth() - 1;
if (!_isDead)
{
_isDead = true;
me->RemoveAurasDueToSpell(SPELL_ALLURING_PERFUME);
DoCastSelf(SPELL_PERMANENT_FEIGN_DEATH, true);
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_UNK_29 | UNIT_FLAG_NOT_SELECTABLE);
Talk(SAY_HUMMEL_DEATH);
}
}
}
void SummonedCreatureDies(Creature* summon, Unit* /*killer*/) override
{
if (summon->GetEntry() == NPC_APOTHECARY_FRYE || summon->GetEntry() == NPC_APOTHECARY_BAXTER)
_deadCount++;
if (me->HasAura(SPELL_PERMANENT_FEIGN_DEATH) && _deadCount == 2)
DoCastSelf(SPELL_QUIET_SUICIDE, true);
}
void JustDied(Unit* /*killer*/) override
{
if (!_isDead)
Talk(SAY_HUMMEL_DEATH);
events.Reset();
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_UNK_29 | UNIT_FLAG_NOT_SELECTABLE);
instance->SetBossState(DATA_APOTHECARY_HUMMEL, DONE);
Map::PlayerList const& players = me->GetMap()->GetPlayers();
if (!players.isEmpty())
{
if (Group* group = players.begin()->GetSource()->GetGroup())
if (group->isLFGGroup())
sLFGMgr->FinishDungeon(group->GetGUID(), 288, me->GetMap());
}
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim() && !events.IsInPhase(PHASE_INTRO))
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
while (uint32 eventId = events.ExecuteEvent())
{
switch (eventId)
{
case EVENT_HUMMEL_SAY_0:
Talk(SAY_INTRO_0);
events.ScheduleEvent(EVENT_HUMMEL_SAY_1, Seconds(4));
break;
case EVENT_HUMMEL_SAY_1:
Talk(SAY_INTRO_1);
events.ScheduleEvent(EVENT_HUMMEL_SAY_2, Seconds(4));
break;
case EVENT_HUMMEL_SAY_2:
Talk(SAY_INTRO_2);
events.ScheduleEvent(EVENT_START_FIGHT, Seconds(4));
break;
case EVENT_START_FIGHT:
{
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC);
me->SetInCombatWithZone();
events.ScheduleEvent(EVENT_CALL_BAXTER, Seconds(6));
events.ScheduleEvent(EVENT_CALL_FRYE, Seconds(14));
events.ScheduleEvent(EVENT_PERFUME_SPRAY, Milliseconds(3640));
events.ScheduleEvent(EVENT_CHAIN_REACTION, Seconds(15));
std::vector<Creature*> trashs;
me->GetCreatureListWithEntryInGrid(trashs, NPC_CROWN_APOTHECARY);
for (Creature* crea : trashs)
crea->DespawnOrUnsummon();
break;
}
case EVENT_CALL_BAXTER:
{
Talk(SAY_CALL_BAXTER);
EntryCheckPredicate pred(NPC_APOTHECARY_BAXTER);
summons.DoAction(ACTION_START_FIGHT, pred);
summons.DoZoneInCombat(NPC_APOTHECARY_BAXTER);
break;
}
case EVENT_CALL_FRYE:
{
Talk(SAY_CALL_FRYE);
EntryCheckPredicate pred(NPC_APOTHECARY_FRYE);
summons.DoAction(ACTION_START_FIGHT, pred);
break;
}
case EVENT_PERFUME_SPRAY:
DoCastVictim(SPELL_PERFUME_SPRAY);
events.Repeat(Milliseconds(3640));
break;
case EVENT_CHAIN_REACTION:
DoCastVictim(SPELL_SUMMON_TABLE, true);
DoCastAOE(SPELL_CHAIN_REACTION);
events.Repeat(Seconds(25));
break;
default:
break;
}
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
}
DoMeleeAttackIfReady();
}
private:
uint8 _deadCount;
bool _isDead;
};
CreatureAI* GetAI(Creature* creature) const override
{
return GetInstanceAI<boss_apothecary_hummelAI>(creature);
}
bool OnQuestReward(Player* /*player*/, Creature* creature, Quest const* quest, uint32 /*opt*/) override
{
if (quest->GetQuestId() == QUEST_YOUVE_BEEN_SERVED)
creature->AI()->DoAction(ACTION_START_EVENT);
return true;
}
};
struct npc_apothecary_genericAI : public ScriptedAI
{
npc_apothecary_genericAI(Creature* creature, Position pos) : ScriptedAI(creature), _movePos(pos) { }
void DoAction(int32 action) override
{
if (action == ACTION_START_EVENT)
{
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC);
me->setFaction(FACTION_APOTHECARY_HOSTILE);
me->GetMotionMaster()->MovePoint(1, _movePos);
}
else if (action == ACTION_START_FIGHT)
{
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC);
me->SetInCombatWithZone();
}
}
void MovementInform(uint32 type, uint32 pointId) override
{
if (type == POINT_MOTION_TYPE && pointId == 1)
me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_USE_STANDING);
}
protected:
Position _movePos;
};
class npc_apothecary_frye : public CreatureScript
{
public:
npc_apothecary_frye() : CreatureScript("npc_apothecary_frye") { }
struct npc_apothecary_fryeAI : public npc_apothecary_genericAI
{
npc_apothecary_fryeAI(Creature* creature) : npc_apothecary_genericAI(creature, FryeMovePos) { }
void JustDied(Unit* /*who*/) override
{
Talk(SAY_FRYE_DEATH);
}
};
CreatureAI* GetAI(Creature* creature) const override
{
return GetInstanceAI<npc_apothecary_fryeAI>(creature);
}
};
class npc_apothecary_baxter : public CreatureScript
{
public:
npc_apothecary_baxter() : CreatureScript("npc_apothecary_baxter") { }
struct npc_apothecary_baxterAI : public npc_apothecary_genericAI
{
npc_apothecary_baxterAI(Creature* creature) : npc_apothecary_genericAI(creature, BaxterMovePos) { }
void Reset() override
{
_events.Reset();
_events.ScheduleEvent(EVENT_COLOGNE_SPRAY, Seconds(7));
_events.ScheduleEvent(EVENT_CHAIN_REACTION, Seconds(12));
}
void JustDied(Unit* /*who*/) override
{
_events.Reset();
Talk(SAY_BAXTER_DEATH);
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
_events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
while (uint32 eventId = _events.ExecuteEvent())
{
switch (eventId)
{
case EVENT_COLOGNE_SPRAY:
DoCastVictim(SPELL_COLOGNE_SPRAY);
_events.Repeat(Seconds(4));
break;
case EVENT_CHAIN_REACTION:
DoCastVictim(SPELL_SUMMON_TABLE);
DoCastVictim(SPELL_CHAIN_REACTION);
_events.Repeat(Seconds(25));
break;
default:
break;
}
}
DoMeleeAttackIfReady();
}
private:
EventMap _events;
};
CreatureAI* GetAI(Creature* creature) const override
{
return GetInstanceAI<npc_apothecary_baxterAI>(creature);
}
};
// 68965 -[DND] Lingering Fumes Targetting (starter)
class spell_apothecary_lingering_fumes : public SpellScriptLoader
{
public:
spell_apothecary_lingering_fumes() : SpellScriptLoader("spell_apothecary_lingering_fumes") { }
class spell_apothecary_lingering_fumes_SpellScript : public SpellScript
{
PrepareSpellScript(spell_apothecary_lingering_fumes_SpellScript);
void HandleAfterCast()
{
Unit* caster = GetCaster();
if (!caster->IsInCombat() || roll_chance_i(50))
return;
std::list<Creature*> triggers;
caster->GetCreatureListWithEntryInGrid(triggers, NPC_VIAL_BUNNY, 100.0f);
Creature* trigger = Trinity::Containers::SelectRandomContainerElement(triggers);
caster->GetMotionMaster()->MovePoint(0, trigger->GetPosition());
}
void HandleScript(SpellEffIndex /*effindex*/)
{
Unit* caster = GetCaster();
caster->CastSpell(GetHitUnit(), SPELL_VALIDATE_AREA, true);
}
void Register() override
{
AfterCast += SpellCastFn(spell_apothecary_lingering_fumes_SpellScript::HandleAfterCast);
OnEffectHitTarget += SpellEffectFn(spell_apothecary_lingering_fumes_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_DUMMY);
}
};
SpellScript* GetSpellScript() const override
{
return new spell_apothecary_lingering_fumes_SpellScript();
}
};
// 68644 - [DND] Valentine Boss Validate Area
class spell_apothecary_validate_area : public SpellScriptLoader
{
public:
spell_apothecary_validate_area() : SpellScriptLoader("spell_apothecary_validate_area") { }
class spell_apothecary_validate_area_SpellScript : public SpellScript
{
PrepareSpellScript(spell_apothecary_validate_area_SpellScript);
void FilterTargets(std::list<WorldObject*>& targets)
{
targets.remove_if(Trinity::UnitAuraCheck(true, SPELL_BUNNY_LOCKDOWN));
if (targets.empty())
return;
WorldObject* target = Trinity::Containers::SelectRandomContainerElement(targets);
targets.clear();
targets.push_back(target);
}
void HandleScript(SpellEffIndex /*effindex*/)
{
GetHitUnit()->CastSpell(GetHitUnit(), SPELL_BUNNY_LOCKDOWN, true);
GetCaster()->CastSpell(GetHitUnit(), RAND(SPELL_THROW_COLOGNE, SPELL_THROW_PERFUME), true);
}
void Register() override
{
OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_apothecary_validate_area_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_DEST_AREA_ENTRY);
OnEffectHitTarget += SpellEffectFn(spell_apothecary_validate_area_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_DUMMY);
}
};
SpellScript* GetSpellScript() const override
{
return new spell_apothecary_validate_area_SpellScript();
}
};
// 69038 - Throw Cologne
class spell_apothecary_throw_cologne : public SpellScriptLoader
{
public:
spell_apothecary_throw_cologne() : SpellScriptLoader("spell_apothecary_throw_cologne") { }
class spell_apothecary_throw_cologne_SpellScript : public SpellScript
{
PrepareSpellScript(spell_apothecary_throw_cologne_SpellScript);
void HandleScript(SpellEffIndex /*effindex*/)
{
GetHitUnit()->CastSpell(GetHitUnit(), SPELL_COLOGNE_SPILL, true);
}
void Register() override
{
OnEffectHitTarget += SpellEffectFn(spell_apothecary_throw_cologne_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_DUMMY);
}
};
SpellScript* GetSpellScript() const override
{
return new spell_apothecary_throw_cologne_SpellScript();
}
};
// 68966 - Throw Perfume
class spell_apothecary_throw_perfume : public SpellScriptLoader
{
public:
spell_apothecary_throw_perfume() : SpellScriptLoader("spell_apothecary_throw_perfume") { }
class spell_apothecary_throw_perfume_SpellScript : public SpellScript
{
PrepareSpellScript(spell_apothecary_throw_perfume_SpellScript);
void HandleScript(SpellEffIndex /*effindex*/)
{
GetHitUnit()->CastSpell(GetHitUnit(), SPELL_PERFUME_SPILL, true);
}
void Register() override
{
OnEffectHitTarget += SpellEffectFn(spell_apothecary_throw_perfume_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_DUMMY);
}
};
SpellScript* GetSpellScript() const override
{
return new spell_apothecary_throw_perfume_SpellScript();
}
};
// 68798 - Concentrated Alluring Perfume Spill
class spell_apothecary_perfume_spill : public SpellScriptLoader
{
public:
spell_apothecary_perfume_spill() : SpellScriptLoader("spell_apothecary_perfume_spill") { }
class spell_apothecary_perfume_spill_AuraScript : public AuraScript
{
PrepareAuraScript(spell_apothecary_perfume_spill_AuraScript);
void OnPeriodic(AuraEffect const* /*aurEff*/)
{
GetTarget()->CastSpell(GetTarget(), SPELL_PERFUME_SPILL_DAMAGE, true);
}
void Register() override
{
OnEffectPeriodic += AuraEffectPeriodicFn(spell_apothecary_perfume_spill_AuraScript::OnPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY);
}
};
AuraScript* GetAuraScript() const override
{
return new spell_apothecary_perfume_spill_AuraScript();
}
};
// 68614 - Concentrated Irresistible Cologne Spill
class spell_apothecary_cologne_spill : public SpellScriptLoader
{
public:
spell_apothecary_cologne_spill() : SpellScriptLoader("spell_apothecary_cologne_spill") { }
class spell_apothecary_cologne_spill_AuraScript : public AuraScript
{
PrepareAuraScript(spell_apothecary_cologne_spill_AuraScript);
void OnPeriodic(AuraEffect const* /*aurEff*/)
{
GetTarget()->CastSpell(GetTarget(), SPELL_COLOGNE_SPILL_DAMAGE, true);
}
void Register() override
{
OnEffectPeriodic += AuraEffectPeriodicFn(spell_apothecary_cologne_spill_AuraScript::OnPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY);
}
};
AuraScript* GetAuraScript() const override
{
return new spell_apothecary_cologne_spill_AuraScript();
}
};
void AddSC_boss_apothecary_hummel()
{
new boss_apothecary_hummel();
new npc_apothecary_baxter();
new npc_apothecary_frye();
new spell_apothecary_lingering_fumes();
new spell_apothecary_validate_area();
new spell_apothecary_throw_cologne();
new spell_apothecary_throw_perfume();
new spell_apothecary_perfume_spill();
new spell_apothecary_cologne_spill();
}

View File

@@ -27,7 +27,8 @@ enum SKDataTypes
TYPE_RETHILGORE = 2,
TYPE_FENRUS = 3,
TYPE_NANDOS = 4,
BOSS_ARUGAL = 5
BOSS_ARUGAL = 5,
DATA_APOTHECARY_HUMMEL = 6
};
#endif

View File

@@ -132,6 +132,7 @@ void AddSC_boss_kirtonos_the_herald();
void AddSC_instance_scholomance();
void AddSC_shadowfang_keep(); //Shadowfang keep
void AddSC_instance_shadowfang_keep();
void AddSC_boss_apothecary_hummel();
void AddSC_boss_magistrate_barthilas(); //Stratholme
void AddSC_boss_maleki_the_pallid();
void AddSC_boss_nerubenkan();
@@ -318,6 +319,7 @@ void AddEasternKingdomsScripts()
AddSC_instance_scholomance();
AddSC_shadowfang_keep(); //Shadowfang keep
AddSC_instance_shadowfang_keep();
AddSC_boss_apothecary_hummel();
AddSC_boss_magistrate_barthilas(); //Stratholme
AddSC_boss_maleki_the_pallid();
AddSC_boss_nerubenkan();