EverWrath/src/server/scripts/World/npcs_special.cpp
Kargatum 4af4cbd3d9
feat(Core/Logging): rework logging (#4692)
* feat(Core/Logging): rework logging

* correct level for sql.sql

* del unused config options

* Correct build

* correct after merge

* whitespace

20:29:37 1. 'Player.cpp'. Replace (1)
20:29:37 2. 'ObjectMgr.cpp'. Replace (3)

* 1

* correct logging

* correct affter merge

* 1

* 2

* LOG_LEVEL_WARN

* #include "AppenderDB.h"

* 3

* 4

* 5

* 1. 'WorldSocket.cpp'. Replace (1)

* 6

* 1
2021-04-17 11:20:07 +02:00

2479 lines
88 KiB
C++

/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*/
/* ScriptData
SDName: Npcs_Special
SD%Complete: 100
SDComment: To be used for special NPCs that are located globally.
SDCategory: NPCs
EndScriptData
*/
/* ContentData
npc_air_force_bots 80% support for misc (invisible) guard bots in areas where player allowed to fly. Summon guards after a preset time if tagged by spell
npc_chicken_cluck 100% support for quest 3861 (Cluck!)
npc_dancing_flames 100% midsummer event NPC
npc_guardian 100% guardianAI used to prevent players from accessing off-limits areas. Not in use by SD2
npc_garments_of_quests 80% NPC's related to all Garments of-quests 5621, 5624, 5625, 5648, 565
npc_injured_patient 100% patients for triage-quests (6622 and 6624)
npc_doctor 100% Gustaf Vanhowzen and Gregory Victor, quest 6622 and 6624 (Triage)
npc_sayge 100% Darkmoon event fortune teller, buff player based on answers given
npc_locksmith 75% list of keys needs to be confirmed
npc_firework 100% NPC's summoned by rockets and rocket clusters, for making them cast visual
EndContentData */
#include "Cell.h"
#include "CellImpl.h"
#include "Chat.h"
#include "CombatAI.h"
#include "CreatureTextMgr.h"
#include "DBCStructure.h"
#include "GameEventMgr.h"
#include "GridNotifiers.h"
#include "GridNotifiersImpl.h"
#include "Group.h"
#include "ObjectMgr.h"
#include "PassiveAI.h"
#include "Pet.h"
#include "ScriptedCreature.h"
#include "ScriptedEscortAI.h"
#include "ScriptedGossip.h"
#include "ScriptMgr.h"
#include "SmartAI.h"
#include "SpellAuras.h"
#include "WaypointManager.h"
#include "World.h"
enum elderClearwater
{
EVENT_CLEARWATER_ANNOUNCE = 1,
CLEARWATER_SAY_PRE = 0,
CLEARWATER_SAY_START = 1,
CLEARWATER_SAY_WINNER = 2,
CLEARWATER_SAY_END = 3,
QUEST_FISHING_DERBY = 24803,
DATA_DERBY_FINISHED = 1,
};
class npc_elder_clearwater : public CreatureScript
{
public:
npc_elder_clearwater() : CreatureScript("npc_elder_clearwater") { }
struct npc_elder_clearwaterAI : public ScriptedAI
{
npc_elder_clearwaterAI(Creature* c) : ScriptedAI(c)
{
events.Reset();
events.ScheduleEvent(EVENT_CLEARWATER_ANNOUNCE, 1000, 1, 0);
finished = false;
preWarning = false;
startWarning = false;
finishWarning = false;
}
EventMap events;
bool finished;
bool preWarning;
bool startWarning;
bool finishWarning;
uint32 GetData(uint32 type) const override
{
if (type == DATA_DERBY_FINISHED)
return (uint32)finished;
return 0;
}
void DoAction(int32 param) override
{
if (param == DATA_DERBY_FINISHED)
finished = true;
}
void UpdateAI(uint32 diff) override
{
events.Update(diff);
switch (events.ExecuteEvent())
{
case EVENT_CLEARWATER_ANNOUNCE:
{
time_t curtime = time(nullptr);
tm strdate;
localtime_r(&curtime, &strdate);
if (!preWarning && strdate.tm_hour == 13 && strdate.tm_min == 55)
{
sCreatureTextMgr->SendChat(me, CLEARWATER_SAY_PRE, 0, CHAT_MSG_MONSTER_YELL, LANG_UNIVERSAL, TEXT_RANGE_MAP);
preWarning = true;
}
if (!startWarning && strdate.tm_hour == 14 && strdate.tm_min == 0)
{
sCreatureTextMgr->SendChat(me, CLEARWATER_SAY_START, 0, CHAT_MSG_MONSTER_YELL, LANG_UNIVERSAL, TEXT_RANGE_MAP);
startWarning = true;
}
if (!finishWarning && strdate.tm_hour == 15 && strdate.tm_min == 0)
{
sCreatureTextMgr->SendChat(me, CLEARWATER_SAY_END, 0, CHAT_MSG_MONSTER_YELL, LANG_UNIVERSAL, TEXT_RANGE_MAP);
finishWarning = true;
// no one won - despawn
if (!finished)
{
me->DespawnOrUnsummon();
break;
}
}
events.RepeatEvent(1000);
break;
}
}
}
};
bool OnGossipHello(Player* player, Creature* creature) override
{
QuestRelationBounds pObjectQR;
QuestRelationBounds pObjectQIR;
// pets also can have quests
if (creature)
{
pObjectQR = sObjectMgr->GetCreatureQuestRelationBounds(creature->GetEntry());
pObjectQIR = sObjectMgr->GetCreatureQuestInvolvedRelationBounds(creature->GetEntry());
}
else
return true;
QuestMenu& qm = player->PlayerTalkClass->GetQuestMenu();
qm.ClearMenu();
for (QuestRelations::const_iterator i = pObjectQIR.first; i != pObjectQIR.second; ++i)
{
uint32 quest_id = i->second;
Quest const* quest = sObjectMgr->GetQuestTemplate(quest_id);
if (!quest)
continue;
if (!creature->AI()->GetData(DATA_DERBY_FINISHED))
{
if (quest_id == QUEST_FISHING_DERBY)
player->PlayerTalkClass->SendQuestGiverRequestItems(quest, creature->GetGUID(), player->CanRewardQuest(quest, false), true);
}
else
{
if (quest_id != QUEST_FISHING_DERBY)
player->PlayerTalkClass->SendQuestGiverRequestItems(quest, creature->GetGUID(), player->CanRewardQuest(quest, false), true);
}
}
return true;
}
bool OnQuestReward(Player* player, Creature* creature, Quest const* quest, uint32 /*opt*/) override
{
if (!creature->AI()->GetData(DATA_DERBY_FINISHED) && quest->GetQuestId() == QUEST_FISHING_DERBY)
{
creature->AI()->DoAction(DATA_DERBY_FINISHED);
sCreatureTextMgr->SendChat(creature, CLEARWATER_SAY_WINNER, player, CHAT_MSG_MONSTER_YELL, LANG_UNIVERSAL, TEXT_RANGE_MAP);
}
return true;
}
CreatureAI* GetAI(Creature* pCreature) const override
{
return new npc_elder_clearwaterAI (pCreature);
}
};
enum riggleBassbait
{
EVENT_RIGGLE_ANNOUNCE = 1,
RIGGLE_SAY_START = 0,
RIGGLE_SAY_WINNER = 1,
RIGGLE_SAY_END = 2,
QUEST_MASTER_ANGLER = 8193,
DATA_ANGLER_FINISHED = 1,
};
class npc_riggle_bassbait : public CreatureScript
{
public:
npc_riggle_bassbait() : CreatureScript("npc_riggle_bassbait") { }
struct npc_riggle_bassbaitAI : public ScriptedAI
{
npc_riggle_bassbaitAI(Creature* c) : ScriptedAI(c)
{
events.Reset();
events.ScheduleEvent(EVENT_RIGGLE_ANNOUNCE, 1000, 1, 0);
finished = false;
startWarning = false;
finishWarning = false;
}
EventMap events;
bool finished;
bool startWarning;
bool finishWarning;
uint32 GetData(uint32 type) const override
{
if (type == DATA_ANGLER_FINISHED)
return (uint32)finished;
return 0;
}
void DoAction(int32 param) override
{
if (param == DATA_ANGLER_FINISHED)
finished = true;
}
void UpdateAI(uint32 diff) override
{
events.Update(diff);
switch (events.ExecuteEvent())
{
case EVENT_RIGGLE_ANNOUNCE:
{
time_t curtime = time(nullptr);
tm strdate;
localtime_r(&curtime, &strdate);
if (!startWarning && strdate.tm_hour == 14 && strdate.tm_min == 0)
{
sCreatureTextMgr->SendChat(me, RIGGLE_SAY_START, 0, CHAT_MSG_MONSTER_YELL, LANG_UNIVERSAL, TEXT_RANGE_ZONE);
startWarning = true;
}
if (!finishWarning && strdate.tm_hour == 16 && strdate.tm_min == 0)
{
sCreatureTextMgr->SendChat(me, RIGGLE_SAY_END, 0, CHAT_MSG_MONSTER_YELL, LANG_UNIVERSAL, TEXT_RANGE_ZONE);
finishWarning = true;
// no one won - despawn
if (!finished)
{
me->DespawnOrUnsummon();
break;
}
}
events.RepeatEvent(1000);
break;
}
}
}
};
bool OnGossipHello(Player* player, Creature* creature) override
{
if (!creature->AI()->GetData(DATA_ANGLER_FINISHED))
player->PrepareQuestMenu(creature->GetGUID());
SendGossipMenuFor(player, player->GetGossipTextId(creature), creature->GetGUID());
return true;
}
bool OnQuestReward(Player* player, Creature* creature, Quest const* quest, uint32 /*opt*/) override
{
if (!creature->AI()->GetData(DATA_ANGLER_FINISHED) && quest->GetQuestId() == QUEST_MASTER_ANGLER)
{
creature->AI()->DoAction(DATA_ANGLER_FINISHED);
sCreatureTextMgr->SendChat(creature, RIGGLE_SAY_WINNER, player, CHAT_MSG_MONSTER_YELL, LANG_UNIVERSAL, TEXT_RANGE_ZONE);
}
return true;
}
CreatureAI* GetAI(Creature* pCreature) const override
{
return new npc_riggle_bassbaitAI (pCreature);
}
};
enum eTrainingDummy
{
SPELL_STUN_PERMANENT = 61204
};
class npc_training_dummy : public CreatureScript
{
public:
npc_training_dummy() : CreatureScript("npc_training_dummy") { }
struct npc_training_dummyAI : ScriptedAI
{
npc_training_dummyAI(Creature* creature) : ScriptedAI(creature)
{
SetCombatMovement(false);
me->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_KNOCK_BACK, true); //imune to knock aways like blast wave
}
uint32 resetTimer;
void Reset() override
{
me->CastSpell(me, SPELL_STUN_PERMANENT, true);
resetTimer = 5000;
}
void EnterEvadeMode() override
{
if (!_EnterEvadeMode())
return;
Reset();
}
void DamageTaken(Unit*, uint32& damage, DamageEffectType, SpellSchoolMask) override
{
resetTimer = 5000;
damage = 0;
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
if (resetTimer <= diff)
{
EnterEvadeMode();
resetTimer = 5000;
}
else
resetTimer -= diff;
}
void MoveInLineOfSight(Unit* /*who*/) override { }
};
CreatureAI* GetAI(Creature* creature) const override
{
return new npc_training_dummyAI(creature);
}
};
class npc_target_dummy : public CreatureScript
{
public:
npc_target_dummy() : CreatureScript("npc_target_dummy") { }
struct npc_target_dummyAI : ScriptedAI
{
npc_target_dummyAI(Creature* creature) : ScriptedAI(creature)
{
SetCombatMovement(false);
deathTimer = 15000;
me->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_KNOCK_BACK, true); //imune to knock aways like blast wave
}
uint32 deathTimer;
void Reset() override
{
me->SetControlled(true, UNIT_STATE_STUNNED); //disable rotate
me->SetLootRecipient(me->GetOwner());
me->SelectLevel();
}
void EnterEvadeMode() override
{
if (!_EnterEvadeMode())
return;
Reset();
}
void UpdateAI(uint32 diff) override
{
if (!me->HasUnitState(UNIT_STATE_STUNNED))
me->SetControlled(true, UNIT_STATE_STUNNED);//disable rotate
if (deathTimer <= diff)
{
me->SetLootRecipient(me->GetOwner());
me->LowerPlayerDamageReq(me->GetMaxHealth());
Unit::Kill(me, me);
deathTimer = 600000;
}
else
deathTimer -= diff;
}
void MoveInLineOfSight(Unit* /*who*/) override { }
};
CreatureAI* GetAI(Creature* creature) const override
{
return new npc_target_dummyAI(creature);
}
};
// Theirs
/*########
# npc_air_force_bots
#########*/
enum SpawnType
{
SPAWNTYPE_TRIPWIRE_ROOFTOP, // no warning, summon Creature at smaller range
SPAWNTYPE_ALARMBOT, // cast guards mark and summon npc - if player shows up with that buff duration < 5 seconds attack
};
struct SpawnAssociation
{
uint32 thisCreatureEntry;
uint32 spawnedCreatureEntry;
SpawnType spawnType;
};
enum AirFoceBots
{
SPELL_GUARDS_MARK = 38067,
AURA_DURATION_TIME_LEFT = 5000
};
float const RANGE_TRIPWIRE = 15.0f;
float const RANGE_GUARDS_MARK = 50.0f;
SpawnAssociation spawnAssociations[] =
{
{2614, 15241, SPAWNTYPE_ALARMBOT}, //Air Force Alarm Bot (Alliance)
{2615, 15242, SPAWNTYPE_ALARMBOT}, //Air Force Alarm Bot (Horde)
{21974, 21976, SPAWNTYPE_ALARMBOT}, //Air Force Alarm Bot (Area 52)
{21993, 15242, SPAWNTYPE_ALARMBOT}, //Air Force Guard Post (Horde - Bat Rider)
{21996, 15241, SPAWNTYPE_ALARMBOT}, //Air Force Guard Post (Alliance - Gryphon)
{21997, 21976, SPAWNTYPE_ALARMBOT}, //Air Force Guard Post (Goblin - Area 52 - Zeppelin)
{21999, 15241, SPAWNTYPE_TRIPWIRE_ROOFTOP}, //Air Force Trip Wire - Rooftop (Alliance)
{22001, 15242, SPAWNTYPE_TRIPWIRE_ROOFTOP}, //Air Force Trip Wire - Rooftop (Horde)
{22002, 15242, SPAWNTYPE_TRIPWIRE_ROOFTOP}, //Air Force Trip Wire - Ground (Horde)
{22003, 15241, SPAWNTYPE_TRIPWIRE_ROOFTOP}, //Air Force Trip Wire - Ground (Alliance)
{22063, 21976, SPAWNTYPE_TRIPWIRE_ROOFTOP}, //Air Force Trip Wire - Rooftop (Goblin - Area 52)
{22065, 22064, SPAWNTYPE_ALARMBOT}, //Air Force Guard Post (Ethereal - Stormspire)
{22066, 22067, SPAWNTYPE_ALARMBOT}, //Air Force Guard Post (Scryer - Dragonhawk)
{22068, 22064, SPAWNTYPE_TRIPWIRE_ROOFTOP}, //Air Force Trip Wire - Rooftop (Ethereal - Stormspire)
{22069, 22064, SPAWNTYPE_ALARMBOT}, //Air Force Alarm Bot (Stormspire)
{22070, 22067, SPAWNTYPE_TRIPWIRE_ROOFTOP}, //Air Force Trip Wire - Rooftop (Scryer)
{22071, 22067, SPAWNTYPE_ALARMBOT}, //Air Force Alarm Bot (Scryer)
{22078, 22077, SPAWNTYPE_ALARMBOT}, //Air Force Alarm Bot (Aldor)
{22079, 22077, SPAWNTYPE_ALARMBOT}, //Air Force Guard Post (Aldor - Gryphon)
{22080, 22077, SPAWNTYPE_TRIPWIRE_ROOFTOP}, //Air Force Trip Wire - Rooftop (Aldor)
{22086, 22085, SPAWNTYPE_ALARMBOT}, //Air Force Alarm Bot (Sporeggar)
{22087, 22085, SPAWNTYPE_ALARMBOT}, //Air Force Guard Post (Sporeggar - Spore Bat)
{22088, 22085, SPAWNTYPE_TRIPWIRE_ROOFTOP}, //Air Force Trip Wire - Rooftop (Sporeggar)
{22090, 22089, SPAWNTYPE_ALARMBOT}, //Air Force Guard Post (Toshley's Station - Flying Machine)
{22124, 22122, SPAWNTYPE_ALARMBOT}, //Air Force Alarm Bot (Cenarion)
{22125, 22122, SPAWNTYPE_ALARMBOT}, //Air Force Guard Post (Cenarion - Stormcrow)
{22126, 22122, SPAWNTYPE_ALARMBOT} //Air Force Trip Wire - Rooftop (Cenarion Expedition)
};
class npc_air_force_bots : public CreatureScript
{
public:
npc_air_force_bots() : CreatureScript("npc_air_force_bots") { }
struct npc_air_force_botsAI : public ScriptedAI
{
npc_air_force_botsAI(Creature* creature) : ScriptedAI(creature)
{
SpawnAssoc = nullptr;
SpawnedGUID = 0;
// find the correct spawnhandling
static uint32 entryCount = sizeof(spawnAssociations) / sizeof(SpawnAssociation);
for (uint8 i = 0; i < entryCount; ++i)
{
if (spawnAssociations[i].thisCreatureEntry == creature->GetEntry())
{
SpawnAssoc = &spawnAssociations[i];
break;
}
}
if (!SpawnAssoc)
LOG_ERROR("sql.sql", "TCSR: Creature template entry %u has ScriptName npc_air_force_bots, but it's not handled by that script", creature->GetEntry());
else
{
CreatureTemplate const* spawnedTemplate = sObjectMgr->GetCreatureTemplate(SpawnAssoc->spawnedCreatureEntry);
if (!spawnedTemplate)
{
LOG_ERROR("sql.sql", "TCSR: Creature template entry %u does not exist in DB, which is required by npc_air_force_bots", SpawnAssoc->spawnedCreatureEntry);
SpawnAssoc = nullptr;
return;
}
}
}
SpawnAssociation* SpawnAssoc;
uint64 SpawnedGUID;
void Reset() override {}
Creature* SummonGuard()
{
Creature* summoned = me->SummonCreature(SpawnAssoc->spawnedCreatureEntry, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 300000);
if (summoned)
SpawnedGUID = summoned->GetGUID();
else
{
LOG_ERROR("sql.sql", "TCSR: npc_air_force_bots: wasn't able to spawn Creature %u", SpawnAssoc->spawnedCreatureEntry);
SpawnAssoc = nullptr;
}
return summoned;
}
Creature* GetSummonedGuard()
{
Creature* creature = ObjectAccessor::GetCreature(*me, SpawnedGUID);
if (creature && creature->IsAlive())
return creature;
return nullptr;
}
void MoveInLineOfSight(Unit* who) override
{
if (!SpawnAssoc)
return;
if (me->IsValidAttackTarget(who))
{
Player* playerTarget = who->ToPlayer();
// airforce guards only spawn for players
if (!playerTarget)
return;
Creature* lastSpawnedGuard = SpawnedGUID == 0 ? nullptr : GetSummonedGuard();
// prevent calling ObjectAccessor::GetUnit at next MoveInLineOfSight call - speedup
if (!lastSpawnedGuard)
SpawnedGUID = 0;
switch (SpawnAssoc->spawnType)
{
case SPAWNTYPE_ALARMBOT:
{
if (!who->IsWithinDistInMap(me, RANGE_GUARDS_MARK))
return;
Aura* markAura = who->GetAura(SPELL_GUARDS_MARK);
if (markAura)
{
// the target wasn't able to move out of our range within 25 seconds
if (!lastSpawnedGuard)
{
lastSpawnedGuard = SummonGuard();
if (!lastSpawnedGuard)
return;
}
if (markAura->GetDuration() < AURA_DURATION_TIME_LEFT)
if (!lastSpawnedGuard->GetVictim())
lastSpawnedGuard->AI()->AttackStart(who);
}
else
{
if (!lastSpawnedGuard)
lastSpawnedGuard = SummonGuard();
if (!lastSpawnedGuard)
return;
lastSpawnedGuard->CastSpell(who, SPELL_GUARDS_MARK, true);
}
break;
}
case SPAWNTYPE_TRIPWIRE_ROOFTOP:
{
if (!who->IsWithinDistInMap(me, RANGE_TRIPWIRE))
return;
if (!lastSpawnedGuard)
lastSpawnedGuard = SummonGuard();
if (!lastSpawnedGuard)
return;
// ROOFTOP only triggers if the player is on the ground
if (!playerTarget->IsFlying() && !lastSpawnedGuard->GetVictim())
lastSpawnedGuard->AI()->AttackStart(who);
break;
}
}
}
}
};
CreatureAI* GetAI(Creature* creature) const override
{
return new npc_air_force_botsAI(creature);
}
};
/*########
# npc_chicken_cluck
#########*/
enum ChickenCluck
{
EMOTE_HELLO = 0,
EMOTE_CLUCK_TEXT = 2,
QUEST_CLUCK = 3861,
FACTION_FRIENDLY = 35,
FACTION_CHICKEN = 31
};
class npc_chicken_cluck : public CreatureScript
{
public:
npc_chicken_cluck() : CreatureScript("npc_chicken_cluck") { }
struct npc_chicken_cluckAI : public ScriptedAI
{
npc_chicken_cluckAI(Creature* creature) : ScriptedAI(creature) { }
uint32 ResetFlagTimer;
void Reset() override
{
ResetFlagTimer = 120000;
me->setFaction(FACTION_CHICKEN);
me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER);
}
void EnterCombat(Unit* /*who*/) override { }
void UpdateAI(uint32 diff) override
{
// Reset flags after a certain time has passed so that the next player has to start the 'event' again
if (me->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER))
{
if (ResetFlagTimer <= diff)
{
EnterEvadeMode();
return;
}
else
ResetFlagTimer -= diff;
}
if (UpdateVictim())
DoMeleeAttackIfReady();
}
void ReceiveEmote(Player* player, uint32 emote) override
{
switch (emote)
{
case TEXT_EMOTE_CHICKEN:
if (player->GetQuestStatus(QUEST_CLUCK) == QUEST_STATUS_NONE && rand() % 30 == 1)
{
me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER);
me->setFaction(FACTION_FRIENDLY);
Talk(EMOTE_HELLO);
}
break;
case TEXT_EMOTE_CHEER:
if (player->GetQuestStatus(QUEST_CLUCK) == QUEST_STATUS_COMPLETE)
{
me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER);
me->setFaction(FACTION_FRIENDLY);
Talk(EMOTE_CLUCK_TEXT);
}
break;
}
}
};
CreatureAI* GetAI(Creature* creature) const override
{
return new npc_chicken_cluckAI(creature);
}
bool OnQuestAccept(Player* /*player*/, Creature* creature, Quest const* quest) override
{
if (quest->GetQuestId() == QUEST_CLUCK)
CAST_AI(npc_chicken_cluck::npc_chicken_cluckAI, creature->AI())->Reset();
return true;
}
bool OnQuestComplete(Player* /*player*/, Creature* creature, Quest const* quest) override
{
if (quest->GetQuestId() == QUEST_CLUCK)
CAST_AI(npc_chicken_cluck::npc_chicken_cluckAI, creature->AI())->Reset();
return true;
}
};
/*######
## npc_dancing_flames
######*/
enum DancingFlames
{
SPELL_BRAZIER = 45423,
SPELL_SEDUCTION = 47057,
SPELL_FIERY_AURA = 45427
};
class npc_dancing_flames : public CreatureScript
{
public:
npc_dancing_flames() : CreatureScript("npc_dancing_flames") { }
struct npc_dancing_flamesAI : public ScriptedAI
{
npc_dancing_flamesAI(Creature* creature) : ScriptedAI(creature) { }
bool Active;
uint32 CanIteract;
void Reset() override
{
Active = true;
CanIteract = 3500;
DoCast(me, SPELL_BRAZIER, true);
DoCast(me, SPELL_FIERY_AURA, false);
me->UpdateHeight(me->GetPositionZ() + 0.94f);
me->SetDisableGravity(true);
me->HandleEmoteCommand(EMOTE_ONESHOT_DANCE);
me->SendMovementFlagUpdate();
}
void UpdateAI(uint32 diff) override
{
if (!Active)
{
if (CanIteract <= diff)
{
Active = true;
CanIteract = 3500;
me->HandleEmoteCommand(EMOTE_ONESHOT_DANCE);
}
else
CanIteract -= diff;
}
}
void EnterCombat(Unit* /*who*/) override { }
void ReceiveEmote(Player* player, uint32 emote) override
{
if (me->IsWithinLOS(player->GetPositionX(), player->GetPositionY(), player->GetPositionZ()) && me->IsWithinDistInMap(player, 30.0f))
{
me->SetFacingToObject(player);
Active = false;
switch (emote)
{
case TEXT_EMOTE_KISS:
me->HandleEmoteCommand(EMOTE_ONESHOT_SHY);
break;
case TEXT_EMOTE_WAVE:
me->HandleEmoteCommand(EMOTE_ONESHOT_WAVE);
break;
case TEXT_EMOTE_BOW:
me->HandleEmoteCommand(EMOTE_ONESHOT_BOW);
break;
case TEXT_EMOTE_JOKE:
me->HandleEmoteCommand(EMOTE_ONESHOT_LAUGH);
break;
case TEXT_EMOTE_DANCE:
if (!player->HasAura(SPELL_SEDUCTION))
DoCast(player, SPELL_SEDUCTION, true);
break;
}
}
}
};
CreatureAI* GetAI(Creature* creature) const override
{
return new npc_dancing_flamesAI(creature);
}
};
/*######
## Triage quest
######*/
enum Doctor
{
SAY_DOC = 0,
DOCTOR_ALLIANCE = 12939,
DOCTOR_HORDE = 12920,
ALLIANCE_COORDS = 7,
HORDE_COORDS = 6
};
struct Location
{
float x, y, z, o;
};
static Location AllianceCoords[] =
{
{-3757.38f, -4533.05f, 14.16f, 3.62f}, // Top-far-right bunk as seen from entrance
{-3754.36f, -4539.13f, 14.16f, 5.13f}, // Top-far-left bunk
{-3749.54f, -4540.25f, 14.28f, 3.34f}, // Far-right bunk
{-3742.10f, -4536.85f, 14.28f, 3.64f}, // Right bunk near entrance
{-3755.89f, -4529.07f, 14.05f, 0.57f}, // Far-left bunk
{-3749.51f, -4527.08f, 14.07f, 5.26f}, // Mid-left bunk
{-3746.37f, -4525.35f, 14.16f, 5.22f}, // Left bunk near entrance
};
//alliance run to where
#define A_RUNTOX -3742.96f
#define A_RUNTOY -4531.52f
#define A_RUNTOZ 11.91f
static Location HordeCoords[] =
{
{-1013.75f, -3492.59f, 62.62f, 4.34f}, // Left, Behind
{-1017.72f, -3490.92f, 62.62f, 4.34f}, // Right, Behind
{-1015.77f, -3497.15f, 62.82f, 4.34f}, // Left, Mid
{-1019.51f, -3495.49f, 62.82f, 4.34f}, // Right, Mid
{-1017.25f, -3500.85f, 62.98f, 4.34f}, // Left, front
{-1020.95f, -3499.21f, 62.98f, 4.34f} // Right, Front
};
//horde run to where
#define H_RUNTOX -1016.44f
#define H_RUNTOY -3508.48f
#define H_RUNTOZ 62.96f
uint32 const AllianceSoldierId[3] =
{
12938, // 12938 Injured Alliance Soldier
12936, // 12936 Badly injured Alliance Soldier
12937 // 12937 Critically injured Alliance Soldier
};
uint32 const HordeSoldierId[3] =
{
12923, //12923 Injured Soldier
12924, //12924 Badly injured Soldier
12925 //12925 Critically injured Soldier
};
/*######
## npc_doctor (handles both Gustaf Vanhowzen and Gregory Victor)
######*/
class npc_doctor : public CreatureScript
{
public:
npc_doctor() : CreatureScript("npc_doctor") { }
struct npc_doctorAI : public ScriptedAI
{
npc_doctorAI(Creature* creature) : ScriptedAI(creature) { }
uint64 PlayerGUID;
uint32 SummonPatientTimer;
uint32 SummonPatientCount;
uint32 PatientDiedCount;
uint32 PatientSavedCount;
bool Event;
std::list<uint64> Patients;
std::vector<Location*> Coordinates;
void Reset() override
{
PlayerGUID = 0;
SummonPatientTimer = 10000;
SummonPatientCount = 0;
PatientDiedCount = 0;
PatientSavedCount = 0;
Patients.clear();
Coordinates.clear();
Event = false;
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
}
void BeginEvent(Player* player)
{
PlayerGUID = player->GetGUID();
SummonPatientTimer = 10000;
SummonPatientCount = 0;
PatientDiedCount = 0;
PatientSavedCount = 0;
switch (me->GetEntry())
{
case DOCTOR_ALLIANCE:
for (uint8 i = 0; i < ALLIANCE_COORDS; ++i)
Coordinates.push_back(&AllianceCoords[i]);
break;
case DOCTOR_HORDE:
for (uint8 i = 0; i < HORDE_COORDS; ++i)
Coordinates.push_back(&HordeCoords[i]);
break;
}
Event = true;
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
}
void PatientDied(Location* point)
{
Player* player = ObjectAccessor::GetPlayer(*me, PlayerGUID);
if (player && ((player->GetQuestStatus(6624) == QUEST_STATUS_INCOMPLETE) || (player->GetQuestStatus(6622) == QUEST_STATUS_INCOMPLETE)))
{
++PatientDiedCount;
if (PatientDiedCount > 5 && Event)
{
if (player->GetQuestStatus(6624) == QUEST_STATUS_INCOMPLETE)
player->FailQuest(6624);
else if (player->GetQuestStatus(6622) == QUEST_STATUS_INCOMPLETE)
player->FailQuest(6622);
Reset();
return;
}
Coordinates.push_back(point);
}
else
// If no player or player abandon quest in progress
Reset();
}
void PatientSaved(Creature* /*soldier*/, Player* player, Location* point)
{
if (player && PlayerGUID == player->GetGUID())
{
if ((player->GetQuestStatus(6624) == QUEST_STATUS_INCOMPLETE) || (player->GetQuestStatus(6622) == QUEST_STATUS_INCOMPLETE))
{
++PatientSavedCount;
if (PatientSavedCount == 15)
{
if (!Patients.empty())
{
std::list<uint64>::const_iterator itr;
for (itr = Patients.begin(); itr != Patients.end(); ++itr)
{
if (Creature* patient = ObjectAccessor::GetCreature((*me), *itr))
patient->setDeathState(JUST_DIED);
}
}
if (player->GetQuestStatus(6624) == QUEST_STATUS_INCOMPLETE)
player->AreaExploredOrEventHappens(6624);
else if (player->GetQuestStatus(6622) == QUEST_STATUS_INCOMPLETE)
player->AreaExploredOrEventHappens(6622);
Reset();
return;
}
Coordinates.push_back(point);
}
}
}
void UpdateAI(uint32 diff) override;
void EnterCombat(Unit* /*who*/) override { }
};
bool OnQuestAccept(Player* player, Creature* creature, Quest const* quest) override
{
if ((quest->GetQuestId() == 6624) || (quest->GetQuestId() == 6622))
CAST_AI(npc_doctor::npc_doctorAI, creature->AI())->BeginEvent(player);
return true;
}
CreatureAI* GetAI(Creature* creature) const override
{
return new npc_doctorAI(creature);
}
};
/*#####
## npc_injured_patient (handles all the patients, no matter Horde or Alliance)
#####*/
class npc_injured_patient : public CreatureScript
{
public:
npc_injured_patient() : CreatureScript("npc_injured_patient") { }
struct npc_injured_patientAI : public ScriptedAI
{
npc_injured_patientAI(Creature* creature) : ScriptedAI(creature) { }
uint64 DoctorGUID;
Location* Coord;
void Reset() override
{
DoctorGUID = 0;
Coord = nullptr;
//no select
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
//no regen health
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IN_COMBAT);
//to make them lay with face down
me->SetUInt32Value(UNIT_FIELD_BYTES_1, UNIT_STAND_STATE_DEAD);
uint32 mobId = me->GetEntry();
switch (mobId)
{
//lower max health
case 12923:
case 12938: //Injured Soldier
me->SetHealth(me->CountPctFromMaxHealth(75));
break;
case 12924:
case 12936: //Badly injured Soldier
me->SetHealth(me->CountPctFromMaxHealth(50));
break;
case 12925:
case 12937: //Critically injured Soldier
me->SetHealth(me->CountPctFromMaxHealth(25));
break;
}
}
void EnterCombat(Unit* /*who*/) override { }
void SpellHit(Unit* caster, SpellInfo const* spell) override
{
Player* player = caster->ToPlayer();
if (!player || !me->IsAlive() || spell->Id != 20804)
return;
if (player->GetQuestStatus(6624) == QUEST_STATUS_INCOMPLETE || player->GetQuestStatus(6622) == QUEST_STATUS_INCOMPLETE)
if (DoctorGUID)
if (Creature* doctor = ObjectAccessor::GetCreature(*me, DoctorGUID))
CAST_AI(npc_doctor::npc_doctorAI, doctor->AI())->PatientSaved(me, player, Coord);
//make not selectable
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
//regen health
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IN_COMBAT);
//stand up
me->SetUInt32Value(UNIT_FIELD_BYTES_1, UNIT_STAND_STATE_STAND);
Talk(SAY_DOC);
uint32 mobId = me->GetEntry();
me->SetWalk(false);
switch (mobId)
{
case 12923:
case 12924:
case 12925:
me->GetMotionMaster()->MovePoint(0, H_RUNTOX, H_RUNTOY, H_RUNTOZ);
break;
case 12936:
case 12937:
case 12938:
me->GetMotionMaster()->MovePoint(0, A_RUNTOX, A_RUNTOY, A_RUNTOZ);
break;
}
}
void UpdateAI(uint32 /*diff*/) override
{
//lower HP on every world tick makes it a useful counter, not officlone though
if (me->IsAlive() && me->GetHealth() > 6)
me->ModifyHealth(-5);
if (me->IsAlive() && me->GetHealth() <= 6)
{
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IN_COMBAT);
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
me->setDeathState(JUST_DIED);
me->SetFlag(UNIT_DYNAMIC_FLAGS, 32);
if (DoctorGUID)
if (Creature* doctor = ObjectAccessor::GetCreature((*me), DoctorGUID))
CAST_AI(npc_doctor::npc_doctorAI, doctor->AI())->PatientDied(Coord);
}
}
};
CreatureAI* GetAI(Creature* creature) const override
{
return new npc_injured_patientAI(creature);
}
};
void npc_doctor::npc_doctorAI::UpdateAI(uint32 diff)
{
if (Event && SummonPatientCount >= 20)
{
Reset();
return;
}
if (Event)
{
if (SummonPatientTimer <= diff)
{
if (Coordinates.empty())
return;
std::vector<Location*>::iterator itr = Coordinates.begin() + rand() % Coordinates.size();
uint32 patientEntry = 0;
switch (me->GetEntry())
{
case DOCTOR_ALLIANCE:
patientEntry = AllianceSoldierId[rand() % 3];
break;
case DOCTOR_HORDE:
patientEntry = HordeSoldierId[rand() % 3];
break;
default:
LOG_ERROR("server", "TSCR: Invalid entry for Triage doctor. Please check your database");
return;
}
if (Location* point = *itr)
{
if (Creature* Patient = me->SummonCreature(patientEntry, point->x, point->y, point->z, point->o, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 5000))
{
//303, this flag appear to be required for client side item->spell to work (TARGET_SINGLE_FRIEND)
Patient->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED);
Patients.push_back(Patient->GetGUID());
CAST_AI(npc_injured_patient::npc_injured_patientAI, Patient->AI())->DoctorGUID = me->GetGUID();
CAST_AI(npc_injured_patient::npc_injured_patientAI, Patient->AI())->Coord = point;
Coordinates.erase(itr);
}
}
SummonPatientTimer = 10000;
++SummonPatientCount;
}
else
SummonPatientTimer -= diff;
}
}
/*######
## npc_garments_of_quests
######*/
/// @todo get text for each NPC
enum Garments
{
SPELL_LESSER_HEAL_R2 = 2052,
SPELL_FORTITUDE_R1 = 1243,
QUEST_MOON = 5621,
QUEST_LIGHT_1 = 5624,
QUEST_LIGHT_2 = 5625,
QUEST_SPIRIT = 5648,
QUEST_DARKNESS = 5650,
ENTRY_SHAYA = 12429,
ENTRY_ROBERTS = 12423,
ENTRY_DOLF = 12427,
ENTRY_KORJA = 12430,
ENTRY_DG_KEL = 12428,
// used by 12429, 12423, 12427, 12430, 12428, but signed for 12429
SAY_THANKS = 0,
SAY_GOODBYE = 1,
SAY_HEALED = 2,
};
class npc_garments_of_quests : public CreatureScript
{
public:
npc_garments_of_quests() : CreatureScript("npc_garments_of_quests") { }
struct npc_garments_of_questsAI : public npc_escortAI
{
npc_garments_of_questsAI(Creature* creature) : npc_escortAI(creature)
{
Reset();
}
uint64 CasterGUID;
bool IsHealed;
bool CanRun;
uint32 RunAwayTimer;
void Reset() override
{
CasterGUID = 0;
IsHealed = false;
CanRun = false;
RunAwayTimer = 5000;
me->SetPvP(true);
me->SetStandState(UNIT_STAND_STATE_KNEEL);
// expect database to have RegenHealth=0
me->SetHealth(me->CountPctFromMaxHealth(70));
}
void EnterCombat(Unit* /*who*/) override { }
void SpellHit(Unit* caster, SpellInfo const* spell) override
{
if (spell->Id == SPELL_LESSER_HEAL_R2 || spell->Id == SPELL_FORTITUDE_R1)
{
//not while in combat
if (me->IsInCombat())
return;
//nothing to be done now
if (IsHealed && CanRun)
return;
if (Player* player = caster->ToPlayer())
{
switch (me->GetEntry())
{
case ENTRY_SHAYA:
if (player->GetQuestStatus(QUEST_MOON) == QUEST_STATUS_INCOMPLETE)
{
if (IsHealed && !CanRun && spell->Id == SPELL_FORTITUDE_R1)
{
Talk(SAY_THANKS, caster);
CanRun = true;
}
else if (!IsHealed && spell->Id == SPELL_LESSER_HEAL_R2)
{
CasterGUID = caster->GetGUID();
me->SetStandState(UNIT_STAND_STATE_STAND);
Talk(SAY_HEALED, caster);
IsHealed = true;
}
}
break;
case ENTRY_ROBERTS:
if (player->GetQuestStatus(QUEST_LIGHT_1) == QUEST_STATUS_INCOMPLETE)
{
if (IsHealed && !CanRun && spell->Id == SPELL_FORTITUDE_R1)
{
Talk(SAY_THANKS, caster);
CanRun = true;
}
else if (!IsHealed && spell->Id == SPELL_LESSER_HEAL_R2)
{
CasterGUID = caster->GetGUID();
me->SetStandState(UNIT_STAND_STATE_STAND);
Talk(SAY_HEALED, caster);
IsHealed = true;
}
}
break;
case ENTRY_DOLF:
if (player->GetQuestStatus(QUEST_LIGHT_2) == QUEST_STATUS_INCOMPLETE)
{
if (IsHealed && !CanRun && spell->Id == SPELL_FORTITUDE_R1)
{
Talk(SAY_THANKS, caster);
CanRun = true;
}
else if (!IsHealed && spell->Id == SPELL_LESSER_HEAL_R2)
{
CasterGUID = caster->GetGUID();
me->SetStandState(UNIT_STAND_STATE_STAND);
Talk(SAY_HEALED, caster);
IsHealed = true;
}
}
break;
case ENTRY_KORJA:
if (player->GetQuestStatus(QUEST_SPIRIT) == QUEST_STATUS_INCOMPLETE)
{
if (IsHealed && !CanRun && spell->Id == SPELL_FORTITUDE_R1)
{
Talk(SAY_THANKS, caster);
CanRun = true;
}
else if (!IsHealed && spell->Id == SPELL_LESSER_HEAL_R2)
{
CasterGUID = caster->GetGUID();
me->SetStandState(UNIT_STAND_STATE_STAND);
Talk(SAY_HEALED, caster);
IsHealed = true;
}
}
break;
case ENTRY_DG_KEL:
if (player->GetQuestStatus(QUEST_DARKNESS) == QUEST_STATUS_INCOMPLETE)
{
if (IsHealed && !CanRun && spell->Id == SPELL_FORTITUDE_R1)
{
Talk(SAY_THANKS, caster);
CanRun = true;
}
else if (!IsHealed && spell->Id == SPELL_LESSER_HEAL_R2)
{
CasterGUID = caster->GetGUID();
me->SetStandState(UNIT_STAND_STATE_STAND);
Talk(SAY_HEALED, caster);
IsHealed = true;
}
}
break;
}
// give quest credit, not expect any special quest objectives
if (CanRun)
player->TalkedToCreature(me->GetEntry(), me->GetGUID());
}
}
}
void WaypointReached(uint32 /*waypointId*/) override
{
}
void UpdateAI(uint32 diff) override
{
if (CanRun && !me->IsInCombat())
{
if (RunAwayTimer <= diff)
{
if (Unit* unit = ObjectAccessor::GetUnit(*me, CasterGUID))
{
switch (me->GetEntry())
{
case ENTRY_SHAYA:
case ENTRY_ROBERTS:
case ENTRY_DOLF:
case ENTRY_KORJA:
case ENTRY_DG_KEL:
Talk(SAY_GOODBYE, unit);
break;
}
Start(false, true, true);
}
else
EnterEvadeMode(); //something went wrong
RunAwayTimer = 30000;
}
else
RunAwayTimer -= diff;
}
npc_escortAI::UpdateAI(diff);
}
};
CreatureAI* GetAI(Creature* creature) const override
{
return new npc_garments_of_questsAI(creature);
}
};
/*######
## npc_guardian
######*/
enum GuardianSpells
{
SPELL_DEATHTOUCH = 5
};
class npc_guardian : public CreatureScript
{
public:
npc_guardian() : CreatureScript("npc_guardian") { }
struct npc_guardianAI : public ScriptedAI
{
npc_guardianAI(Creature* creature) : ScriptedAI(creature) { }
void Reset() override
{
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
}
void EnterCombat(Unit* /*who*/) override
{
}
void UpdateAI(uint32 /*diff*/) override
{
if (!UpdateVictim())
return;
if (me->isAttackReady())
{
DoCastVictim(SPELL_DEATHTOUCH, true);
me->resetAttackTimer();
}
}
};
CreatureAI* GetAI(Creature* creature) const override
{
return new npc_guardianAI(creature);
}
};
/*######
## npc_sayge
######*/
enum Sayge
{
SPELL_DMG = 23768, // dmg
SPELL_RES = 23769, // res
SPELL_ARM = 23767, // arm
SPELL_SPI = 23738, // spi
SPELL_INT = 23766, // int
SPELL_STM = 23737, // stm
SPELL_STR = 23735, // str
SPELL_AGI = 23736, // agi
SPELL_FORTUNE = 23765 // faire fortune
};
#define GOSSIP_HELLO_SAYGE "Yes"
#define GOSSIP_SENDACTION_SAYGE1 "Slay the Man"
#define GOSSIP_SENDACTION_SAYGE2 "Turn him over to liege"
#define GOSSIP_SENDACTION_SAYGE3 "Confiscate the corn"
#define GOSSIP_SENDACTION_SAYGE4 "Let him go and have the corn"
#define GOSSIP_SENDACTION_SAYGE5 "Execute your friend painfully"
#define GOSSIP_SENDACTION_SAYGE6 "Execute your friend painlessly"
#define GOSSIP_SENDACTION_SAYGE7 "Let your friend go"
#define GOSSIP_SENDACTION_SAYGE8 "Confront the diplomat"
#define GOSSIP_SENDACTION_SAYGE9 "Show not so quiet defiance"
#define GOSSIP_SENDACTION_SAYGE10 "Remain quiet"
#define GOSSIP_SENDACTION_SAYGE11 "Speak against your brother openly"
#define GOSSIP_SENDACTION_SAYGE12 "Help your brother in"
#define GOSSIP_SENDACTION_SAYGE13 "Keep your brother out without letting him know"
#define GOSSIP_SENDACTION_SAYGE14 "Take credit, keep gold"
#define GOSSIP_SENDACTION_SAYGE15 "Take credit, share the gold"
#define GOSSIP_SENDACTION_SAYGE16 "Let the knight take credit"
#define GOSSIP_SENDACTION_SAYGE17 "Thanks"
class npc_sayge : public CreatureScript
{
public:
npc_sayge() : CreatureScript("npc_sayge") { }
bool OnGossipHello(Player* player, Creature* creature) override
{
if (creature->IsQuestGiver())
player->PrepareQuestMenu(creature->GetGUID());
if (player->HasSpellCooldown(SPELL_INT) ||
player->HasSpellCooldown(SPELL_ARM) ||
player->HasSpellCooldown(SPELL_DMG) ||
player->HasSpellCooldown(SPELL_RES) ||
player->HasSpellCooldown(SPELL_STR) ||
player->HasSpellCooldown(SPELL_AGI) ||
player->HasSpellCooldown(SPELL_STM) ||
player->HasSpellCooldown(SPELL_SPI))
SendGossipMenuFor(player, 7393, creature->GetGUID());
else
{
AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_HELLO_SAYGE, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
SendGossipMenuFor(player, 7339, creature->GetGUID());
}
return true;
}
void SendAction(Player* player, Creature* creature, uint32 action)
{
switch (action)
{
case GOSSIP_ACTION_INFO_DEF + 1:
AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_SENDACTION_SAYGE1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2);
AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_SENDACTION_SAYGE2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3);
AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_SENDACTION_SAYGE3, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 4);
AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_SENDACTION_SAYGE4, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 5);
SendGossipMenuFor(player, 7340, creature->GetGUID());
break;
case GOSSIP_ACTION_INFO_DEF + 2:
AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_SENDACTION_SAYGE5, GOSSIP_SENDER_MAIN + 1, GOSSIP_ACTION_INFO_DEF);
AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_SENDACTION_SAYGE6, GOSSIP_SENDER_MAIN + 2, GOSSIP_ACTION_INFO_DEF);
AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_SENDACTION_SAYGE7, GOSSIP_SENDER_MAIN + 3, GOSSIP_ACTION_INFO_DEF);
SendGossipMenuFor(player, 7341, creature->GetGUID());
break;
case GOSSIP_ACTION_INFO_DEF + 3:
AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_SENDACTION_SAYGE8, GOSSIP_SENDER_MAIN + 4, GOSSIP_ACTION_INFO_DEF);
AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_SENDACTION_SAYGE9, GOSSIP_SENDER_MAIN + 5, GOSSIP_ACTION_INFO_DEF);
AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_SENDACTION_SAYGE10, GOSSIP_SENDER_MAIN + 2, GOSSIP_ACTION_INFO_DEF);
SendGossipMenuFor(player, 7361, creature->GetGUID());
break;
case GOSSIP_ACTION_INFO_DEF + 4:
AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_SENDACTION_SAYGE11, GOSSIP_SENDER_MAIN + 6, GOSSIP_ACTION_INFO_DEF);
AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_SENDACTION_SAYGE12, GOSSIP_SENDER_MAIN + 7, GOSSIP_ACTION_INFO_DEF);
AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_SENDACTION_SAYGE13, GOSSIP_SENDER_MAIN + 8, GOSSIP_ACTION_INFO_DEF);
SendGossipMenuFor(player, 7362, creature->GetGUID());
break;
case GOSSIP_ACTION_INFO_DEF + 5:
AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_SENDACTION_SAYGE14, GOSSIP_SENDER_MAIN + 5, GOSSIP_ACTION_INFO_DEF);
AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_SENDACTION_SAYGE15, GOSSIP_SENDER_MAIN + 4, GOSSIP_ACTION_INFO_DEF);
AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_SENDACTION_SAYGE16, GOSSIP_SENDER_MAIN + 3, GOSSIP_ACTION_INFO_DEF);
SendGossipMenuFor(player, 7363, creature->GetGUID());
break;
case GOSSIP_ACTION_INFO_DEF:
AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_SENDACTION_SAYGE17, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 6);
SendGossipMenuFor(player, 7364, creature->GetGUID());
break;
case GOSSIP_ACTION_INFO_DEF + 6:
creature->CastSpell(player, SPELL_FORTUNE, false);
SendGossipMenuFor(player, 7365, creature->GetGUID());
break;
}
}
bool OnGossipSelect(Player* player, Creature* creature, uint32 sender, uint32 action) override
{
ClearGossipMenuFor(player);
switch (sender)
{
case GOSSIP_SENDER_MAIN:
SendAction(player, creature, action);
break;
case GOSSIP_SENDER_MAIN + 1:
creature->CastSpell(player, SPELL_DMG, false);
player->AddSpellCooldown(SPELL_DMG, 0, 2 * HOUR * IN_MILLISECONDS);
SendAction(player, creature, action);
break;
case GOSSIP_SENDER_MAIN + 2:
creature->CastSpell(player, SPELL_RES, false);
player->AddSpellCooldown(SPELL_RES, 0, 2 * HOUR * IN_MILLISECONDS);
SendAction(player, creature, action);
break;
case GOSSIP_SENDER_MAIN + 3:
creature->CastSpell(player, SPELL_ARM, false);
player->AddSpellCooldown(SPELL_ARM, 0, 2 * HOUR * IN_MILLISECONDS);
SendAction(player, creature, action);
break;
case GOSSIP_SENDER_MAIN + 4:
creature->CastSpell(player, SPELL_SPI, false);
player->AddSpellCooldown(SPELL_SPI, 0, 2 * HOUR * IN_MILLISECONDS);
SendAction(player, creature, action);
break;
case GOSSIP_SENDER_MAIN + 5:
creature->CastSpell(player, SPELL_INT, false);
player->AddSpellCooldown(SPELL_INT, 0, 2 * HOUR * IN_MILLISECONDS);
SendAction(player, creature, action);
break;
case GOSSIP_SENDER_MAIN + 6:
creature->CastSpell(player, SPELL_STM, false);
player->AddSpellCooldown(SPELL_STM, 0, 2 * HOUR * IN_MILLISECONDS);
SendAction(player, creature, action);
break;
case GOSSIP_SENDER_MAIN + 7:
creature->CastSpell(player, SPELL_STR, false);
player->AddSpellCooldown(SPELL_STR, 0, 2 * HOUR * IN_MILLISECONDS);
SendAction(player, creature, action);
break;
case GOSSIP_SENDER_MAIN + 8:
creature->CastSpell(player, SPELL_AGI, false);
player->AddSpellCooldown(SPELL_AGI, 0, 2 * HOUR * IN_MILLISECONDS);
SendAction(player, creature, action);
break;
}
return true;
}
};
class npc_steam_tonk : public CreatureScript
{
public:
npc_steam_tonk() : CreatureScript("npc_steam_tonk") { }
struct npc_steam_tonkAI : public ScriptedAI
{
npc_steam_tonkAI(Creature* creature) : ScriptedAI(creature) { }
void Reset() override { }
void EnterCombat(Unit* /*who*/) override { }
void OnPossess(bool apply)
{
if (apply)
{
// Initialize the action bar without the melee attack command
me->InitCharmInfo();
me->GetCharmInfo()->InitEmptyActionBar(false);
me->SetReactState(REACT_PASSIVE);
}
else
me->SetReactState(REACT_AGGRESSIVE);
}
};
CreatureAI* GetAI(Creature* creature) const override
{
return new npc_steam_tonkAI(creature);
}
};
/*######
# npc_wormhole
######*/
#define GOSSIP_ENGINEERING1 "Borean Tundra"
#define GOSSIP_ENGINEERING2 "Howling Fjord"
#define GOSSIP_ENGINEERING3 "Sholazar Basin"
#define GOSSIP_ENGINEERING4 "Icecrown"
#define GOSSIP_ENGINEERING5 "Storm Peaks"
#define GOSSIP_ENGINEERING6 "Underground..."
enum WormholeSpells
{
SPELL_BOREAN_TUNDRA = 67834,
SPELL_SHOLAZAR_BASIN = 67835,
SPELL_ICECROWN = 67836,
SPELL_STORM_PEAKS = 67837,
SPELL_HOWLING_FJORD = 67838,
SPELL_UNDERGROUND = 68081,
TEXT_WORMHOLE = 907,
DATA_SHOW_UNDERGROUND = 1,
};
class npc_wormhole : public CreatureScript
{
public:
npc_wormhole() : CreatureScript("npc_wormhole") { }
struct npc_wormholeAI : public PassiveAI
{
npc_wormholeAI(Creature* creature) : PassiveAI(creature) { }
void InitializeAI() override
{
_showUnderground = urand(0, 100) == 0; // Guessed value, it is really rare though
}
uint32 GetData(uint32 type) const override
{
return (type == DATA_SHOW_UNDERGROUND && _showUnderground) ? 1 : 0;
}
private:
bool _showUnderground;
};
bool OnGossipHello(Player* player, Creature* creature) override
{
if (creature->IsSummon())
{
if (player == creature->ToTempSummon()->GetSummoner())
{
AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_ENGINEERING1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_ENGINEERING2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2);
AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_ENGINEERING3, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3);
AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_ENGINEERING4, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 4);
AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_ENGINEERING5, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 5);
if (creature->AI()->GetData(DATA_SHOW_UNDERGROUND))
AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_ENGINEERING6, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 6);
SendGossipMenuFor(player, TEXT_WORMHOLE, creature);
}
}
return true;
}
bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override
{
ClearGossipMenuFor(player);
switch (action)
{
case GOSSIP_ACTION_INFO_DEF + 1: // Borean Tundra
CloseGossipMenuFor(player);
creature->CastSpell(player, SPELL_BOREAN_TUNDRA, false);
break;
case GOSSIP_ACTION_INFO_DEF + 2: // Howling Fjord
CloseGossipMenuFor(player);
creature->CastSpell(player, SPELL_HOWLING_FJORD, false);
break;
case GOSSIP_ACTION_INFO_DEF + 3: // Sholazar Basin
CloseGossipMenuFor(player);
creature->CastSpell(player, SPELL_SHOLAZAR_BASIN, false);
break;
case GOSSIP_ACTION_INFO_DEF + 4: // Icecrown
CloseGossipMenuFor(player);
creature->CastSpell(player, SPELL_ICECROWN, false);
break;
case GOSSIP_ACTION_INFO_DEF + 5: // Storm peaks
CloseGossipMenuFor(player);
creature->CastSpell(player, SPELL_STORM_PEAKS, false);
break;
case GOSSIP_ACTION_INFO_DEF + 6: // Underground
CloseGossipMenuFor(player);
creature->CastSpell(player, SPELL_UNDERGROUND, false);
break;
}
return true;
}
CreatureAI* GetAI(Creature* creature) const override
{
return new npc_wormholeAI(creature);
}
};
/*######
## npc_pet_trainer
######*/
enum PetTrainer
{
PET_UNLEARN = 6520,
YES_PLEASE_DO = 0
};
class npc_pet_trainer : public CreatureScript
{
public:
npc_pet_trainer() : CreatureScript("npc_pet_trainer") { }
struct npc_pet_trainerAI : public ScriptedAI
{
npc_pet_trainerAI(Creature* creature) : ScriptedAI(creature) { }
void sGossipSelect(Player* player, uint32 menuId, uint32 gossipListId) override
{
if (menuId == PET_UNLEARN && gossipListId == YES_PLEASE_DO)
{
player->ResetPetTalents();
player->PlayerTalkClass->SendCloseGossip();
}
}
};
CreatureAI* GetAI(Creature* creature) const override
{
return new npc_pet_trainerAI(creature);
}
};
/*######
## npc_locksmith
######*/
enum LockSmith
{
QUEST_HOW_TO_BRAKE_IN_TO_THE_ARCATRAZ = 10704,
QUEST_DARK_IRON_LEGACY = 3802,
QUEST_THE_KEY_TO_SCHOLOMANCE_A = 5505,
QUEST_THE_KEY_TO_SCHOLOMANCE_H = 5511,
QUEST_HOTTER_THAN_HELL_A = 10758,
QUEST_HOTTER_THAN_HELL_H = 10764,
QUEST_RETURN_TO_KHAGDAR = 9837,
QUEST_CONTAINMENT = 13159,
QUEST_ETERNAL_VIGILANCE = 11011,
QUEST_KEY_TO_THE_FOCUSING_IRIS = 13372,
QUEST_HC_KEY_TO_THE_FOCUSING_IRIS = 13375,
ITEM_ARCATRAZ_KEY = 31084,
ITEM_SHADOWFORGE_KEY = 11000,
ITEM_SKELETON_KEY = 13704,
ITEM_SHATTERED_HALLS_KEY = 28395,
ITEM_THE_MASTERS_KEY = 24490,
ITEM_VIOLET_HOLD_KEY = 42482,
ITEM_ESSENCE_INFUSED_MOONSTONE = 32449,
ITEM_KEY_TO_THE_FOCUSING_IRIS = 44582,
ITEM_HC_KEY_TO_THE_FOCUSING_IRIS = 44581,
SPELL_ARCATRAZ_KEY = 54881,
SPELL_SHADOWFORGE_KEY = 54882,
SPELL_SKELETON_KEY = 54883,
SPELL_SHATTERED_HALLS_KEY = 54884,
SPELL_THE_MASTERS_KEY = 54885,
SPELL_VIOLET_HOLD_KEY = 67253,
SPELL_ESSENCE_INFUSED_MOONSTONE = 40173,
};
#define GOSSIP_LOST_ARCATRAZ_KEY "I've lost my key to the Arcatraz."
#define GOSSIP_LOST_SHADOWFORGE_KEY "I've lost my key to the Blackrock Depths."
#define GOSSIP_LOST_SKELETON_KEY "I've lost my key to the Scholomance."
#define GOSSIP_LOST_SHATTERED_HALLS_KEY "I've lost my key to the Shattered Halls."
#define GOSSIP_LOST_THE_MASTERS_KEY "I've lost my key to the Karazhan."
#define GOSSIP_LOST_VIOLET_HOLD_KEY "I've lost my key to the Violet Hold."
#define GOSSIP_LOST_ESSENCE_INFUSED_MOONSTONE "I've lost my Essence-Infused Moonstone."
#define GOSSIP_LOST_KEY_TO_THE_FOCUSING_IRIS "I've lost my Key to the Focusing Iris."
#define GOSSIP_LOST_HC_KEY_TO_THE_FOCUSING_IRIS "I've lost my Heroic Key to the Focusing Iris."
class npc_locksmith : public CreatureScript
{
public:
npc_locksmith() : CreatureScript("npc_locksmith") { }
bool OnGossipHello(Player* player, Creature* creature) override
{
// Arcatraz Key
if (player->GetQuestRewardStatus(QUEST_HOW_TO_BRAKE_IN_TO_THE_ARCATRAZ) && !player->HasItemCount(ITEM_ARCATRAZ_KEY, 1, true))
AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_LOST_ARCATRAZ_KEY, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
// Shadowforge Key
if (player->GetQuestRewardStatus(QUEST_DARK_IRON_LEGACY) && !player->HasItemCount(ITEM_SHADOWFORGE_KEY, 1, true))
AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_LOST_SHADOWFORGE_KEY, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2);
// Skeleton Key
if ((player->GetQuestRewardStatus(QUEST_THE_KEY_TO_SCHOLOMANCE_A) || player->GetQuestRewardStatus(QUEST_THE_KEY_TO_SCHOLOMANCE_H)) &&
!player->HasItemCount(ITEM_SKELETON_KEY, 1, true))
AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_LOST_SKELETON_KEY, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3);
// Shatered Halls Key
if ((player->GetQuestRewardStatus(QUEST_HOTTER_THAN_HELL_A) || player->GetQuestRewardStatus(QUEST_HOTTER_THAN_HELL_H)) &&
!player->HasItemCount(ITEM_SHATTERED_HALLS_KEY, 1, true))
AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_LOST_SHATTERED_HALLS_KEY, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 4);
// Master's Key
if (player->GetQuestRewardStatus(QUEST_RETURN_TO_KHAGDAR) && !player->HasItemCount(ITEM_THE_MASTERS_KEY, 1, true))
AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_LOST_THE_MASTERS_KEY, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 5);
// Violet Hold Key
if (player->GetQuestRewardStatus(QUEST_CONTAINMENT) && !player->HasItemCount(ITEM_VIOLET_HOLD_KEY, 1, true))
AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_LOST_VIOLET_HOLD_KEY, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 6);
// Essence-Infused Moonstone
if (player->GetQuestRewardStatus(QUEST_ETERNAL_VIGILANCE) && !player->HasItemCount(ITEM_ESSENCE_INFUSED_MOONSTONE, 1, true))
AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_LOST_ESSENCE_INFUSED_MOONSTONE, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 7);
// Key to the Focusing Iris
if (player->GetQuestRewardStatus(QUEST_KEY_TO_THE_FOCUSING_IRIS) && !player->HasItemCount(ITEM_KEY_TO_THE_FOCUSING_IRIS, 1, true))
AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_LOST_KEY_TO_THE_FOCUSING_IRIS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 8);
// Heroic Key to the Focusing Iris
if (player->GetQuestRewardStatus(QUEST_HC_KEY_TO_THE_FOCUSING_IRIS) && !player->HasItemCount(ITEM_HC_KEY_TO_THE_FOCUSING_IRIS, 1, true))
AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_LOST_HC_KEY_TO_THE_FOCUSING_IRIS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 9);
SendGossipMenuFor(player, player->GetGossipTextId(creature), creature->GetGUID());
return true;
}
bool OnGossipSelect(Player* player, Creature* /*creature*/, uint32 /*sender*/, uint32 action) override
{
ClearGossipMenuFor(player);
switch (action)
{
case GOSSIP_ACTION_INFO_DEF + 1:
CloseGossipMenuFor(player);
player->CastSpell(player, SPELL_ARCATRAZ_KEY, false);
break;
case GOSSIP_ACTION_INFO_DEF + 2:
CloseGossipMenuFor(player);
player->CastSpell(player, SPELL_SHADOWFORGE_KEY, false);
break;
case GOSSIP_ACTION_INFO_DEF + 3:
CloseGossipMenuFor(player);
player->CastSpell(player, SPELL_SKELETON_KEY, false);
break;
case GOSSIP_ACTION_INFO_DEF + 4:
CloseGossipMenuFor(player);
player->CastSpell(player, SPELL_SHATTERED_HALLS_KEY, false);
break;
case GOSSIP_ACTION_INFO_DEF + 5:
CloseGossipMenuFor(player);
player->CastSpell(player, SPELL_THE_MASTERS_KEY, false);
break;
case GOSSIP_ACTION_INFO_DEF + 6:
CloseGossipMenuFor(player);
player->CastSpell(player, SPELL_VIOLET_HOLD_KEY, false);
break;
case GOSSIP_ACTION_INFO_DEF + 7:
CloseGossipMenuFor(player);
player->CastSpell(player, SPELL_ESSENCE_INFUSED_MOONSTONE, false);
break;
case GOSSIP_ACTION_INFO_DEF + 8:
CloseGossipMenuFor(player);
player->AddItem(ITEM_KEY_TO_THE_FOCUSING_IRIS, 1);
break;
case GOSSIP_ACTION_INFO_DEF + 9:
CloseGossipMenuFor(player);
player->AddItem(ITEM_HC_KEY_TO_THE_FOCUSING_IRIS, 1);
break;
}
return true;
}
};
/*######
## npc_experience
######*/
#define GOSSIP_TEXT_EXP 14736
#define GOSSIP_XP_OFF "I no longer wish to gain experience."
#define GOSSIP_XP_ON "I wish to start gaining experience again."
class npc_experience : public CreatureScript
{
public:
npc_experience() : CreatureScript("npc_experience") { }
bool OnGossipHello(Player* player, Creature* creature) override
{
AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_XP_OFF, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_XP_ON, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2);
SendGossipMenuFor(player, GOSSIP_TEXT_EXP, creature);
return true;
}
bool OnGossipSelect(Player* player, Creature* /*creature*/, uint32 /*sender*/, uint32 action) override
{
ClearGossipMenuFor(player);
bool noXPGain = player->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_NO_XP_GAIN);
bool doSwitch = false;
auto toggleXpCost = sWorld->getIntConfig(CONFIG_TOGGLE_XP_COST);
switch (action)
{
case GOSSIP_ACTION_INFO_DEF + 1://xp off
{
if (!noXPGain)//does gain xp
doSwitch = true;//switch to don't gain xp
}
break;
case GOSSIP_ACTION_INFO_DEF + 2://xp on
{
if (noXPGain)//doesn't gain xp
doSwitch = true;//switch to gain xp
}
break;
}
if (doSwitch)
{
if (!player->HasEnoughMoney(toggleXpCost))
{
player->SendBuyError(BUY_ERR_NOT_ENOUGHT_MONEY, 0, 0, 0);
}
else if (noXPGain)
{
player->ModifyMoney(-toggleXpCost);
player->RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_NO_XP_GAIN);
}
else if (!noXPGain)
{
player->ModifyMoney(-toggleXpCost);
player->SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_NO_XP_GAIN);
}
}
player->PlayerTalkClass->SendCloseGossip();
return true;
}
};
enum Fireworks
{
NPC_OMEN = 15467,
NPC_MINION_OF_OMEN = 15466,
NPC_FIREWORK_BLUE = 15879,
NPC_FIREWORK_GREEN = 15880,
NPC_FIREWORK_PURPLE = 15881,
NPC_FIREWORK_RED = 15882,
NPC_FIREWORK_YELLOW = 15883,
NPC_FIREWORK_WHITE = 15884,
NPC_FIREWORK_BIG_BLUE = 15885,
NPC_FIREWORK_BIG_GREEN = 15886,
NPC_FIREWORK_BIG_PURPLE = 15887,
NPC_FIREWORK_BIG_RED = 15888,
NPC_FIREWORK_BIG_YELLOW = 15889,
NPC_FIREWORK_BIG_WHITE = 15890,
NPC_CLUSTER_BLUE = 15872,
NPC_CLUSTER_RED = 15873,
NPC_CLUSTER_GREEN = 15874,
NPC_CLUSTER_PURPLE = 15875,
NPC_CLUSTER_WHITE = 15876,
NPC_CLUSTER_YELLOW = 15877,
NPC_CLUSTER_BIG_BLUE = 15911,
NPC_CLUSTER_BIG_GREEN = 15912,
NPC_CLUSTER_BIG_PURPLE = 15913,
NPC_CLUSTER_BIG_RED = 15914,
NPC_CLUSTER_BIG_WHITE = 15915,
NPC_CLUSTER_BIG_YELLOW = 15916,
NPC_CLUSTER_ELUNE = 15918,
GO_FIREWORK_LAUNCHER_1 = 180771,
GO_FIREWORK_LAUNCHER_2 = 180868,
GO_FIREWORK_LAUNCHER_3 = 180850,
GO_CLUSTER_LAUNCHER_1 = 180772,
GO_CLUSTER_LAUNCHER_2 = 180859,
GO_CLUSTER_LAUNCHER_3 = 180869,
GO_CLUSTER_LAUNCHER_4 = 180874,
SPELL_ROCKET_BLUE = 26344,
SPELL_ROCKET_GREEN = 26345,
SPELL_ROCKET_PURPLE = 26346,
SPELL_ROCKET_RED = 26347,
SPELL_ROCKET_WHITE = 26348,
SPELL_ROCKET_YELLOW = 26349,
SPELL_ROCKET_BIG_BLUE = 26351,
SPELL_ROCKET_BIG_GREEN = 26352,
SPELL_ROCKET_BIG_PURPLE = 26353,
SPELL_ROCKET_BIG_RED = 26354,
SPELL_ROCKET_BIG_WHITE = 26355,
SPELL_ROCKET_BIG_YELLOW = 26356,
SPELL_LUNAR_FORTUNE = 26522,
ANIM_GO_LAUNCH_FIREWORK = 3,
ZONE_MOONGLADE = 493,
};
Position omenSummonPos = {7558.993f, -2839.999f, 450.0214f, 4.46f};
class npc_firework : public CreatureScript
{
public:
npc_firework() : CreatureScript("npc_firework") { }
struct npc_fireworkAI : public ScriptedAI
{
npc_fireworkAI(Creature* creature) : ScriptedAI(creature) { }
bool isCluster()
{
switch (me->GetEntry())
{
case NPC_FIREWORK_BLUE:
case NPC_FIREWORK_GREEN:
case NPC_FIREWORK_PURPLE:
case NPC_FIREWORK_RED:
case NPC_FIREWORK_YELLOW:
case NPC_FIREWORK_WHITE:
case NPC_FIREWORK_BIG_BLUE:
case NPC_FIREWORK_BIG_GREEN:
case NPC_FIREWORK_BIG_PURPLE:
case NPC_FIREWORK_BIG_RED:
case NPC_FIREWORK_BIG_YELLOW:
case NPC_FIREWORK_BIG_WHITE:
return false;
case NPC_CLUSTER_BLUE:
case NPC_CLUSTER_GREEN:
case NPC_CLUSTER_PURPLE:
case NPC_CLUSTER_RED:
case NPC_CLUSTER_YELLOW:
case NPC_CLUSTER_WHITE:
case NPC_CLUSTER_BIG_BLUE:
case NPC_CLUSTER_BIG_GREEN:
case NPC_CLUSTER_BIG_PURPLE:
case NPC_CLUSTER_BIG_RED:
case NPC_CLUSTER_BIG_YELLOW:
case NPC_CLUSTER_BIG_WHITE:
case NPC_CLUSTER_ELUNE:
default:
return true;
}
}
GameObject* FindNearestLauncher()
{
GameObject* launcher = nullptr;
if (isCluster())
{
GameObject* launcher1 = GetClosestGameObjectWithEntry(me, GO_CLUSTER_LAUNCHER_1, 0.5f);
GameObject* launcher2 = GetClosestGameObjectWithEntry(me, GO_CLUSTER_LAUNCHER_2, 0.5f);
GameObject* launcher3 = GetClosestGameObjectWithEntry(me, GO_CLUSTER_LAUNCHER_3, 0.5f);
GameObject* launcher4 = GetClosestGameObjectWithEntry(me, GO_CLUSTER_LAUNCHER_4, 0.5f);
if (launcher1)
launcher = launcher1;
else if (launcher2)
launcher = launcher2;
else if (launcher3)
launcher = launcher3;
else if (launcher4)
launcher = launcher4;
}
else
{
GameObject* launcher1 = GetClosestGameObjectWithEntry(me, GO_FIREWORK_LAUNCHER_1, 0.5f);
GameObject* launcher2 = GetClosestGameObjectWithEntry(me, GO_FIREWORK_LAUNCHER_2, 0.5f);
GameObject* launcher3 = GetClosestGameObjectWithEntry(me, GO_FIREWORK_LAUNCHER_3, 0.5f);
if (launcher1)
launcher = launcher1;
else if (launcher2)
launcher = launcher2;
else if (launcher3)
launcher = launcher3;
}
return launcher;
}
uint32 GetFireworkSpell(uint32 entry)
{
switch (entry)
{
case NPC_FIREWORK_BLUE:
return SPELL_ROCKET_BLUE;
case NPC_FIREWORK_GREEN:
return SPELL_ROCKET_GREEN;
case NPC_FIREWORK_PURPLE:
return SPELL_ROCKET_PURPLE;
case NPC_FIREWORK_RED:
return SPELL_ROCKET_RED;
case NPC_FIREWORK_YELLOW:
return SPELL_ROCKET_YELLOW;
case NPC_FIREWORK_WHITE:
return SPELL_ROCKET_WHITE;
case NPC_FIREWORK_BIG_BLUE:
return SPELL_ROCKET_BIG_BLUE;
case NPC_FIREWORK_BIG_GREEN:
return SPELL_ROCKET_BIG_GREEN;
case NPC_FIREWORK_BIG_PURPLE:
return SPELL_ROCKET_BIG_PURPLE;
case NPC_FIREWORK_BIG_RED:
return SPELL_ROCKET_BIG_RED;
case NPC_FIREWORK_BIG_YELLOW:
return SPELL_ROCKET_BIG_YELLOW;
case NPC_FIREWORK_BIG_WHITE:
return SPELL_ROCKET_BIG_WHITE;
default:
return 0;
}
}
uint32 GetFireworkGameObjectId()
{
uint32 spellId = 0;
switch (me->GetEntry())
{
case NPC_CLUSTER_BLUE:
spellId = GetFireworkSpell(NPC_FIREWORK_BLUE);
break;
case NPC_CLUSTER_GREEN:
spellId = GetFireworkSpell(NPC_FIREWORK_GREEN);
break;
case NPC_CLUSTER_PURPLE:
spellId = GetFireworkSpell(NPC_FIREWORK_PURPLE);
break;
case NPC_CLUSTER_RED:
spellId = GetFireworkSpell(NPC_FIREWORK_RED);
break;
case NPC_CLUSTER_YELLOW:
spellId = GetFireworkSpell(NPC_FIREWORK_YELLOW);
break;
case NPC_CLUSTER_WHITE:
spellId = GetFireworkSpell(NPC_FIREWORK_WHITE);
break;
case NPC_CLUSTER_BIG_BLUE:
spellId = GetFireworkSpell(NPC_FIREWORK_BIG_BLUE);
break;
case NPC_CLUSTER_BIG_GREEN:
spellId = GetFireworkSpell(NPC_FIREWORK_BIG_GREEN);
break;
case NPC_CLUSTER_BIG_PURPLE:
spellId = GetFireworkSpell(NPC_FIREWORK_BIG_PURPLE);
break;
case NPC_CLUSTER_BIG_RED:
spellId = GetFireworkSpell(NPC_FIREWORK_BIG_RED);
break;
case NPC_CLUSTER_BIG_YELLOW:
spellId = GetFireworkSpell(NPC_FIREWORK_BIG_YELLOW);
break;
case NPC_CLUSTER_BIG_WHITE:
spellId = GetFireworkSpell(NPC_FIREWORK_BIG_WHITE);
break;
case NPC_CLUSTER_ELUNE:
spellId = GetFireworkSpell(urand(NPC_FIREWORK_BLUE, NPC_FIREWORK_WHITE));
break;
}
const SpellInfo* spellInfo = sSpellMgr->GetSpellInfo(spellId);
if (spellInfo && spellInfo->Effects[0].Effect == SPELL_EFFECT_SUMMON_OBJECT_WILD)
return spellInfo->Effects[0].MiscValue;
return 0;
}
void Reset() override
{
if (GameObject* launcher = FindNearestLauncher())
{
launcher->SendCustomAnim(ANIM_GO_LAUNCH_FIREWORK);
me->SetOrientation(launcher->GetOrientation() + M_PI / 2);
}
else
return;
if (isCluster())
{
// Check if we are near Elune'ara lake south, if so try to summon Omen or a minion
if (me->GetZoneId() == ZONE_MOONGLADE)
{
if (!me->FindNearestCreature(NPC_OMEN, 100.0f, false) && me->GetDistance2d(omenSummonPos.GetPositionX(), omenSummonPos.GetPositionY()) <= 100.0f)
{
switch (urand(0, 9))
{
case 0:
case 1:
case 2:
case 3:
if (Creature* minion = me->SummonCreature(NPC_MINION_OF_OMEN, me->GetPositionX() + frand(-5.0f, 5.0f), me->GetPositionY() + frand(-5.0f, 5.0f), me->GetPositionZ(), 0.0f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 20000))
minion->AI()->AttackStart(me->SelectNearestPlayer(20.0f));
break;
case 9:
me->SummonCreature(NPC_OMEN, omenSummonPos);
break;
}
}
}
if (me->GetEntry() == NPC_CLUSTER_ELUNE)
DoCast(SPELL_LUNAR_FORTUNE);
float displacement = 0.7f;
for (uint8 i = 0; i < 4; i++)
me->SummonGameObject(GetFireworkGameObjectId(), me->GetPositionX() + (i % 2 == 0 ? displacement : -displacement), me->GetPositionY() + (i > 1 ? displacement : -displacement), me->GetPositionZ() + 4.0f, me->GetOrientation(), 0.0f, 0.0f, 0.0f, 0.0f, 1);
}
else
//me->CastSpell(me, GetFireworkSpell(me->GetEntry()), true);
me->CastSpell(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), GetFireworkSpell(me->GetEntry()), true);
}
};
CreatureAI* GetAI(Creature* creature) const override
{
return new npc_fireworkAI(creature);
}
};
/*#####
# npc_spring_rabbit
#####*/
enum rabbitSpells
{
SPELL_SPRING_FLING = 61875,
SPELL_SPRING_RABBIT_JUMP = 61724,
SPELL_SPRING_RABBIT_WANDER = 61726,
SPELL_SUMMON_BABY_BUNNY = 61727,
SPELL_SPRING_RABBIT_IN_LOVE = 61728,
NPC_SPRING_RABBIT = 32791
};
class npc_spring_rabbit : public CreatureScript
{
public:
npc_spring_rabbit() : CreatureScript("npc_spring_rabbit") { }
CreatureAI* GetAI(Creature* creature) const override
{
return new npc_spring_rabbitAI(creature);
}
struct npc_spring_rabbitAI : public ScriptedAI
{
npc_spring_rabbitAI(Creature* creature) : ScriptedAI(creature) { }
bool inLove;
uint32 jumpTimer;
uint32 bunnyTimer;
uint32 searchTimer;
uint64 rabbitGUID;
void Reset() override
{
inLove = false;
rabbitGUID = 0;
jumpTimer = urand(5000, 10000);
bunnyTimer = urand(10000, 20000);
searchTimer = urand(5000, 10000);
if (Unit* owner = me->GetOwner())
me->GetMotionMaster()->MoveFollow(owner, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE);
}
void EnterCombat(Unit* /*who*/) override { }
void DoAction(int32 /*param*/) override
{
inLove = true;
if (Unit* owner = me->GetOwner())
owner->CastSpell(owner, SPELL_SPRING_FLING, true);
}
void UpdateAI(uint32 diff) override
{
if (inLove)
{
if (jumpTimer <= diff)
{
if (Unit* rabbit = ObjectAccessor::GetUnit(*me, rabbitGUID))
DoCast(rabbit, SPELL_SPRING_RABBIT_JUMP);
jumpTimer = urand(5000, 10000);
}
else jumpTimer -= diff;
if (bunnyTimer <= diff)
{
DoCast(SPELL_SUMMON_BABY_BUNNY);
bunnyTimer = urand(20000, 40000);
}
else bunnyTimer -= diff;
}
else
{
if (searchTimer <= diff)
{
if (Creature* rabbit = me->FindNearestCreature(NPC_SPRING_RABBIT, 10.0f))
{
if (rabbit == me || rabbit->HasAura(SPELL_SPRING_RABBIT_IN_LOVE))
return;
me->AddAura(SPELL_SPRING_RABBIT_IN_LOVE, me);
DoAction(1);
rabbit->AddAura(SPELL_SPRING_RABBIT_IN_LOVE, rabbit);
rabbit->AI()->DoAction(1);
rabbit->CastSpell(rabbit, SPELL_SPRING_RABBIT_JUMP, true);
rabbitGUID = rabbit->GetGUID();
}
searchTimer = urand(5000, 10000);
}
else searchTimer -= diff;
}
}
};
};
enum StableMasters
{
SPELL_MINIWING = 54573,
SPELL_JUBLING = 54611,
SPELL_DARTER = 54619,
SPELL_WORG = 54631,
SPELL_SMOLDERWEB = 54634,
SPELL_CHIKEN = 54677,
SPELL_WOLPERTINGER = 54688,
STABLE_MASTER_GOSSIP_SUB_MENU = 9820
};
class npc_stable_master : public CreatureScript
{
public:
npc_stable_master() : CreatureScript("npc_stable_master") { }
struct npc_stable_masterAI : public SmartAI
{
npc_stable_masterAI(Creature* creature) : SmartAI(creature) { }
void sGossipSelect(Player* player, uint32 menuId, uint32 gossipListId) override
{
SmartAI::sGossipSelect(player, menuId, gossipListId);
if (menuId != STABLE_MASTER_GOSSIP_SUB_MENU)
return;
switch (gossipListId)
{
case 0:
player->CastSpell(player, SPELL_MINIWING, false);
break;
case 1:
player->CastSpell(player, SPELL_JUBLING, false);
break;
case 2:
player->CastSpell(player, SPELL_DARTER, false);
break;
case 3:
player->CastSpell(player, SPELL_WORG, false);
break;
case 4:
player->CastSpell(player, SPELL_SMOLDERWEB, false);
break;
case 5:
player->CastSpell(player, SPELL_CHIKEN, false);
break;
case 6:
player->CastSpell(player, SPELL_WOLPERTINGER, false);
break;
default:
return;
}
player->PlayerTalkClass->SendCloseGossip();
}
};
CreatureAI* GetAI(Creature* creature) const override
{
return new npc_stable_masterAI(creature);
}
};
void AddSC_npcs_special()
{
// Ours
new npc_elder_clearwater();
new npc_riggle_bassbait();
new npc_target_dummy();
new npc_training_dummy();
// Theirs
new npc_air_force_bots();
new npc_chicken_cluck();
new npc_dancing_flames();
new npc_doctor();
new npc_injured_patient();
new npc_garments_of_quests();
new npc_guardian();
new npc_sayge();
new npc_steam_tonk();
new npc_wormhole();
new npc_pet_trainer();
new npc_locksmith();
new npc_experience();
new npc_firework();
new npc_spring_rabbit();
new npc_stable_master();
}