Npcs Special
Npcs Special
* This file is part of the AzerothCore 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 Affero General Public License as published by the
* Free Software Foundation; either version 3 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 Affero 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/>.
*/
/* 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 "CellImpl.h"
#include "Chat.h"
#include "CombatAI.h"
#include "CreatureTextMgr.h"
#include "DBCStructure.h"
#include "GameEventMgr.h"
#include "GameTime.h"
#include "GridNotifiers.h"
#include "ObjectMgr.h"
#include "PassiveAI.h"
#include "Pet.h"
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "ScriptedEscortAI.h"
#include "ScriptedGossip.h"
#include "SmartAI.h"
#include "SpellAuras.h"
#include "WaypointMgr.h"
#include "World.h"
// TODO: this import is not necessary for compilation and marked as unused by the
IDE
// however, for some reasons removing it would cause a damn linking issue
// there is probably some underlying problem with imports which should properly
addressed
// see: https://github.com/azerothcore/azerothcore-wotlk/issues/9766
#include "GridNotifiersImpl.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,
};
EventMap events;
bool finished;
bool preWarning;
bool startWarning;
bool finishWarning;
return 0;
}
void DoAction(int32 param) override
{
if (param == DATA_DERBY_FINISHED)
finished = true;
}
events.RepeatEvent(1000);
break;
}
}
}
};
QuestMenu& qm = player->PlayerTalkClass->GetQuestMenu();
qm.ClearMenu();
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;
}
return true;
}
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,
GAME_EVENT_FISHING = 62
};
EventMap events;
bool finished;
bool startWarning;
bool finishWarning;
return 0;
}
events.RepeatEvent(1000);
break;
}
}
}
};
enum eTrainingDummy
{
SPELL_STUN_PERMANENT = 61204
};
uint32 resetTimer;
Reset();
}
uint32 deathTimer;
Reset();
}
// 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
};
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)
};
if (!SpawnAssoc)
LOG_ERROR("sql.sql", "TCSR: Creature template entry {} 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 {} does not
exist in DB, which is required by npc_air_force_bots", SpawnAssoc-
>spawnedCreatureEntry);
SpawnAssoc = nullptr;
return;
}
}
}
SpawnAssociation* SpawnAssoc;
ObjectGuid SpawnedGUID;
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 {}", SpawnAssoc->spawnedCreatureEntry);
SpawnAssoc = nullptr;
}
return summoned;
}
Creature* GetSummonedGuard()
{
Creature* creature = ObjectAccessor::GetCreature(*me, SpawnedGUID);
return nullptr;
}
{
if (!SpawnAssoc)
return;
if (me->IsValidAttackTarget(who))
{
Player* playerTarget = who->ToPlayer();
// airforce guards only spawn for players
if (!playerTarget)
return;
switch (SpawnAssoc->spawnType)
{
case SPAWNTYPE_ALARMBOT:
{
if (!who->IsWithinDistInMap(me, RANGE_GUARDS_MARK))
return;
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;
break;
}
}
}
}
};
/*########
# npc_chicken_cluck
#########*/
enum ChickenCluck
{
EMOTE_HELLO = 0,
EMOTE_CLUCK_TEXT = 2,
QUEST_CLUCK = 3861
};
uint32 ResetFlagTimer;
if (UpdateVictim())
DoMeleeAttackIfReady();
}
return true;
}
/*######
## npc_dancing_flames
######*/
enum DancingFlames
{
SPELL_BRAZIER = 45423,
SPELL_SEDUCTION = 47057,
SPELL_FIERY_AURA = 45427
};
bool Active;
uint32 CanIteract;
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;
}
}
}
};
/*######
## 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;
};
/*######
## npc_doctor (handles both Gustaf Vanhowzen and Gregory Victor)
######*/
class npc_doctor : public CreatureScript
{
public:
npc_doctor() : CreatureScript("npc_doctor") { }
uint32 SummonPatientTimer;
uint32 SummonPatientCount;
uint32 PatientDiedCount;
uint32 PatientSavedCount;
bool Event;
GuidList Patients;
std::vector<Location*> Coordinates;
SummonPatientTimer = 10000;
SummonPatientCount = 0;
PatientDiedCount = 0;
PatientSavedCount = 0;
Patients.clear();
Coordinates.clear();
Event = false;
me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
}
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->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
}
Reset();
return;
}
Coordinates.push_back(point);
}
else
// If no player or player abandon quest in progress
Reset();
}
if (PatientSavedCount == 15)
{
if (!Patients.empty())
{
for (ObjectGuid const& guid : Patients)
{
if (Creature* patient =
ObjectAccessor::GetCreature(*me, guid))
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);
}
}
}
return true;
}
/*#####
## npc_injured_patient (handles all the patients, no matter Horde or Alliance)
#####*/
ObjectGuid DoctorGUID;
Location* Coord;
//no select
me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
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;
}
}
//regen health
me->RemoveUnitFlag(UNIT_FLAG_IN_COMBAT);
//stand up
me->SetUInt32Value(UNIT_FIELD_BYTES_1, UNIT_STAND_STATE_STAND);
Talk(SAY_DOC);
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;
}
}
if (DoctorGUID)
if (Creature* doctor = ObjectAccessor::GetCreature((*me),
DoctorGUID))
CAST_AI(npc_doctor::npc_doctorAI, doctor->AI())-
>PatientDied(Coord);
}
}
};
if (Event)
{
if (SummonPatientTimer <= diff)
{
if (Coordinates.empty())
return;
switch (me->GetEntry())
{
case DOCTOR_ALLIANCE:
patientEntry = AllianceSoldierId[rand() % 3];
break;
case DOCTOR_HORDE:
patientEntry = HordeSoldierId[rand() % 3];
break;
default:
LOG_ERROR("scripts", "Invalid entry for Triage doctor. Please
check your database");
return;
}
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
######*/
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,
};
ObjectGuid CasterGUID;
bool IsHealed;
bool CanRun;
uint32 RunAwayTimer;
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));
}
Start(false, true);
}
else
EnterEvadeMode(); //something went
wrong
RunAwayTimer = 30000;
}
else
RunAwayTimer -= diff;
}
npc_escortAI::UpdateAI(diff);
}
};
/*######
## npc_guardian
######*/
enum GuardianSpells
{
SPELL_DEATHTOUCH = 5
};
if (me->isAttackReady())
{
DoCastVictim(SPELL_DEATHTOUCH, true);
me->resetAttackTimer();
}
}
};
/*######
## 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
};
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;
}
me->SetReactState(REACT_PASSIVE);
}
else
me->SetReactState(REACT_AGGRESSIVE);
}
};
/*######
# npc_wormhole
######*/
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,
};
private:
bool _showUnderground;
};
if (creature->AI()->GetData(DATA_SHOW_UNDERGROUND))
AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_ENGINEERING6,
GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 6);
return true;
}
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;
}
/*######
## npc_pet_trainer
######*/
enum PetTrainer
{
PET_UNLEARN = 6520,
YES_PLEASE_DO = 0
};
/*######
## 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,
};
// 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);
// 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);
// 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);
return true;
}
/*######
## npc_experience
######*/
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->RemovePlayerFlag(PLAYER_FLAGS_NO_XP_GAIN);
}
else if (!noXPGain)
{
player->ModifyMoney(-toggleXpCost);
player->SetPlayerFlag(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,
};
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 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;
}
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);
/*#####
# 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
};
bool inLove;
uint32 jumpTimer;
uint32 bunnyTimer;
uint32 searchTimer;
ObjectGuid rabbitGUID;
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
};
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();
}
};
enum VenomhideHatchlingMisc
{
ITEM_VENOMHIDE_BABY_TOOTH = 47196,
MODEL_BABY_RAPTOR = 29251,
MODEL_BABY_RAPTOR_REPTILE_EYES = 29809,
MODEL_ADOLESCENT_RAPTOR = 29103,
MODEL_FULL_RAPTOR = 5291,
};
enum VenomhideHatchlingTexts
{
TALK_EMOTE_EAT = 0,
};
enum VenomhideHatchlingSpellEmotes
{
SPELL_SILITHID_MEAT = 65258,
SPELL_SILITHID_EGG = 65265,
SPELL_FRESH_DINOSAUR_MEAT = 65200,
};
if (summoner->ToPlayer()->GetItemCount(ITEM_VENOMHIDE_BABY_TOOTH) >= 6)
{
me->SetDisplayId(MODEL_BABY_RAPTOR_REPTILE_EYES);
}
if (summoner->ToPlayer()->GetItemCount(ITEM_VENOMHIDE_BABY_TOOTH) >=
11)
{
me->SetDisplayId(MODEL_ADOLESCENT_RAPTOR);
}
if (summoner->ToPlayer()->GetItemCount(ITEM_VENOMHIDE_BABY_TOOTH) >=
16)
{
me->SetDisplayId(MODEL_FULL_RAPTOR);
}
}
return true;
};
void AddSC_npcs_special()
{
// Ours
new npc_elder_clearwater();
new npc_riggle_bassbait();
new npc_target_dummy();
new npc_training_dummy();
new npc_venomhide_hatchling();
// 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();
}