diff options
Diffstat (limited to 'src')
49 files changed, 2504 insertions, 402 deletions
diff --git a/src/server/game/AI/SmartScripts/SmartScript.cpp b/src/server/game/AI/SmartScripts/SmartScript.cpp index 32b9f8b5903..f37ae4fa60e 100644 --- a/src/server/game/AI/SmartScripts/SmartScript.cpp +++ b/src/server/game/AI/SmartScripts/SmartScript.cpp @@ -130,12 +130,10 @@ void SmartScript::ProcessEventsFor(SMART_EVENT e, Unit* unit, uint32 var0, uint3 if (eventType == e/* && (!(*i).event.event_phase_mask || IsInPhase((*i).event.event_phase_mask)) && !((*i).event.event_flags & SMART_EVENT_FLAG_NOT_REPEATABLE && (*i).runOnce)*/) { - bool meets = true; ConditionList conds = sConditionMgr->GetConditionsForSmartEvent((*i).entryOrGuid, (*i).event_id, (*i).source_type); ConditionSourceInfo info = ConditionSourceInfo(unit, GetBaseObject()); - meets = sConditionMgr->IsObjectMeetToConditions(info, conds); - if (meets) + if (sConditionMgr->IsObjectMeetToConditions(info, conds)) ProcessEvent(*i, unit, var0, var1, bvar, spell, gob); } } diff --git a/src/server/game/Battlefield/Battlefield.cpp b/src/server/game/Battlefield/Battlefield.cpp index ae58f3504b4..1c99934bafc 100644 --- a/src/server/game/Battlefield/Battlefield.cpp +++ b/src/server/game/Battlefield/Battlefield.cpp @@ -999,7 +999,7 @@ bool BfCapturePoint::Update(uint32 diff) // get the difference of numbers float fact_diff = ((float) m_activePlayers[0].size() - (float) m_activePlayers[1].size()) * diff / BATTLEFIELD_OBJECTIVE_UPDATE_INTERVAL; - if (!fact_diff) + if (G3D::fuzzyEq(fact_diff, 0.0f)) return false; uint32 Challenger = 0; @@ -1069,7 +1069,7 @@ bool BfCapturePoint::Update(uint32 diff) m_team = TEAM_NEUTRAL; } - if (m_value != oldValue) + if (G3D::fuzzyNe(m_value, oldValue)) SendChangePhase(); if (m_OldState != m_State) diff --git a/src/server/game/Battlefield/Zones/BattlefieldWG.cpp b/src/server/game/Battlefield/Zones/BattlefieldWG.cpp index de1ee1785d8..119880bae8d 100644 --- a/src/server/game/Battlefield/Zones/BattlefieldWG.cpp +++ b/src/server/game/Battlefield/Zones/BattlefieldWG.cpp @@ -528,12 +528,12 @@ void BattlefieldWG::OnCreatureCreate(Creature* creature) case NPC_WINTERGRASP_CATAPULT: case NPC_WINTERGRASP_DEMOLISHER: { - if (!creature->ToTempSummon()->GetSummonerGUID() || !sObjectAccessor->FindPlayer(creature->ToTempSummon()->GetSummonerGUID())) + if (!creature->ToTempSummon() || !creature->ToTempSummon()->GetSummonerGUID() || !sObjectAccessor->FindPlayer(creature->ToTempSummon()->GetSummonerGUID())) { - creature->setDeathState(DEAD); - creature->RemoveFromWorld(); + creature->DespawnOrUnsummon(); return; } + Player* creator = sObjectAccessor->FindPlayer(creature->ToTempSummon()->GetSummonerGUID()); TeamId team = creator->GetTeamId(); @@ -548,8 +548,7 @@ void BattlefieldWG::OnCreatureCreate(Creature* creature) } else { - creature->setDeathState(DEAD); - creature->RemoveFromWorld(); + creature->DespawnOrUnsummon(); return; } } @@ -564,8 +563,7 @@ void BattlefieldWG::OnCreatureCreate(Creature* creature) } else { - creature->setDeathState(DEAD); - creature->RemoveFromWorld(); + creature->DespawnOrUnsummon(); return; } } diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundAV.cpp b/src/server/game/Battlegrounds/Zones/BattlegroundAV.cpp index c57eeff9911..a348ec69b8a 100755 --- a/src/server/game/Battlegrounds/Zones/BattlegroundAV.cpp +++ b/src/server/game/Battlegrounds/Zones/BattlegroundAV.cpp @@ -177,7 +177,7 @@ void BattlegroundAV::HandleQuestComplete(uint32 questid, Player* player) case AV_QUEST_H_COMMANDER3: m_Team_QuestStatus[team][3]++; RewardReputationToTeam(team, 1, player->GetTeam()); - if (m_Team_QuestStatus[team][1] == 120) + if (m_Team_QuestStatus[team][3] == 120) sLog->outDebug(LOG_FILTER_BATTLEGROUND, "BG_AV Quest %i completed (need to implement some events here", questid); break; case AV_QUEST_A_BOSS1: diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundAV.h b/src/server/game/Battlegrounds/Zones/BattlegroundAV.h index 28d524977fc..dab67fe3258 100755 --- a/src/server/game/Battlegrounds/Zones/BattlegroundAV.h +++ b/src/server/game/Battlegrounds/Zones/BattlegroundAV.h @@ -1514,12 +1514,12 @@ enum BG_AV_Objectives struct BG_AV_NodeInfo { - uint16 TotalOwner; - uint16 Owner; - uint16 PrevOwner; BG_AV_States State; BG_AV_States PrevState; uint32 Timer; + uint16 TotalOwner; + uint16 Owner; + uint16 PrevOwner; bool Tower; }; diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundBE.cpp b/src/server/game/Battlegrounds/Zones/BattlegroundBE.cpp index d4086b2e48e..402406f7fd0 100755 --- a/src/server/game/Battlegrounds/Zones/BattlegroundBE.cpp +++ b/src/server/game/Battlegrounds/Zones/BattlegroundBE.cpp @@ -96,7 +96,7 @@ void BattlegroundBE::HandleKillPlayer(Player* player, Player* killer) bool BattlegroundBE::HandlePlayerUnderMap(Player* player) { - player->TeleportTo(GetMapId(), 6238.930176f, 262.963470f, 0.889519f, player->GetOrientation(), false); + player->TeleportTo(GetMapId(), 6238.930176f, 262.963470f, 0.889519f, player->GetOrientation()); return true; } diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundDS.cpp b/src/server/game/Battlegrounds/Zones/BattlegroundDS.cpp index d8f1883cbda..a64184261c7 100644 --- a/src/server/game/Battlegrounds/Zones/BattlegroundDS.cpp +++ b/src/server/game/Battlegrounds/Zones/BattlegroundDS.cpp @@ -202,7 +202,7 @@ void BattlegroundDS::HandleAreaTrigger(Player* player, uint32 trigger) bool BattlegroundDS::HandlePlayerUnderMap(Player* player) { - player->TeleportTo(GetMapId(), 1299.046f, 784.825f, 9.338f, 2.422f, false); + player->TeleportTo(GetMapId(), 1299.046f, 784.825f, 9.338f, 2.422f); return true; } diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundIC.cpp b/src/server/game/Battlegrounds/Zones/BattlegroundIC.cpp index 1c5041da017..90c0dae9f5e 100755 --- a/src/server/game/Battlegrounds/Zones/BattlegroundIC.cpp +++ b/src/server/game/Battlegrounds/Zones/BattlegroundIC.cpp @@ -843,12 +843,6 @@ void BattlegroundIC::DestroyGate(Player* player, GameObject* go) switch (go->GetEntry()) { case GO_HORDE_GATE_1: - if (!AddCreature(BG_IC_NpcSpawnlocs[BG_IC_NPC_OVERLORD_AGMAR].entry, BG_IC_NpcSpawnlocs[BG_IC_NPC_OVERLORD_AGMAR].type, BG_IC_NpcSpawnlocs[BG_IC_NPC_OVERLORD_AGMAR].team, - BG_IC_NpcSpawnlocs[BG_IC_NPC_OVERLORD_AGMAR].x, BG_IC_NpcSpawnlocs[BG_IC_NPC_OVERLORD_AGMAR].y, BG_IC_NpcSpawnlocs[BG_IC_NPC_OVERLORD_AGMAR].z, BG_IC_NpcSpawnlocs[BG_IC_NPC_OVERLORD_AGMAR].o, - RESPAWN_ONE_DAY)) - { - sLog->outError(LOG_FILTER_BATTLEGROUND, "Isle of Conquest: There was an error spawning creature %u", BG_IC_NpcSpawnlocs[BG_IC_NPC_OVERLORD_AGMAR].entry); - } lang_entry = LANG_BG_IC_NORTH_GATE_DESTROYED; break; case GO_HORDE_GATE_2: @@ -860,18 +854,23 @@ void BattlegroundIC::DestroyGate(Player* player, GameObject* go) lang_entry = LANG_BG_IC_EAST_GATE_DESTROYED; break; case GO_ALLIANCE_GATE_3: - if (!AddCreature(BG_IC_NpcSpawnlocs[BG_IC_NPC_HIGH_COMMANDER_HALFORD_WYRMBANE].entry, BG_IC_NpcSpawnlocs[BG_IC_NPC_HIGH_COMMANDER_HALFORD_WYRMBANE].type, BG_IC_NpcSpawnlocs[BG_IC_NPC_HIGH_COMMANDER_HALFORD_WYRMBANE].team, - BG_IC_NpcSpawnlocs[BG_IC_NPC_HIGH_COMMANDER_HALFORD_WYRMBANE].x, BG_IC_NpcSpawnlocs[BG_IC_NPC_HIGH_COMMANDER_HALFORD_WYRMBANE].y, BG_IC_NpcSpawnlocs[BG_IC_NPC_HIGH_COMMANDER_HALFORD_WYRMBANE].z, BG_IC_NpcSpawnlocs[BG_IC_NPC_HIGH_COMMANDER_HALFORD_WYRMBANE].o, - RESPAWN_ONE_DAY)) - { - sLog->outError(LOG_FILTER_BATTLEGROUND, "Isle of Conquest: There was an error spawning creature %u", BG_IC_NpcSpawnlocs[BG_IC_NPC_HIGH_COMMANDER_HALFORD_WYRMBANE].entry); - } lang_entry = LANG_BG_IC_SOUTH_GATE_DESTROYED; break; default: break; } + if (go->GetEntry() == GO_HORDE_GATE_1 || go->GetEntry() == GO_HORDE_GATE_2 || go->GetEntry() == GO_HORDE_GATE_3) + { + if (!GetBgMap()->GetCreature(BgCreatures[BG_IC_NpcSpawnlocs[BG_IC_NPC_OVERLORD_AGMAR].type]) && !AddCreature(BG_IC_NpcSpawnlocs[BG_IC_NPC_OVERLORD_AGMAR].entry, BG_IC_NpcSpawnlocs[BG_IC_NPC_OVERLORD_AGMAR].type, BG_IC_NpcSpawnlocs[BG_IC_NPC_OVERLORD_AGMAR].team, BG_IC_NpcSpawnlocs[BG_IC_NPC_OVERLORD_AGMAR].x, BG_IC_NpcSpawnlocs[BG_IC_NPC_OVERLORD_AGMAR].y, BG_IC_NpcSpawnlocs[BG_IC_NPC_OVERLORD_AGMAR].z, BG_IC_NpcSpawnlocs[BG_IC_NPC_OVERLORD_AGMAR].o, RESPAWN_ONE_DAY)) + sLog->outError(LOG_FILTER_BATTLEGROUND, "Isle of Conquest: There was an error spawning creature %u", BG_IC_NpcSpawnlocs[BG_IC_NPC_OVERLORD_AGMAR].entry); + } + else if (go->GetEntry() == GO_ALLIANCE_GATE_1 || go->GetEntry() == GO_ALLIANCE_GATE_2 || go->GetEntry() == GO_ALLIANCE_GATE_3) + { + if (!GetBgMap()->GetCreature(BgCreatures[BG_IC_NpcSpawnlocs[BG_IC_NPC_HIGH_COMMANDER_HALFORD_WYRMBANE].type]) && !AddCreature(BG_IC_NpcSpawnlocs[BG_IC_NPC_HIGH_COMMANDER_HALFORD_WYRMBANE].entry, BG_IC_NpcSpawnlocs[BG_IC_NPC_HIGH_COMMANDER_HALFORD_WYRMBANE].type, BG_IC_NpcSpawnlocs[BG_IC_NPC_HIGH_COMMANDER_HALFORD_WYRMBANE].team, BG_IC_NpcSpawnlocs[BG_IC_NPC_HIGH_COMMANDER_HALFORD_WYRMBANE].x, BG_IC_NpcSpawnlocs[BG_IC_NPC_HIGH_COMMANDER_HALFORD_WYRMBANE].y, BG_IC_NpcSpawnlocs[BG_IC_NPC_HIGH_COMMANDER_HALFORD_WYRMBANE].z, BG_IC_NpcSpawnlocs[BG_IC_NPC_HIGH_COMMANDER_HALFORD_WYRMBANE].o, RESPAWN_ONE_DAY)) + sLog->outError(LOG_FILTER_BATTLEGROUND, "Isle of Conquest: There was an error spawning creature %u", BG_IC_NpcSpawnlocs[BG_IC_NPC_HIGH_COMMANDER_HALFORD_WYRMBANE].entry); + } + SendMessage2ToAll(lang_entry, CHAT_MSG_BG_SYSTEM_NEUTRAL, NULL, (player->GetTeamId() == TEAM_ALLIANCE ? LANG_BG_IC_HORDE_KEEP : LANG_BG_IC_ALLIANCE_KEEP)); } diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundNA.cpp b/src/server/game/Battlegrounds/Zones/BattlegroundNA.cpp index deb2e565b07..6677d665290 100755 --- a/src/server/game/Battlegrounds/Zones/BattlegroundNA.cpp +++ b/src/server/game/Battlegrounds/Zones/BattlegroundNA.cpp @@ -93,7 +93,7 @@ void BattlegroundNA::HandleKillPlayer(Player* player, Player* killer) bool BattlegroundNA::HandlePlayerUnderMap(Player* player) { - player->TeleportTo(GetMapId(), 4055.504395f, 2919.660645f, 13.611241f, player->GetOrientation(), false); + player->TeleportTo(GetMapId(), 4055.504395f, 2919.660645f, 13.611241f, player->GetOrientation()); return true; } diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundRL.cpp b/src/server/game/Battlegrounds/Zones/BattlegroundRL.cpp index 14c1052ed64..2c715193a3b 100755 --- a/src/server/game/Battlegrounds/Zones/BattlegroundRL.cpp +++ b/src/server/game/Battlegrounds/Zones/BattlegroundRL.cpp @@ -93,7 +93,7 @@ void BattlegroundRL::HandleKillPlayer(Player* player, Player* killer) bool BattlegroundRL::HandlePlayerUnderMap(Player* player) { - player->TeleportTo(GetMapId(), 1285.810547f, 1667.896851f, 39.957642f, player->GetOrientation(), false); + player->TeleportTo(GetMapId(), 1285.810547f, 1667.896851f, 39.957642f, player->GetOrientation()); return true; } diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundRV.cpp b/src/server/game/Battlegrounds/Zones/BattlegroundRV.cpp index 80018bce0a2..130ba4f3290 100755 --- a/src/server/game/Battlegrounds/Zones/BattlegroundRV.cpp +++ b/src/server/game/Battlegrounds/Zones/BattlegroundRV.cpp @@ -137,7 +137,7 @@ void BattlegroundRV::HandleKillPlayer(Player* player, Player* killer) bool BattlegroundRV::HandlePlayerUnderMap(Player* player) { - player->TeleportTo(GetMapId(), 763.5f, -284, 28.276f, 2.422f, false); + player->TeleportTo(GetMapId(), 763.5f, -284, 28.276f, 2.422f); return true; } diff --git a/src/server/game/DataStores/DBCStructure.h b/src/server/game/DataStores/DBCStructure.h index ef949f43f49..e4ba3ebb12e 100755 --- a/src/server/game/DataStores/DBCStructure.h +++ b/src/server/game/DataStores/DBCStructure.h @@ -1203,7 +1203,7 @@ struct LFGDungeonEntry uint32 recmaxlevel; // 22 int32 map; // 23 uint32 difficulty; // 24 - //uint32 flags; // 25 (flags & 4) = IsHoliday + uint32 flags; // 25 uint32 type; // 26 //uint32 unk; // 27 //char* iconname; // 28 diff --git a/src/server/game/DataStores/DBCfmt.h b/src/server/game/DataStores/DBCfmt.h index 39d031e96f9..39f9009419e 100755 --- a/src/server/game/DataStores/DBCfmt.h +++ b/src/server/game/DataStores/DBCfmt.h @@ -77,7 +77,7 @@ const char ItemLimitCategoryEntryfmt[]="nxxxxxxxxxxxxxxxxxii"; const char ItemRandomPropertiesfmt[]="nxiiixxssssssssssssssssx"; const char ItemRandomSuffixfmt[]="nssssssssssssssssxxiiixxiiixx"; const char ItemSetEntryfmt[]="dssssssssssssssssxiiiiiiiiiixxxxxxxiiiiiiiiiiiiiiiiii"; -const char LFGDungeonEntryfmt[]="nxxxxxxxxxxxxxxxxxiiiiiiixixxixixxxxxxxxxxxxxxxxx"; +const char LFGDungeonEntryfmt[]="nxxxxxxxxxxxxxxxxxiiiiiiiiixxixixxxxxxxxxxxxxxxxx"; const char LiquidTypefmt[]="nxxixixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; const char LockEntryfmt[]="niiiiiiiiiiiiiiiiiiiiiiiixxxxxxxx"; const char MailTemplateEntryfmt[]="nxxxxxxxxxxxxxxxxxssssssssssssssssx"; diff --git a/src/server/game/DungeonFinding/LFGMgr.cpp b/src/server/game/DungeonFinding/LFGMgr.cpp index 3a75066fa2d..2b425dffff0 100755 --- a/src/server/game/DungeonFinding/LFGMgr.cpp +++ b/src/server/game/DungeonFinding/LFGMgr.cpp @@ -24,6 +24,7 @@ #include "SocialMgr.h" #include "LFGMgr.h" #include "GroupMgr.h" +#include "GameEventMgr.h" #include "LFGScripts.h" #include "LFGGroupData.h" #include "LFGPlayerData.h" @@ -185,6 +186,38 @@ void LFGMgr::LoadRewards() sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u lfg dungeon rewards in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); } +void LFGMgr::LoadEntrancePositions() +{ + uint32 oldMSTime = getMSTime(); + m_entrancePositions.clear(); + + QueryResult result = WorldDatabase.Query("SELECT dungeonId, position_x, position_y, position_z, orientation FROM lfg_entrances"); + + if (!result) + { + sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 lfg entrance positions. DB table `lfg_entrances` is empty!"); + return; + } + + uint32 count = 0; + + do + { + Field* fields = result->Fetch(); + uint32 dungeonId = fields[0].GetUInt32(); + Position pos; + pos.m_positionX = fields[1].GetFloat(); + pos.m_positionY = fields[2].GetFloat(); + pos.m_positionZ = fields[3].GetFloat(); + pos.m_orientation = fields[4].GetFloat(); + m_entrancePositions[dungeonId] = pos; + ++count; + } + while (result->NextRow()); + + sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u lfg entrance positions in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); +} + void LFGMgr::Update(uint32 diff) { if (!m_update) @@ -438,6 +471,12 @@ void LFGMgr::InitializeLockedDungeons(Player* player) locktype = LFG_LOCKSTATUS_TOO_LOW_LEVEL; else if (dungeon->maxlevel < level) locktype = LFG_LOCKSTATUS_TOO_HIGH_LEVEL; + else if (dungeon->flags & LFG_FLAG_SEASONAL) + { + if (HolidayIds holiday = sLFGMgr->GetDungeonSeason(dungeon->ID)) + if (!IsHolidayActive(holiday)) + locktype = LFG_LOCKSTATUS_NOT_IN_SEASON; + } else if (locktype == LFG_LOCKSTATUS_OK && ar) { if (ar->achievement && !player->HasAchieved(ar->achievement)) @@ -460,7 +499,6 @@ void LFGMgr::InitializeLockedDungeons(Player* player) locktype = LFG_LOCKSTATUS_TOO_HIGH_GEAR_SCORE; locktype = LFG_LOCKSTATUS_ATTUNEMENT_TOO_LOW_LEVEL; locktype = LFG_LOCKSTATUS_ATTUNEMENT_TOO_HIGH_LEVEL; - locktype = LFG_LOCKSTATUS_NOT_IN_SEASON; // Need list of instances and needed season to open */ if (locktype != LFG_LOCKSTATUS_OK) @@ -1808,13 +1846,16 @@ void LFGMgr::TeleportPlayer(Player* player, bool out, bool fromOpcode /*= false* if (!mapid) { - AreaTrigger const* at = sObjectMgr->GetMapEntranceTrigger(dungeon->map); - if (!at) + LfgEntrancePositionMap::const_iterator itr = m_entrancePositions.find(dungeon->ID); + if (itr != m_entrancePositions.end()) { - sLog->outError(LOG_FILTER_LFG, "LfgMgr::TeleportPlayer: Failed to teleport [" UI64FMTD "]: No areatrigger found for map: %u difficulty: %u", player->GetGUID(), dungeon->map, dungeon->difficulty); - error = LFG_TELEPORTERROR_INVALID_LOCATION; + mapid = dungeon->map; + x = itr->second.GetPositionX(); + y = itr->second.GetPositionY(); + z = itr->second.GetPositionZ(); + orientation = itr->second.GetOrientation(); } - else + else if (AreaTrigger const* at = sObjectMgr->GetMapEntranceTrigger(dungeon->map)) { mapid = at->target_mapId; x = at->target_X; @@ -1822,6 +1863,11 @@ void LFGMgr::TeleportPlayer(Player* player, bool out, bool fromOpcode /*= false* z = at->target_Z; orientation = at->target_Orientation; } + else + { + sLog->outError(LOG_FILTER_LFG, "LfgMgr::TeleportPlayer: Failed to teleport [" UI64FMTD "]: No areatrigger found for map: %u difficulty: %u", player->GetGUID(), dungeon->map, dungeon->difficulty); + error = LFG_TELEPORTERROR_INVALID_LOCATION; + } } if (error == LFG_TELEPORTERROR_OK) @@ -1889,11 +1935,11 @@ void LFGMgr::RewardDungeonDoneFor(const uint32 dungeonId, Player* player) ClearState(guid); SetState(guid, LFG_STATE_FINISHED_DUNGEON); - // Give rewards only if its a random dungeon + // Give rewards only if its a random or seasonal dungeon LFGDungeonEntry const* dungeon = sLFGDungeonStore.LookupEntry(rDungeonId); - if (!dungeon || dungeon->type != LFG_TYPE_RANDOM) + if (!dungeon || (dungeon->type != LFG_TYPE_RANDOM && !(dungeon->flags & LFG_FLAG_SEASONAL))) { - sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::RewardDungeonDoneFor: [" UI64FMTD "] dungeon %u is not random", guid, rDungeonId); + sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::RewardDungeonDoneFor: [" UI64FMTD "] dungeon %u is not random nor seasonal", guid, rDungeonId); return; } @@ -2001,6 +2047,31 @@ std::string LFGMgr::ConcatenateGuids(LfgGuidList check) return o.str(); } +HolidayIds LFGMgr::GetDungeonSeason(uint32 dungeonId) +{ + HolidayIds holiday = HOLIDAY_NONE; + + switch (dungeonId) + { + case 285: + holiday = HOLIDAY_HALLOWS_END; + break; + case 286: + holiday = HOLIDAY_FIRE_FESTIVAL; + break; + case 287: + holiday = HOLIDAY_BREWFEST; + break; + case 288: + holiday = HOLIDAY_LOVE_IS_IN_THE_AIR; + break; + default: + break; + } + + return holiday; +} + LfgState LFGMgr::GetState(uint64 guid) { sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::GetState: [" UI64FMTD "]", guid); diff --git a/src/server/game/DungeonFinding/LFGMgr.h b/src/server/game/DungeonFinding/LFGMgr.h index e341b21706f..9937759741b 100755 --- a/src/server/game/DungeonFinding/LFGMgr.h +++ b/src/server/game/DungeonFinding/LFGMgr.h @@ -41,6 +41,14 @@ enum LFGenum LFG_SPELL_LUCK_OF_THE_DRAW = 72221 }; +enum LfgFlags +{ + LFG_FLAG_UNK1 = 0x1, + LFG_FLAG_UNK2 = 0x2, + LFG_FLAG_SEASONAL = 0x4, + LFG_FLAG_UNK3 = 0x8 +}; + /// Determines the type of instance enum LfgType { @@ -143,6 +151,7 @@ typedef std::map<uint64, LfgProposalPlayer*> LfgProposalPlayerMap; typedef std::map<uint32, LfgPlayerBoot*> LfgPlayerBootMap; typedef std::map<uint64, LfgGroupData> LfgGroupDataMap; typedef std::map<uint64, LfgPlayerData> LfgPlayerDataMap; +typedef std::map<uint32, Position> LfgEntrancePositionMap; // Data needed by SMSG_LFG_JOIN_RESULT struct LfgJoinResultData @@ -279,6 +288,7 @@ class LFGMgr void UpdateProposal(uint32 proposalId, uint64 guid, bool accept); // Teleportation + void LoadEntrancePositions(); void TeleportPlayer(Player* player, bool out, bool fromOpcode = false); // Vote kick @@ -286,6 +296,8 @@ class LFGMgr void UpdateBoot(Player* player, bool accept); void OfferContinue(Group* grp); + HolidayIds GetDungeonSeason(uint32 dungeonId); + void InitializeLockedDungeons(Player* player); void _LoadFromDB(Field* fields, uint64 guid); @@ -349,6 +361,7 @@ class LFGMgr uint32 m_NumWaitTimeHealer; ///< Num of players used to calc healers wait time uint32 m_NumWaitTimeDps; ///< Num of players used to calc dps wait time LfgDungeonMap m_CachedDungeonMap; ///< Stores all dungeons by groupType + LfgEntrancePositionMap m_entrancePositions; ///< Stores special entrance positions // Reward System LfgRewardMap m_RewardMap; ///< Stores rewards for random dungeons // Queue diff --git a/src/server/game/DungeonFinding/LFGScripts.cpp b/src/server/game/DungeonFinding/LFGScripts.cpp index e4b67e22221..36f04b3020b 100644 --- a/src/server/game/DungeonFinding/LFGScripts.cpp +++ b/src/server/game/DungeonFinding/LFGScripts.cpp @@ -123,8 +123,8 @@ void LFGGroupScript::OnRemoveMember(Group* group, uint64 guid, RemoveMethod meth sLFGMgr->SetState(guid, LFG_STATE_NONE); if (Player* player = ObjectAccessor::FindPlayer(guid)) { - if (method == GROUP_REMOVEMETHOD_LEAVE && state != LFG_STATE_FINISHED_DUNGEON && player->HasAura(LFG_SPELL_DUNGEON_COOLDOWN)) - player->CastSpell(player, LFG_SPELL_DUNGEON_DESERTER, false); + if (method == GROUP_REMOVEMETHOD_LEAVE && sLFGMgr->GetState(gguid) != LFG_STATE_FINISHED_DUNGEON && sLFGMgr->GetDungeon(gguid, false)) + player->CastSpell(player, LFG_SPELL_DUNGEON_DESERTER, true); /* else if (group->isLfgKickActive()) // Update internal kick cooldown of kicked diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp index c4d42f7a537..da6e4ef8407 100755 --- a/src/server/game/Entities/GameObject/GameObject.cpp +++ b/src/server/game/Entities/GameObject/GameObject.cpp @@ -293,7 +293,7 @@ void GameObject::Update(uint32 diff) else if (Unit* owner = GetOwner()) { if (owner->isInCombat()) - m_cooldownTime = time(NULL) + goInfo->trap.cooldown; + m_cooldownTime = time(NULL) + goInfo->trap.startDelay; } m_lootState = GO_READY; break; @@ -417,7 +417,7 @@ void GameObject::Update(uint32 diff) bool IsBattlegroundTrap = false; //FIXME: this is activation radius (in different casting radius that must be selected from spell data) //TODO: move activated state code (cast itself) to GO_ACTIVATED, in this place only check activating and set state - float radius = (float)(goInfo->trap.radius)/2; // TODO rename radius to diameter (goInfo->trap.radius) should be (goInfo->trap.diameter) + float radius = (float)(goInfo->trap.radius)/3*2; // TODO rename radius to diameter (goInfo->trap.radius) should be (goInfo->trap.diameter) if (!radius) { if (goInfo->trap.cooldown != 3) // cast in other case (at some triggering/linked go/etc explicit call) @@ -1080,7 +1080,8 @@ void GameObject::Use(Unit* user) if (sScriptMgr->OnGossipHello(playerUser, this)) return; - AI()->GossipHello(playerUser); + if (AI()->GossipHello(playerUser)) + return; } // If cooldown data present in template diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index c41436e1947..50f5fd4c978 100755 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -7620,21 +7620,19 @@ void Player::DuelComplete(DuelCompleteType type) break; case DUEL_WON: UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOSE_DUEL, 1); - if (duel->opponent) - { - duel->opponent->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL, 1); + duel->opponent->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL, 1); + + // Credit for quest Death's Challenge + if (getClass() == CLASS_DEATH_KNIGHT && duel->opponent->GetQuestStatus(12733) == QUEST_STATUS_INCOMPLETE) + duel->opponent->CastSpell(duel->opponent, 52994, true); - //Credit for quest Death's Challenge - if (getClass() == CLASS_DEATH_KNIGHT && duel->opponent->GetQuestStatus(12733) == QUEST_STATUS_INCOMPLETE) - duel->opponent->CastSpell(duel->opponent, 52994, true); - } break; default: break; } // Victory emote spell - if (type != DUEL_INTERRUPTED && duel->opponent) + if (type != DUEL_INTERRUPTED) duel->opponent->CastSpell(duel->opponent, 52852, true); //Remove Duel Flag object @@ -9113,6 +9111,9 @@ void Player::SendInitWorldStates(uint32 zoneid, uint32 areaid) case 4100: // The Culling of Stratholme NumberOfFields = 13; break; + case 4987: // The Ruby Sanctum + NumberOfFields = 3; + break; case 4273: // Ulduar NumberOfFields = 10; break; @@ -9639,6 +9640,17 @@ void Player::SendInitWorldStates(uint32 zoneid, uint32 areaid) data << uint32(4345) << uint32(1); // 24 unknown } break; + // The Ruby Sanctum + case 4987: + if (instance && mapid == 724) + instance->FillInitialWorldStates(data); + else + { + data << uint32(5049) << uint32(50); // 9 WORLDSTATE_CORPOREALITY_MATERIAL + data << uint32(5050) << uint32(50); // 10 WORLDSTATE_CORPOREALITY_TWILIGHT + data << uint32(5051) << uint32(0); // 11 WORLDSTATE_CORPOREALITY_TOGGLE + } + break; // Icecrown Citadel case 4812: if (instance && mapid == 631) @@ -20831,6 +20843,9 @@ void Player::ContinueTaxiFlight() sLog->outDebug(LOG_FILTER_UNITS, "WORLD: Restart character %u taxi flight", GetGUIDLow()); uint32 mountDisplayId = sObjectMgr->GetTaxiMountDisplayId(sourceNode, GetTeam(), true); + if (!mountDisplayId) + return; + uint32 path = m_taxi.GetCurrentTaxiPath(); // search appropriate start path node diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 3742503c786..d171fa635d2 100755 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -10102,8 +10102,7 @@ Unit* Unit::GetMagicHitRedirectTarget(Unit* victim, SpellInfo const* spellInfo) if (Unit* magnet = (*itr)->GetBase()->GetCaster()) if (spellInfo->CheckExplicitTarget(this, magnet) == SPELL_CAST_OK && spellInfo->CheckTarget(this, magnet, false) == SPELL_CAST_OK - && _IsValidAttackTarget(magnet, spellInfo) - && IsWithinLOSInMap(magnet)) + && _IsValidAttackTarget(magnet, spellInfo)) { // TODO: handle this charge drop by proc in cast phase on explicit target (*itr)->GetBase()->DropCharge(AURA_REMOVE_BY_EXPIRE); diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index 9b29d644c36..ce329098bc6 100755 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -795,21 +795,21 @@ enum MeleeHitOutcome class DispelInfo { -private: - Unit* const m_dispeller; - uint32 const m_dispellerSpellId; - uint8 m_chargesRemoved; public: - explicit DispelInfo(Unit* _dispeller, uint32 _dispellerSpellId, uint8 _chargesRemoved) : - m_dispeller(_dispeller), m_dispellerSpellId(_dispellerSpellId), m_chargesRemoved(_chargesRemoved) {} + explicit DispelInfo(Unit* dispeller, uint32 dispellerSpellId, uint8 chargesRemoved) : + _dispellerUnit(dispeller), _dispellerSpell(dispellerSpellId), _chargesRemoved(chargesRemoved) {} - Unit* GetDispeller() { return m_dispeller; } - uint32 GetDispellerSpellId() const { return m_dispellerSpellId; } - uint8 GetRemovedCharges() const { return m_chargesRemoved; } + Unit* GetDispeller() const { return _dispellerUnit; } + uint32 GetDispellerSpellId() const { return _dispellerSpell; } + uint8 GetRemovedCharges() const { return _chargesRemoved; } void SetRemovedCharges(uint8 amount) { - m_chargesRemoved = amount; + _chargesRemoved = amount; } +private: + Unit* _dispellerUnit; + uint32 _dispellerSpell; + uint8 _chargesRemoved; }; struct CleanDamage diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index fd2a2fe143a..95a27fd14e1 100755 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -5531,7 +5531,7 @@ uint32 ObjectMgr::GetTaxiMountDisplayId(uint32 id, uint32 team, bool allowed_alt if (!mount_id) { sLog->outError(LOG_FILTER_SQL, "No displayid found for the taxi mount with the entry %u! Can't load it!", mount_entry); - return false; + return 0; } } } diff --git a/src/server/game/Handlers/LFGHandler.cpp b/src/server/game/Handlers/LFGHandler.cpp index aa6d208ad3a..f5d5a0b67e2 100755 --- a/src/server/game/Handlers/LFGHandler.cpp +++ b/src/server/game/Handlers/LFGHandler.cpp @@ -23,6 +23,7 @@ #include "LFGMgr.h" #include "ObjectMgr.h" #include "GroupMgr.h" +#include "GameEventMgr.h" #include "InstanceScript.h" void BuildPlayerLockDungeonBlock(WorldPacket& data, const LfgLockMap& lock) @@ -156,16 +157,25 @@ void WorldSession::HandleLfgPlayerLockInfoRequestOpcode(WorldPacket& /*recv_data sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_LFD_PLAYER_LOCK_INFO_REQUEST [" UI64FMTD "]", guid); // Get Random dungeons that can be done at a certain level and expansion - // FIXME - Should return seasonals (when not disabled) LfgDungeonSet randomDungeons; uint8 level = GetPlayer()->getLevel(); uint8 expansion = GetPlayer()->GetSession()->Expansion(); for (uint32 i = 0; i < sLFGDungeonStore.GetNumRows(); ++i) { LFGDungeonEntry const* dungeon = sLFGDungeonStore.LookupEntry(i); - if (dungeon && dungeon->type == LFG_TYPE_RANDOM && dungeon->expansion <= expansion && - dungeon->minlevel <= level && level <= dungeon->maxlevel) + if (dungeon && dungeon->expansion <= expansion && dungeon->minlevel <= level && level <= dungeon->maxlevel) + { + if (dungeon->flags & LFG_FLAG_SEASONAL) + { + if (HolidayIds holiday = sLFGMgr->GetDungeonSeason(dungeon->ID)) + if (!IsHolidayActive(holiday)) + continue; + } + else if (dungeon->type != LFG_TYPE_RANDOM) + continue; + randomDungeons.insert(dungeon->Entry()); + } } // Get player locked Dungeons diff --git a/src/server/game/Handlers/SpellHandler.cpp b/src/server/game/Handlers/SpellHandler.cpp index afaa38527bb..0e5d68d51e4 100755 --- a/src/server/game/Handlers/SpellHandler.cpp +++ b/src/server/game/Handlers/SpellHandler.cpp @@ -303,7 +303,8 @@ void WorldSession::HandleGameobjectReportUse(WorldPacket& recvPacket) if (!go->IsWithinDistInMap(_player, INTERACTION_DISTANCE)) return; - go->AI()->GossipHello(_player); + if (go->AI()->GossipHello(_player)) + return; _player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT, go->GetEntry()); } diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index 25ffc1d19f2..954dac232ab 100755 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -2430,7 +2430,7 @@ bool InstanceMap::AddPlayerToMap(Player* player) { // solo saves should be reset when entering a group InstanceGroupBind* groupBind = group->GetBoundInstance(this); - if (playerBind) + if (playerBind && playerBind->save != mapSave) { sLog->outError(LOG_FILTER_MAPS, "InstanceMap::Add: player %s(%d) is being put into instance %d, %d, %d, %d, %d, %d but he is in group %d and is bound to instance %d, %d, %d, %d, %d, %d!", player->GetName(), player->GetGUIDLow(), mapSave->GetMapId(), mapSave->GetInstanceId(), mapSave->GetDifficulty(), mapSave->GetPlayerCount(), mapSave->GetGroupCount(), mapSave->CanReset(), GUID_LOPART(group->GetLeaderGUID()), playerBind->save->GetMapId(), playerBind->save->GetInstanceId(), playerBind->save->GetDifficulty(), playerBind->save->GetPlayerCount(), playerBind->save->GetGroupCount(), playerBind->save->CanReset()); if (groupBind) diff --git a/src/server/game/Movement/Waypoints/WaypointManager.h b/src/server/game/Movement/Waypoints/WaypointManager.h index 73d611e0419..df20c513c90 100755 --- a/src/server/game/Movement/Waypoints/WaypointManager.h +++ b/src/server/game/Movement/Waypoints/WaypointManager.h @@ -27,9 +27,9 @@ struct WaypointData { uint32 id; float x, y, z, orientation; - bool run; uint32 delay; uint32 event_id; + bool run; uint8 event_chance; }; diff --git a/src/server/game/Scripting/ScriptLoader.cpp b/src/server/game/Scripting/ScriptLoader.cpp index 6442a35f0c3..5f3400804c3 100755 --- a/src/server/game/Scripting/ScriptLoader.cpp +++ b/src/server/game/Scripting/ScriptLoader.cpp @@ -503,6 +503,7 @@ void AddSC_ruby_sanctum(); void AddSC_boss_baltharus_the_warborn(); void AddSC_boss_saviana_ragefire(); void AddSC_boss_general_zarithrian(); +void AddSC_boss_halion(); void AddSC_dalaran(); void AddSC_borean_tundra(); @@ -1224,6 +1225,7 @@ void AddNorthrendScripts() AddSC_boss_baltharus_the_warborn(); AddSC_boss_saviana_ragefire(); AddSC_boss_general_zarithrian(); + AddSC_boss_halion(); AddSC_dalaran(); AddSC_borean_tundra(); diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 5db0e589e1b..a88ffffd5ea 100755 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -2002,7 +2002,8 @@ void Spell::SendLoot(uint64 guid, LootType loottype) if (sScriptMgr->OnGossipHello(player, gameObjTarget)) return; - gameObjTarget->AI()->GossipHello(player); + if (gameObjTarget->AI()->GossipHello(player)) + return; switch (gameObjTarget->GetGoType()) { diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp index c89442855e1..ec7359a7ba4 100755 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -645,7 +645,7 @@ SpellSpellGroupMapBounds SpellMgr::GetSpellSpellGroupMapBounds(uint32 spell_id) return SpellSpellGroupMapBounds(mSpellSpellGroup.lower_bound(spell_id), mSpellSpellGroup.upper_bound(spell_id)); } -uint32 SpellMgr::IsSpellMemberOfSpellGroup(uint32 spellid, SpellGroup groupid) const +bool SpellMgr::IsSpellMemberOfSpellGroup(uint32 spellid, SpellGroup groupid) const { SpellSpellGroupMapBounds spellGroup = GetSpellSpellGroupMapBounds(spellid); for (SpellSpellGroupMap::const_iterator itr = spellGroup.first; itr != spellGroup.second; ++itr) @@ -3402,7 +3402,7 @@ void SpellMgr::LoadDbcDataCorrections() break; case 69055: // Saber Lash (Lord Marrowgar) case 70814: // Saber Lash (Lord Marrowgar) - spellInfo->EffectRadiusIndex[0] = EFFECT_RADIUS_5_YARDS; // 5yd + spellInfo->EffectRadiusIndex[0] = EFFECT_RADIUS_5_YARDS; // 5yd break; case 69075: // Bone Storm (Lord Marrowgar) case 70834: // Bone Storm (Lord Marrowgar) @@ -3412,7 +3412,7 @@ void SpellMgr::LoadDbcDataCorrections() case 71160: // Plague Stench (Stinky) case 71161: // Plague Stench (Stinky) case 71123: // Decimate (Stinky & Precious) - spellInfo->EffectRadiusIndex[0] = EFFECT_RADIUS_100_YARDS; // 100yd + spellInfo->EffectRadiusIndex[0] = EFFECT_RADIUS_100_YARDS; // 100yd break; case 72378: // Blood Nova (Deathbringer Saurfang) case 73058: // Blood Nova (Deathbringer Saurfang) @@ -3430,7 +3430,7 @@ void SpellMgr::LoadDbcDataCorrections() spellInfo->Effect[2] = 0; break; case 70460: // Coldflame Jets (Traps after Saurfang) - spellInfo->DurationIndex = 1; // 10 seconds + spellInfo->DurationIndex = 1; // 10 seconds break; case 71412: // Green Ooze Summon (Professor Putricide) case 71415: // Orange Ooze Summon (Professor Putricide) @@ -3453,7 +3453,7 @@ void SpellMgr::LoadDbcDataCorrections() case 72464: // Mutated Plague (Professor Putricide) case 72506: // Mutated Plague (Professor Putricide) case 72507: // Mutated Plague (Professor Putricide) - spellInfo->EffectRadiusIndex[0] = EFFECT_RADIUS_50000_YARDS; // 50000yd + spellInfo->EffectRadiusIndex[0] = EFFECT_RADIUS_50000_YARDS; // 50000yd break; case 70911: // Unbound Plague (Professor Putricide) (needs target selection script) case 72854: // Unbound Plague (Professor Putricide) (needs target selection script) @@ -3464,7 +3464,7 @@ void SpellMgr::LoadDbcDataCorrections() case 71518: // Unholy Infusion Quest Credit (Professor Putricide) case 72934: // Blood Infusion Quest Credit (Blood-Queen Lana'thel) case 72289: // Frost Infusion Quest Credit (Sindragosa) - spellInfo->EffectRadiusIndex[0] = EFFECT_RADIUS_50000_YARDS; // another missing radius + spellInfo->EffectRadiusIndex[0] = EFFECT_RADIUS_50000_YARDS; // another missing radius break; case 71708: // Empowered Flare (Blood Prince Council) case 72785: // Empowered Flare (Blood Prince Council) @@ -3513,62 +3513,62 @@ void SpellMgr::LoadDbcDataCorrections() case 73708: // Defile case 73709: // Defile case 73710: // Defile - spellInfo->EffectRadiusIndex[0] = EFFECT_RADIUS_200_YARDS; // 200yd - spellInfo->EffectRadiusIndex[1] = EFFECT_RADIUS_200_YARDS; // 200yd + spellInfo->EffectRadiusIndex[0] = EFFECT_RADIUS_200_YARDS; // 200yd + spellInfo->EffectRadiusIndex[1] = EFFECT_RADIUS_200_YARDS; // 200yd break; case 69030: // Val'kyr Target Search - spellInfo->EffectRadiusIndex[0] = EFFECT_RADIUS_200_YARDS; // 200yd - spellInfo->EffectRadiusIndex[1] = EFFECT_RADIUS_200_YARDS; // 200yd + spellInfo->EffectRadiusIndex[0] = EFFECT_RADIUS_200_YARDS; // 200yd + spellInfo->EffectRadiusIndex[1] = EFFECT_RADIUS_200_YARDS; // 200yd break; case 69198: // Raging Spirit Visual - spellInfo->rangeIndex = 13; // 50000yd + spellInfo->rangeIndex = 13; // 50000yd break; case 73654: // Harvest Souls case 74295: // Harvest Souls case 74296: // Harvest Souls case 74297: // Harvest Souls - spellInfo->EffectRadiusIndex[0] = EFFECT_RADIUS_50000_YARDS; // 50000yd - spellInfo->EffectRadiusIndex[1] = EFFECT_RADIUS_50000_YARDS; // 50000yd - spellInfo->EffectRadiusIndex[2] = EFFECT_RADIUS_50000_YARDS; // 50000yd + spellInfo->EffectRadiusIndex[0] = EFFECT_RADIUS_50000_YARDS; // 50000yd + spellInfo->EffectRadiusIndex[1] = EFFECT_RADIUS_50000_YARDS; // 50000yd + spellInfo->EffectRadiusIndex[2] = EFFECT_RADIUS_50000_YARDS; // 50000yd break; case 73655: // Harvest Soul spellInfo->AttributesEx3 |= SPELL_ATTR3_NO_DONE_BONUS; break; case 73540: // Summon Shadow Trap - spellInfo->DurationIndex = 23; // 90 seconds + spellInfo->DurationIndex = 23; // 90 seconds break; case 73530: // Shadow Trap (visual) - spellInfo->DurationIndex = 28; // 5 seconds + spellInfo->DurationIndex = 28; // 5 seconds break; case 73529: // Shadow Trap - spellInfo->EffectRadiusIndex[1] = EFFECT_RADIUS_10_YARDS; // 10yd + spellInfo->EffectRadiusIndex[1] = EFFECT_RADIUS_10_YARDS; // 10yd break; case 74282: // Shadow Trap (searcher) - spellInfo->EffectRadiusIndex[0] = EFFECT_RADIUS_3_YARDS; // 3yd + spellInfo->EffectRadiusIndex[0] = EFFECT_RADIUS_3_YARDS; // 3yd break; case 72595: // Restore Soul case 73650: // Restore Soul - spellInfo->EffectRadiusIndex[0] = EFFECT_RADIUS_200_YARDS; // 200yd + spellInfo->EffectRadiusIndex[0] = EFFECT_RADIUS_200_YARDS; // 200yd break; case 74086: // Destroy Soul - spellInfo->EffectRadiusIndex[0] = EFFECT_RADIUS_200_YARDS; // 200yd + spellInfo->EffectRadiusIndex[0] = EFFECT_RADIUS_200_YARDS; // 200yd break; case 74302: // Summon Spirit Bomb case 74342: // Summon Spirit Bomb - spellInfo->EffectRadiusIndex[0] = EFFECT_RADIUS_200_YARDS; // 200yd + spellInfo->EffectRadiusIndex[0] = EFFECT_RADIUS_200_YARDS; // 200yd spellInfo->MaxAffectedTargets = 1; break; case 74341: // Summon Spirit Bomb case 74343: // Summon Spirit Bomb - spellInfo->EffectRadiusIndex[0] = EFFECT_RADIUS_200_YARDS; // 200yd + spellInfo->EffectRadiusIndex[0] = EFFECT_RADIUS_200_YARDS; // 200yd spellInfo->MaxAffectedTargets = 3; break; case 73579: // Summon Spirit Bomb - spellInfo->EffectRadiusIndex[0] = EFFECT_RADIUS_25_YARDS; // 25yd + spellInfo->EffectRadiusIndex[0] = EFFECT_RADIUS_25_YARDS; // 25yd break; case 72350: // Fury of Frostmourne - spellInfo->EffectRadiusIndex[0] = EFFECT_RADIUS_50000_YARDS; // 50000yd - spellInfo->EffectRadiusIndex[1] = EFFECT_RADIUS_50000_YARDS; // 50000yd + spellInfo->EffectRadiusIndex[0] = EFFECT_RADIUS_50000_YARDS; // 50000yd + spellInfo->EffectRadiusIndex[1] = EFFECT_RADIUS_50000_YARDS; // 50000yd break; case 75127: // Kill Frostmourne Players case 72351: // Fury of Frostmourne @@ -3576,19 +3576,35 @@ void SpellMgr::LoadDbcDataCorrections() case 72429: // Mass Resurrection case 73159: // Play Movie case 73582: // Trigger Vile Spirit (Inside, Heroic) - spellInfo->EffectRadiusIndex[0] = EFFECT_RADIUS_50000_YARDS; // 50000yd + spellInfo->EffectRadiusIndex[0] = EFFECT_RADIUS_50000_YARDS; // 50000yd break; case 72376: // Raise Dead spellInfo->MaxAffectedTargets = 3; - spellInfo->EffectRadiusIndex[0] = EFFECT_RADIUS_50000_YARDS; // 50000yd + spellInfo->EffectRadiusIndex[0] = EFFECT_RADIUS_50000_YARDS; // 50000yd break; case 71809: // Jump - spellInfo->rangeIndex = 3; // 20yd - spellInfo->EffectRadiusIndex[0] = EFFECT_RADIUS_25_YARDS; // 25yd + spellInfo->rangeIndex = 3; // 20yd + spellInfo->EffectRadiusIndex[0] = EFFECT_RADIUS_25_YARDS; // 25yd break; case 72405: // Broken Frostmourne - spellInfo->EffectRadiusIndex[1] = EFFECT_RADIUS_200_YARDS; // 200yd + spellInfo->EffectRadiusIndex[1] = EFFECT_RADIUS_200_YARDS; // 200yd + break; + // ENDOF ICECROWN CITADEL SPELLS + // + // RUBY SANCTUM SPELLS + // + case 74769: // Twilight Cutter + case 77844: // Twilight Cutter + case 77845: // Twilight Cutter + case 77846: // Twilight Cutter + spellInfo->EffectRadiusIndex[0] = EFFECT_RADIUS_100_YARDS; // 100yd break; + case 75509: // Twilight Mending + spellInfo->AttributesEx6 |= SPELL_ATTR6_CAN_TARGET_INVISIBLE; + spellInfo->AttributesEx2 |= SPELL_ATTR2_CAN_TARGET_NOT_IN_LOS; + break; + // ENDOF RUBY SANCTUM SPELLS + // case 40055: // Introspection case 40165: // Introspection case 40166: // Introspection @@ -3630,5 +3646,5 @@ void SpellMgr::LoadDbcDataCorrections() properties = const_cast<SummonPropertiesEntry*>(sSummonPropertiesStore.LookupEntry(647)); // 52893 properties->Type = SUMMON_TYPE_TOTEM; - sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loading spell dbc data corrections in %u ms", GetMSTimeDiffToNow(oldMSTime)); + sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded spell dbc data corrections in %u ms", GetMSTimeDiffToNow(oldMSTime)); } diff --git a/src/server/game/Spells/SpellMgr.h b/src/server/game/Spells/SpellMgr.h index 9423ebf2893..9646bc9dabf 100755 --- a/src/server/game/Spells/SpellMgr.h +++ b/src/server/game/Spells/SpellMgr.h @@ -643,7 +643,7 @@ class SpellMgr // Spell Groups table SpellSpellGroupMapBounds GetSpellSpellGroupMapBounds(uint32 spell_id) const; - uint32 IsSpellMemberOfSpellGroup(uint32 spellid, SpellGroup groupid) const; + bool IsSpellMemberOfSpellGroup(uint32 spellid, SpellGroup groupid) const; SpellGroupSpellMapBounds GetSpellGroupSpellMapBounds(SpellGroup group_id) const; void GetSetOfSpellsInSpellGroup(SpellGroup group_id, std::set<uint32>& foundSpells) const; diff --git a/src/server/game/Spells/SpellScript.h b/src/server/game/Spells/SpellScript.h index a5d77806739..c8126f8aaf3 100755 --- a/src/server/game/Spells/SpellScript.h +++ b/src/server/game/Spells/SpellScript.h @@ -582,8 +582,8 @@ class AuraScript : public _SpellScript class ScriptStateStore { public: - uint8 _currentScriptState; AuraApplication const* _auraApplication; + uint8 _currentScriptState; bool _defaultActionPrevented; ScriptStateStore(uint8 currentScriptState, AuraApplication const* auraApplication, bool defaultActionPrevented) : _currentScriptState(currentScriptState), _auraApplication(auraApplication), _defaultActionPrevented(defaultActionPrevented) diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 39effcf217e..2f022fac4e6 100755 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -1475,6 +1475,9 @@ void World::SetInitialWorldSettings() sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Loading LFG rewards..."); sLFGMgr->LoadRewards(); + sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Loading LFG entrance positions..."); + sLFGMgr->LoadEntrancePositions(); + sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Loading SpellArea Data..."); // must be after quest load sSpellMgr->LoadSpellAreas(); diff --git a/src/server/scripts/Commands/cs_misc.cpp b/src/server/scripts/Commands/cs_misc.cpp index 2c9623b23a6..f7280cf906e 100644 --- a/src/server/scripts/Commands/cs_misc.cpp +++ b/src/server/scripts/Commands/cs_misc.cpp @@ -476,12 +476,12 @@ public: } else if (map->IsDungeon()) { - Map* map = target->GetMap(); + Map* destMap = target->GetMap(); - if (map->Instanceable() && map->GetInstanceId() != map->GetInstanceId()) + if (destMap->Instanceable() && destMap->GetInstanceId() != map->GetInstanceId()) target->UnbindInstance(map->GetInstanceId(), target->GetDungeonDifficulty(), true); - // we are in instance, and can summon only player in our group with us as lead + // we are in an instance, and can only summon players in our group with us as leader if (!handler->GetSession()->GetPlayer()->GetGroup() || !target->GetGroup() || (target->GetGroup()->GetLeaderGUID() != handler->GetSession()->GetPlayer()->GetGUID()) || (handler->GetSession()->GetPlayer()->GetGroup()->GetLeaderGUID() != handler->GetSession()->GetPlayer()->GetGUID())) @@ -2568,7 +2568,7 @@ public: { pet->SavePetToDB(PET_SAVE_AS_CURRENT); // not let dismiss dead pet - if (pet && pet->isAlive()) + if (pet->isAlive()) player->RemovePet(pet, PET_SAVE_NOT_IN_SLOT); } } diff --git a/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_headless_horseman.cpp b/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_headless_horseman.cpp index 028cf640d21..ca91fb920b1 100644 --- a/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_headless_horseman.cpp +++ b/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_headless_horseman.cpp @@ -27,6 +27,7 @@ EndScriptData */ #include "ScriptedCreature.h" #include "SpellMgr.h" #include "scarlet_monastery.h" +#include "LFGMgr.h" //this texts are already used by 3975 and 3976 enum Says @@ -562,6 +563,13 @@ public: CAST_AI(mob_wisp_invis::mob_wisp_invisAI, wisp->AI())->SetType(4); if (instance) instance->SetData(DATA_HORSEMAN_EVENT, DONE); + + Map::PlayerList const& players = me->GetMap()->GetPlayers(); + if (!players.isEmpty()) + for (Map::PlayerList::const_iterator i = players.begin(); i != players.end(); ++i) + if (Player* player = i->getSource()) + if (player->IsAtGroupRewardDistance(me)) + sLFGMgr->RewardDungeonDoneFor(285, player); } void SpellHit(Unit* caster, const SpellInfo* spell) diff --git a/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_amanitar.cpp b/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_amanitar.cpp index ebab2cb99a9..b34a990d39d 100644 --- a/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_amanitar.cpp +++ b/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_amanitar.cpp @@ -44,7 +44,7 @@ enum event { EVENT_SPAWN = 1, EVENT_MINI, - EVENT_ROOT, + EVENT_ROOT, EVENT_BASH, EVENT_BOLT, EVENT_AURA @@ -58,13 +58,13 @@ public: struct boss_amanitarAI : public BossAI { boss_amanitarAI(Creature* creature) : BossAI(creature, DATA_AMANITAR) { } - + void Reset() { _Reset(); - + me->SetMeleeDamageSchool(SPELL_SCHOOL_NATURE); - me->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_NATURE, true); + me->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_NATURE, true); summons.DespawnAll(); if (instance) @@ -119,7 +119,7 @@ public: { trigger->DisappearAndDie(); } - else + else { u = 1 - u; trigger->DisappearAndDie(); @@ -143,7 +143,7 @@ public: { switch (eventId) { - case EVENT_SPAWN: + case EVENT_SPAWN: SpawnAdds(); events.ScheduleEvent(EVENT_SPAWN, 20*IN_MILLISECONDS); break; @@ -151,7 +151,7 @@ public: DoCast(SPELL_MINI); events.ScheduleEvent(EVENT_MINI, urand(25,30)*IN_MILLISECONDS); break; - case EVENT_ROOT: + case EVENT_ROOT: DoCast(SelectTarget(SELECT_TARGET_RANDOM,0, 100, true),SPELL_ENTANGLING_ROOTS,true); events.ScheduleEvent(EVENT_ROOT, urand(10,15)*IN_MILLISECONDS); break; @@ -192,7 +192,7 @@ public: { events.Reset(); events.ScheduleEvent(EVENT_AURA, 1*IN_MILLISECONDS); - + me->SetDisplayId(me->GetCreatureTemplate()->Modelid2); DoCast(SPELL_PUTRID_MUSHROOM); diff --git a/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_elder_nadox.cpp b/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_elder_nadox.cpp index 9274a016e2f..fe86f54ae15 100644 --- a/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_elder_nadox.cpp +++ b/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_elder_nadox.cpp @@ -17,6 +17,7 @@ #include "ScriptMgr.h" #include "ScriptedCreature.h" +#include "SpellScript.h" #include "ahnkahet.h" enum Yells @@ -30,22 +31,31 @@ enum Yells enum Spells { - SPELL_BROOD_PLAGUE = 56130, - H_SPELL_BROOD_PLAGUE = 59467, - H_SPELL_BROOD_RAGE = 59465, - SPELL_ENRAGE = 26662, // Enraged if too far away from home - SPELL_SUMMON_SWARMERS = 56119, //2x 30178 -- 2x every 10secs - SPELL_SUMMON_SWARM_GUARD = 56120, //1x 30176 -- every 25secs + SPELL_BROOD_PLAGUE = 56130, + H_SPELL_BROOD_RAGE = 59465, + SPELL_ENRAGE = 26662, // Enraged if too far away from home + SPELL_SUMMON_SWARMERS = 56119, // 2x 30178 -- 2x every 10secs + SPELL_SUMMON_SWARM_GUARD = 56120, // 1x 30176 -- every 25% + // Spells Adds + SPELL_SPRINT = 56354, + SPELL_GUARDIAN_AURA = 56151 }; enum Creatures { - MOB_AHNKAHAR_SWARMER = 30178, - MOB_AHNKAHAR_GUARDIAN_ENTRY = 30176 + NPC_AHNKAHAR_SWARMER = 30178, + NPC_AHNKAHAR_GUARDIAN = 30176 }; -#define ACTION_AHNKAHAR_GUARDIAN_DEAD 1 -#define DATA_RESPECT_YOUR_ELDERS 2 +enum Events +{ + EVENT_PLAGUE = 1, + EVENT_RAGE, + EVENT_SUMMON_SWARMER, + EVENT_CHECK_ENRAGE, + EVENT_SPRINT, + DATA_RESPECT_YOUR_ELDERS +}; class boss_elder_nadox : public CreatureScript { @@ -54,32 +64,24 @@ class boss_elder_nadox : public CreatureScript struct boss_elder_nadoxAI : public ScriptedAI { - boss_elder_nadoxAI(Creature* creature) : ScriptedAI(creature) + boss_elder_nadoxAI(Creature* creature) : ScriptedAI(creature), summons(me) { instance = creature->GetInstanceScript(); } - uint32 uiPlagueTimer; - uint32 uiRagueTimer; - - uint32 uiSwarmerSpawnTimer; - uint32 uiGuardSpawnTimer; - uint32 uiEnrageTimer; - - bool bGuardSpawned; - bool respectYourElders; - + bool GuardianDied; + uint8 AmountHealthModifier; InstanceScript* instance; + SummonList summons; + EventMap events; void Reset() { - uiPlagueTimer = 13000; - uiRagueTimer = 20000; - uiSwarmerSpawnTimer = 10000; - uiGuardSpawnTimer = 25000; - uiEnrageTimer = 5000; - bGuardSpawned = false; - respectYourElders = true; + events.Reset(); + summons.DespawnAll(); + + AmountHealthModifier = 1; + GuardianDied = false; if (instance) instance->SetData(DATA_ELDER_NADOX_EVENT, NOT_STARTED); @@ -91,98 +93,95 @@ class boss_elder_nadox : public CreatureScript if (instance) instance->SetData(DATA_ELDER_NADOX_EVENT, IN_PROGRESS); - } - void KilledUnit(Unit* /*who*/) - { - Talk(SAY_SLAY); + events.ScheduleEvent(EVENT_PLAGUE, 13*IN_MILLISECONDS); + events.ScheduleEvent(EVENT_SUMMON_SWARMER, 10*IN_MILLISECONDS); + + if (IsHeroic()) + { + events.ScheduleEvent(EVENT_RAGE, 12*IN_MILLISECONDS); + events.ScheduleEvent(EVENT_CHECK_ENRAGE, 5*IN_MILLISECONDS); + } } - void JustDied(Unit* /*killer*/) + void JustSummoned(Creature* summon) { - Talk(SAY_DEATH); - - if (instance) - instance->SetData(DATA_ELDER_NADOX_EVENT, DONE); + summons.Summon(summon); + summon->AI()->DoZoneInCombat(); } - void DoAction(int32 const action) + void SummonedCreatureDies(Creature* summon, Unit* /*killer*/) { - if (action == ACTION_AHNKAHAR_GUARDIAN_DEAD) - respectYourElders = false; + if (summon->GetEntry() == NPC_AHNKAHAR_GUARDIAN) + GuardianDied = true; } uint32 GetData(uint32 type) { if (type == DATA_RESPECT_YOUR_ELDERS) - return respectYourElders ? 1 : 0; + return !GuardianDied ? 1 : 0; return 0; } + void KilledUnit(Unit* /*victim*/) + { + Talk(SAY_SLAY); + } + + void JustDied(Unit* /*killer*/) + { + Talk(SAY_DEATH); + + summons.DespawnAll(); + + if (instance) + instance->SetData(DATA_ELDER_NADOX_EVENT, DONE); + } + void UpdateAI(uint32 const diff) { if (!UpdateVictim()) return; - if (uiPlagueTimer <= diff) - { - DoCastVictim(SPELL_BROOD_PLAGUE); - uiPlagueTimer = 15000; - } - else - uiPlagueTimer -= diff; + events.Update(diff); - if (IsHeroic()) + while (uint32 eventId = events.ExecuteEvent()) { - if (uiRagueTimer <= diff) + switch (eventId) { - if (Creature* Swarmer = me->FindNearestCreature(MOB_AHNKAHAR_SWARMER, 35.0f)) - { - DoCast(Swarmer, H_SPELL_BROOD_RAGE, true); - uiRagueTimer = 15000; - } + case EVENT_PLAGUE: + DoCast(SelectTarget(SELECT_TARGET_RANDOM,0, 100, true),SPELL_BROOD_PLAGUE,true); + events.ScheduleEvent(EVENT_PLAGUE, 15*IN_MILLISECONDS); + break; + case EVENT_RAGE: + DoCast(H_SPELL_BROOD_RAGE); + events.ScheduleEvent(EVENT_RAGE, urand(10*IN_MILLISECONDS, 50*IN_MILLISECONDS)); + break; + case EVENT_SUMMON_SWARMER: + DoCast(me, SPELL_SUMMON_SWARMERS); + if (urand(1, 3) == 3) // 33% chance of dialog + Talk(SAY_EGG_SAC); + events.ScheduleEvent(EVENT_SUMMON_SWARMER, 10*IN_MILLISECONDS); + break; + case EVENT_CHECK_ENRAGE: + if (me->HasAura(SPELL_ENRAGE)) + return; + if (me->GetPositionZ() < 24.0f) + DoCast(me, SPELL_ENRAGE, true); + events.ScheduleEvent(EVENT_CHECK_ENRAGE, 5*IN_MILLISECONDS); + break; + default: + break; } - else - uiRagueTimer -= diff; } - if (uiSwarmerSpawnTimer <= diff) - { - DoCast(me, SPELL_SUMMON_SWARMERS, true); - DoCast(me, SPELL_SUMMON_SWARMERS); - if (urand(1, 3) == 3) // 33% chance of dialog - Talk(SAY_EGG_SAC); - - uiSwarmerSpawnTimer = 10000; - } - else - uiSwarmerSpawnTimer -= diff; - - if (!bGuardSpawned && uiGuardSpawnTimer <= diff) + if (me->HealthBelowPct(100 - AmountHealthModifier * 25)) { Talk(EMOTE_HATCHES, me->GetGUID()); DoCast(me, SPELL_SUMMON_SWARM_GUARD); - bGuardSpawned = true; + ++AmountHealthModifier; } - else - uiGuardSpawnTimer -= diff; - - if (uiEnrageTimer <= diff) - { - if (me->HasAura(SPELL_ENRAGE, 0)) - return; - - float x, y, z, o; - me->GetHomePosition(x, y, z, o); - if (z < 24) - if (!me->IsNonMeleeSpellCasted(false)) - DoCast(me, SPELL_ENRAGE, true); - - uiEnrageTimer = 5000; - } - else - uiEnrageTimer -= diff; DoMeleeAttackIfReady(); } @@ -194,12 +193,6 @@ class boss_elder_nadox : public CreatureScript } }; -enum AddSpells -{ - SPELL_SPRINT = 56354, - SPELL_GUARDIAN_AURA = 56151 -}; - class mob_ahnkahar_nerubian : public CreatureScript { public: @@ -207,50 +200,44 @@ class mob_ahnkahar_nerubian : public CreatureScript struct mob_ahnkahar_nerubianAI : public ScriptedAI { - mob_ahnkahar_nerubianAI(Creature* creature) : ScriptedAI(creature) - { - instance = creature->GetInstanceScript(); - } + mob_ahnkahar_nerubianAI(Creature* creature) : ScriptedAI(creature) { } - InstanceScript* instance; - uint32 uiSprintTimer; + EventMap events; void Reset() { - if (me->GetEntry() == MOB_AHNKAHAR_GUARDIAN_ENTRY) + if (me->GetEntry() == NPC_AHNKAHAR_GUARDIAN) DoCast(me, SPELL_GUARDIAN_AURA, true); - uiSprintTimer = 10000; + + events.ScheduleEvent(EVENT_SPRINT, 13*IN_MILLISECONDS); } void JustDied(Unit* /*killer*/) { - if (me->GetEntry() == MOB_AHNKAHAR_GUARDIAN_ENTRY) - if (Creature* Nadox = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_ELDER_NADOX))) - Nadox->AI()->DoAction(ACTION_AHNKAHAR_GUARDIAN_DEAD); + if (me->GetEntry() == NPC_AHNKAHAR_GUARDIAN) + me->RemoveAurasDueToSpell(SPELL_GUARDIAN_AURA); } - void EnterCombat(Unit* /*who*/) {} - - void UpdateAI(uint32 const diff) + void UpdateAI(const uint32 diff) { if (!UpdateVictim()) return; - if (me->GetEntry() == MOB_AHNKAHAR_GUARDIAN_ENTRY) - me->RemoveAurasDueToSpell(SPELL_GUARDIAN_AURA); + events.Update(diff); - if (instance) - if (instance->GetData(DATA_ELDER_NADOX_EVENT) != IN_PROGRESS) - me->DespawnOrUnsummon(); + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; - if (uiSprintTimer <= diff) + while (uint32 eventId = events.ExecuteEvent()) { - DoCast(me, SPELL_SPRINT); - uiSprintTimer = 25000; + switch (eventId) + { + case EVENT_SPRINT: + DoCast(me, SPELL_SPRINT); + events.ScheduleEvent(EVENT_SPRINT, 20*IN_MILLISECONDS); + break; + } } - else - uiSprintTimer -= diff; - DoMeleeAttackIfReady(); } }; @@ -274,6 +261,7 @@ public: creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE); creature->UpdateAllStats(); } + void Reset() {} void EnterCombat(Unit* /*who*/) {} void AttackStart(Unit* /*victim*/) {} @@ -287,28 +275,68 @@ public: } }; -class achievement_respect_your_elders : public AchievementCriteriaScript +class GuardianCheck { - public: - achievement_respect_your_elders() : AchievementCriteriaScript("achievement_respect_your_elders") {} +public: + bool operator()(const WorldObject* target) const + { + if (target->GetEntry() == NPC_AHNKAHAR_GUARDIAN) + return true; + + return false; + } +}; - bool OnCheck(Player* /*player*/, Unit* target) +class spell_elder_nadox_guardian : public SpellScriptLoader +{ +public: + spell_elder_nadox_guardian() : SpellScriptLoader("spell_elder_nadox_guardian") { } + + class spell_elder_nadox_guardian_SpellScript : public SpellScript + { + PrepareSpellScript(spell_elder_nadox_guardian_SpellScript); + + void FilterTargets(std::list<WorldObject*>& targets) { - if (!target) - return false; + targets.remove_if(GuardianCheck()); + } + + void Register() + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_elder_nadox_guardian_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ALLY); + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_elder_nadox_guardian_SpellScript::FilterTargets, EFFECT_1, TARGET_UNIT_SRC_AREA_ALLY); + } + }; + + SpellScript* GetSpellScript() const + { + return new spell_elder_nadox_guardian_SpellScript(); + } +}; - if (Creature* Nadox = target->ToCreature()) - if (Nadox->AI()->GetData(DATA_RESPECT_YOUR_ELDERS)) - return true; +class achievement_respect_your_elders : public AchievementCriteriaScript +{ +public: + achievement_respect_your_elders() : AchievementCriteriaScript("achievement_respect_your_elders") {} + bool OnCheck(Player* /*player*/, Unit* target) + { + if (!target) return false; - } + + if (Creature* Nadox = target->ToCreature()) + if (Nadox->AI()->GetData(DATA_RESPECT_YOUR_ELDERS)) + return true; + + return false; + } }; void AddSC_boss_elder_nadox() { - new boss_elder_nadox; - new mob_ahnkahar_nerubian; - new mob_nadox_eggs; + new boss_elder_nadox(); + new mob_ahnkahar_nerubian(); + new mob_nadox_eggs(); + new spell_elder_nadox_guardian(); new achievement_respect_your_elders(); } diff --git a/src/server/scripts/Northrend/CMakeLists.txt b/src/server/scripts/Northrend/CMakeLists.txt index dd8dd17c947..22d0f37a37f 100644 --- a/src/server/scripts/Northrend/CMakeLists.txt +++ b/src/server/scripts/Northrend/CMakeLists.txt @@ -51,6 +51,7 @@ set(scripts_STAT_SRCS Northrend/ChamberOfAspects/RubySanctum/boss_baltharus_the_warborn.cpp Northrend/ChamberOfAspects/RubySanctum/boss_saviana_ragefire.cpp Northrend/ChamberOfAspects/RubySanctum/boss_general_zarithrian.cpp + Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.h Northrend/FrozenHalls/HallsOfReflection/boss_falric.cpp Northrend/FrozenHalls/HallsOfReflection/instance_halls_of_reflection.cpp diff --git a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_general_zarithrian.cpp b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_general_zarithrian.cpp index 41eb31d815d..531e5d9268e 100644 --- a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_general_zarithrian.cpp +++ b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_general_zarithrian.cpp @@ -158,9 +158,9 @@ class boss_general_zarithrian : public CreatureScript { case EVENT_SUMMON_ADDS: { - if (Creature* stalker1 = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_ZARITHIAN_SPAWN_STALKER_1))) + if (Creature* stalker1 = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_ZARITHRIAN_SPAWN_STALKER_1))) stalker1->AI()->DoCast(stalker1, SPELL_SUMMON_FLAMECALLER); - if (Creature* stalker2 = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_ZARITHIAN_SPAWN_STALKER_2))) + if (Creature* stalker2 = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_ZARITHRIAN_SPAWN_STALKER_2))) stalker2->AI()->DoCast(stalker2, SPELL_SUMMON_FLAMECALLER); Talk(SAY_ADDS); events.ScheduleEvent(EVENT_SUMMON_ADDS, 42000); diff --git a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp new file mode 100644 index 00000000000..64210e97122 --- /dev/null +++ b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp @@ -0,0 +1,1740 @@ +/* + * Copyright (C) 2008-2012 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 "SpellScript.h" +#include "SpellAuraEffects.h" +#include "Spell.h" +#include "Vehicle.h" +#include "MapManager.h" +#include "GameObjectAI.h" +#include "ScriptedCreature.h" +#include "ruby_sanctum.h" + +/* ScriptData +SDName: ruby_sanctum +SDAuthors: Kaelima, Warpten +SD%Complete: 90% +SDComment: Based on Kaelima's initial work (nearly half of it). Corporeality handling is a pure guess, we lack info. +SDCategory: Chamber of Aspects +EndScriptData */ + +enum Texts +{ + // Shared + SAY_REGENERATE = 0, // Without pressure in both realms, %s begins to regenerate. + + // Halion + SAY_INTRO = 1, // Meddlesome insects! You are too late. The Ruby Sanctum is lost! + SAY_AGGRO = 2, // Your world teeters on the brink of annihilation. You will ALL bear witness to the coming of a new age of DESTRUCTION! + SAY_METEOR_STRIKE = 3, // The heavens burn! + SAY_PHASE_TWO = 4, // You will find only suffering within the realm of twilight! Enter if you dare! + SAY_DEATH = 5, // Relish this victory, mortals, for it will be your last! This world will burn with the master's return! + SAY_KILL = 6, // Another "hero" falls. + SAY_BERSERK = 7, // Not good enough. + EMOTE_CORPOREALITY_POT = 8, // Your efforts force %s further out of the physical realm! + EMOTE_CORPOREALITY_PIP = 9, // Your companions' efforts force %s further into the physical realm! + + // Twilight Halion + SAY_SPHERE_PULSE = 1, // Beware the shadow! + SAY_PHASE_THREE = 2, // I am the light and the darkness! Cower, mortals, before the herald of Deathwing! + EMOTE_CORPOREALITY_TIT = 3, // Your companions' efforts force %s further into the twilight realm! + EMOTE_CORPOREALITY_TOT = 4, // Your efforts force %s further out of the twilight realm! + + EMOTE_WARN_LASER = 0, // The orbiting spheres pulse with dark energy! +}; + +enum Spells +{ + // Halion + SPELL_FLAME_BREATH = 74525, + SPELL_CLEAVE = 74524, + SPELL_METEOR_STRIKE = 74637, + SPELL_TAIL_LASH = 74531, + + SPELL_FIERY_COMBUSTION = 74562, + SPELL_MARK_OF_COMBUSTION = 74567, + SPELL_FIERY_COMBUSTION_EXPLOSION = 74607, + SPELL_FIERY_COMBUSTION_SUMMON = 74610, + + // Combustion & Consumption + SPELL_SCALE_AURA = 70507, // Aura created in spell_dbc. + SPELL_COMBUSTION_DAMAGE_AURA = 74629, + SPELL_CONSUMPTION_DAMAGE_AURA = 74803, + + // Twilight Halion + SPELL_DARK_BREATH = 74806, + + SPELL_MARK_OF_CONSUMPTION = 74795, + SPELL_SOUL_CONSUMPTION = 74792, + SPELL_SOUL_CONSUMPTION_EXPLOSION = 74799, + SPELL_SOUL_CONSUMPTION_SUMMON = 74800, + + // Living Inferno + SPELL_BLAZING_AURA = 75885, + + // Halion Controller + SPELL_COSMETIC_FIRE_PILLAR = 76006, + SPELL_FIERY_EXPLOSION = 76010, + SPELL_CLEAR_DEBUFFS = 75396, + + // Meteor Strike + SPELL_METEOR_STRIKE_COUNTDOWN = 74641, + SPELL_METEOR_STRIKE_AOE_DAMAGE = 74648, + SPELL_METEOR_STRIKE_FIRE_AURA_1 = 74713, + SPELL_METEOR_STRIKE_FIRE_AURA_2 = 74718, + SPELL_BIRTH_NO_VISUAL = 40031, + + // Shadow Orb + SPELL_TWILIGHT_CUTTER = 74768, // Unknown dummy effect (EFFECT_0) + SPELL_TWILIGHT_CUTTER_TRIGGERED = 74769, + SPELL_TWILIGHT_PULSE_PERIODIC = 78861, + SPELL_TRACK_ROTATION = 74758, + + // Misc + SPELL_TWILIGHT_DIVISION = 75063, // Phase spell from phase 2 to phase 3 + SPELL_LEAVE_TWILIGHT_REALM = 74812, + SPELL_TWILIGHT_PHASING = 74808, // Phase spell from phase 1 to phase 2 + SPELL_SUMMON_TWILIGHT_PORTAL = 74809, // Summons go 202794 + SPELL_SUMMON_EXIT_PORTALS = 74805, // Custom spell created in spell_dbc. + SPELL_TWILIGHT_MENDING = 75509, + SPELL_TWILIGHT_REALM = 74807, + SPELL_COPY_DAMAGE = 74810 // Aura not found in DBCs. +}; + +enum Events +{ + // Halion + EVENT_ACTIVATE_FIREWALL = 1, + EVENT_CLEAVE = 2, + EVENT_FLAME_BREATH = 3, + EVENT_METEOR_STRIKE = 4, + EVENT_FIERY_COMBUSTION = 5, + EVENT_TAIL_LASH = 6, + + // Twilight Halion + EVENT_DARK_BREATH = 7, + EVENT_SOUL_CONSUMPTION = 8, + + // Meteor Strike + EVENT_SPAWN_METEOR_FLAME = 9, + + // Halion Controller + EVENT_START_INTRO = 10, + EVENT_INTRO_PROGRESS_1 = 11, + EVENT_INTRO_PROGRESS_2 = 12, + EVENT_INTRO_PROGRESS_3 = 13, + EVENT_CHECK_CORPOREALITY = 14, + EVENT_SHADOW_PULSARS_SHOOT = 15, + EVENT_TRIGGER_BERSERK = 16, + EVENT_TWILIGHT_MENDING = 17 +}; + +enum Actions +{ + // Meteor Strike + ACTION_METEOR_STRIKE_BURN = 1, + ACTION_METEOR_STRIKE_AOE = 2, + + // Halion Controller + ACTION_PHASE_TWO = 3, + ACTION_PHASE_THREE = 4, + ACTION_CLEANUP = 5, + + // Orb Carrier + ACTION_SHOOT = 6 +}; + +enum Phases +{ + PHASE_ALL = 0, + PHASE_INTRO = 1, + PHASE_ONE = 2, + PHASE_TWO = 3, + PHASE_THREE = 4, + + PHASE_INTRO_MASK = 1 << PHASE_INTRO, + PHASE_ONE_MASK = 1 << PHASE_ONE, + PHASE_TWO_MASK = 1 << PHASE_TWO, + PHASE_THREE_MASK = 1 << PHASE_THREE +}; + +enum Misc +{ + DATA_TWILIGHT_DAMAGE_TAKEN = 1, + DATA_MATERIAL_DAMAGE_TAKEN = 2, + DATA_STACKS_DISPELLED = 3, + DATA_FIGHT_PHASE = 4, + DATA_EVADE_METHOD = 5 +}; + +enum OrbCarrierSeats +{ + SEAT_NORTH = 0, + SEAT_SOUTH = 1, + SEAT_EAST = 2, + SEAT_WEST = 3 +}; + +enum CorporealityEvent +{ + CORPOREALITY_NONE = 0, + CORPOREALITY_TWILIGHT_MENDING = 1, + CORPOREALITY_INCREASE = 2, + CORPOREALITY_DECREASE = 3 +}; + +Position const HalionSpawnPos = {3156.67f, 533.8108f, 72.98822f, 3.159046f}; + +uint8 const MAX_CORPOREALITY_STATE = 11; + +struct CorporealityEntry +{ + uint32 materialRealmSpell; + uint32 twilightRealmSpell; +}; + +CorporealityEntry const _corporealityReference[MAX_CORPOREALITY_STATE] = { + {74836, 74831}, + {74835, 74830}, + {74834, 74829}, + {74833, 74828}, + {74832, 74827}, + {74826, 74826}, + {74827, 74832}, + {74828, 74833}, + {74829, 74834}, + {74830, 74835}, + {74831, 74836} +}; + +struct generic_halionAI : public BossAI +{ + generic_halionAI(Creature* creature, uint32 bossId) : BossAI(creature, bossId), _canEvade(false) { } + + void EnterCombat(Unit* /*who*/) + { + Talk(SAY_AGGRO); + _EnterCombat(); + _canEvade = false; + events.Reset(); + events.ScheduleEvent(EVENT_CLEAVE, urand(8000, 10000)); + } + + void EnterEvadeMode() + { + instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me); + _EnterEvadeMode(); + } + + void ExecuteEvent(uint32 const eventId) + { + switch (eventId) + { + case EVENT_CLEAVE: + DoCastVictim(SPELL_CLEAVE); + events.ScheduleEvent(EVENT_CLEAVE, urand(8000, 10000)); + break; + } + } + + void UpdateAI(uint32 const diff) + { + if (!UpdateVictim()) + return; + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + while (uint32 eventId = events.ExecuteEvent()) + ExecuteEvent(eventId); + + DoMeleeAttackIfReady(); + } + + void SetData(uint32 index, uint32 dataValue) + { + switch (index) + { + case DATA_EVADE_METHOD: + _canEvade = (dataValue == 1); + break; + default: + break; + } + } + + void SpellHit(Unit* /*who*/, SpellInfo const* spellInfo) + { + if (spellInfo->Id == SPELL_TWILIGHT_MENDING) + Talk(SAY_REGENERATE); + } + +protected: + bool _canEvade; +}; + +class boss_halion : public CreatureScript +{ + public: + boss_halion() : CreatureScript("boss_halion") { } + + struct boss_halionAI : public generic_halionAI + { + boss_halionAI(Creature* creature) : generic_halionAI(creature, DATA_HALION) { } + + void Reset() + { + generic_halionAI::Reset(); + me->SetReactState(REACT_DEFENSIVE); + + instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me); + + me->RemoveAurasDueToSpell(SPELL_TWILIGHT_PHASING); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + } + + void EnterEvadeMode() + { + // Phase 1: We always can evade. Phase 2 & 3: We can evade if and only if the controller tells us to. + // Controller has absolute priority over the phasemask. + if ((events.GetPhaseMask() & PHASE_ONE_MASK) || _canEvade) + generic_halionAI::EnterEvadeMode(); + } + + void EnterCombat(Unit* who) + { + generic_halionAI::EnterCombat(who); + + instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, me, 1); + instance->SetBossState(DATA_HALION, IN_PROGRESS); + + events.SetPhase(PHASE_ONE); + events.ScheduleEvent(EVENT_ACTIVATE_FIREWALL, 10000); + events.ScheduleEvent(EVENT_FLAME_BREATH, urand(10000, 12000)); + events.ScheduleEvent(EVENT_METEOR_STRIKE, urand(20000, 25000)); + events.ScheduleEvent(EVENT_FIERY_COMBUSTION, urand(15000, 18000)); + events.ScheduleEvent(EVENT_TAIL_LASH, 10000); + + if (Creature* controller = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_HALION_CONTROLLER))) + controller->AI()->SetData(DATA_FIGHT_PHASE, PHASE_ONE); + } + + void JustDied(Unit* /*killer*/) + { + _JustDied(); + + Talk(SAY_DEATH); + instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + + if (Creature* controller = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_HALION_CONTROLLER))) + me->Kill(controller); + } + + Position const* GetMeteorStrikePosition() const { return &_meteorStrikePos; } + + void DamageTaken(Unit* attacker, uint32& damage) + { + if (me->HealthBelowPctDamaged(75, damage) && (events.GetPhaseMask() & PHASE_ONE_MASK)) + { + events.SetPhase(PHASE_TWO); + Talk(SAY_PHASE_TWO); + + me->CastStop(); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + DoCast(me, SPELL_TWILIGHT_PHASING); + + if (Creature* controller = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_HALION_CONTROLLER))) + controller->AI()->SetData(DATA_FIGHT_PHASE, PHASE_TWO); + return; + } + + if (events.GetPhaseMask() & PHASE_THREE_MASK) + { + // Don't consider copied damage. + if (!me->InSamePhase(attacker)) + return; + + if (Creature* controller = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_HALION_CONTROLLER))) + controller->AI()->SetData(DATA_MATERIAL_DAMAGE_TAKEN, damage); + } + } + + void UpdateAI(uint32 const diff) + { + if (events.GetPhaseMask() & PHASE_TWO_MASK) + return; + + generic_halionAI::UpdateAI(diff); + } + + void ExecuteEvent(uint32 const eventId) + { + switch (eventId) + { + case EVENT_ACTIVATE_FIREWALL: + { + // Flame ring is activated 10 seconds after starting encounter, DOOR_TYPE_ROOM is only instant. + for (uint8 i = DATA_FLAME_RING; i <= DATA_TWILIGHT_FLAME_RING; ++i) + if (GameObject* flameRing = ObjectAccessor::GetGameObject(*me, instance->GetData64(i))) + instance->HandleGameObject(instance->GetData64(DATA_FLAME_RING), false, flameRing); + break; + } + case EVENT_FLAME_BREATH: + DoCast(me, SPELL_FLAME_BREATH); + events.ScheduleEvent(EVENT_FLAME_BREATH, 25000); + break; + case EVENT_TAIL_LASH: + DoCastAOE(SPELL_TAIL_LASH); + events.ScheduleEvent(EVENT_TAIL_LASH, 10000); + break; + case EVENT_METEOR_STRIKE: + { + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 0.0f, true, -SPELL_TWILIGHT_REALM)) + { + target->GetPosition(&_meteorStrikePos); + me->CastSpell(_meteorStrikePos.GetPositionX(), _meteorStrikePos.GetPositionY(), _meteorStrikePos.GetPositionZ(), SPELL_METEOR_STRIKE, true, NULL, NULL, me->GetGUID()); + Talk(SAY_METEOR_STRIKE); + } + events.ScheduleEvent(EVENT_METEOR_STRIKE, 40000); + break; + } + case EVENT_FIERY_COMBUSTION: + { + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1, 0.0f, true, -SPELL_TWILIGHT_REALM)) + DoCast(target, SPELL_FIERY_COMBUSTION); + events.ScheduleEvent(EVENT_FIERY_COMBUSTION, 25000); + break; + } + default: + generic_halionAI::ExecuteEvent(eventId); + break; + } + } + + void SetData(uint32 index, uint32 value) + { + switch (index) + { + case DATA_FIGHT_PHASE: + events.SetPhase(value); + break; + default: + generic_halionAI::SetData(index, value); + } + } + + private: + Position _meteorStrikePos; + }; + + CreatureAI* GetAI(Creature* creature) const + { + return GetRubySanctumAI<boss_halionAI>(creature); + } +}; + +typedef boss_halion::boss_halionAI HalionAI; + +class boss_twilight_halion : public CreatureScript +{ + public: + boss_twilight_halion() : CreatureScript("boss_twilight_halion") { } + + struct boss_twilight_halionAI : public generic_halionAI + { + boss_twilight_halionAI(Creature* creature) : generic_halionAI(creature, DATA_TWILIGHT_HALION) + { + Creature* halion = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_HALION)); + if (!halion) + return; + + // We use explicit targeting here to avoid conditions + SPELL_ATTR6_CANT_TARGET_SELF. + // Using AddAura because no spell cast packet in sniffs. + halion->AddAura(SPELL_COPY_DAMAGE, me); + me->AddAura(SPELL_COPY_DAMAGE, halion); + + me->SetHealth(halion->GetHealth()); + me->SetPhaseMask(0x20, true); + me->SetReactState(REACT_AGGRESSIVE); + + instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, me, 2); + + events.Reset(); + events.SetPhase(PHASE_TWO); + events.ScheduleEvent(EVENT_DARK_BREATH, urand(10000, 15000)); + events.ScheduleEvent(EVENT_SOUL_CONSUMPTION, 20000); + events.ScheduleEvent(EVENT_TAIL_LASH, 10000); + } + + void EnterEvadeMode() + { + // We don't care about evading, we will be despawned. + } + + void KilledUnit(Unit* victim) + { + if (victim->GetTypeId() == TYPEID_PLAYER) + Talk(SAY_KILL); + + // Victims should not be in the Twilight Realm + me->CastSpell(victim, SPELL_LEAVE_TWILIGHT_REALM, true); + } + + void JustDied(Unit* killer) + { + if (Creature* halion = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_HALION))) + { + // Ensure looting + if (me->IsDamageEnoughForLootingAndReward()) + halion->LowerPlayerDamageReq(halion->GetMaxHealth()); + + if (halion->isAlive()) + killer->Kill(halion); + } + + if (Creature* controller = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_HALION_CONTROLLER))) + controller->Kill(controller); + + instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me); + } + + void DamageTaken(Unit* attacker, uint32& damage) + { + if (me->HealthBelowPctDamaged(50, damage) && (events.GetPhaseMask() & PHASE_TWO_MASK)) + { + events.SetPhase(PHASE_THREE); + me->CastStop(); + DoCast(me, SPELL_TWILIGHT_DIVISION); + Talk(SAY_PHASE_THREE); + return; + } + + if (events.GetPhaseMask() & PHASE_THREE_MASK) + { + // Don't consider copied damage. + if (!me->InSamePhase(attacker)) + return; + + if (Creature* controller = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_HALION_CONTROLLER))) + controller->AI()->SetData(DATA_TWILIGHT_DAMAGE_TAKEN, damage); + } + } + + void SpellHit(Unit* who, SpellInfo const* spell) + { + switch (spell->Id) + { + case SPELL_TWILIGHT_DIVISION: + if (Creature* controller = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_HALION_CONTROLLER))) + controller->AI()->SetData(DATA_FIGHT_PHASE, PHASE_THREE); + break; + default: + generic_halionAI::SpellHit(who, spell); + break; + } + } + + void ExecuteEvent(uint32 const eventId) + { + switch (eventId) + { + case EVENT_DARK_BREATH: + DoCast(me, SPELL_DARK_BREATH); + events.ScheduleEvent(EVENT_DARK_BREATH, urand(10000, 15000)); + break; + case EVENT_SOUL_CONSUMPTION: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1, 0.0f, true, SPELL_TWILIGHT_REALM)) + DoCast(target, SPELL_SOUL_CONSUMPTION); + events.ScheduleEvent(EVENT_SOUL_CONSUMPTION, 20000); + break; + case EVENT_TAIL_LASH: + DoCastAOE(SPELL_TAIL_LASH); + events.ScheduleEvent(EVENT_TAIL_LASH, 10000); + break; + default: + generic_halionAI::ExecuteEvent(eventId); + break; + } + } + + private: + EventMap events; + }; + + CreatureAI* GetAI(Creature* creature) const + { + return GetRubySanctumAI<boss_twilight_halionAI>(creature); + } +}; + +class npc_halion_controller : public CreatureScript +{ + public: + npc_halion_controller() : CreatureScript("npc_halion_controller") { } + + struct npc_halion_controllerAI : public ScriptedAI + { + npc_halion_controllerAI(Creature* creature) : ScriptedAI(creature), + _instance(creature->GetInstanceScript()), _summons(me) + { + me->SetPhaseMask(me->GetPhaseMask() | 0x20, true); + _events.SetPhase(PHASE_INTRO); + } + + void Reset() + { + _summons.DespawnAll(); + _events.Reset(); + _materialCorporealityValue = 5; + + DoCast(me, SPELL_CLEAR_DEBUFFS); + } + + void JustSummoned(Creature* who) + { + _summons.Summon(who); + } + + void JustDied(Unit* /*killer*/) + { + _events.Reset(); + _summons.DespawnAll(); + + DoCast(me, SPELL_CLEAR_DEBUFFS); + } + + void EnterCombat(Unit* /*who*/) + { + _twilightDamageTaken = 0; + _materialDamageTaken = 0; + + _events.ScheduleEvent(EVENT_TRIGGER_BERSERK, 8 * MINUTE * IN_MILLISECONDS); + } + + void JustReachedHome() + { + if (Creature* twilightHalion = ObjectAccessor::GetCreature(*me, _instance->GetData64(DATA_TWILIGHT_HALION))) + twilightHalion->DespawnOrUnsummon(); + + if (Creature* halion = ObjectAccessor::GetCreature(*me, _instance->GetData64(DATA_HALION))) + { + halion->AI()->SetData(DATA_EVADE_METHOD, 1); + halion->AI()->EnterEvadeMode(); + } + + _instance->SetBossState(DATA_HALION, FAIL); + } + + void DoAction(int32 const action) + { + switch (action) + { + case ACTION_INTRO_HALION: + _events.Reset(); + _events.SetPhase(PHASE_INTRO); + _events.ScheduleEvent(EVENT_START_INTRO, 2000); + break; + default: + break; + } + } + + void UpdateAI(uint32 const diff) + { + // The isInCombat() check is needed because that check should be false when Halion is + // not engaged, while it would return true without as UpdateVictim() checks for + // combat state. + if (!(_events.GetPhaseMask() & PHASE_INTRO_MASK) && me->isInCombat() && !UpdateVictim()) + { + EnterEvadeMode(); + return; + } + + _events.Update(diff); + + while (uint32 eventId = _events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_START_INTRO: + DoCast(me, SPELL_COSMETIC_FIRE_PILLAR, true); + _events.ScheduleEvent(EVENT_INTRO_PROGRESS_1, 4000); + break; + case EVENT_INTRO_PROGRESS_1: + for (uint8 i = DATA_BURNING_TREE_3; i <= DATA_BURNING_TREE_4; ++i) + if (GameObject* tree = ObjectAccessor::GetGameObject(*me, _instance->GetData64(i))) + _instance->HandleGameObject(_instance->GetData64(i), true, tree); + _events.ScheduleEvent(EVENT_INTRO_PROGRESS_2, 4000); + break; + case EVENT_INTRO_PROGRESS_2: + for (uint8 i = DATA_BURNING_TREE_1; i <= DATA_BURNING_TREE_2; ++i) + if (GameObject* tree = ObjectAccessor::GetGameObject(*me, _instance->GetData64(i))) + _instance->HandleGameObject(_instance->GetData64(i), true, tree); + _events.ScheduleEvent(EVENT_INTRO_PROGRESS_3, 4000); + break; + case EVENT_INTRO_PROGRESS_3: + DoCast(me, SPELL_FIERY_EXPLOSION); + if (Creature* halion = me->GetMap()->SummonCreature(NPC_HALION, HalionSpawnPos)) + halion->AI()->Talk(SAY_INTRO); + break; + case EVENT_TWILIGHT_MENDING: + if (Creature* halion = ObjectAccessor::GetCreature(*me, _instance->GetData64(DATA_HALION))) + if (Creature* twilightHalion = ObjectAccessor::GetCreature(*me, _instance->GetData64(DATA_TWILIGHT_HALION))) + twilightHalion->CastSpell((Unit*)NULL, SPELL_TWILIGHT_MENDING, true); + break; + case EVENT_TRIGGER_BERSERK: + for (uint8 i = DATA_HALION; i <= DATA_TWILIGHT_HALION; i++) + if (Creature* halion = ObjectAccessor::GetCreature(*me, _instance->GetData64(i))) + halion->CastSpell(halion, SPELL_BERSERK, true); + break; + case EVENT_SHADOW_PULSARS_SHOOT: + if (Creature* twilightHalion = ObjectAccessor::GetCreature(*me, _instance->GetData64(DATA_TWILIGHT_HALION))) + twilightHalion->AI()->Talk(SAY_SPHERE_PULSE); + + if (Creature* orbCarrier = ObjectAccessor::GetCreature(*me, _instance->GetData64(DATA_ORB_CARRIER))) + orbCarrier->AI()->DoAction(ACTION_SHOOT); + + _events.ScheduleEvent(EVENT_SHADOW_PULSARS_SHOOT, 29000); + break; + case EVENT_CHECK_CORPOREALITY: + UpdateCorporeality(); + _events.ScheduleEvent(EVENT_CHECK_CORPOREALITY, 5000); + break; + default: + break; + } + } + } + + void SetData(uint32 id, uint32 value) + { + switch (id) + { + case DATA_MATERIAL_DAMAGE_TAKEN: + _materialDamageTaken += value; + break; + case DATA_TWILIGHT_DAMAGE_TAKEN: + _twilightDamageTaken += value; + break; + case DATA_FIGHT_PHASE: + _events.SetPhase(value); + switch (value) + { + case PHASE_ONE: + DoZoneInCombat(); + break; + case PHASE_TWO: + // Timer taken from a 4.3.4 solo video and confirmed by TankSpot's 3.3.5 guide. http://www.tankspot.com/showthread.php?67195-Halion-Encounter-Guide-Live + _events.ScheduleEvent(EVENT_SHADOW_PULSARS_SHOOT, 29000); + break; + case PHASE_THREE: + _events.ScheduleEvent(EVENT_CHECK_CORPOREALITY, 5000); + // Load up corporeality data. + for (uint8 itr = DATA_HALION; itr <= DATA_TWILIGHT_HALION; itr++) + { + Creature* halion = ObjectAccessor::GetCreature(*me, _instance->GetData64(itr)); + if (!halion) + continue; + + halion->CastSpell(halion, GetSpell(_materialCorporealityValue, itr == DATA_TWILIGHT_HALION), false); + halion->AI()->SetData(DATA_FIGHT_PHASE, PHASE_THREE); + + if (itr == DATA_TWILIGHT_HALION) + continue; + + halion->RemoveAurasDueToSpell(SPELL_TWILIGHT_PHASING); + halion->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + } + + // Summon Twilight portals + DoCast(me, SPELL_SUMMON_EXIT_PORTALS); + + _instance->DoUpdateWorldState(WORLDSTATE_CORPOREALITY_TOGGLE, 1); + // Hardcoding doesn't really matter here. + _instance->DoUpdateWorldState(WORLDSTATE_CORPOREALITY_MATERIAL, 50); + _instance->DoUpdateWorldState(WORLDSTATE_CORPOREALITY_TWILIGHT, 50); + break; + default: + break; + } + break; + default: + break; + } + } + + private: + /// TODO: Find out a better scaling, if any. + // [0 , 0.98[: Corporeality goes down + // [0.98, 0.99]: Do nothing + // ]0.99, 1.01[: Twilight Mending + // [1.01, 1.02]: Do nothing + // ]1.02, +oo [: Corporeality goes up + void UpdateCorporeality() + { + uint8 oldValue = _materialCorporealityValue; + if (_twilightDamageTaken == 0 || _materialDamageTaken == 0) + { + _events.ScheduleEvent(EVENT_TWILIGHT_MENDING, 100); + _twilightDamageTaken = 0; + _materialDamageTaken = 0; + return; + } + + float damageRatio = float(_materialDamageTaken) / float(_twilightDamageTaken); + + CorporealityEvent action = CORPOREALITY_NONE; + if (damageRatio < 0.98f) // [0 , 0.98[: Corporeality goes down + action = CORPOREALITY_DECREASE; + else if (0.99f < damageRatio && damageRatio < 1.0f) // ]0.99, 1.01[: Twilight Mending + action = CORPOREALITY_TWILIGHT_MENDING; + else if (1.02f < damageRatio) // ]1.02, +oo [: Corporeality goes up + action = CORPOREALITY_INCREASE; + + switch (action) + { + case CORPOREALITY_NONE: + return; + case CORPOREALITY_INCREASE: + { + if (_materialCorporealityValue >= (MAX_CORPOREALITY_STATE - 1)) + return; + ++_materialCorporealityValue; + break; + } + case CORPOREALITY_DECREASE: + { + if (_materialCorporealityValue <= 0) + return; + --_materialCorporealityValue; + break; + } + case CORPOREALITY_TWILIGHT_MENDING: + { + _events.ScheduleEvent(EVENT_TWILIGHT_MENDING, 100); + _materialDamageTaken = 0; + _twilightDamageTaken = 0; + return; + } + default: + break; + } + + _materialDamageTaken = 0; + _twilightDamageTaken = 0; + + _instance->DoUpdateWorldState(WORLDSTATE_CORPOREALITY_MATERIAL, _materialCorporealityValue * 10); + _instance->DoUpdateWorldState(WORLDSTATE_CORPOREALITY_TWILIGHT, 100 - _materialCorporealityValue * 10); + + for (uint8 itr = DATA_HALION; itr <= DATA_TWILIGHT_HALION; itr++) + { + if (Creature* halion = ObjectAccessor::GetCreature(*me, _instance->GetData64(itr))) + { + RemoveCorporeality(halion, itr == DATA_TWILIGHT_HALION); + halion->CastSpell(halion, GetSpell(_materialCorporealityValue, itr == DATA_TWILIGHT_HALION), true); + + if (itr == DATA_TWILIGHT_HALION) + halion->AI()->Talk(oldValue < _materialCorporealityValue ? EMOTE_CORPOREALITY_TOT : EMOTE_CORPOREALITY_TIT, halion->GetGUID()); + else // if (itr == DATA_HALION) + halion->AI()->Talk(oldValue > _materialCorporealityValue ? EMOTE_CORPOREALITY_POT : EMOTE_CORPOREALITY_PIP, halion->GetGUID()); + } + } + } + + void RemoveCorporeality(Creature* who, bool isTwilight = false) + { + for (uint8 i = 0; i < MAX_CORPOREALITY_STATE; i++) + { + uint32 spellID = (isTwilight ? _corporealityReference[i].twilightRealmSpell : _corporealityReference[i].materialRealmSpell); + if (who->HasAura(spellID)) + { + who->RemoveAurasDueToSpell(spellID); + break; + } + } + } + + uint32 GetSpell(uint8 pctValue, bool isTwilight = false) const + { + CorporealityEntry entry = _corporealityReference[pctValue]; + return isTwilight ? entry.twilightRealmSpell : entry.materialRealmSpell; + } + + EventMap _events; + InstanceScript* _instance; + SummonList _summons; + + bool _corporealityCheck; + + uint32 _twilightDamageTaken; + uint32 _materialDamageTaken; + uint8 _materialCorporealityValue; + }; + + CreatureAI* GetAI(Creature* creature) const + { + return GetRubySanctumAI<npc_halion_controllerAI>(creature); + } +}; + +typedef npc_halion_controller::npc_halion_controllerAI controllerAI; + +class npc_orb_carrier : public CreatureScript +{ + public: + npc_orb_carrier() : CreatureScript("npc_orb_carrier") { } + + struct npc_orb_carrierAI : public ScriptedAI + { + npc_orb_carrierAI(Creature* creature) : ScriptedAI(creature), + instance(creature->GetInstanceScript()) + { + ASSERT(creature->GetVehicleKit()); + } + + void UpdateAI(uint32 const /*diff*/) + { + /// According to sniffs this spell is cast every 1 or 2 seconds. + /// However, refreshing it looks bad, so just cast the spell if + /// we are not channeling it. + if (!me->HasUnitState(UNIT_STATE_CASTING)) + me->CastSpell((Unit*)NULL, SPELL_TRACK_ROTATION, false); + + /// Workaround: This is here because even though the above spell has SPELL_ATTR1_CHANNEL_TRACK_TARGET, + /// we are having two creatures involded here. This attribute is handled clientside, meaning the client + /// sends orientation update itself. Here, no packet is sent, and the creature does not rotate. By + /// forcing the carrier to always be facing the rotation focus, we ensure everything works as it should. + if (Creature* rotationFocus = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_ORB_ROTATION_FOCUS))) + me->SetFacingToObject(rotationFocus); // setInFront + } + + void DoAction(int32 const action) + { + if (action == ACTION_SHOOT) + { + Vehicle* vehicle = me->GetVehicleKit(); + Unit* southOrb = vehicle->GetPassenger(SEAT_SOUTH); + Unit* northOrb = vehicle->GetPassenger(SEAT_NORTH); + if (southOrb && northOrb) + { + if (northOrb->GetTypeId() == TYPEID_UNIT) + northOrb->ToCreature()->AI()->Talk(EMOTE_WARN_LASER); + TriggerCutter(northOrb, southOrb); + } + + if (!IsHeroic()) + return; + + Unit* eastOrb = vehicle->GetPassenger(SEAT_EAST); + Unit* westOrb = vehicle->GetPassenger(SEAT_WEST); + if (eastOrb && westOrb) + TriggerCutter(eastOrb, westOrb); + } + } + private: + InstanceScript* instance; + + void TriggerCutter(Unit* caster, Unit* target) + { + caster->CastSpell(caster, SPELL_TWILIGHT_PULSE_PERIODIC, true); + target->CastSpell(target, SPELL_TWILIGHT_PULSE_PERIODIC, true); + caster->CastSpell(target, SPELL_TWILIGHT_CUTTER, false); + } + }; + + CreatureAI* GetAI(Creature* creature) const + { + return GetRubySanctumAI<npc_orb_carrierAI>(creature); + } +}; + +class npc_meteor_strike_initial : public CreatureScript +{ + public: + npc_meteor_strike_initial() : CreatureScript("npc_meteor_strike_initial") { } + + struct npc_meteor_strike_initialAI : public Scripted_NoMovementAI + { + npc_meteor_strike_initialAI(Creature* creature) : Scripted_NoMovementAI(creature), + _instance(creature->GetInstanceScript()) + { } + + void DoAction(int32 const action) + { + switch (action) + { + case ACTION_METEOR_STRIKE_AOE: + DoCast(me, SPELL_METEOR_STRIKE_AOE_DAMAGE, true); + DoCast(me, SPELL_METEOR_STRIKE_FIRE_AURA_1, true); + for (std::list<Creature*>::iterator itr = _meteorList.begin(); itr != _meteorList.end(); ++itr) + (*itr)->AI()->DoAction(ACTION_METEOR_STRIKE_BURN); + break; + } + } + + void IsSummonedBy(Unit* summoner) + { + Creature* owner = summoner->ToCreature(); + if (!owner) + return; + + // Let Halion Controller count as summoner + if (Creature* controller = ObjectAccessor::GetCreature(*me, _instance->GetData64(DATA_HALION_CONTROLLER))) + controller->AI()->JustSummoned(me); + + DoCast(me, SPELL_METEOR_STRIKE_COUNTDOWN); + DoCast(me, SPELL_BIRTH_NO_VISUAL); // Unknown purpose + + if (HalionAI* halionAI = CAST_AI(HalionAI, owner->AI())) + { + Position const* ownerPos = halionAI->GetMeteorStrikePosition(); + Position newPos; + float angle[4]; + angle[0] = me->GetAngle(ownerPos); + angle[1] = me->GetAngle(ownerPos) - static_cast<float>(M_PI/2); + angle[2] = me->GetAngle(ownerPos) - static_cast<float>(-M_PI/2); + angle[3] = me->GetAngle(ownerPos) - static_cast<float>(M_PI); + + _meteorList.clear(); + for (uint8 i = 0; i < 4; i++) + { + angle[i] = MapManager::NormalizeOrientation(angle[i]); + me->SetOrientation(angle[i]); + me->GetNearPosition(newPos, 10.0f, 0.0f); // Exact distance + if (Creature* meteor = me->SummonCreature(NPC_METEOR_STRIKE_NORTH + i, newPos, TEMPSUMMON_TIMED_DESPAWN, 30000)) + _meteorList.push_back(meteor); + } + } + } + + void UpdateAI(uint32 const /*diff*/) { } + void EnterEvadeMode() { } + private: + InstanceScript* _instance; + std::list<Creature*> _meteorList; + }; + + CreatureAI* GetAI(Creature* creature) const + { + return GetRubySanctumAI<npc_meteor_strike_initialAI>(creature); + } +}; + +class npc_meteor_strike : public CreatureScript +{ + public: + npc_meteor_strike() : CreatureScript("npc_meteor_strike") { } + + struct npc_meteor_strikeAI : public Scripted_NoMovementAI + { + npc_meteor_strikeAI(Creature* creature) : Scripted_NoMovementAI(creature), + _instance(creature->GetInstanceScript()) + { + _range = 5.0f; + _spawnCount = 0; + } + + void DoAction(int32 const action) + { + if (action == ACTION_METEOR_STRIKE_BURN) + { + DoCast(me, SPELL_METEOR_STRIKE_FIRE_AURA_2, true); + me->setActive(true); + _events.ScheduleEvent(EVENT_SPAWN_METEOR_FLAME, 500); + } + } + + void IsSummonedBy(Unit* /*summoner*/) + { + // Let Halion Controller count as summoner. + if (Creature* controller = ObjectAccessor::GetCreature(*me, _instance->GetData64(DATA_HALION_CONTROLLER))) + controller->AI()->JustSummoned(me); + } + + void UpdateAI(uint32 const diff) + { + if (_spawnCount > 5) + return; + + _events.Update(diff); + + if (_events.ExecuteEvent() == EVENT_SPAWN_METEOR_FLAME) + { + Position pos; + me->GetNearPosition(pos, _range, 0.0f); + + if (Creature* flame = me->SummonCreature(NPC_METEOR_STRIKE_FLAME, pos, TEMPSUMMON_TIMED_DESPAWN, 25000)) + { + if (Creature* controller = ObjectAccessor::GetCreature(*me, _instance->GetData64(DATA_HALION_CONTROLLER))) + controller->AI()->JustSummoned(flame); + + flame->CastSpell(flame, SPELL_METEOR_STRIKE_FIRE_AURA_2, true); + ++_spawnCount; + } + _range += 5.0f; + _events.ScheduleEvent(EVENT_SPAWN_METEOR_FLAME, 800); + } + } + + private: + InstanceScript* _instance; + EventMap _events; + float _range; + uint8 _spawnCount; + }; + + CreatureAI* GetAI(Creature* creature) const + { + return GetRubySanctumAI<npc_meteor_strikeAI>(creature); + } +}; + +class npc_combustion_consumption : public CreatureScript +{ + public: + npc_combustion_consumption() : CreatureScript("npc_combustion_consumption") { } + + struct npc_combustion_consumptionAI : public Scripted_NoMovementAI + { + npc_combustion_consumptionAI(Creature* creature) : Scripted_NoMovementAI(creature), + _summonerGuid(0), _instance(creature->GetInstanceScript()) + { + switch (me->GetEntry()) + { + case NPC_COMBUSTION: + _explosionSpell = SPELL_FIERY_COMBUSTION_EXPLOSION; + _damageSpell = SPELL_COMBUSTION_DAMAGE_AURA; + me->SetPhaseMask(0x01, true); + break; + case NPC_CONSUMPTION: + _explosionSpell = SPELL_SOUL_CONSUMPTION_EXPLOSION; + _damageSpell = SPELL_CONSUMPTION_DAMAGE_AURA; + me->SetPhaseMask(0x20, true); + break; + default: // Should never happen + _explosionSpell = 0; + _damageSpell = 0; + break; + } + + if (IsHeroic()) + me->SetPhaseMask(0x01 | 0x20, true); + } + + void IsSummonedBy(Unit* summoner) + { + // Let Halion Controller count as summoner + if (Creature* controller = ObjectAccessor::GetCreature(*me, _instance->GetData64(DATA_HALION_CONTROLLER))) + controller->AI()->JustSummoned(me); + + _summonerGuid = summoner->GetGUID(); + } + + void SetData(uint32 type, uint32 stackAmount) + { + Unit* summoner = ObjectAccessor::GetUnit(*me, _summonerGuid); + + if (type != DATA_STACKS_DISPELLED || !_damageSpell || !_explosionSpell || !summoner) + return; + + me->CastCustomSpell(SPELL_SCALE_AURA, SPELLVALUE_AURA_STACK, stackAmount, me); + DoCast(me, _damageSpell); + + int32 damage = 1200 + (stackAmount * 1290); // Needs more researches. + summoner->CastCustomSpell(_explosionSpell, SPELLVALUE_BASE_POINT0, damage, summoner); + } + + void UpdateAI(uint32 const /*diff*/) { } + + private: + InstanceScript* _instance; + uint32 _explosionSpell; + uint32 _damageSpell; + uint64 _summonerGuid; + }; + + CreatureAI* GetAI(Creature* creature) const + { + return GetRubySanctumAI<npc_combustion_consumptionAI>(creature); + } +}; + +class npc_living_inferno : public CreatureScript +{ + public: + npc_living_inferno() : CreatureScript("npc_living_inferno") { } + + struct npc_living_infernoAI : public ScriptedAI + { + npc_living_infernoAI(Creature* creature) : ScriptedAI(creature) { } + + void JustSummoned(Creature* /*summoner*/) + { + me->SetInCombatWithZone(); + DoCast(me, SPELL_BLAZING_AURA); + } + }; + + CreatureAI* GetAI(Creature* creature) const + { + return GetRubySanctumAI<npc_living_infernoAI>(creature); + } +}; + +//! Need sniff data +class npc_living_ember : public CreatureScript +{ + public: + npc_living_ember() : CreatureScript("npc_living_ember") { } + + struct npc_living_emberAI : public ScriptedAI + { + npc_living_emberAI(Creature* creature) : ScriptedAI(creature) { } + + void Reset() + { + _hasEnraged = false; + } + + void EnterCombat(Unit* /*who*/) + { + _enrageTimer = 20000; + _hasEnraged = false; + } + + void UpdateAI(uint32 const diff) + { + if (!UpdateVictim() || me->HasUnitState(UNIT_STATE_CASTING)) + return; + + if (!_hasEnraged && _enrageTimer <= diff) + { + _hasEnraged = true; + DoCast(me, SPELL_BERSERK); + } + else _enrageTimer -= diff; + + DoMeleeAttackIfReady(); + } + + private: + uint32 _enrageTimer; + bool _hasEnraged; + }; + + CreatureAI* GetAI(Creature* creature) const + { + return GetRubySanctumAI<npc_living_emberAI>(creature); + } +}; + +class go_twilight_portal : public GameObjectScript +{ + public: + go_twilight_portal() : GameObjectScript("go_twilight_portal") { } + + struct go_twilight_portalAI : public GameObjectAI + { + go_twilight_portalAI(GameObject* gameobject) : GameObjectAI(gameobject), + _instance(gameobject->GetInstanceScript()), _deleted(false) + { + switch (gameobject->GetEntry()) + { + case GO_HALION_PORTAL_EXIT: + gameobject->SetPhaseMask(0x20, true); + _spellId = gameobject->GetGOInfo()->goober.spellId; + break; + case GO_HALION_PORTAL_1: + case GO_HALION_PORTAL_2: // Not used, not seen in sniffs. Just in case. + gameobject->SetPhaseMask(0x1, true); + /// Because WDB template has non-existent spell ID, not seen in sniffs either, meh + _spellId = SPELL_TWILIGHT_REALM; + break; + default: + _spellId = 0; + break; + } + } + + bool GossipHello(Player* player) + { + if (_spellId != 0) + player->CastSpell(player, _spellId, true); + return true; + } + + void UpdateAI(uint32 /*diff*/) + { + if (_instance->GetBossState(DATA_HALION) == IN_PROGRESS) + return; + + if (!_deleted) + { + _deleted = true; + go->Delete(); + } + } + + private: + InstanceScript* _instance; + uint32 _spellId; + bool _deleted; + }; + + GameObjectAI* GetAI(GameObject* gameobject) const + { + return GetRubySanctumAI<go_twilight_portalAI>(gameobject); + } +}; + +class spell_halion_meteor_strike_marker : public SpellScriptLoader +{ + public: + spell_halion_meteor_strike_marker() : SpellScriptLoader("spell_halion_meteor_strike_marker") { } + + class spell_halion_meteor_strike_marker_AuraScript : public AuraScript + { + PrepareAuraScript(spell_halion_meteor_strike_marker_AuraScript); + + void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + if (!GetCaster()) + return; + + if (GetTargetApplication()->GetRemoveMode() == AURA_REMOVE_BY_EXPIRE) + if (Creature* creCaster = GetCaster()->ToCreature()) + creCaster->AI()->DoAction(ACTION_METEOR_STRIKE_AOE); + } + + void Register() + { + AfterEffectRemove += AuraEffectRemoveFn(spell_halion_meteor_strike_marker_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); + } + }; + + AuraScript* GetAuraScript() const + { + return new spell_halion_meteor_strike_marker_AuraScript(); + } +}; + +class spell_halion_combustion_consumption : public SpellScriptLoader +{ + public: + spell_halion_combustion_consumption(char const* scriptName, uint32 spell) : SpellScriptLoader(scriptName), _spellID(spell) { } + + class spell_halion_combustion_consumption_AuraScript : public AuraScript + { + PrepareAuraScript(spell_halion_combustion_consumption_AuraScript); + + public: + spell_halion_combustion_consumption_AuraScript(uint32 spellID) : AuraScript(), _markSpell(spellID) { } + + bool Validate(SpellEntry const* /*spell*/) + { + if (!sSpellMgr->GetSpellInfo(_markSpell)) + return false; + return true; + } + + void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + if (GetTargetApplication()->GetRemoveMode() == AURA_REMOVE_BY_DEATH) + return; + + if (GetTarget()->HasAura(_markSpell)) + GetTarget()->RemoveAurasDueToSpell(_markSpell, 0, 0, AURA_REMOVE_BY_EXPIRE); + } + + void OnApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + GetTarget()->CastSpell(GetTarget(), _markSpell, true); + } + + void AddMarkStack(AuraEffect const* /*aurEff*/) + { + GetTarget()->CastSpell(GetTarget(), _markSpell, true); + } + + void Register() + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_halion_combustion_consumption_AuraScript::AddMarkStack, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE); + AfterEffectApply += AuraEffectApplyFn(spell_halion_combustion_consumption_AuraScript::OnApply, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE, AURA_EFFECT_HANDLE_REAL); + AfterEffectRemove += AuraEffectRemoveFn(spell_halion_combustion_consumption_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_PERIODIC_DAMAGE, AURA_EFFECT_HANDLE_REAL); + } + + uint32 _markSpell; + }; + + AuraScript* GetAuraScript() const + { + return new spell_halion_combustion_consumption_AuraScript(_spellID); + } + + private: + uint32 _spellID; +}; + +class spell_halion_marks : public SpellScriptLoader +{ + public: + spell_halion_marks(char const* scriptName, uint32 summonSpell, uint32 removeSpell) : SpellScriptLoader(scriptName), + _summonSpell(summonSpell), _removeSpell(removeSpell) { } + + class spell_halion_marks_AuraScript : public AuraScript + { + PrepareAuraScript(spell_halion_marks_AuraScript); + + public: + spell_halion_marks_AuraScript(uint32 summonSpell, uint32 removeSpell) : AuraScript(), + _summonSpellId(summonSpell), _removeSpellId(removeSpell) { } + + bool Validate(SpellEntry const* /*spell*/) + { + if (!sSpellMgr->GetSpellInfo(_summonSpellId)) + return false; + return true; + } + + /// We were purged. Force removed stacks to zero and trigger the appropriated remove handler. + void BeforeDispel(DispelInfo* dispelData) + { + // Prevent any stack from being removed at this point. + dispelData->SetRemovedCharges(0); + + if (Unit* dispelledUnit = GetUnitOwner()) + if (dispelledUnit->HasAura(_removeSpellId)) + dispelledUnit->RemoveAurasDueToSpell(_removeSpellId, 0, 0, AURA_REMOVE_BY_EXPIRE); + } + + void OnRemove(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/) + { + if (GetTargetApplication()->GetRemoveMode() != AURA_REMOVE_BY_EXPIRE) + return; + + // Stacks marker + GetTarget()->CastCustomSpell(_summonSpellId, SPELLVALUE_BASE_POINT1, aurEff->GetBase()->GetStackAmount(), GetTarget(), TRIGGERED_FULL_MASK, NULL, NULL, GetCasterGUID()); + } + + void Register() + { + OnDispel += AuraDispelFn(spell_halion_marks_AuraScript::BeforeDispel); + AfterEffectRemove += AuraEffectRemoveFn(spell_halion_marks_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); + } + + uint32 _summonSpellId; + uint32 _removeSpellId; + }; + + AuraScript* GetAuraScript() const + { + return new spell_halion_marks_AuraScript(_summonSpell, _removeSpell); + } + + private: + uint32 _summonSpell; + uint32 _removeSpell; +}; + +class spell_halion_damage_aoe_summon : public SpellScriptLoader +{ + public: + spell_halion_damage_aoe_summon() : SpellScriptLoader("spell_halion_damage_aoe_summon") { } + + class spell_halion_damage_aoe_summon_SpellScript : public SpellScript + { + PrepareSpellScript(spell_halion_damage_aoe_summon_SpellScript); + + void HandleSummon(SpellEffIndex effIndex) + { + PreventHitDefaultEffect(effIndex); + Unit* caster = GetCaster(); + uint32 entry = uint32(GetSpellInfo()->Effects[effIndex].MiscValue); + SummonPropertiesEntry const* properties = sSummonPropertiesStore.LookupEntry(uint32(GetSpellInfo()->Effects[effIndex].MiscValueB)); + uint32 duration = uint32(GetSpellInfo()->GetDuration()); + + Position pos; + caster->GetPosition(&pos); + if (Creature* summon = caster->GetMap()->SummonCreature(entry, pos, properties, duration, caster, GetSpellInfo()->Id)) + if (summon->IsAIEnabled) + summon->AI()->SetData(DATA_STACKS_DISPELLED, GetSpellValue()->EffectBasePoints[EFFECT_1]); + } + + void Register() + { + OnEffectHit += SpellEffectFn(spell_halion_damage_aoe_summon_SpellScript::HandleSummon, EFFECT_0, SPELL_EFFECT_SUMMON); + } + }; + + SpellScript* GetSpellScript() const + { + return new spell_halion_damage_aoe_summon_SpellScript(); + } +}; + +class spell_halion_twilight_realm_handlers : public SpellScriptLoader +{ + public: + spell_halion_twilight_realm_handlers(const char* scriptName, uint32 beforeHitSpell, bool isApplyHandler) : SpellScriptLoader(scriptName), + _beforeHitSpell(beforeHitSpell), _isApplyHandler(isApplyHandler) + { } + + class spell_halion_twilight_realm_handlers_AuraScript : public AuraScript + { + PrepareAuraScript(spell_halion_twilight_realm_handlers_AuraScript); + + public: + spell_halion_twilight_realm_handlers_AuraScript(uint32 beforeHitSpell, bool isApplyHandler) : AuraScript(), + _isApply(isApplyHandler), _beforeHitSpellId(beforeHitSpell) + { } + + bool Validate(SpellInfo const* /*spell*/) + { + if (!sSpellMgr->GetSpellInfo(_beforeHitSpellId)) + return false; + return true; + } + + void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*handle*/) + { + GetTarget()->RemoveAurasDueToSpell(SPELL_TWILIGHT_REALM); + if (InstanceScript* instance = GetTarget()->GetInstanceScript()) + instance->SendEncounterUnit(ENCOUNTER_FRAME_UNK7); + } + + void OnApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*handle*/) + { + Unit* target = GetTarget(); + if (!target) + return; + + target->RemoveAurasDueToSpell(_beforeHitSpellId, 0, 0, AURA_REMOVE_BY_ENEMY_SPELL); + if (InstanceScript* instance = target->GetInstanceScript()) + instance->SendEncounterUnit(ENCOUNTER_FRAME_UNK7); + } + + void Register() + { + if (!_isApply) + { + AfterEffectApply += AuraEffectApplyFn(spell_halion_twilight_realm_handlers_AuraScript::OnApply, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); + AfterEffectRemove += AuraEffectRemoveFn(spell_halion_twilight_realm_handlers_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); + } + else + AfterEffectApply += AuraEffectApplyFn(spell_halion_twilight_realm_handlers_AuraScript::OnApply, EFFECT_0, SPELL_AURA_PHASE, AURA_EFFECT_HANDLE_REAL); + } + + bool _isApply; + uint32 _beforeHitSpellId; + }; + + AuraScript* GetAuraScript() const + { + return new spell_halion_twilight_realm_handlers_AuraScript(_beforeHitSpell, _isApplyHandler); + } + + private: + uint32 _beforeHitSpell; + bool _isApplyHandler; +}; + +class spell_halion_clear_debuffs : public SpellScriptLoader +{ + public: + spell_halion_clear_debuffs() : SpellScriptLoader("spell_halion_clear_debuffs") { } + + class spell_halion_clear_debuffs_SpellScript : public SpellScript + { + PrepareSpellScript(spell_halion_clear_debuffs_SpellScript); + + bool Validate(SpellInfo const* /*spell*/) + { + if (!sSpellMgr->GetSpellInfo(SPELL_CLEAR_DEBUFFS)) + return false; + if (!sSpellMgr->GetSpellInfo(SPELL_TWILIGHT_REALM)) + return false; + return true; + } + + void HandleScript(SpellEffIndex effIndex) + { + if (GetHitUnit()->HasAura(GetSpellInfo()->Effects[effIndex].CalcValue())) + GetHitUnit()->RemoveAurasDueToSpell(GetSpellInfo()->Effects[effIndex].CalcValue()); + } + + void Register() + { + OnEffectHitTarget += SpellEffectFn(spell_halion_clear_debuffs_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } + }; + + SpellScript* GetSpellScript() const + { + return new spell_halion_clear_debuffs_SpellScript(); + } +}; + +class TwilightCutterSelector +{ + public: + TwilightCutterSelector(Unit* caster, Unit* cutterCaster) : _caster(caster), _cutterCaster(cutterCaster) {} + + bool operator()(WorldObject* unit) + { + return !unit->IsInBetween(_caster, _cutterCaster, 4.0f); + } + + private: + Unit* _caster; + Unit* _cutterCaster; +}; + +class spell_halion_twilight_cutter : public SpellScriptLoader +{ + public: + spell_halion_twilight_cutter() : SpellScriptLoader("spell_halion_twilight_cutter") { } + + class spell_halion_twilight_cutter_SpellScript : public SpellScript + { + PrepareSpellScript(spell_halion_twilight_cutter_SpellScript); + + void RemoveNotBetween(std::list<WorldObject*>& unitList) + { + if (unitList.empty()) + return; + + Unit* caster = GetCaster(); + if (Aura* cutter = caster->GetAura(SPELL_TWILIGHT_CUTTER)) + { + if (Unit* cutterCaster = cutter->GetCaster()) + { + unitList.remove_if(TwilightCutterSelector(caster, cutterCaster)); + return; + } + } + + // In case cutter caster werent found for some reason + unitList.clear(); + } + + void Register() + { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_halion_twilight_cutter_SpellScript::RemoveNotBetween, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY); + } + }; + + SpellScript* GetSpellScript() const + { + return new spell_halion_twilight_cutter_SpellScript(); + } +}; + +class spell_halion_twilight_phasing : public SpellScriptLoader +{ + public: + spell_halion_twilight_phasing() : SpellScriptLoader("spell_halion_twilight_phasing") { } + + class spell_halion_twilight_phasing_SpellScript : public SpellScript + { + PrepareSpellScript(spell_halion_twilight_phasing_SpellScript); + + void Phase() + { + Unit* caster = GetCaster(); + caster->CastSpell(caster->GetPositionX(), caster->GetPositionY(), caster->GetPositionZ(), SPELL_SUMMON_TWILIGHT_PORTAL, true); + caster->GetMap()->SummonCreature(NPC_TWILIGHT_HALION, HalionSpawnPos); + } + + void Register() + { + OnHit += SpellHitFn(spell_halion_twilight_phasing_SpellScript::Phase); + } + }; + + SpellScript* GetSpellScript() const + { + return new spell_halion_twilight_phasing_SpellScript(); + } +}; + +class spell_halion_summon_exit_portals : public SpellScriptLoader +{ + public: + spell_halion_summon_exit_portals() : SpellScriptLoader("spell_halion_summon_exit_portals") { } + + class spell_halion_summon_exit_portals_SpellScript : public SpellScript + { + PrepareSpellScript(spell_halion_summon_exit_portals_SpellScript); + + void OnSummon(SpellEffIndex effIndex) + { + WorldLocation summonPos = *GetExplTargetDest(); + Position offset = {0.0f, 20.0f, 0.0f, 0.0f}; + if (effIndex == EFFECT_1) + offset.m_positionY = -20.0f; + + summonPos.RelocateOffset(offset); + + SetExplTargetDest(summonPos); + GetHitDest()->RelocateOffset(offset); + } + + void Register() + { + OnEffectLaunch += SpellEffectFn(spell_halion_summon_exit_portals_SpellScript::OnSummon, EFFECT_0, SPELL_EFFECT_SUMMON_OBJECT_WILD); + OnEffectLaunch += SpellEffectFn(spell_halion_summon_exit_portals_SpellScript::OnSummon, EFFECT_1, SPELL_EFFECT_SUMMON_OBJECT_WILD); + } + }; + + SpellScript* GetSpellScript() const + { + return new spell_halion_summon_exit_portals_SpellScript(); + } +}; + +void AddSC_boss_halion() +{ + new boss_halion(); + new boss_twilight_halion(); + + new npc_halion_controller(); + new npc_meteor_strike_initial(); + new npc_meteor_strike(); + new npc_combustion_consumption(); + new npc_orb_carrier(); + new npc_living_inferno(); + new npc_living_ember(); + + new go_twilight_portal(); + + new spell_halion_meteor_strike_marker(); + new spell_halion_combustion_consumption("spell_halion_soul_consumption", SPELL_MARK_OF_CONSUMPTION); + new spell_halion_combustion_consumption("spell_halion_fiery_combustion", SPELL_MARK_OF_COMBUSTION); + new spell_halion_marks("spell_halion_mark_of_combustion", SPELL_FIERY_COMBUSTION_SUMMON, SPELL_FIERY_COMBUSTION); + new spell_halion_marks("spell_halion_mark_of_consumption", SPELL_SOUL_CONSUMPTION_SUMMON, SPELL_SOUL_CONSUMPTION); + new spell_halion_damage_aoe_summon(); + new spell_halion_twilight_realm_handlers("spell_halion_leave_twilight_realm", SPELL_SOUL_CONSUMPTION, false); + new spell_halion_twilight_realm_handlers("spell_halion_enter_twilight_realm", SPELL_FIERY_COMBUSTION, true); + new spell_halion_summon_exit_portals(); + new spell_halion_twilight_phasing(); + new spell_halion_twilight_cutter(); + new spell_halion_clear_debuffs(); +} diff --git a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/instance_ruby_sanctum.cpp b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/instance_ruby_sanctum.cpp index 5678bbbeb83..a6b50467538 100644 --- a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/instance_ruby_sanctum.cpp +++ b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/instance_ruby_sanctum.cpp @@ -37,18 +37,23 @@ class instance_ruby_sanctum : public InstanceMapScript { SetBossNumber(EncounterCount); LoadDoorData(doorData); - BaltharusTheWarbornGUID = 0; - GeneralZarithrianGUID = 0; - SavianaRagefireGUID = 0; - HalionGUID = 0; - HalionControllerGUID = 0; + BaltharusTheWarbornGUID = 0; + GeneralZarithrianGUID = 0; + SavianaRagefireGUID = 0; + HalionGUID = 0; + TwilightHalionGUID = 0; + OrbCarrierGUID = 0; + OrbRotationFocusGUID = 0; + HalionControllerGUID = 0; + CombatStalkerGUID = 0; CrystalChannelTargetGUID = 0; - XerestraszaGUID = 0; - BaltharusSharedHealth = 0; - FlameWallsGUID = 0; - FlameRingGUID = 0; - memset(ZarithianSpawnStalkerGUID, 0, 2*sizeof(uint64)); - memset(BurningTreeGUID, 0, 4*sizeof(uint64)); + XerestraszaGUID = 0; + BaltharusSharedHealth = 0; + FlameWallsGUID = 0; + FlameRingGUID = 0; + + memset(ZarithrianSpawnStalkerGUID, 0, 2 * sizeof(uint64)); + memset(BurningTreeGUID, 0, 4 * sizeof(uint64)); } void OnCreatureCreate(Creature* creature) @@ -67,19 +72,32 @@ class instance_ruby_sanctum : public InstanceMapScript case NPC_HALION: HalionGUID = creature->GetGUID(); break; + case NPC_TWILIGHT_HALION: + TwilightHalionGUID = creature->GetGUID(); + break; case NPC_HALION_CONTROLLER: HalionControllerGUID = creature->GetGUID(); + break; + case NPC_ORB_CARRIER: + OrbCarrierGUID = creature->GetGUID(); + break; + case NPC_ORB_ROTATION_FOCUS: + OrbRotationFocusGUID = creature->GetGUID(); + break; + case NPC_COMBAT_STALKER: + CombatStalkerGUID = creature->GetGUID(); + break; case NPC_BALTHARUS_TARGET: CrystalChannelTargetGUID = creature->GetGUID(); break; case NPC_XERESTRASZA: XerestraszaGUID = creature->GetGUID(); break; - case NPC_ZARITHIAN_SPAWN_STALKER: - if (!ZarithianSpawnStalkerGUID[0]) - ZarithianSpawnStalkerGUID[0] = creature->GetGUID(); + case NPC_ZARITHRIAN_SPAWN_STALKER: + if (!ZarithrianSpawnStalkerGUID[0]) + ZarithrianSpawnStalkerGUID[0] = creature->GetGUID(); else - ZarithianSpawnStalkerGUID[1] = creature->GetGUID(); + ZarithrianSpawnStalkerGUID[1] = creature->GetGUID(); break; default: break; @@ -101,6 +119,9 @@ class instance_ruby_sanctum : public InstanceMapScript case GO_FLAME_RING: FlameRingGUID = go->GetGUID(); break; + case GO_TWILIGHT_FLAME_RING: + TwilightFlameRingGUID = go->GetGUID(); + break; case GO_BURNING_TREE_1: BurningTreeGUID[0] = go->GetGUID(); if (GetBossState(DATA_GENERAL_ZARITHRIAN) == DONE) @@ -152,24 +173,30 @@ class instance_ruby_sanctum : public InstanceMapScript return SavianaRagefireGUID; case DATA_GENERAL_ZARITHRIAN: return GeneralZarithrianGUID; - case DATA_ZARITHIAN_SPAWN_STALKER_1: - return ZarithianSpawnStalkerGUID[0]; - case DATA_ZARITHIAN_SPAWN_STALKER_2: - return ZarithianSpawnStalkerGUID[1]; + case DATA_ZARITHRIAN_SPAWN_STALKER_1: + case DATA_ZARITHRIAN_SPAWN_STALKER_2: + return ZarithrianSpawnStalkerGUID[type - DATA_ZARITHRIAN_SPAWN_STALKER_1]; case DATA_HALION: return HalionGUID; + case DATA_TWILIGHT_HALION: + return TwilightHalionGUID; + case DATA_ORB_CARRIER: + return OrbCarrierGUID; + case DATA_ORB_ROTATION_FOCUS: + return OrbRotationFocusGUID; case DATA_HALION_CONTROLLER: return HalionControllerGUID; case DATA_BURNING_TREE_1: - return BurningTreeGUID[0]; case DATA_BURNING_TREE_2: - return BurningTreeGUID[1]; case DATA_BURNING_TREE_3: - return BurningTreeGUID[2]; case DATA_BURNING_TREE_4: - return BurningTreeGUID[3]; + return BurningTreeGUID[type - DATA_BURNING_TREE_1]; case DATA_FLAME_RING: return FlameRingGUID; + case DATA_TWILIGHT_FLAME_RING: + return TwilightFlameRingGUID; + case DATA_COMBAT_STALKER: + return CombatStalkerGUID; default: break; } @@ -180,7 +207,14 @@ class instance_ruby_sanctum : public InstanceMapScript bool SetBossState(uint32 type, EncounterState state) { if (!InstanceScript::SetBossState(type, state)) + { + // Summon Halion on instance loading if conditions are met. Without those lines, + // InstanceScript::SetBossState returns false, thus preventing the switch from being called. + if (type == DATA_HALION && state != DONE && GetBossState(DATA_GENERAL_ZARITHRIAN) == DONE && !GetData64(DATA_HALION_CONTROLLER)) + if (Creature* halionController = instance->SummonCreature(NPC_HALION_CONTROLLER, HalionControllerSpawnPos)) + halionController->AI()->DoAction(ACTION_INTRO_HALION); return false; + } switch (type) { @@ -205,20 +239,30 @@ class instance_ruby_sanctum : public InstanceMapScript break; } case DATA_GENERAL_ZARITHRIAN: + { if (GetBossState(DATA_SAVIANA_RAGEFIRE) == DONE && GetBossState(DATA_BALTHARUS_THE_WARBORN) == DONE) HandleGameObject(FlameWallsGUID, state != IN_PROGRESS); - /* - if (state == DONE) + + // Not called at instance loading, no big deal. + if (state == DONE && GetBossState(DATA_HALION) != DONE) if (Creature* halionController = instance->SummonCreature(NPC_HALION_CONTROLLER, HalionControllerSpawnPos)) halionController->AI()->DoAction(ACTION_INTRO_HALION); - */ break; + } case DATA_HALION: - /* - if (state != IN_PROGRESS) + { + DoUpdateWorldState(WORLDSTATE_CORPOREALITY_TOGGLE, 0); + DoUpdateWorldState(WORLDSTATE_CORPOREALITY_TWILIGHT, 0); + DoUpdateWorldState(WORLDSTATE_CORPOREALITY_MATERIAL, 0); + + // Reopen rings on wipe or success + if (state == DONE || state == FAIL) + { HandleGameObject(FlameRingGUID, true); - */ + HandleGameObject(TwilightFlameRingGUID, true); + } break; + } default: break; } @@ -228,25 +272,18 @@ class instance_ruby_sanctum : public InstanceMapScript void SetData(uint32 type, uint32 data) { - switch (type) - { - case DATA_BALTHARUS_SHARED_HEALTH: - BaltharusSharedHealth = data; - break; - } + if (type != DATA_BALTHARUS_SHARED_HEALTH) + return; + + BaltharusSharedHealth = data; } uint32 GetData(uint32 type) { - switch (type) - { - case DATA_BALTHARUS_SHARED_HEALTH: - return BaltharusSharedHealth; - default: - break; - } + if (type != DATA_BALTHARUS_SHARED_HEALTH) + return 0; - return 0; + return BaltharusSharedHealth; } std::string GetSaveData() @@ -260,6 +297,13 @@ class instance_ruby_sanctum : public InstanceMapScript return saveStream.str(); } + void FillInitialWorldStates(WorldPacket& data) + { + data << uint32(WORLDSTATE_CORPOREALITY_MATERIAL) << uint32(50); + data << uint32(WORLDSTATE_CORPOREALITY_TWILIGHT) << uint32(50); + data << uint32(WORLDSTATE_CORPOREALITY_TOGGLE) << uint32(0); + } + void Load(char const* str) { if (!str) @@ -298,13 +342,19 @@ class instance_ruby_sanctum : public InstanceMapScript uint64 GeneralZarithrianGUID; uint64 SavianaRagefireGUID; uint64 HalionGUID; + uint64 TwilightHalionGUID; uint64 HalionControllerGUID; + uint64 OrbCarrierGUID; + uint64 OrbRotationFocusGUID; uint64 CrystalChannelTargetGUID; uint64 XerestraszaGUID; uint64 FlameWallsGUID; - uint64 ZarithianSpawnStalkerGUID[2]; + uint64 ZarithrianSpawnStalkerGUID[2]; uint64 BurningTreeGUID[4]; uint64 FlameRingGUID; + uint64 TwilightFlameRingGUID; + uint64 CombatStalkerGUID; + uint32 BaltharusSharedHealth; }; diff --git a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/ruby_sanctum.h b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/ruby_sanctum.h index 02ade2ff3e7..7eb1b73721c 100644 --- a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/ruby_sanctum.h +++ b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/ruby_sanctum.h @@ -21,6 +21,7 @@ #include "SpellScript.h" #include "Map.h" #include "Creature.h" +#include "GameObjectAI.h" #define RSScriptName "instance_ruby_sanctum" uint32 const EncounterCount = 4; @@ -36,17 +37,22 @@ enum DataTypes DATA_HALION = 3, // Etc - DATA_XERESTRASZA = 4, - DATA_CRYSTAL_CHANNEL_TARGET = 5, - DATA_BALTHARUS_SHARED_HEALTH = 6, - DATA_ZARITHIAN_SPAWN_STALKER_1 = 7, - DATA_ZARITHIAN_SPAWN_STALKER_2 = 8, - DATA_HALION_CONTROLLER = 9, - DATA_BURNING_TREE_1 = 10, - DATA_BURNING_TREE_2 = 11, - DATA_BURNING_TREE_3 = 12, - DATA_BURNING_TREE_4 = 13, - DATA_FLAME_RING = 14, + DATA_TWILIGHT_HALION = 4, + DATA_XERESTRASZA = 5, + DATA_CRYSTAL_CHANNEL_TARGET = 6, + DATA_BALTHARUS_SHARED_HEALTH = 7, + DATA_ZARITHRIAN_SPAWN_STALKER_1 = 8, + DATA_ZARITHRIAN_SPAWN_STALKER_2 = 9, + DATA_HALION_CONTROLLER = 10, + DATA_ORB_CARRIER = 11, + DATA_ORB_ROTATION_FOCUS = 12, + DATA_BURNING_TREE_1 = 13, + DATA_BURNING_TREE_2 = 14, + DATA_BURNING_TREE_3 = 15, + DATA_BURNING_TREE_4 = 16, + DATA_FLAME_RING = 17, + DATA_TWILIGHT_FLAME_RING = 18, + DATA_COMBAT_STALKER = 19, }; enum SharedActions @@ -66,14 +72,14 @@ enum CreaturesIds // General Zarithrian NPC_GENERAL_ZARITHRIAN = 39746, NPC_ONYX_FLAMECALLER = 39814, - NPC_ZARITHIAN_SPAWN_STALKER = 39794, + NPC_ZARITHRIAN_SPAWN_STALKER = 39794, // Saviana Ragefire NPC_SAVIANA_RAGEFIRE = 39747, // Halion NPC_HALION = 39863, - NPC_HALION_TWILIGHT = 40142, + NPC_TWILIGHT_HALION = 40142, NPC_HALION_CONTROLLER = 40146, NPC_LIVING_INFERNO = 40681, NPC_LIVING_EMBER = 40683, @@ -81,6 +87,8 @@ enum CreaturesIds NPC_ORB_ROTATION_FOCUS = 40091, NPC_SHADOW_ORB_N = 40083, NPC_SHADOW_ORB_S = 40100, + NPC_SHADOW_ORB_E = 40468, + NPC_SHADOW_ORB_W = 40469, NPC_METEOR_STRIKE_MARK = 40029, NPC_METEOR_STRIKE_NORTH = 40041, NPC_METEOR_STRIKE_EAST = 40042, @@ -88,6 +96,8 @@ enum CreaturesIds NPC_METEOR_STRIKE_SOUTH = 40044, NPC_METEOR_STRIKE_FLAME = 40055, NPC_COMBUSTION = 40001, + NPC_CONSUMPTION = 40135, + NPC_COMBAT_STALKER = 40151, // Xerestrasza NPC_XERESTRASZA = 40429, @@ -101,6 +111,7 @@ enum GameObjectsIds GO_FIRE_FIELD = 203005, GO_FLAME_WALLS = 203006, GO_FLAME_RING = 203007, + GO_TWILIGHT_FLAME_RING = 203624, GO_BURNING_TREE_1 = 203034, GO_BURNING_TREE_2 = 203035, GO_BURNING_TREE_3 = 203036, @@ -114,6 +125,11 @@ enum WorldStatesRS WORLDSTATE_CORPOREALITY_TOGGLE = 5051, }; +enum InstanceSpell +{ + SPELL_BERSERK = 26662, +}; + template<class AI> CreatureAI* GetRubySanctumAI(Creature* creature) { @@ -124,4 +140,15 @@ CreatureAI* GetRubySanctumAI(Creature* creature) return NULL; } +template<class AI> +GameObjectAI* GetRubySanctumAI(GameObject* go) +{ + if (InstanceMap* instance = go->GetMap()->ToInstanceMap()) + if (instance->GetInstanceScript()) + if (instance->GetScriptId() == sObjectMgr->GetScriptId(RSScriptName)) + return new AI(go); + + return NULL; +} + #endif // RUBY_SANCTUM_H_ diff --git a/src/server/scripts/Northrend/Nexus/Oculus/boss_urom.cpp b/src/server/scripts/Northrend/Nexus/Oculus/boss_urom.cpp index 8e7863259ad..179dedb290b 100644 --- a/src/server/scripts/Northrend/Nexus/Oculus/boss_urom.cpp +++ b/src/server/scripts/Northrend/Nexus/Oculus/boss_urom.cpp @@ -105,8 +105,8 @@ public: void Reset() { me->CastSpell(me, SPELL_EVOCATE); - - _Reset(); + + _Reset(); if (instance->GetData(DATA_UROM_PLATAFORM) == 0) { diff --git a/src/server/scripts/Northrend/Nexus/Oculus/instance_oculus.cpp b/src/server/scripts/Northrend/Nexus/Oculus/instance_oculus.cpp index 1f4a3d1b229..a0b5aded315 100644 --- a/src/server/scripts/Northrend/Nexus/Oculus/instance_oculus.cpp +++ b/src/server/scripts/Northrend/Nexus/Oculus/instance_oculus.cpp @@ -278,7 +278,7 @@ public: eternos->GetMotionMaster()->MovePoint(0, 943.202f, 1059.35f, 359.967f); if (Creature* verdisa = instance->GetCreature(verdisaGUID)) verdisa->SetWalk(true), - verdisa->GetMotionMaster()->MovePoint(0, 949.188f, 1032.91f, 359.967f); + verdisa->GetMotionMaster()->MovePoint(0, 949.188f, 1032.91f, 359.967f); } void GreaterWhelps() diff --git a/src/server/scripts/Northrend/Nexus/Oculus/oculus.cpp b/src/server/scripts/Northrend/Nexus/Oculus/oculus.cpp index f2fa3158603..39fff139b52 100644 --- a/src/server/scripts/Northrend/Nexus/Oculus/oculus.cpp +++ b/src/server/scripts/Northrend/Nexus/Oculus/oculus.cpp @@ -108,10 +108,10 @@ class npc_verdisa_beglaristrasz_eternos : public CreatureScript { public: npc_verdisa_beglaristrasz_eternos() : CreatureScript("npc_verdisa_beglaristrasz_eternos") { } - - InstanceScript* instance; - - bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) + + InstanceScript* instance; + + bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) { player->PlayerTalkClass->ClearMenus(); switch (creature->GetEntry()) @@ -245,7 +245,7 @@ public: me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); } }; - + CreatureAI* GetAI(Creature* creature) const { return new npc_verdisa_beglaristrasz_eternosAI(creature); @@ -293,7 +293,7 @@ public: { instance = creature->GetInstanceScript(); } - + InstanceScript* instance; uint64 summonerGUID; @@ -324,9 +324,9 @@ public: HealthWarningOff = false; DisableTakeOff = false; } - + void IsSummonedBy(Unit* summoner) - { + { if (instance->GetBossState(DATA_EREGOS_EVENT) == IN_PROGRESS) if (Creature* eregos = me->FindNearestCreature(NPC_EREGOS, 450.0f, true)) { @@ -357,7 +357,7 @@ public: void UpdateAI(const uint32 diff) { if (!(instance->GetBossState(DATA_VAROS_EVENT) == DONE)) - { + { if (me->HasAuraType(SPELL_AURA_CONTROL_VEHICLE)) { if (!(WelcomeOff)) @@ -380,7 +380,7 @@ public: { Talk(WHISPER_DRAKES_ABILITIES, me->GetCreatorGUID()); WelcomeSequelOff = false; - } + } else WelcomeSequelTimer -= diff; } } @@ -391,10 +391,10 @@ public: if (!(SpecialOff)) { if (SpecialTimer <= diff) - { + { Talk(WHISPER_DRAKES_SPECIAL, me->GetCreatorGUID()); SpecialOff = true; - } + } else SpecialTimer -= diff; } } @@ -402,25 +402,25 @@ public: if (me->HasAuraType(SPELL_AURA_CONTROL_VEHICLE)) { if (!(HealthWarningOff)) - { + { if (me->GetHealthPct() <= 40.0f) { Talk(WHISPER_DRAKES_LOWHEALTH, me->GetCreatorGUID()); HealthWarningOff = true; } } - } + } if (me->HasAuraType(SPELL_AURA_CONTROL_VEHICLE)) - { + { if (HealthWarningOff) - { + { if (WarningTimer <= diff) { HealthWarningOff = false; WarningTimer = 25000; } else WarningTimer -= diff; - } + } } if (!(me->HasAuraType(SPELL_AURA_CONTROL_VEHICLE))) { @@ -433,7 +433,7 @@ public: me->SetSpeed(MOVE_FLIGHT, 1.0f, true); Talk(SAY_DRAKES_TAKEOFF); Position pos; - me->GetPosition(&pos); + me->GetPosition(&pos); pos.m_positionX += 10.0f; pos.m_positionY += 10.0f; pos.m_positionZ += 12.0f; diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_assembly_of_iron.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_assembly_of_iron.cpp index 0967c38c2e7..92e56d4dd9a 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_assembly_of_iron.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_assembly_of_iron.cpp @@ -255,7 +255,12 @@ class boss_steelbreaker : public CreatureScript { DoScriptText(RAND(SAY_STEELBREAKER_DEATH_1, SAY_STEELBREAKER_DEATH_2), me); if (IsEncounterComplete(instance, me)) - instance->SetData(BOSS_ASSEMBLY_OF_IRON, DONE); + { + instance->SetBossState(BOSS_ASSEMBLY_OF_IRON, DONE); + instance->SetBossState(BOSS_STEELBREAKER, DONE); + instance->SetBossState(BOSS_MOLGEIM, DONE); + instance->SetBossState(BOSS_BRUNDIR, DONE); + } else me->SetLootRecipient(NULL); @@ -379,7 +384,12 @@ class boss_runemaster_molgeim : public CreatureScript { DoScriptText(RAND(SAY_MOLGEIM_DEATH_1, SAY_MOLGEIM_DEATH_2), me); if (IsEncounterComplete(instance, me)) - instance->SetData(BOSS_ASSEMBLY_OF_IRON, DONE); + { + instance->SetBossState(BOSS_ASSEMBLY_OF_IRON, DONE); + instance->SetBossState(BOSS_STEELBREAKER, DONE); + instance->SetBossState(BOSS_MOLGEIM, DONE); + instance->SetBossState(BOSS_BRUNDIR, DONE); + } else me->SetLootRecipient(NULL); @@ -620,7 +630,12 @@ class boss_stormcaller_brundir : public CreatureScript { DoScriptText(RAND(SAY_BRUNDIR_DEATH_1, SAY_BRUNDIR_DEATH_2), me); if (IsEncounterComplete(instance, me)) - instance->SetData(BOSS_ASSEMBLY_OF_IRON, DONE); + { + instance->SetBossState(BOSS_ASSEMBLY_OF_IRON, DONE); + instance->SetBossState(BOSS_STEELBREAKER, DONE); + instance->SetBossState(BOSS_MOLGEIM, DONE); + instance->SetBossState(BOSS_BRUNDIR, DONE); + } else me->SetLootRecipient(NULL); diff --git a/src/server/scripts/Northrend/wintergrasp.cpp b/src/server/scripts/Northrend/wintergrasp.cpp index 59e9a31c4cf..2aed813550d 100644 --- a/src/server/scripts/Northrend/wintergrasp.cpp +++ b/src/server/scripts/Northrend/wintergrasp.cpp @@ -541,6 +541,11 @@ public: } }; +enum WgTeleport +{ + SPELL_WINTERGRASP_TELEPORT_TRIGGER = 54643, +}; + class spell_wintergrasp_defender_teleport : public SpellScriptLoader { public: @@ -554,7 +559,7 @@ public: { if (Battlefield* wg = sBattlefieldMgr->GetBattlefieldByBattleId(BATTLEFIELD_BATTLEID_WG)) if (Player* target = GetExplTargetUnit()->ToPlayer()) - if (target->GetTeamId() != wg->GetDefenderTeam()) + if (target->GetTeamId() != wg->GetDefenderTeam() || target->HasAura(SPELL_WINTERGRASP_TELEPORT_TRIGGER)) return SPELL_FAILED_BAD_TARGETS; return SPELL_CAST_OK; } @@ -571,6 +576,37 @@ public: } }; +class spell_wintergrasp_defender_teleport_trigger : public SpellScriptLoader +{ +public: + spell_wintergrasp_defender_teleport_trigger() : SpellScriptLoader("spell_wintergrasp_defender_teleport_trigger") { } + + class spell_wintergrasp_defender_teleport_trigger_SpellScript : public SpellScript + { + PrepareSpellScript(spell_wintergrasp_defender_teleport_trigger_SpellScript); + + void HandleDummy(SpellEffIndex /*effindex*/) + { + if (Unit* target = GetHitUnit()) + { + WorldLocation loc; + target->GetPosition(&loc); + SetExplTargetDest(loc); + } + } + + void Register() + { + OnEffectHitTarget += SpellEffectFn(spell_wintergrasp_defender_teleport_trigger_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } + }; + + SpellScript* GetSpellScript() const + { + return new spell_wintergrasp_defender_teleport_trigger_SpellScript(); + } +}; + void AddSC_wintergrasp() { new npc_wg_queue(); @@ -582,4 +618,5 @@ void AddSC_wintergrasp() new spell_wintergrasp_grab_passenger(); new achievement_wg_didnt_stand_a_chance(); new spell_wintergrasp_defender_teleport(); + new spell_wintergrasp_defender_teleport_trigger(); } diff --git a/src/server/scripts/Spells/spell_hunter.cpp b/src/server/scripts/Spells/spell_hunter.cpp index 31aafe8dd38..9c922f2c6fb 100644 --- a/src/server/scripts/Spells/spell_hunter.cpp +++ b/src/server/scripts/Spells/spell_hunter.cpp @@ -277,27 +277,31 @@ class spell_hun_masters_call : public SpellScriptLoader return true; } + void HandleDummy(SpellEffIndex /*effIndex*/) + { + if (Unit* ally = GetHitUnit()) + if (Player* caster = GetCaster()->ToPlayer()) + if (Pet* target = caster->GetPet()) + { + TriggerCastFlags castMask = TriggerCastFlags(TRIGGERED_FULL_MASK & ~TRIGGERED_IGNORE_CASTER_AURASTATE); + target->CastSpell(ally, GetEffectValue(), castMask); + target->CastSpell(ally, GetSpellInfo()->Effects[EFFECT_0].CalcValue(), castMask); + } + } + void HandleScriptEffect(SpellEffIndex /*effIndex*/) { if (Unit* target = GetHitUnit()) { // Cannot be processed while pet is dead TriggerCastFlags castMask = TriggerCastFlags(TRIGGERED_FULL_MASK & ~TRIGGERED_IGNORE_CASTER_AURASTATE); - target->CastSpell(target, GetEffectValue(), castMask); target->CastSpell(target, HUNTER_SPELL_MASTERS_CALL_TRIGGERED, castMask); - // there is a possibility that this effect should access effect 0 (dummy) target, but i dubt that - // it's more likely that on on retail it's possible to call target selector based on dbc values - // anyways, we're using GetExplTargetUnit() here and it's ok - if (Unit* ally = GetExplTargetUnit()) - { - target->CastSpell(ally, GetEffectValue(), castMask); - target->CastSpell(ally, GetSpellInfo()->Effects[EFFECT_0].CalcValue(), castMask); - } } } void Register() { + OnEffectHitTarget += SpellEffectFn(spell_hun_masters_call_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); OnEffectHitTarget += SpellEffectFn(spell_hun_masters_call_SpellScript::HandleScriptEffect, EFFECT_1, SPELL_EFFECT_SCRIPT_EFFECT); } }; diff --git a/src/server/scripts/World/go_scripts.cpp b/src/server/scripts/World/go_scripts.cpp index 3ce136b9737..2a5d58122ed 100644 --- a/src/server/scripts/World/go_scripts.cpp +++ b/src/server/scripts/World/go_scripts.cpp @@ -53,12 +53,14 @@ EndContentData */ #include "ScriptMgr.h" #include "ScriptedCreature.h" #include "ScriptedGossip.h" +#include "GameObjectAI.h" +#include "Spell.h" /*###### ## go_cat_figurine ######*/ -enum eCatFigurine +enum CatFigurine { SPELL_SUMMON_GHOST_SABER = 5968, }; @@ -178,7 +180,7 @@ public: ## go_gilded_brazier (Paladin First Trail quest (9678)) ######*/ -enum eGildedBrazier +enum GildedBrazier { NPC_STILLBLADE = 17716, }; @@ -282,7 +284,7 @@ public: ## go_ethereum_prison ######*/ -enum eEthereumPrison +enum EthereumPrison { SPELL_REP_LC = 39456, SPELL_REP_SHAT = 39457, @@ -367,7 +369,7 @@ public: ## go_resonite_cask ######*/ -enum eResoniteCask +enum ResoniteCask { NPC_GOGGEROC = 11920 }; @@ -410,7 +412,7 @@ public: ## go_shrine_of_the_birds ######*/ -enum eShrineOfTheBirds +enum ShrineOfTheBirds { NPC_HAWK_GUARD = 22992, NPC_EAGLE_GUARD = 22993, @@ -456,7 +458,7 @@ public: ## go_southfury_moonstone ######*/ -enum eSouthfury +enum Southfury { NPC_RIZZLE = 23002, SPELL_BLACKJACK = 39865, //stuns player @@ -484,7 +486,7 @@ public: ## go_tele_to_dalaran_crystal ######*/ -enum eDalaranCrystal +enum DalaranCrystal { QUEST_LEARN_LEAVE_RETURN = 12790, QUEST_TELE_CRYSTAL_FLAG = 12845 @@ -536,7 +538,7 @@ public: #define GOSSIP_FEL_CRYSTALFORGE_ITEM_5 "Purchase 5 Unstable Flask of the Beast for the cost of 50 Apexis Shards" #define GOSSIP_FEL_CRYSTALFORGE_ITEM_RETURN "Use the fel crystalforge to make another purchase." -enum eFelCrystalforge +enum FelCrystalforge { SPELL_CREATE_1_FLASK_OF_BEAST = 40964, SPELL_CREATE_5_FLASK_OF_BEAST = 40965, @@ -595,7 +597,7 @@ public: #define GOSSIP_BASHIR_CRYSTALFORGE_ITEM_5 "Purchase 5 Unstable Flask of the Sorcerer for the cost of 50 Apexis Shards" #define GOSSIP_BASHIR_CRYSTALFORGE_ITEM_RETURN "Use the bashir crystalforge to make another purchase." -enum eBashirCrystalforge +enum BashirCrystalforge { SPELL_CREATE_1_FLASK_OF_SORCERER = 40968, SPELL_CREATE_5_FLASK_OF_SORCERER = 40970, @@ -648,7 +650,7 @@ public: ## matrix_punchograph ######*/ -enum eMatrixPunchograph +enum MatrixPunchograph { ITEM_WHITE_PUNCH_CARD = 9279, ITEM_YELLOW_PUNCH_CARD = 9280, @@ -713,7 +715,7 @@ public: ## go_scourge_cage ######*/ -enum eScourgeCage +enum ScourgeCage { NPC_SCOURGE_PRISONER = 25610 }; @@ -740,7 +742,7 @@ public: ## go_arcane_prison ######*/ -enum eArcanePrison +enum ArcanePrison { QUEST_PRISON_BREAK = 11587, SPELL_ARCANE_PRISONER_KILL_CREDIT = 45456 @@ -787,7 +789,7 @@ public: ## go_jotunheim_cage ######*/ -enum eJotunheimCage +enum JotunheimCage { NPC_EBON_BLADE_PRISONER_HUMAN = 30186, NPC_EBON_BLADE_PRISONER_NE = 30194, @@ -842,7 +844,7 @@ public: } }; -enum eTableTheka +enum TableTheka { GOSSIP_TABLE_THEKA = 1653, @@ -869,7 +871,7 @@ public: ## go_inconspicuous_landmark ######*/ -enum eInconspicuousLandmark +enum InconspicuousLandmark { SPELL_SUMMON_PIRATES_TREASURE_AND_TRIGGER_MOB = 11462, ITEM_CUERGOS_KEY = 9275, @@ -895,7 +897,7 @@ public: ## go_ethereal_teleport_pad ######*/ -enum eEtherealTeleportPad +enum EtherealTeleportPad { NPC_IMAGE_WIND_TRADER = 20518, ITEM_TELEPORTER_POWER_PACK = 28969, @@ -921,45 +923,105 @@ public: ## go_soulwell ######*/ -class go_soulwell : public GameObjectScript +enum SoulWellData { -public: - go_soulwell() : GameObjectScript("go_soulwell") { } + GO_SOUL_WELL_R1 = 181621, + GO_SOUL_WELL_R2 = 193169, - bool OnGossipHello(Player* player, GameObject* go) - { - Unit* caster = go->GetOwner(); - if (!caster || caster->GetTypeId() != TYPEID_PLAYER) - return true; + SPELL_IMPROVED_HEALTH_STONE_R1 = 18692, + SPELL_IMPROVED_HEALTH_STONE_R2 = 18693, - if (!player->IsInSameRaidWith(static_cast<Player*>(caster))) - return true; + SPELL_CREATE_MASTER_HEALTH_STONE_R0 = 34130, + SPELL_CREATE_MASTER_HEALTH_STONE_R1 = 34149, + SPELL_CREATE_MASTER_HEALTH_STONE_R2 = 34150, + + SPELL_CREATE_FEL_HEALTH_STONE_R0 = 58890, + SPELL_CREATE_FEL_HEALTH_STONE_R1 = 58896, + SPELL_CREATE_FEL_HEALTH_STONE_R2 = 58898, +}; - // Repeating this at every use is ugly and inefficient. But as long as we don't have proper - // GO scripting with at least On Create and On Update events, the other options are no less - // ugly and hacky. - uint32 newSpell = 0; - if (go->GetEntry() == 193169) // Soulwell for rank 2 +class go_soulwell : public GameObjectScript +{ + public: + go_soulwell() : GameObjectScript("go_soulwell") {} + + struct go_soulwellAI : public GameObjectAI { - if (caster->HasAura(18693)) // Improved Healthstone rank 2 - newSpell = 58898; - else if (caster->HasAura(18692)) // Improved Healthstone rank 1 - newSpell = 58896; - else newSpell = 58890; - } - else if (go->GetEntry() == 181621) // Soulwell for rank 1 + go_soulwellAI(GameObject* go) : GameObjectAI(go) + { + _stoneSpell = 0; + _stoneId = 0; + switch (go->GetEntry()) + { + case GO_SOUL_WELL_R1: + _stoneSpell = SPELL_CREATE_MASTER_HEALTH_STONE_R0; + if (Unit* owner = go->GetOwner()) + { + if (owner->HasAura(SPELL_IMPROVED_HEALTH_STONE_R1)) + _stoneSpell = SPELL_CREATE_MASTER_HEALTH_STONE_R1; + else if (owner->HasAura(SPELL_CREATE_MASTER_HEALTH_STONE_R2)) + _stoneSpell = SPELL_CREATE_MASTER_HEALTH_STONE_R2; + } + break; + case GO_SOUL_WELL_R2: + _stoneSpell = SPELL_CREATE_FEL_HEALTH_STONE_R0; + if (Unit* owner = go->GetOwner()) + { + if (owner->HasAura(SPELL_IMPROVED_HEALTH_STONE_R1)) + _stoneSpell = SPELL_CREATE_FEL_HEALTH_STONE_R1; + else if (owner->HasAura(SPELL_CREATE_MASTER_HEALTH_STONE_R2)) + _stoneSpell = SPELL_CREATE_FEL_HEALTH_STONE_R2; + } + break; + } + if (_stoneSpell == 0) // Should never happen + return; + + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(_stoneSpell); + if (!spellInfo) + return; + + _stoneId = spellInfo->Effects[EFFECT_0].ItemType; + } + + /// Due to the fact that this GameObject triggers CMSG_GAMEOBJECT_USE + /// _and_ CMSG_GAMEOBJECT_REPORT_USE, this GossipHello hook is called + /// twice. The script's handling is fine as it won't remove two charges + /// on the well. We have to find how to segregate REPORT_USE and USE. + bool GossipHello(Player* player) + { + Unit* owner = go->GetOwner(); + if (_stoneSpell == 0 || _stoneId == 0) + return true; + + if (!owner || owner->GetTypeId() != TYPEID_PLAYER || !player->IsInSameRaidWith(owner->ToPlayer())) + return true; + + // Don't try to add a stone if we already have one. + if (player->HasItemCount(_stoneId, 1)) + { + if (SpellInfo const* spell = sSpellMgr->GetSpellInfo(_stoneSpell)) + Spell::SendCastResult(player, spell, 0, SPELL_FAILED_TOO_MANY_OF_ITEM); + return true; + } + + owner->CastSpell(player, _stoneSpell, true); + // Item has to actually be created to remove a charge on the well. + if (player->HasItemCount(_stoneId, 1)) + go->AddUse(); + + return false; + } + + private: + uint32 _stoneSpell; + uint32 _stoneId; + }; + + GameObjectAI* GetAI(GameObject* go) const { - if (caster->HasAura(18693)) // Improved Healthstone rank 2 - newSpell = 34150; - else if (caster->HasAura(18692)) // Improved Healthstone rank 1 - newSpell = 34149; - else newSpell = 34130; + return new go_soulwellAI(go); } - - go->AddUse(); - player->CastSpell(player, newSpell, true); - return true; - } }; /*###### @@ -967,7 +1029,7 @@ public: ## go_dragonflayer_cage ######*/ -enum ePrisonersOfWyrmskull +enum PrisonersOfWyrmskull { QUEST_PRISONERS_OF_WYRMSKULL = 11255, NPC_PRISONER_PRIEST = 24086, @@ -1017,7 +1079,7 @@ public: ## go_tadpole_cage ######*/ -enum eTadpoles +enum Tadpoles { QUEST_OH_NOES_THE_TADPOLES = 11560, NPC_WINTERFIN_TADPOLE = 25201 @@ -1052,7 +1114,7 @@ public: #define GOSSIP_USE_OUTHOUSE "Use the outhouse." #define GO_ANDERHOLS_SLIDER_CIDER_NOT_FOUND "Quest item Anderhol's Slider Cider not found." -enum eAmberpineOuthouse +enum AmberpineOuthouse { ITEM_ANDERHOLS_SLIDER_CIDER = 37247, NPC_OUTHOUSE_BUNNY = 27326, @@ -1114,7 +1176,7 @@ public: ## go_hive_pod ######*/ -enum eHives +enum Hives { QUEST_HIVE_IN_THE_TOWER = 9544, NPC_HIVE_AMBUSHER = 13301 @@ -1281,7 +1343,7 @@ public: ## go_midsummer_bonfire ######*/ -enum eMidsummerBonfire +enum MidsummerBonfire { STAMP_OUT_BONFIRE_QUEST_COMPLETE = 45458, }; diff --git a/src/server/shared/Utilities/Util.cpp b/src/server/shared/Utilities/Util.cpp index 9778e86d444..0897c8814ab 100755 --- a/src/server/shared/Utilities/Util.cpp +++ b/src/server/shared/Utilities/Util.cpp @@ -28,16 +28,19 @@ static SFMTRandTSS sfmtRand; int32 irand(int32 min, int32 max) { + assert(max >= min); return int32(sfmtRand->IRandom(min, max)); } uint32 urand(uint32 min, uint32 max) { + assert(max >= min); return sfmtRand->URandom(min, max); } float frand(float min, float max) { + assert(max >= min); return float(sfmtRand->Random() * (max - min) + min); } diff --git a/src/server/shared/Utilities/Util.h b/src/server/shared/Utilities/Util.h index 21aaa36498d..f84e5155bb1 100755 --- a/src/server/shared/Utilities/Util.h +++ b/src/server/shared/Utilities/Util.h @@ -31,7 +31,7 @@ template<typename T, class S> struct Finder { T val_; T S::* idMember_; - + Finder(T val, T S::* idMember) : val_(val), idMember_(idMember) {} bool operator()(const std::pair<int, S> &obj) { return obj.second.*idMember_ == val_; } }; |