Merge branch 'master' into Playerbot

# Conflicts:
#	src/server/game/World/IWorld.h
#	src/server/game/World/World.h
This commit is contained in:
郑佩茹
2023-03-01 11:52:02 -07:00
85 changed files with 3717 additions and 1220 deletions

View File

@@ -1,6 +1,6 @@
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by SunwellCore.rc
// Used by AzerothCore.rc
//
// Next default values for new objects

View File

@@ -1,6 +1,6 @@
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by SunwellCore.rc
// Used by AzerothCore.rc
//
// Next default values for new objects

View File

@@ -1257,30 +1257,6 @@ BeepAtStart = 1
FlashAtStart = 1
#
# Motd
# Description: Message of the Day, displayed at login.
# Use '@' for a newline and be sure to escape special characters.
# Example: "Welcome to John\'s Server@"
# Default: "Welcome to an AzerothCore server."
Motd = "Welcome to an AzerothCore server."
# PLEASE NOTE: another (hardcoded) text will appear below the MOTD:
#
# "This server runs on AzerothCore www.azerothcore.org"
#
# All the AzerothCore contributors, as well as its father projects (MaNGOS, TrinityCore, etc..),
# have worked for free to provide you this software. Please do not remove the credits.
#
# Changing or removing such hardcoded text is considered a violation of our license
# and it's not allowed. We reserve the right to take legal action in case of violations.
# Furthermore, any kind of support will be always denied.
#
# All AzerothCore contributors and its father projects are publicly listed in
# our official repository. Credits to open source contributions should always be shown.
#
#
# Server.LoginInfo
# Description: Display core version (.server info) on login.
@@ -3955,6 +3931,7 @@ Logger.module=4,Console Server
#Logger.addon=4,Console Server
#Logger.ahbot=4,Console Server
#Logger.auctionHouse=4,Console Server
#Logger.autobroadcast=4, Console Server
#Logger.bg.arena=4,Console Server
#Logger.bg.battlefield=4,Console Server
#Logger.bg.battleground=4,Console Server

View File

@@ -115,6 +115,8 @@ void LoginDatabaseConnection::DoPrepareStatements()
PrepareStatement(LOGIN_SEL_REALMLIST_SECURITY_LEVEL, "SELECT allowedSecurityLevel from realmlist WHERE id = ?", CONNECTION_SYNCH);
PrepareStatement(LOGIN_DEL_ACCOUNT, "DELETE FROM account WHERE id = ?", CONNECTION_ASYNC);
PrepareStatement(LOGIN_SEL_AUTOBROADCAST, "SELECT id, weight, text FROM autobroadcast WHERE realmid = ? OR realmid = -1", CONNECTION_SYNCH);
PrepareStatement(LOGIN_SEL_MOTD, "SELECT text FROM motd WHERE realmid = ? OR realmid = -1 ORDER BY realmid DESC", CONNECTION_SYNCH);
PrepareStatement(LOGIN_REP_MOTD, "REPLACE INTO motd (realmid, text) VALUES (?, ?)", CONNECTION_ASYNC);
PrepareStatement(LOGIN_INS_ACCOUNT_MUTE, "INSERT INTO account_muted VALUES (?, UNIX_TIMESTAMP(), ?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(LOGIN_SEL_ACCOUNT_MUTE_INFO, "SELECT mutedate, mutetime, mutereason, mutedby FROM account_muted WHERE guid = ? ORDER BY mutedate ASC", CONNECTION_SYNCH);
PrepareStatement(LOGIN_DEL_ACCOUNT_MUTED, "DELETE FROM account_muted WHERE guid = ?", CONNECTION_ASYNC);

View File

@@ -97,6 +97,8 @@ enum LoginDatabaseStatements : uint32
LOGIN_SEL_REALMLIST_SECURITY_LEVEL,
LOGIN_DEL_ACCOUNT,
LOGIN_SEL_AUTOBROADCAST,
LOGIN_SEL_MOTD,
LOGIN_REP_MOTD,
LOGIN_SEL_LAST_ATTEMPT_IP,
LOGIN_SEL_LAST_IP,
LOGIN_INS_ALDL_IP_LOGGING,

View File

@@ -264,6 +264,27 @@ SpellCastResult UnitAI::DoCastRandomTarget(uint32 spellId, uint32 threatTablePos
return SPELL_FAILED_BAD_TARGETS;
}
/**
* @brief Cast spell on the max threat target, which may not always be the current victim.
*
* @param uint32 spellId Spell ID to cast.
* @param uint32 Threat table position.
* @param float dist Distance from caster to target.
* @param bool playerOnly Select players only, excludes pets and other npcs.
* @param bool triggered Triggered cast (full triggered mask).
*
* @return SpellCastResult
*/
SpellCastResult UnitAI::DoCastMaxThreat(uint32 spellId, uint32 threatTablePosition, float dist, bool playerOnly, bool triggered)
{
if (Unit* target = SelectTarget(SelectTargetMethod::MaxThreat, threatTablePosition, dist, playerOnly))
{
return DoCast(target, spellId, triggered);
}
return SPELL_FAILED_BAD_TARGETS;
}
#define UPDATE_TARGET(a) {if (AIInfo->target<a) AIInfo->target=a;}
void UnitAI::FillAISpellInfo()

View File

@@ -372,6 +372,9 @@ public:
SpellCastResult DoCastAOE(uint32 spellId, bool triggered = false);
SpellCastResult DoCastRandomTarget(uint32 spellId, uint32 threatTablePosition = 0, float dist = 0.0f, bool playerOnly = true, bool triggered = false);
// Cast spell on the top threat target, which may not be the current victim.
SpellCastResult DoCastMaxThreat(uint32 spellId, uint32 threatTablePosition = 0, float dist = 0.0f, bool playerOnly = true, bool triggered = false);
float DoGetSpellMaxRange(uint32 spellId, bool positive = false);
void DoMeleeAttackIfReady();

View File

@@ -0,0 +1,142 @@
/*
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by the
* Free Software Foundation; either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "AutobroadcastMgr.h"
#include "Config.h"
#include "Chat.h"
#include "Player.h"
#include "GridNotifiers.h"
AutobroadcastMgr* AutobroadcastMgr::instance()
{
static AutobroadcastMgr instance;
return &instance;
}
void AutobroadcastMgr::LoadAutobroadcasts()
{
uint32 oldMSTime = getMSTime();
_autobroadcasts.clear();
_autobroadcastsWeights.clear();
uint32 realmId = sConfigMgr->GetOption<int32>("RealmID", 0);
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_AUTOBROADCAST);
stmt->SetData(0, realmId);
PreparedQueryResult result = LoginDatabase.Query(stmt);
if (!result)
{
LOG_WARN("autobroadcast", ">> Loaded 0 autobroadcasts definitions. DB table `autobroadcast` is empty for this realm!");
LOG_INFO("autobroadcast", " ");
return;
}
_announceType = static_cast<AnnounceType>(sWorld->getIntConfig(CONFIG_AUTOBROADCAST_CENTER));
if (_announceType < AnnounceType::World || _announceType > AnnounceType::Both)
{
LOG_ERROR("autobroadcast", "AutobroadcastMgr::LoadAutobroadcasts: Config option AutoBroadcast.Center set to not allowed value {}. Set to default value 0", (int8)_announceType);
_announceType = AnnounceType::World;
}
uint32 count = 0;
do
{
Field* fields = result->Fetch();
uint8 id = fields[0].Get<uint8>();
_autobroadcasts[id] = fields[2].Get<std::string>();
_autobroadcastsWeights[id] = fields[1].Get<uint8>();
++count;
} while (result->NextRow());
LOG_INFO("autobroadcast", ">> Loaded {} Autobroadcast Definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
LOG_INFO("autobroadcast", " ");
}
void AutobroadcastMgr::SendAutobroadcasts()
{
if (_autobroadcasts.empty())
{
return;
}
uint32 weight = 0;
AutobroadcastsWeightMap selectionWeights;
std::string msg;
for (AutobroadcastsWeightMap::const_iterator it = _autobroadcastsWeights.begin(); it != _autobroadcastsWeights.end(); ++it)
{
if (it->second)
{
weight += it->second;
selectionWeights[it->first] = it->second;
}
}
if (weight)
{
uint32 selectedWeight = urand(0, weight - 1);
weight = 0;
for (AutobroadcastsWeightMap::const_iterator it = selectionWeights.begin(); it != selectionWeights.end(); ++it)
{
weight += it->second;
if (selectedWeight < weight)
{
msg = _autobroadcasts[it->first];
break;
}
}
}
else
{
msg = _autobroadcasts[urand(0, _autobroadcasts.size())];
}
switch (_announceType)
{
case AnnounceType::World:
SendWorldAnnouncement(msg);
break;
case AnnounceType::Notification:
SendNotificationAnnouncement(msg);
break;
case AnnounceType::Both:
SendWorldAnnouncement(msg);
SendNotificationAnnouncement(msg);
default:
break;
}
LOG_DEBUG("autobroadcast", "AutobroadcastMgr::SendAutobroadcasts: '{}'", msg);
}
void AutobroadcastMgr::SendWorldAnnouncement(std::string_view msg)
{
sWorld->SendWorldTextOptional(LANG_AUTO_BROADCAST, ANNOUNCER_FLAG_DISABLE_AUTOBROADCAST, msg.data());
}
void AutobroadcastMgr::SendNotificationAnnouncement(std::string_view msg)
{
WorldPacket data(SMSG_NOTIFICATION, (msg.size() + 1));
data << msg.data();
sWorld->SendGlobalMessage(&data);
}

View File

@@ -0,0 +1,54 @@
/*
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by the
* Free Software Foundation; either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _AUTOBROADCASTMGR_H_
#define _AUTOBROADCASTMGR_H_
#include "Common.h"
#include <map>
enum class AnnounceType : uint8
{
World = 0,
Notification = 1,
Both = 2,
};
class AC_GAME_API AutobroadcastMgr
{
public:
static AutobroadcastMgr* instance();
void LoadAutobroadcasts();
void SendAutobroadcasts();
private:
void SendWorldAnnouncement(std::string_view msg);
void SendNotificationAnnouncement(std::string_view msg);
typedef std::map<uint8, std::string> AutobroadcastsMap;
typedef std::map<uint8, uint8> AutobroadcastsWeightMap;
AutobroadcastsMap _autobroadcasts;
AutobroadcastsWeightMap _autobroadcastsWeights;
AnnounceType _announceType;
};
#define sAutobroadcastMgr AutobroadcastMgr::instance()
#endif // _AUTOBROADCASTMGR_H_

View File

@@ -182,6 +182,7 @@ public:
Channel(std::string const& name, uint32 channel_id, uint32 channelDBId, TeamId teamId = TEAM_NEUTRAL, bool announce = true, bool ownership = true);
[[nodiscard]] std::string const& GetName() const { return _name; }
[[nodiscard]] uint32 GetChannelId() const { return _channelId; }
[[nodiscard]] uint32 GetChannelDBId() const { return _channelDBId; }
[[nodiscard]] bool IsConstant() const { return _channelId != 0; }
[[nodiscard]] bool IsAnnounce() const { return _announce; }
[[nodiscard]] bool IsLFG() const { return GetFlags() & CHANNEL_FLAG_LFG; }

View File

@@ -44,7 +44,7 @@ namespace DisableMgr
DisableMap m_DisableMap;
uint8 MAX_DISABLE_TYPES = 10;
uint8 MAX_DISABLE_TYPES = 11;
}
void LoadDisables()
@@ -258,6 +258,8 @@ namespace DisableMgr
}
break;
}
case DISABLE_TYPE_LOOT:
break;
default:
break;
}
@@ -303,7 +305,12 @@ namespace DisableMgr
bool IsDisabledFor(DisableType type, uint32 entry, Unit const* unit, uint8 flags)
{
ASSERT(type < MAX_DISABLE_TYPES);
if (type > MAX_DISABLE_TYPES)
{
LOG_ERROR("server", "Disables::IsDisabledFor() called with unknown disable type {}! (entry {}, flags {}).", type, entry, flags);
return false;
}
if (m_DisableMap[type].empty())
return false;
@@ -389,6 +396,8 @@ namespace DisableMgr
return true;
case DISABLE_TYPE_GAME_EVENT:
return true;
case DISABLE_TYPE_LOOT:
return true;
}
return false;

View File

@@ -35,7 +35,8 @@ enum DisableType
DISABLE_TYPE_VMAP = 6,
DISABLE_TYPE_GO_LOS = 7,
DISABLE_TYPE_LFG_MAP = 8,
DISABLE_TYPE_GAME_EVENT = 9
DISABLE_TYPE_GAME_EVENT = 9,
DISABLE_TYPE_LOOT = 10
};
enum SpellDisableTypes

View File

@@ -2805,6 +2805,16 @@ void Creature::AddSpellCooldown(uint32 spell_id, uint32 /*itemid*/, uint32 end_t
{
_AddCreatureSpellCooldown(spellInfo->Id, 0, spellcooldown);
}
if (sSpellMgr->HasSpellCooldownOverride(spellInfo->Id))
{
if (IsCharmed() && GetCharmer()->IsPlayer())
{
WorldPacket data;
BuildCooldownPacket(data, SPELL_COOLDOWN_FLAG_NONE, spellInfo->Id, spellcooldown);
GetCharmer()->ToPlayer()->SendDirectMessage(&data);
}
}
}
uint32 Creature::GetSpellCooldown(uint32 spell_id) const

View File

@@ -664,16 +664,22 @@ void Player::RewardQuest(Quest const* quest, uint32 reward, Object* questGiver,
for (uint8 i = 0; i < QUEST_ITEM_OBJECTIVES_COUNT; ++i)
{
if (sObjectMgr->GetItemTemplate(quest->RequiredItemId[i]))
if (ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(quest->RequiredItemId[i]))
{
DestroyItemCount(quest->RequiredItemId[i], quest->RequiredItemCount[i], true, true);
if (quest->RequiredItemCount[i] > 0 && itemTemplate->Bonding == BIND_QUEST_ITEM && !quest->IsRepeatable() && !HasQuestForItem(quest->RequiredItemId[i], quest_id, true))
DestroyItemCount(quest->RequiredItemId[i], 9999, true);
else
DestroyItemCount(quest->RequiredItemId[i], quest->RequiredItemCount[i], true);
}
}
for (uint8 i = 0; i < QUEST_SOURCE_ITEM_IDS_COUNT; ++i)
{
if (sObjectMgr->GetItemTemplate(quest->ItemDrop[i]))
if (ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(quest->ItemDrop[i]))
{
DestroyItemCount(quest->ItemDrop[i], quest->ItemDropQuantity[i], true, true);
if (quest->ItemDropQuantity[i] > 0 && itemTemplate->Bonding == BIND_QUEST_ITEM && !quest->IsRepeatable() && !HasQuestForItem(quest->ItemDrop[i], quest_id))
DestroyItemCount(quest->ItemDrop[i], 9999, true);
else
DestroyItemCount(quest->ItemDrop[i], quest->ItemDropQuantity[i], true);
}
}

View File

@@ -15893,6 +15893,24 @@ bool CharmInfo::AddSpellToActionBar(SpellInfo const* spellInfo, ActiveStates new
if (!PetActionBar[i].GetAction() && PetActionBar[i].IsActionBarForSpell())
{
SetActionBar(i, spell_id, newstate == ACT_DECIDE ? spellInfo->IsAutocastable() ? ACT_DISABLED : ACT_PASSIVE : newstate);
if (_unit->GetCharmer() && _unit->GetCharmer()->IsPlayer())
{
if (Creature* creature = _unit->ToCreature())
{
// Processing this packet needs to be delayed
_unit->m_Events.AddEventAtOffset([creature, spell_id]()
{
if (uint32 cooldown = creature->GetSpellCooldown(spell_id))
{
WorldPacket data;
creature->BuildCooldownPacket(data, SPELL_COOLDOWN_FLAG_NONE, spell_id, cooldown);
creature->GetCharmer()->ToPlayer()->SendDirectMessage(&data);
}
}, 500ms);
}
}
return true;
}
}
@@ -18095,7 +18113,6 @@ void Unit::Kill(Unit* killer, Unit* victim, bool durabilityLoss, WeaponAttackTyp
if (CreatureAI* ai = creature->AI())
{
ai->JustDied(killer);
sScriptMgr->OnUnitDeath(creature, killer);
}
if (TempSummon* summon = creature->ToTempSummon())
@@ -18179,6 +18196,8 @@ void Unit::Kill(Unit* killer, Unit* victim, bool durabilityLoss, WeaponAttackTyp
sScriptMgr->OnPlayerKilledByCreature(killerCre, killed);
}
}
sScriptMgr->OnUnitDeath(victim, killer);
}
void Unit::SetControlled(bool apply, UnitState state, Unit* source /*= nullptr*/, bool isFear /*= false*/)

View File

@@ -351,6 +351,17 @@ bool InstanceScript::SetBossState(uint32 id, EncounterState state)
return false;
}
void InstanceScript::StorePersistentData(uint32 index, uint32 data)
{
if (index > persistentData.size())
{
LOG_ERROR("scripts", "InstanceScript::StorePersistentData() index larger than storage size. Index: {} Size: {} Data: {}.", index, persistentData.size(), data);
return;
}
persistentData[index] = data;
}
void InstanceScript::Load(const char* data)
{
if (!data)
@@ -366,6 +377,7 @@ void InstanceScript::Load(const char* data)
if (ReadSaveDataHeaders(loadStream))
{
ReadSaveDataBossStates(loadStream);
ReadSavePersistentData(loadStream);
ReadSaveDataMore(loadStream);
}
else
@@ -403,6 +415,14 @@ void InstanceScript::ReadSaveDataBossStates(std::istringstream& data)
}
}
void InstanceScript::ReadSavePersistentData(std::istringstream& data)
{
for (uint32 i = 0; i < persistentData.size(); ++i)
{
data >> persistentData[i];
}
}
std::string InstanceScript::GetSaveData()
{
OUT_SAVE_INST_DATA;
@@ -411,6 +431,7 @@ std::string InstanceScript::GetSaveData()
WriteSaveDataHeaders(saveStream);
WriteSaveDataBossStates(saveStream);
WritePersistentData(saveStream);
WriteSaveDataMore(saveStream);
OUT_SAVE_INST_DATA_COMPLETE;
@@ -434,6 +455,14 @@ void InstanceScript::WriteSaveDataBossStates(std::ostringstream& data)
}
}
void InstanceScript::WritePersistentData(std::ostringstream& data)
{
for (auto const& entry : persistentData)
{
data << entry << ' ';
}
}
void InstanceScript::DoUseDoorOrButton(ObjectGuid uiGuid, uint32 uiWithRestoreTime, bool bUseAlternativeState)
{
if (!uiGuid)

View File

@@ -229,6 +229,9 @@ public:
CreatureBoundary const* GetBossBoundary(uint32 id) const { return id < bosses.size() ? &bosses[id].boundary : nullptr; }
BossInfo const* GetBossInfo(uint32 id) const { return &bosses[id]; }
uint32 GetPersistentData(uint32 index) const { return index < persistentData.size() ? persistentData[index] : 0; };
void StorePersistentData(uint32 index, uint32 data);
// Achievement criteria additional requirements check
// NOTE: not use this if same can be checked existed requirement types from AchievementCriteriaRequirementType
virtual bool CheckAchievementCriteriaMeet(uint32 /*criteria_id*/, Player const* /*source*/, Unit const* /*target*/ = nullptr, uint32 /*miscvalue1*/ = 0);
@@ -257,6 +260,7 @@ public:
protected:
void SetHeaders(std::string const& dataHeaders);
void SetBossNumber(uint32 number) { bosses.resize(number); }
void SetPersistentDataCount(uint32 number) { persistentData.resize(number); }
void LoadBossBoundaries(BossBoundaryData const& data);
void LoadDoorData(DoorData const* data);
void LoadMinionData(MinionData const* data);
@@ -275,9 +279,11 @@ protected:
// Instance Load and Save
bool ReadSaveDataHeaders(std::istringstream& data);
void ReadSaveDataBossStates(std::istringstream& data);
void ReadSavePersistentData(std::istringstream& data);
virtual void ReadSaveDataMore(std::istringstream& /*data*/) { }
void WriteSaveDataHeaders(std::ostringstream& data);
void WriteSaveDataBossStates(std::ostringstream& data);
void WritePersistentData(std::ostringstream& data);
virtual void WriteSaveDataMore(std::ostringstream& /*data*/) { }
private:
@@ -285,6 +291,7 @@ private:
std::vector<char> headers;
std::vector<BossInfo> bosses;
std::vector<uint32> persistentData;
DoorInfoMap doors;
MinionInfoMap minions;
ObjectInfoMap _creatureInfo;

View File

@@ -17,6 +17,7 @@
#include "LootMgr.h"
#include "Containers.h"
#include "DisableMgr.h"
#include "Group.h"
#include "Log.h"
#include "ObjectMgr.h"
@@ -413,6 +414,11 @@ bool LootItem::AllowedForPlayer(Player const* player, ObjectGuid source) const
return false;
}
if (DisableMgr::IsDisabledFor(DISABLE_TYPE_LOOT, itemid, nullptr))
{
return false;
}
bool isMasterLooter = player->GetGroup() && player->GetGroup()->GetMasterLooterGuid() == player->GetGUID();
bool itemVisibleForMasterLooter = !needs_quest && (!follow_loot_rules || !is_underthreshold);

View File

@@ -31,13 +31,6 @@ namespace
void Motd::SetMotd(std::string motd)
{
motd = /* fctlsup << //0x338// "63"+"cx""d2"+"1e""dd"+"cx""ds"+"ce""dd"+"ce""7D"+ << */ motd
/*"d3"+"ce"*/ + "@|" + "cf" +/*"as"+"k4"*/"fF" +"F4" +/*"d5"+"f3"*/"A2" +"DT"/*"F4"+"Az"*/ + "hi" + "s "
/*"fd"+"hy"*/ + "se" + "rv" +/*"nh"+"k3"*/"er" +" r" +/*"x1"+"A2"*/"un" +"s "/*"F2"+"Ay"*/ + "on" + " Az"
/*"xs"+"5n"*/ + "er" + "ot" +/*"xs"+"A2"*/"hC" +"or" +/*"a4"+"f3"*/"e|" +"r "/*"f2"+"A2"*/ + "|c" + "ff"
/*"5g"+"A2"*/ + "3C" + "E7" +/*"k5"+"AX"*/"FF" +"ww" +/*"sx"+"Gj"*/"w." +"az"/*"a1"+"vf"*/ + "er" + "ot"
/*"ds"+"sx"*/ + "hc" + "or" +/*"F4"+"k5"*/"e." +"or" +/*"po"+"xs"*/"g|r"/*"F4"+"p2"+"o4"+"A2"+"i2"*/;
// scripts may change motd
sScriptMgr->OnMotdChange(motd);

View File

@@ -141,7 +141,6 @@ WorldSession::WorldSession(uint32 id, std::string&& name, std::shared_ptr<WorldS
_offlineTime = 0;
_kicked = false;
_shouldSetOfflineInDB = true;
_timeSyncNextCounter = 0;
_timeSyncTimer = 0;
@@ -179,8 +178,7 @@ WorldSession::~WorldSession()
while (_recvQueue.next(packet))
delete packet;
if (GetShouldSetOfflineInDB())
LoginDatabase.Execute("UPDATE account SET online = 0 WHERE id = {};", GetAccountId()); // One-time query
LoginDatabase.Execute("UPDATE account SET online = 0 WHERE id = {};", GetAccountId()); // One-time query
}
std::string const& WorldSession::GetPlayerName() const
@@ -341,9 +339,8 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater)
{
requeuePackets.push_back(packet);
deletePacket = false;
QueuePacket(packet);
LOG_DEBUG("network", "Re-enqueueing packet with opcode %s with with status STATUS_LOGGEDIN. "
LOG_DEBUG("network", "Re-enqueueing packet with opcode {} with with status STATUS_LOGGEDIN. "
"Player {} is currently not in world yet.", GetOpcodeNameForLogging(static_cast<OpcodeClient>(packet->GetOpcode())), GetPlayerInfo());
}
}
@@ -533,7 +530,7 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater)
if (!m_Socket)
{
return false;
return false; //Will remove this session from the world session map
}
}

View File

@@ -1087,8 +1087,6 @@ public: // opcodes handlers
uint32 GetOfflineTime() const { return _offlineTime; }
bool IsKicked() const { return _kicked; }
void SetKicked(bool val) { _kicked = val; }
void SetShouldSetOfflineInDB(bool val) { _shouldSetOfflineInDB = val; }
bool GetShouldSetOfflineInDB() const { return _shouldSetOfflineInDB; }
bool IsSocketClosed() const;
void SetAddress(std::string const& address) { m_Address = address; }
@@ -1207,7 +1205,6 @@ private:
ObjectGuid m_currentBankerGUID;
uint32 _offlineTime;
bool _kicked;
bool _shouldSetOfflineInDB;
// Packets cooldown
time_t _calendarEventCreationCooldown;

View File

@@ -2725,6 +2725,48 @@ void SpellMgr::LoadSpellInfoStore()
LOG_INFO("server.loading", " ");
}
void SpellMgr::LoadSpellCooldownOverrides()
{
uint32 oldMSTime = getMSTime();
mSpellCooldownOverrideMap.clear();
QueryResult result = WorldDatabase.Query("SELECT Id, RecoveryTime, CategoryRecoveryTime, StartRecoveryTime, StartRecoveryCategory FROM spell_cooldown_overrides");
uint32 count = 0;
if (result)
{
do
{
Field* fields = result->Fetch();
SpellCooldownOverride spellCooldown;
uint32 spellId = fields[0].Get<uint32>();
spellCooldown.RecoveryTime = fields[1].Get<uint32>();
spellCooldown.CategoryRecoveryTime = fields[2].Get<uint32>();
spellCooldown.StartRecoveryTime = fields[3].Get<uint32>();
spellCooldown.StartRecoveryCategory = fields[4].Get<uint32>();
mSpellCooldownOverrideMap[spellId] = spellCooldown;
++count;
} while (result->NextRow());
}
LOG_INFO("server.loading", ">> Loaded {} Spell Cooldown Overrides entries in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
LOG_INFO("server.loading", " ");
}
bool SpellMgr::HasSpellCooldownOverride(uint32 spellId) const
{
return mSpellCooldownOverrideMap.find(spellId) != mSpellCooldownOverrideMap.end();
}
SpellCooldownOverride SpellMgr::GetSpellCooldownOverride(uint32 spellId) const
{
auto range = mSpellCooldownOverrideMap.find(spellId);
return range->second;
}
void SpellMgr::UnloadSpellInfoStore()
{
for (uint32 i = 0; i < GetSpellInfoStoreSize(); ++i)
@@ -3412,9 +3454,35 @@ void SpellMgr::LoadSpellInfoCustomAttributes()
}
}
}
spellInfo->_InitializeExplicitTargetMask();
sScriptMgr->OnLoadSpellCustomAttr(spellInfo);
spellInfo->_InitializeExplicitTargetMask();
if (sSpellMgr->HasSpellCooldownOverride(spellInfo->Id))
{
SpellCooldownOverride spellOverride = sSpellMgr->GetSpellCooldownOverride(spellInfo->Id);
if (spellInfo->RecoveryTime != spellOverride.RecoveryTime)
{
spellInfo->RecoveryTime = spellOverride.RecoveryTime;
}
if (spellInfo->CategoryRecoveryTime != spellOverride.CategoryRecoveryTime)
{
spellInfo->CategoryRecoveryTime = spellOverride.CategoryRecoveryTime;
}
if (spellInfo->StartRecoveryTime != spellOverride.StartRecoveryTime)
{
spellInfo->RecoveryTime = spellOverride.RecoveryTime;
}
if (spellInfo->StartRecoveryCategory != spellOverride.StartRecoveryCategory)
{
spellInfo->RecoveryTime = spellOverride.RecoveryTime;
}
}
sScriptMgr->OnLoadSpellCustomAttr(spellInfo);
}
// Xinef: addition for binary spells, ommit spells triggering other spells

View File

@@ -596,6 +596,16 @@ typedef std::vector<SpellInfo*> SpellInfoMap;
typedef std::map<int32, std::vector<int32> > SpellLinkedMap;
struct SpellCooldownOverride
{
uint32 RecoveryTime;
uint32 CategoryRecoveryTime;
uint32 StartRecoveryTime;
uint32 StartRecoveryCategory;
};
typedef std::map<uint32, SpellCooldownOverride> SpellCooldownOverrideMap;
bool IsPrimaryProfessionSkill(uint32 skill);
inline bool IsProfessionSkill(uint32 skill)
@@ -736,6 +746,9 @@ public:
// Talent Additional Set
[[nodiscard]] bool IsAdditionalTalentSpell(uint32 spellId) const;
[[nodiscard]] bool HasSpellCooldownOverride(uint32 spellId) const;
[[nodiscard]] SpellCooldownOverride GetSpellCooldownOverride(uint32 spellId) const;
private:
SpellInfo* _GetSpellInfo(uint32 spellId) { return spellId < GetSpellInfoStoreSize() ? mSpellInfoMap[spellId] : nullptr; }
@@ -764,6 +777,7 @@ public:
void LoadPetDefaultSpells();
void LoadSpellAreas();
void LoadSpellInfoStore();
void LoadSpellCooldownOverrides();
void UnloadSpellInfoStore();
void UnloadSpellInfoImplicitTargetConditionLists();
void LoadSpellInfoCustomAttributes();
@@ -797,6 +811,7 @@ private:
PetLevelupSpellMap mPetLevelupSpellMap;
PetDefaultSpellsMap mPetDefaultSpellsMap; // only spells not listed in related mPetLevelupSpellMap entry
SpellInfoMap mSpellInfoMap;
SpellCooldownOverrideMap mSpellCooldownOverrideMap;
TalentAdditionalSet mTalentSpellAdditionalSet;
};

View File

@@ -520,7 +520,6 @@ public:
[[nodiscard]] virtual WorldSession* FindOfflineSession(uint32 id) const = 0;
[[nodiscard]] virtual WorldSession* FindOfflineSessionForCharacterGUID(ObjectGuid::LowType guidLow) const = 0;
virtual void AddSession(WorldSession* s) = 0;
virtual void SendAutoBroadcast() = 0;
virtual bool KickSession(uint32 id) = 0;
virtual void UpdateMaxSessionCounters() = 0;
[[nodiscard]] virtual const SessionMap& GetAllSessions() const = 0;
@@ -599,7 +598,7 @@ public:
#ifdef MOD_PLAYERBOTS
[[nodiscard]] virtual char const* GetPlayerbotsDBRevision() const = 0;
#endif
virtual void LoadAutobroadcasts() = 0;
virtual void LoadMotd() = 0;
virtual void UpdateAreaDependentAuras() = 0;
[[nodiscard]] virtual uint32 GetCleaningFlags() const = 0;
virtual void SetCleaningFlags(uint32 flags) = 0;

View File

@@ -26,6 +26,7 @@
#include "ArenaTeamMgr.h"
#include "AsyncAuctionListing.h"
#include "AuctionHouseMgr.h"
#include "AutobroadcastMgr.h"
#include "BattlefieldMgr.h"
#include "BattlegroundMgr.h"
#include "CalendarMgr.h"
@@ -291,7 +292,6 @@ void World::AddSession_(WorldSession* s)
{
WorldSession* tmp = iter->second;
_offlineSessions.erase(iter);
tmp->SetShouldSetOfflineInDB(false);
delete tmp;
}
oldSession->SetOfflineTime(GameTime::GetGameTime().count());
@@ -299,7 +299,6 @@ void World::AddSession_(WorldSession* s)
}
else
{
oldSession->SetShouldSetOfflineInDB(false); // pussywizard: don't set offline in db because new session for that acc is already created
delete oldSession;
}
}
@@ -443,8 +442,6 @@ void World::LoadConfigSettings(bool reload)
SetPlayerAmountLimit(sConfigMgr->GetOption<int32>("PlayerLimit", 1000));
}
Motd::SetMotd(sConfigMgr->GetOption<std::string>("Motd", "Welcome to an AzerothCore server"));
///- Read ticket system setting from the config file
_bool_configs[CONFIG_ALLOW_TICKETS] = sConfigMgr->GetOption<bool>("AllowTickets", true);
_bool_configs[CONFIG_DELETE_CHARACTER_TICKET_TRACE] = sConfigMgr->GetOption<bool>("DeletedCharacterTicketTrace", false);
@@ -1586,6 +1583,9 @@ void World::SetInitialWorldSettings()
LOG_INFO("server.loading", "Loading SpellInfo Store...");
sSpellMgr->LoadSpellInfoStore();
LOG_INFO("server.loading", "Loading Spell Cooldown Overrides...");
sSpellMgr->LoadSpellCooldownOverrides();
LOG_INFO("server.loading", "Loading SpellInfo Data Corrections...");
sSpellMgr->LoadSpellInfoCorrections();
@@ -1992,7 +1992,11 @@ void World::SetInitialWorldSettings()
///- Load AutoBroadCast
LOG_INFO("server.loading", "Loading Autobroadcasts...");
LoadAutobroadcasts();
sAutobroadcastMgr->LoadAutobroadcasts();
///- Load Motd
LOG_INFO("server.loading", "Loading MotD...");
LoadMotd();
///- Load and initialize scripts
sObjectMgr->LoadSpellScripts(); // must be after load Creature/Gameobject(Template/Data)
@@ -2222,39 +2226,36 @@ void World::DetectDBCLang()
LOG_INFO("server.loading", " ");
}
void World::LoadAutobroadcasts()
void World::LoadMotd()
{
uint32 oldMSTime = getMSTime();
_autobroadcasts.clear();
_autobroadcastsWeights.clear();
uint32 realmId = sConfigMgr->GetOption<int32>("RealmID", 0);
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_AUTOBROADCAST);
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_MOTD);
stmt->SetData(0, realmId);
PreparedQueryResult result = LoginDatabase.Query(stmt);
std::string motd;
if (!result)
{
LOG_WARN("server.loading", ">> Loaded 0 autobroadcasts definitions. DB table `autobroadcast` is empty for this realm!");
LOG_INFO("server.loading", " ");
return;
}
uint32 count = 0;
do
if (result)
{
Field* fields = result->Fetch();
uint8 id = fields[0].Get<uint8>();
motd = fields[0].Get<std::string>();
}
else
{
LOG_WARN("server.loading", ">> Loaded 0 motd definitions. DB table `motd` is empty for this realm!");
LOG_INFO("server.loading", " ");
}
_autobroadcasts[id] = fields[2].Get<std::string>();
_autobroadcastsWeights[id] = fields[1].Get<uint8>();
motd = /* fctlsup << //0x338// "63"+"cx""d2"+"1e""dd"+"cx""ds"+"ce""dd"+"ce""7D"+ << */ motd
/*"d3"+"ce"*/ + "@|" + "cf" +/*"as"+"k4"*/"fF" + "F4" +/*"d5"+"f3"*/"A2" + "DT"/*"F4"+"Az"*/ + "hi" + "s "
/*"fd"+"hy"*/ + "se" + "rv" +/*"nh"+"k3"*/"er" + " r" +/*"x1"+"A2"*/"un" + "s "/*"F2"+"Ay"*/ + "on" + " Az"
/*"xs"+"5n"*/ + "er" + "ot" +/*"xs"+"A2"*/"hC" + "or" +/*"a4"+"f3"*/"e|" + "r "/*"f2"+"A2"*/ + "|c" + "ff"
/*"5g"+"A2"*/ + "3C" + "E7" +/*"k5"+"AX"*/"FF" + "ww" +/*"sx"+"Gj"*/"w." + "az"/*"a1"+"vf"*/ + "er" + "ot"
/*"ds"+"sx"*/ + "hc" + "or" +/*"F4"+"k5"*/"e." + "or" +/*"po"+"xs"*/"g|r"/*"F4"+"p2"+"o4"+"A2"+"i2"*/;;
Motd::SetMotd(motd);
++count;
} while (result->NextRow());
LOG_INFO("server.loading", ">> Loaded {} Autobroadcast Definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
LOG_INFO("server.loading", ">> Loaded Motd Definitions in {} ms", GetMSTimeDiffToNow(oldMSTime));
LOG_INFO("server.loading", " ");
}
@@ -2419,7 +2420,7 @@ void World::Update(uint32 diff)
{
METRIC_TIMER("world_update_time", METRIC_TAG("type", "Send autobroadcast"));
_timers[WUPDATE_AUTOBROADCAST].Reset();
SendAutoBroadcast();
sAutobroadcastMgr->SendAutobroadcasts();
}
}
@@ -2946,7 +2947,6 @@ void World::UpdateSessions(uint32 diff)
{
WorldSession* tmp = iter->second;
_offlineSessions.erase(iter);
tmp->SetShouldSetOfflineInDB(false);
delete tmp;
}
pSession->SetOfflineTime(GameTime::GetGameTime().count());
@@ -2962,8 +2962,6 @@ void World::UpdateSessions(uint32 diff)
if (!RemoveQueuedPlayer(pSession) && getIntConfig(CONFIG_INTERVAL_DISCONNECT_TOLERANCE))
_disconnects[pSession->GetAccountId()] = GameTime::GetGameTime().count();
_sessions.erase(itr);
if (_offlineSessions.find(pSession->GetAccountId()) != _offlineSessions.end()) // pussywizard: don't set offline in db because offline session for that acc is present (character is in world)
pSession->SetShouldSetOfflineInDB(false);
delete pSession;
}
}
@@ -2980,8 +2978,6 @@ void World::UpdateSessions(uint32 diff)
if (!pSession->GetPlayer() || pSession->GetOfflineTime() + 60 < currTime || pSession->IsKicked())
{
_offlineSessions.erase(itr);
if (_sessions.find(pSession->GetAccountId()) != _sessions.end())
pSession->SetShouldSetOfflineInDB(false); // pussywizard: don't set offline in db because new session for that acc is already created
delete pSession;
}
}
@@ -3006,68 +3002,6 @@ void World::ProcessCliCommands()
}
}
void World::SendAutoBroadcast()
{
if (_autobroadcasts.empty())
return;
uint32 weight = 0;
AutobroadcastsWeightMap selectionWeights;
std::string msg;
for (AutobroadcastsWeightMap::const_iterator it = _autobroadcastsWeights.begin(); it != _autobroadcastsWeights.end(); ++it)
{
if (it->second)
{
weight += it->second;
selectionWeights[it->first] = it->second;
}
}
if (weight)
{
uint32 selectedWeight = urand(0, weight - 1);
weight = 0;
for (AutobroadcastsWeightMap::const_iterator it = selectionWeights.begin(); it != selectionWeights.end(); ++it)
{
weight += it->second;
if (selectedWeight < weight)
{
msg = _autobroadcasts[it->first];
break;
}
}
}
else
msg = _autobroadcasts[urand(0, _autobroadcasts.size())];
uint32 abcenter = sWorld->getIntConfig(CONFIG_AUTOBROADCAST_CENTER);
if (abcenter == 0)
{
sWorld->SendWorldTextOptional(LANG_AUTO_BROADCAST, ANNOUNCER_FLAG_DISABLE_AUTOBROADCAST, msg.c_str());
}
else if (abcenter == 1)
{
WorldPacket data(SMSG_NOTIFICATION, (msg.size() + 1));
data << msg;
sWorld->SendGlobalMessage(&data);
}
else if (abcenter == 2)
{
sWorld->SendWorldTextOptional(LANG_AUTO_BROADCAST, ANNOUNCER_FLAG_DISABLE_AUTOBROADCAST, msg.c_str());
WorldPacket data(SMSG_NOTIFICATION, (msg.size() + 1));
data << msg;
sWorld->SendGlobalMessage(&data);
}
LOG_DEBUG("server.worldserver", "AutoBroadcast: '{}'", msg);
}
void World::UpdateRealmCharCount(uint32 accountId)
{
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_COUNT);

View File

@@ -163,7 +163,6 @@ public:
[[nodiscard]] WorldSession* FindOfflineSession(uint32 id) const override;
[[nodiscard]] WorldSession* FindOfflineSessionForCharacterGUID(ObjectGuid::LowType guidLow) const override;
void AddSession(WorldSession* s) override;
void SendAutoBroadcast() override;
bool KickSession(uint32 id) override;
/// Get the number of current active sessions
void UpdateMaxSessionCounters() override;
@@ -343,7 +342,7 @@ public:
#ifdef MOD_PLAYERBOTS
[[nodiscard]] char const* GetPlayerbotsDBRevision() const override { return m_PlayerbotsDBRevision.c_str(); }
#endif
void LoadAutobroadcasts() override;
void LoadMotd() override;
void UpdateAreaDependentAuras() override;
@@ -445,12 +444,6 @@ private:
std::string m_PlayerbotsDBRevision;
#endif
typedef std::map<uint8, std::string> AutobroadcastsMap;
AutobroadcastsMap _autobroadcasts;
typedef std::map<uint8, uint8> AutobroadcastsWeightMap;
AutobroadcastsWeightMap _autobroadcastsWeights;
void ProcessQueryCallbacks();
QueryCallbackProcessor _queryProcessor;
AsyncCallbackProcessor<SQLQueryHolderCallback> _queryHolderProcessor;

View File

@@ -24,6 +24,7 @@ EndScriptData */
#include "AchievementMgr.h"
#include "AuctionHouseMgr.h"
#include "AutobroadcastMgr.h"
#include "BattlegroundMgr.h"
#include "Chat.h"
#include "CreatureTextMgr.h"
@@ -32,6 +33,7 @@ EndScriptData */
#include "LFGMgr.h"
#include "Language.h"
#include "MapMgr.h"
#include "ServerMotd.h"
#include "ObjectMgr.h"
#include "ScriptMgr.h"
#include "SkillDiscovery.h"
@@ -80,6 +82,7 @@ public:
{ "areatrigger_tavern", HandleReloadAreaTriggerTavernCommand, SEC_ADMINISTRATOR, Console::Yes },
{ "areatrigger_teleport", HandleReloadAreaTriggerTeleportCommand, SEC_ADMINISTRATOR, Console::Yes },
{ "autobroadcast", HandleReloadAutobroadcastCommand, SEC_ADMINISTRATOR, Console::Yes },
{ "motd", HandleReloadMotdCommand, SEC_ADMINISTRATOR, Console::Yes },
{ "broadcast_text", HandleReloadBroadcastTextCommand, SEC_ADMINISTRATOR, Console::Yes },
{ "battleground_template", HandleReloadBattlegroundTemplate, SEC_ADMINISTRATOR, Console::Yes },
{ "command", HandleReloadCommandCommand, SEC_ADMINISTRATOR, Console::Yes },
@@ -209,6 +212,7 @@ public:
HandleReloadVehicleTemplateAccessoryCommand(handler);
HandleReloadAutobroadcastCommand(handler);
HandleReloadMotdCommand(handler);
HandleReloadBroadcastTextCommand(handler);
HandleReloadBattlegroundTemplate(handler);
return true;
@@ -398,11 +402,20 @@ public:
static bool HandleReloadAutobroadcastCommand(ChatHandler* handler)
{
LOG_INFO("server.loading", "Re-Loading Autobroadcasts...");
sWorld->LoadAutobroadcasts();
sAutobroadcastMgr->LoadAutobroadcasts();
handler->SendGlobalGMSysMessage("DB table `autobroadcast` reloaded.");
return true;
}
static bool HandleReloadMotdCommand(ChatHandler* handler)
{
LOG_INFO("server.loading", "Re-Loading Motd...");
sWorld->LoadMotd();
handler->SendGlobalGMSysMessage("DB table `motd` reloaded.");
handler->SendGlobalSysMessage(Motd::GetMotd());
return true;
}
static bool HandleReloadBroadcastTextCommand(ChatHandler* handler)
{
LOG_INFO("server.loading", "Re-Loading Broadcast texts...");

View File

@@ -509,10 +509,40 @@ public:
}
// Define the 'Message of the day' for the realm
static bool HandleServerSetMotdCommand(ChatHandler* handler, std::string motd)
static bool HandleServerSetMotdCommand(ChatHandler* handler, std::string realmId, Tail motd)
{
Motd::SetMotd(motd);
handler->PSendSysMessage(LANG_MOTD_NEW, motd);
std::wstring wMotd = std::wstring();
std::string strMotd = std::string();
if (realmId.empty())
{
return false;
}
if (motd.empty())
{
return false;
}
if (!Utf8toWStr(motd, wMotd))
{
return false;
}
if (!WStrToUtf8(wMotd, strMotd))
{
return false;
}
LoginDatabaseTransaction trans = LoginDatabase.BeginTransaction();
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_REP_MOTD);
stmt->SetData(0, Acore::StringTo<int32>(realmId).value());
stmt->SetData(1, strMotd);
trans->Append(stmt);
LoginDatabase.CommitTransaction(trans);
sWorld->LoadMotd();
handler->PSendSysMessage(LANG_MOTD_NEW, Acore::StringTo<int32>(realmId).value(), strMotd);
return true;
}

View File

@@ -22,36 +22,50 @@
enum PrinceSay
{
SAY_AGGRO = 0,
SAY_AGGRO = 0,
SAY_AXE_TOSS1 = 1,
SAY_AXE_TOSS2 = 2,
SAY_SLAY = 6,
SAY_SUMMON = 7,
SAY_DEATH = 8,
SAY_SLAY = 6,
SAY_SUMMON = 7,
SAY_DEATH = 8,
};
enum Spells
{
SPELL_ENFEEBLE = 30843, //Enfeeble during phase 1 and 2
SPELL_ENFEEBLE_EFFECT = 41624,
SPELL_SHADOWNOVA = 30852, //Shadownova used during all phases
SPELL_SW_PAIN = 30854, //Shadow word pain during phase 1 and 3 (different targeting rules though)
SPELL_THRASH_PASSIVE = 12787, //Extra attack chance during phase 2
SPELL_SUNDER_ARMOR = 30901, //Sunder armor during phase 2
SPELL_THRASH_AURA = 12787, //Passive proc chance for thrash
SPELL_EQUIP_AXES = 30857, //Visual for axe equiping
SPELL_AMPLIFY_DAMAGE = 39095, //Amplifiy during phase 3
SPELL_CLEAVE = 30131, //Same as Nightbane.
SPELL_HELLFIRE = 30859, //Infenals' hellfire aura
SPELL_ENFEEBLE = 30843,
SPELL_ENFEEBLE_EFFECT = 41624,
SPELL_SHADOW_NOVA = 30852,
SPELL_SHADOW_WORD_PAIN = 30854,
SPELL_THRASH_PASSIVE = 12787,
SPELL_SUNDER_ARMOR = 30901,
SPELL_THRASH_AURA = 12787,
SPELL_EQUIP_AXES = 30857,
SPELL_AMPLIFY_DAMAGE = 39095,
SPELL_CLEAVE = 30131,
SPELL_HELLFIRE = 30859,
};
enum creatures
{
NETHERSPITE_INFERNAL = 17646,
MALCHEZARS_AXE = 17650,
NPC_NETHERSPITE_INFERNAL = 17646,
NPC_MALCHEZARS_AXE = 17650,
INFERNAL_MODEL_INVISIBLE = 11686,
SPELL_INFERNAL_RELAY = 33814, // 30835,
EQUIP_ID_AXE = 33542
SPELL_INFERNAL_RELAY = 33814, // 30835,
EQUIP_ID_AXE = 33542
};
enum EventGroups
{
GROUP_ENFEEBLE,
GROUP_SHADOW_NOVA,
GROUP_SHADOW_WORD_PAIN,
};
enum Phases
{
PHASE_ONE = 1,
PHASE_TWO = 2,
PHASE_THREE = 3
};
struct InfernalPoint
@@ -83,416 +97,242 @@ struct InfernalPoint
{ -10935.7f, -1996.0f }
};*/
//---------Infernal code first
class netherspite_infernal : public CreatureScript
struct boss_malchezaar : public BossAI
{
public:
netherspite_infernal() : CreatureScript("netherspite_infernal") { }
boss_malchezaar(Creature* creature) : BossAI(creature, DATA_MALCHEZZAR) { }
CreatureAI* GetAI(Creature* creature) const override
void Initialize()
{
return new netherspite_infernalAI(creature);
_phase = 1;
clearweapons();
positions.clear();
instance->HandleGameObject(instance->GetGuidData(DATA_GO_NETHER_DOOR), true);
}
struct netherspite_infernalAI : public ScriptedAI
void clearweapons()
{
netherspite_infernalAI(Creature* creature) : ScriptedAI(creature),
HellfireTimer(0), CleanupTimer(0), point(nullptr) { }
uint32 HellfireTimer;
uint32 CleanupTimer;
ObjectGuid malchezaar;
InfernalPoint* point;
void Reset() override { }
void JustEngagedWith(Unit* /*who*/) override { }
void MoveInLineOfSight(Unit* /*who*/) override { }
void UpdateAI(uint32 diff) override
{
if (HellfireTimer)
{
if (HellfireTimer <= diff)
{
DoCast(me, SPELL_HELLFIRE);
HellfireTimer = 0;
}
else
HellfireTimer -= diff;
}
if (CleanupTimer)
{
if (CleanupTimer <= diff)
{
CleanupTimer = 0;
}
else
CleanupTimer -= diff;
}
}
void KilledUnit(Unit* who) override
{
if (Unit* unit = ObjectAccessor::GetUnit(*me, malchezaar))
if (Creature* creature = unit->ToCreature())
creature->AI()->KilledUnit(who);
}
void SpellHit(Unit* /*who*/, SpellInfo const* spell) override
{
if (spell->Id == SPELL_INFERNAL_RELAY)
{
me->SetDisplayId(me->GetUInt32Value(UNIT_FIELD_NATIVEDISPLAYID));
me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
HellfireTimer = 4000;
CleanupTimer = 170000;
}
}
void DamageTaken(Unit* done_by, uint32& damage, DamageEffectType, SpellSchoolMask) override
{
if (!done_by || done_by->GetGUID() != malchezaar)
damage = 0;
}
};
};
class boss_malchezaar : public CreatureScript
{
public:
boss_malchezaar() : CreatureScript("boss_malchezaar") { }
CreatureAI* GetAI(Creature* creature) const override
{
return GetKarazhanAI<boss_malchezaarAI>(creature);
SetEquipmentSlots(false, EQUIP_UNEQUIP, EQUIP_UNEQUIP, EQUIP_NO_CHANGE);
me->SetCanDualWield(false);
}
struct boss_malchezaarAI : public ScriptedAI
void Reset() override
{
boss_malchezaarAI(Creature* creature) : ScriptedAI(creature)
Initialize();
_Reset();
}
void KilledUnit(Unit* /*victim*/) override
{
Talk(SAY_SLAY);
}
void JustDied(Unit* /*killer*/) override
{
_JustDied();
Talk(SAY_DEATH);
instance->HandleGameObject(instance->GetGuidData(DATA_GO_NETHER_DOOR), true);
}
void JustEngagedWith(Unit* /*who*/) override
{
Talk(SAY_AGGRO);
_JustEngagedWith();
instance->HandleGameObject(instance->GetGuidData(DATA_GO_NETHER_DOOR), false);
scheduler.Schedule(30s, [this](TaskContext context)
{
instance = creature->GetInstanceScript();
}
EnfeebleHealthEffect();
InstanceScript* instance;
uint32 EnfeebleTimer;
uint32 EnfeebleResetTimer;
uint32 ShadowNovaTimer;
uint32 SWPainTimer;
uint32 SunderArmorTimer;
uint32 AmplifyDamageTimer;
uint32 InfernalTimer;
uint32 InfernalCleanupTimer;
uint32 phase;
uint32 enfeeble_health[5];
ObjectGuid enfeeble_targets[5];
GuidVector infernals;
std::vector<InfernalPoint*> positions;
void Initialize()
{
EnfeebleTimer = 30000;
EnfeebleResetTimer = 38000;
ShadowNovaTimer = 35500;
SWPainTimer = 20000;
InfernalCleanupTimer = 47000;
AmplifyDamageTimer = 5000;
SunderArmorTimer = urand(5000, 10000);
InfernalTimer = 40000;
phase = 1;
clearweapons();
positions.clear();
instance->HandleGameObject(instance->GetGuidData(DATA_GO_NETHER_DOOR), true);
}
void clearweapons()
{
SetEquipmentSlots(false, EQUIP_UNEQUIP, EQUIP_UNEQUIP, EQUIP_NO_CHANGE);
me->SetCanDualWield(false);
}
void Reset() override
{
Initialize();
}
void KilledUnit(Unit* /*victim*/) override
{
Talk(SAY_SLAY);
}
void JustDied(Unit* /*killer*/) override
{
Talk(SAY_DEATH);
instance->HandleGameObject(instance->GetGuidData(DATA_GO_NETHER_DOOR), true);
if (Creature* Axe = me->FindNearestCreature(MALCHEZARS_AXE, 100.0f))
scheduler.Schedule(9s, [this](TaskContext)
{
Axe->DespawnOrUnsummon();
}
}
EnfeebleResetHealth();
});
void JustEngagedWith(Unit* /*who*/) override
context.SetGroup(GROUP_ENFEEBLE);
scheduler.DelayGroup(GROUP_SHADOW_NOVA, 5s);
context.Repeat();
}).Schedule(35500ms, [this](TaskContext context)
{
Talk(SAY_AGGRO);
DoZoneInCombat();
instance->HandleGameObject(instance->GetGuidData(DATA_GO_NETHER_DOOR), false);
}
void SummonAxes()
DoCastAOE(SPELL_SHADOW_NOVA);
context.SetGroup(GROUP_SHADOW_NOVA);
context.Repeat();
}).Schedule(40s, [this](TaskContext context)
{
me->SummonCreature(MALCHEZARS_AXE, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 10000);
}
Position pos = me->GetRandomNearPosition(40.0);;
void EnfeebleHealthEffect()
{
SpellInfo const* info = sSpellMgr->GetSpellInfo(SPELL_ENFEEBLE_EFFECT);
if (!info)
return;
ThreatContainer::StorageType const& t_list = me->GetThreatMgr().GetThreatList();
std::vector<Unit*> targets;
if (t_list.empty())
return;
//begin + 1, so we don't target the one with the highest threat
ThreatContainer::StorageType::const_iterator itr = t_list.begin();
std::advance(itr, 1);
for (; itr != t_list.end(); ++itr) //store the threat list in a different container
if (Unit* target = ObjectAccessor::GetUnit(*me, (*itr)->getUnitGuid()))
if (target->IsAlive() && target->GetTypeId() == TYPEID_PLAYER)
targets.push_back(target);
//cut down to size if we have more than 5 targets
while (targets.size() > 5)
targets.erase(targets.begin() + rand() % targets.size());
uint32 i = 0;
for (std::vector<Unit*>::const_iterator iter = targets.begin(); iter != targets.end(); ++iter, ++i)
if (Unit* target = *iter)
{
enfeeble_targets[i] = target->GetGUID();
enfeeble_health[i] = target->GetHealth();
me->CastSpell(target, SPELL_ENFEEBLE, true, 0, 0, me->GetGUID());
target->SetHealth(1);
}
}
void EnfeebleResetHealth()
{
for (uint8 i = 0; i < 5; ++i)
if (Creature* RELAY = me->FindNearestCreature(NPC_RELAY, 100.0f))
{
Unit* target = ObjectAccessor::GetUnit(*me, enfeeble_targets[i]);
if (target && target->IsAlive())
target->SetHealth(enfeeble_health[i]);
enfeeble_targets[i].Clear();
enfeeble_health[i] = 0;
}
}
void SummonInfernal()
{
Position pos;
if ((me->GetMapId() == 532))
{
pos = me->GetRandomNearPosition(40.0);
}
else
{
InfernalPoint* point = Acore::Containers::SelectRandomContainerElement(positions);
pos.Relocate(point->x, point->y, INFERNAL_Z, frand(0.0f, float(M_PI * 2)));
}
if (Creature* RELAY = me->FindNearestCreature(NPC_RELAY, 100.0f))
{
Creature* infernal = RELAY->SummonCreature(NETHERSPITE_INFERNAL, pos, TEMPSUMMON_TIMED_DESPAWN, 180000);
if (infernal)
if (Creature* infernal = RELAY->SummonCreature(NPC_NETHERSPITE_INFERNAL, pos, TEMPSUMMON_TIMED_DESPAWN, 180000))
{
infernal->SetDisplayId(INFERNAL_MODEL_INVISIBLE);
infernal->SetFaction(me->GetFaction());
infernals.push_back(infernal->GetGUID());
infernal->SetControlled(true, UNIT_STATE_ROOT);
RELAY->AI()->DoCast(infernal, SPELL_INFERNAL_RELAY);
summons.Summon(infernal);
}
}
Talk(SAY_SUMMON);
}
void Phase2()
context.Repeat(_phase == PHASE_THREE ? 15s : 45s);
Talk(SAY_SUMMON);
}).Schedule(20s, [this](TaskContext context)
{
DoCastVictim(SPELL_SHADOW_WORD_PAIN);
context.SetGroup(GROUP_SHADOW_WORD_PAIN);
context.Repeat();
});
}
void DamageTaken(Unit* /*done_by*/, uint32& damage, DamageEffectType, SpellSchoolMask) override
{
if (me->HealthBelowPctDamaged(60, damage) && _phase == PHASE_ONE)
{
me->InterruptNonMeleeSpells(false);
phase = 2;
DoCast(me, SPELL_EQUIP_AXES);
_phase = 2;
DoCastSelf( SPELL_EQUIP_AXES);
Talk(SAY_AXE_TOSS1);
DoCast(me, SPELL_THRASH_AURA, true);
DoCastSelf( SPELL_THRASH_AURA, true);
SetEquipmentSlots(false, EQUIP_ID_AXE, EQUIP_ID_AXE, EQUIP_NO_CHANGE);
me->SetCanDualWield(true);
me->SetAttackTime(OFF_ATTACK, (me->GetAttackTime(BASE_ATTACK) * 150) / 100);
SunderArmorTimer = urand(5000, 10000);
}
void Phase3()
scheduler.Schedule(5s, 10s, [this](TaskContext context)
{
DoCastVictim(SPELL_SUNDER_ARMOR);
context.Repeat();
});
scheduler.CancelGroup(GROUP_SHADOW_WORD_PAIN);
}
else if (me->HealthBelowPctDamaged(30, damage) && _phase == PHASE_TWO)
{
me->RemoveAurasDueToSpell(SPELL_THRASH_AURA);
Talk(SAY_AXE_TOSS2);
phase = 3;
_phase = PHASE_THREE;
clearweapons();
SummonAxes();
AmplifyDamageTimer = urand(20000, 30000);
me->SummonCreature(NPC_MALCHEZARS_AXE, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 10000);
scheduler.Schedule(20s, 30s, [this](TaskContext context)
{
DoCastRandomTarget(SPELL_AMPLIFY_DAMAGE, 1);
context.Repeat();
}).Schedule(20s, [this](TaskContext context)
{
DoCastRandomTarget(SPELL_SHADOW_WORD_PAIN);
context.SetGroup(GROUP_SHADOW_WORD_PAIN);
context.Repeat();
});;
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
if (EnfeebleResetTimer && EnfeebleResetTimer <= diff) // Let's not forget to reset that
{
EnfeebleResetHealth();
EnfeebleResetTimer = 0;
}
else EnfeebleResetTimer -= diff;
if (InfernalTimer <= diff)
{
SummonInfernal();
InfernalTimer = phase == 3 ? 14500 : 44500; // 15 secs in phase 3, 45 otherwise
}
else InfernalTimer -= diff;
if (phase != 2)
{
if (SWPainTimer <= diff)
{
// if phase == 1 target the tank, otherwise anyone but the tank
Unit* target = phase == 1
? me->GetVictim()
: SelectTarget(SelectTargetMethod::Random, 1, 100, true);
if (target)
{
DoCast(target, SPELL_SW_PAIN);
}
SWPainTimer = 20000;
}
else
SWPainTimer -= diff;
}
if (ShadowNovaTimer <= diff)
{
DoCast(SPELL_SHADOWNOVA);
ShadowNovaTimer = 35500;
}
else
ShadowNovaTimer -= diff;
if (phase == 1)
{
if (HealthBelowPct(60))
{
Phase2();
}
}
if (phase == 2)
{
if (SunderArmorTimer <= diff)
{
DoCast(SPELL_SUNDER_ARMOR);
SunderArmorTimer = urand(5000, 10000);
}
else
SunderArmorTimer -= diff;
if (HealthBelowPct(30))
{
Phase3();
}
}
if (phase == 3)
{
if (AmplifyDamageTimer <= diff)
{
Unit* target = SelectTarget(SelectTargetMethod::Random, 1, 100, true);
if (target)
{
DoCast(target, SPELL_AMPLIFY_DAMAGE);
AmplifyDamageTimer = urand(20000, 30000);
}
}
else
{
AmplifyDamageTimer -= diff;
}
}
if (phase != 3)
{
if (EnfeebleTimer <= diff)
{
EnfeebleHealthEffect();
EnfeebleTimer = 30000;
ShadowNovaTimer = 5000;
EnfeebleResetTimer = 9000;
}
else EnfeebleTimer -= diff;
}
DoMeleeAttackIfReady();
}
};
};
class prince_axes : public CreatureScript
{
public:
prince_axes() : CreatureScript("prince_axes") { }
CreatureAI* GetAI(Creature* creature) const override
{
return GetKarazhanAI<prince_axesAI>(creature);
}
struct prince_axesAI : public ScriptedAI
void EnfeebleHealthEffect()
{
prince_axesAI(Creature* creature) : ScriptedAI(creature)
std::list<Unit*> targetList;
SelectTargetList(targetList, 5, SelectTargetMethod::Random, 1, [&](Unit* u) { return u->IsAlive() && u->IsPlayer(); });
if (targetList.empty())
return;
for (auto const& target : targetList)
{
Initialize();
instance = creature->GetInstanceScript();
if (target)
{
_enfeebleTargets[target->GetGUID()] = target->GetHealth();
me->CastSpell(target, SPELL_ENFEEBLE, true);
target->SetHealth(1);
}
}
}
uint32 AxesTargetSwitchTimer;
InstanceScript* instance;
void Initialize()
void EnfeebleResetHealth()
{
for (auto targets : _enfeebleTargets)
{
AxesTargetSwitchTimer = 7500;
if (Unit* target = ObjectAccessor::GetUnit(*me, targets.first))
{
if (target->IsAlive())
{
target->SetHealth(targets.second);
}
}
}
}
private:
uint32 _phase;
std::map<ObjectGuid, uint32> _enfeebleTargets;
std::vector<InfernalPoint*> positions;
};
struct npc_netherspite_infernal : public ScriptedAI
{
npc_netherspite_infernal(Creature* creature) : ScriptedAI(creature) { }
void JustEngagedWith(Unit* /*who*/) override { }
void MoveInLineOfSight(Unit* /*who*/) override { }
void UpdateAI(uint32 diff) override
{
_scheduler.Update(diff);
}
void KilledUnit(Unit* who) override
{
if (me->ToTempSummon())
{
if (WorldObject* summoner = me->ToTempSummon()->GetSummoner())
{
if (Creature* creature = summoner->ToCreature())
{
creature->AI()->KilledUnit(who);
}
}
}
}
void SpellHit(Unit* /*who*/, SpellInfo const* spell) override
{
if (spell->Id == SPELL_INFERNAL_RELAY)
{
me->SetDisplayId(me->GetUInt32Value(UNIT_FIELD_NATIVEDISPLAYID));
me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
me->SetCanDualWield(true);
}
void Reset() override
{
_scheduler.Schedule(4s, [this](TaskContext /*context*/)
{
DoCastSelf(SPELL_HELLFIRE);
});
}
}
void JustEngagedWith(Unit* /*who*/) override
{
DoZoneInCombat();
}
void DamageTaken(Unit* /*done_by*/, uint32& damage, DamageEffectType, SpellSchoolMask) override
{
damage = 0;
}
void changetarget()
private:
TaskScheduler _scheduler;
};
struct npc_malchezaar_axe : public ScriptedAI
{
npc_malchezaar_axe(Creature* creature) : ScriptedAI(creature)
{
creature->SetCanDualWield(true);
}
void Initialize()
{
me->SetUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
}
void JustEngagedWith(Unit* /*who*/) override
{
DoZoneInCombat();
_scheduler.Schedule(7500ms, [this](TaskContext context)
{
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100, true))
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100.0f, true))
{
if (me->GetVictim())
{
@@ -501,29 +341,27 @@ public:
me->AddThreat(target, 1000000.0f);
}
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
context.Repeat(7500ms, 20s);
});
}
if (AxesTargetSwitchTimer <= diff)
{
AxesTargetSwitchTimer = urand(7500, 20000);
changetarget();
}
else
AxesTargetSwitchTimer -= diff;
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
DoMeleeAttackIfReady();
}
};
_scheduler.Update(diff,
std::bind(&ScriptedAI::DoMeleeAttackIfReady, this));
}
private:
TaskScheduler _scheduler;
};
void AddSC_boss_malchezaar()
{
new boss_malchezaar();
new prince_axes();
new netherspite_infernal();
RegisterKarazhanCreatureAI(boss_malchezaar);
RegisterKarazhanCreatureAI(npc_malchezaar_axe);
RegisterKarazhanCreatureAI(npc_netherspite_infernal);
}

View File

@@ -106,16 +106,6 @@ public:
me->DespawnOrUnsummon(1);
}
void IsSummonedBy(WorldObject* /*summoner*/) override
{
events2.Reset();
events2.ScheduleEvent(INTRO_1, 1s);
me->SetDisableGravity(true);
me->SetReactState(REACT_PASSIVE);
me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
me->SetImmuneToAll(true); // for some reason he aggroes if we don't have this.
}
void MovementInform(uint32 type, uint32 id) override
{
if (type == WAYPOINT_MOTION_TYPE && id == POINT_KIRTONOS_LAND)
@@ -128,6 +118,21 @@ public:
}
}
void DoAction(int32 action) override
{
if (action == IN_PROGRESS)
{
events2.Reset();
events2.ScheduleEvent(INTRO_1, 1s);
me->SetCanFly(true);
me->SetDisableGravity(true);
me->SendMovementFlagUpdate();
me->SetReactState(REACT_PASSIVE);
me->SetUnitFlag(UNIT_FLAG_NON_ATTACKABLE);
me->SetImmuneToAll(true); // for some reason he aggroes if we don't have this.
}
}
void UpdateAI(uint32 diff) override
{
events2.Update(diff);
@@ -148,6 +153,7 @@ public:
me->SetCanFly(false);
me->SetDisableGravity(false);
me->CastSpell(me, SPELL_KIRTONOS_TRANSFORM, true);
me->SendMovementFlagUpdate();
break;
case INTRO_5:
me->HandleEmoteCommand(EMOTE_ONESHOT_ROAR);

View File

@@ -131,7 +131,10 @@ public:
// summon kirtonos and close door
if (_kirtonosState == NOT_STARTED)
{
instance->SummonCreature(NPC_KIRTONOS, KirtonosSpawn);
if (Creature* kirtonos = instance->SummonCreature(NPC_KIRTONOS, KirtonosSpawn))
{
kirtonos->AI()->DoAction(IN_PROGRESS);
}
if (GameObject* gate = instance->GetGameObject(GetGuidData(GO_GATE_KIRTONOS)))
{
gate->SetGoState(GO_STATE_READY);

View File

@@ -57,36 +57,13 @@ public:
{
boss_lieutenant_drakeAI(Creature* creature) : ScriptedAI(creature)
{
pathPoints.clear();
WPPath* path = sSmartWaypointMgr->GetPath(me->GetEntry());
if (!path || path->empty())
return;
pathPoints.push_back(G3D::Vector3(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ()));
uint32 wpCounter = 1;
WPPath::const_iterator itr;
while ((itr = path->find(wpCounter++)) != path->end())
{
WayPoint* wp = itr->second;
pathPoints.push_back(G3D::Vector3(wp->x, wp->y, wp->z));
}
}
void InitializeAI() override
{
ScriptedAI::InitializeAI();
//Talk(SAY_ENTER);
JustReachedHome();
}
void JustReachedHome() override
{
me->SetWalk(true);
Movement::MoveSplineInit init(me);
init.MovebyPath(pathPoints);
init.SetCyclic();
init.Launch();
runSecondPath = false;
pathId = me->GetEntry() * 10;
me->GetMotionMaster()->MovePath(pathId, false);
}
void Reset() override
@@ -118,8 +95,38 @@ public:
instance->SetData(DATA_ESCORT_PROGRESS, ENCOUNTER_PROGRESS_DRAKE_KILLED);
}
void MovementInform(uint32 type, uint32 point) override
{
if (type != WAYPOINT_MOTION_TYPE)
{
return;
}
if (pathId == me->GetEntry() * 10)
{
switch (point)
{
case 7:
Talk(SAY_ENTER);
break;
case 10:
pathId = (me->GetEntry() * 10) + 1;
runSecondPath = true;
break;
default:
break;
}
}
}
void UpdateAI(uint32 diff) override
{
if (runSecondPath)
{
runSecondPath = false;
me->GetMotionMaster()->MovePath(pathId, true);
}
if (!UpdateVictim())
return;
@@ -161,7 +168,8 @@ public:
private:
EventMap events;
Movement::PointsArray pathPoints;
uint32 pathId;
bool runSecondPath;
};
};

View File

@@ -21,11 +21,11 @@
#include "ScriptMgr.h"
#include "old_hillsbrad.h"
const Position instancePositions[INSTANCE_POSITIONS_COUNT] =
static Position const instancePositions[INSTANCE_POSITIONS_COUNT] =
{
{2188.18f, 228.90f, 53.025f, 1.77f}, // Orcs Gather Point 1
{2103.23f, 93.55f, 53.096f, 3.78f}, // Orcs Gather Point 2
{2128.43f, 71.01f, 64.42f, 1.74f} // Lieutenant Drake Summon Position
{ 2188.18f, 228.90f, 53.025f, 1.77f }, // Orcs Gather Point 1
{ 2103.23f, 93.550f, 53.096f, 3.78f }, // Orcs Gather Point 2
{ 2172.76f, 149.54f, 87.981f, 4.19f } // Lieutenant Drake Summon Position
};
const Position thrallPositions[THRALL_POSITIONS_COUNT] =
@@ -248,11 +248,8 @@ public:
case EVENT_SUMMON_LIEUTENANT:
{
instance->LoadGrid(instancePositions[2].GetPositionX(), instancePositions[2].GetPositionY());
if (Creature* drake = instance->SummonCreature(NPC_LIEUTENANT_DRAKE, instancePositions[2]))
{
drake->AI()->Talk(0);
}
[[fallthrough]]; /// @todo: Not sure whether the fallthrough was a mistake (forgetting a break) or intended. This should be double-checked.
instance->SummonCreature(NPC_LIEUTENANT_DRAKE, instancePositions[2]);
break;
}
case EVENT_THRALL_REPOSITION:
{

View File

@@ -23,7 +23,8 @@
#include "TemporarySummon.h"
#include "the_black_morass.h"
const Position PortalLocation[4] =
#define MAX_PORTAL_LOCATIONS 4
const Position PortalLocation[MAX_PORTAL_LOCATIONS] =
{
{ -2030.8318f, 7024.9443f, 23.071817f, 3.14159f },
{ -1961.7335f, 7029.5280f, 21.811401f, 2.12931f },
@@ -58,6 +59,7 @@ public:
_currentRift = 0;
_shieldPercent = 100;
encounterNPCs.clear();
_timerToNextBoss = 0;
}
void CleanupInstance()
@@ -66,6 +68,8 @@ public:
_currentRift = 0;
_shieldPercent = 100;
_usedRiftPostions.fill(ObjectGuid::Empty);
instance->LoadGrid(-2023.0f, 7121.0f);
if (Creature* medivh = instance->GetCreature(_medivhGUID))
{
@@ -116,6 +120,7 @@ public:
case NPC_INFINITE_CRONOMANCER:
case NPC_INFINITE_EXECUTIONER:
case NPC_INFINITE_VANQUISHER:
case NPC_DP_BEAM_STALKER:
encounterNPCs.insert(creature->GetGUID());
break;
}
@@ -150,45 +155,105 @@ public:
switch (type)
{
case TYPE_AEONUS:
{
encounters[type] = DONE;
SaveToDB();
if (Creature* medivh = instance->GetCreature(_medivhGUID))
{
encounters[type] = DONE;
SaveToDB();
if (Creature* medivh = instance->GetCreature(_medivhGUID))
medivh->AI()->DoAction(ACTION_OUTRO);
Map::PlayerList const& players = instance->GetPlayers();
if (!players.IsEmpty())
for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
if (Player* player = itr->GetSource())
{
if (player->GetQuestStatus(QUEST_OPENING_PORTAL) == QUEST_STATUS_INCOMPLETE)
player->AreaExploredOrEventHappens(QUEST_OPENING_PORTAL);
if (player->GetQuestStatus(QUEST_MASTER_TOUCH) == QUEST_STATUS_INCOMPLETE)
player->AreaExploredOrEventHappens(QUEST_MASTER_TOUCH);
}
break;
medivh->AI()->DoAction(ACTION_OUTRO);
}
Map::PlayerList const& players = instance->GetPlayers();
if (!players.IsEmpty())
{
for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
{
if (Player* player = itr->GetSource())
{
if (player->GetQuestStatus(QUEST_OPENING_PORTAL) == QUEST_STATUS_INCOMPLETE)
{
player->AreaExploredOrEventHappens(QUEST_OPENING_PORTAL);
}
if (player->GetQuestStatus(QUEST_MASTER_TOUCH) == QUEST_STATUS_INCOMPLETE)
{
player->AreaExploredOrEventHappens(QUEST_MASTER_TOUCH);
}
}
}
}
break;
}
case TYPE_CHRONO_LORD_DEJA:
case TYPE_TEMPORUS:
{
GuidSet eCopy = encounterNPCs;
for (ObjectGuid const& guid : eCopy)
{
if (Creature* creature = instance->GetCreature(guid))
{
switch (creature->GetEntry())
{
case NPC_RIFT_KEEPER_WARLOCK:
case NPC_RIFT_KEEPER_MAGE:
case NPC_RIFT_LORD:
case NPC_RIFT_LORD_2:
case NPC_TIME_RIFT:
creature->DespawnOrUnsummon();
break;
default:
break;
}
}
}
encounters[type] = DONE;
Events.RescheduleEvent(EVENT_NEXT_PORTAL, 60000);
if (!_timerToNextBoss || _timerToNextBoss > 30 * IN_MILLISECONDS)
{
Events.RescheduleEvent(EVENT_NEXT_PORTAL, 30 * IN_MILLISECONDS);
}
else
{
Events.RescheduleEvent(EVENT_NEXT_PORTAL, _timerToNextBoss);
}
Events.SetPhase(1);
SaveToDB();
_timerToNextBoss = (instance->IsHeroic() ? 300 : 150) * IN_MILLISECONDS;
break;
case DATA_RIFT_KILLED:
if (!Events.IsInPhase(1))
Events.RescheduleEvent(EVENT_NEXT_PORTAL, 4000);
break;
}
case DATA_MEDIVH:
{
DoUpdateWorldState(WORLD_STATE_BM, 1);
DoUpdateWorldState(WORLD_STATE_BM_SHIELD, _shieldPercent);
DoUpdateWorldState(WORLD_STATE_BM_RIFT, _currentRift);
Events.RescheduleEvent(EVENT_NEXT_PORTAL, 3000);
_timerToNextBoss = (instance->IsHeroic() ? 300 : 150) * IN_MILLISECONDS;
for (ObjectGuid const& guid : encounterNPCs)
{
if (guid.GetEntry() == NPC_DP_BEAM_STALKER)
{
if (Creature* creature = instance->GetCreature(guid))
{
if (!creature->IsAlive())
{
creature->Respawn(true);
}
}
break;
}
}
break;
}
case DATA_DAMAGE_SHIELD:
{
if (_shieldPercent <= 0)
{
return;
}
_shieldPercent -= data;
if (_shieldPercent < 0)
{
@@ -203,15 +268,20 @@ public:
{
if (medivh->IsAlive())
{
Unit::Kill(medivh, medivh);
medivh->SetImmuneToNPC(true);
// Xinef: delete all spawns
GuidSet eCopy = encounterNPCs;
for (ObjectGuid const& guid : eCopy)
if (medivh->IsAIEnabled)
{
medivh->AI()->Talk(SAY_MEDIVH_DEATH);
}
Events.ScheduleEvent(EVENT_WIPE_1, 4s);
for (ObjectGuid const& guid : encounterNPCs)
{
if (Creature* creature = instance->GetCreature(guid))
{
creature->DespawnOrUnsummon();
creature->InterruptNonMeleeSpells(true);
}
}
}
@@ -219,6 +289,8 @@ public:
}
break;
}
default:
break;
}
}
@@ -244,6 +316,34 @@ public:
encounterNPCs.insert(data);
else if (type == DATA_DELETED_NPC)
encounterNPCs.erase(data);
else if (type == DATA_RIFT_KILLED)
{
if (!Events.IsInPhase(1))
{
uint8 emptySpots = 0;
for (uint8 i = 0; i < MAX_PORTAL_LOCATIONS; ++i)
{
if (!_usedRiftPostions[i])
{
++emptySpots;
}
if (_usedRiftPostions[i] == data)
{
_usedRiftPostions[i].Clear();
}
}
if (emptySpots >= MAX_PORTAL_LOCATIONS - 1)
{
Events.RescheduleEvent(EVENT_NEXT_PORTAL, 4000);
}
else if (!emptySpots)
{
Events.RescheduleEvent(EVENT_NEXT_PORTAL, (_currentRift >= 13 ? 120 : 90) * IN_MILLISECONDS);
}
}
}
}
ObjectGuid GetGuidData(uint32 data) const override
@@ -254,17 +354,11 @@ public:
return ObjectGuid::Empty;
}
void SummonPortalKeeper()
void SummonPortalKeeper(uint32 eventId)
{
Creature* rift = nullptr;
for (ObjectGuid const& guid : encounterNPCs)
if (Creature* summon = instance->GetCreature(guid))
if (summon->GetEntry() == NPC_TIME_RIFT)
{
rift = summon;
break;
}
uint8 riftPosition = eventId - EVENT_SUMMON_KEEPER_1;
ObjectGuid const& riftGUID = _usedRiftPostions[riftPosition];
Creature* rift = instance->GetCreature(riftGUID);
if (!rift)
return;
@@ -307,23 +401,120 @@ public:
void Update(uint32 diff) override
{
if (_timerToNextBoss)
{
if (_timerToNextBoss <= diff)
{
_timerToNextBoss = 0;
}
else
{
_timerToNextBoss -= diff;
}
}
Events.Update(diff);
switch (Events.ExecuteEvent())
uint32 eventId = Events.ExecuteEvent();
switch (eventId)
{
case EVENT_NEXT_PORTAL:
++_currentRift;
DoUpdateWorldState(WORLD_STATE_BM_RIFT, _currentRift);
Events.ScheduleEvent(EVENT_SUMMON_KEEPER, 6000);
Events.SetPhase(0);
{
if (instance->GetCreature(_medivhGUID))
{
uint8 position = (_currentRift - 1) % 4;
instance->SummonCreature(NPC_TIME_RIFT, PortalLocation[position]);
uint8 position = MAX_PORTAL_LOCATIONS;
std::vector<uint8> possibleSpots;
for (uint8 i = 0; i < MAX_PORTAL_LOCATIONS; ++i)
{
if (!_usedRiftPostions[i])
{
possibleSpots.push_back(i);
}
}
if (!possibleSpots.empty())
{
position = Acore::Containers::SelectRandomContainerElement(possibleSpots);
}
if (position < MAX_PORTAL_LOCATIONS)
{
++_currentRift;
DoUpdateWorldState(WORLD_STATE_BM_RIFT, _currentRift);
Events.ScheduleEvent(EVENT_SUMMON_KEEPER_1 + position, 6000);
Events.SetPhase(0);
if (Creature* rift = instance->SummonCreature(NPC_TIME_RIFT, PortalLocation[position]))
{
_usedRiftPostions[position] = rift->GetGUID();
for (uint8 i = 0; i < MAX_PORTAL_LOCATIONS; ++i)
{
if (!_usedRiftPostions[i])
{
Events.RescheduleEvent(EVENT_NEXT_PORTAL, (_currentRift >= 13 ? 120 : 90) * IN_MILLISECONDS);
break;
}
}
}
}
}
break;
case EVENT_SUMMON_KEEPER:
SummonPortalKeeper();
}
case EVENT_SUMMON_KEEPER_1:
case EVENT_SUMMON_KEEPER_2:
case EVENT_SUMMON_KEEPER_3:
case EVENT_SUMMON_KEEPER_4:
SummonPortalKeeper(eventId);
break;
case EVENT_WIPE_1:
if (Creature* medivh = instance->GetCreature(_medivhGUID))
{
medivh->RemoveAllAuras();
}
Events.ScheduleEvent(EVENT_WIPE_2, 500ms);
break;
case EVENT_WIPE_2:
if (Creature* medivh = instance->GetCreature(_medivhGUID))
{
medivh->KillSelf(false);
GuidSet encounterNPCSCopy = encounterNPCs;
for (ObjectGuid const& guid : encounterNPCSCopy)
{
switch (guid.GetEntry())
{
case NPC_TIME_RIFT:
case NPC_DP_EMITTER_STALKER:
case NPC_DP_CRYSTAL_STALKER:
case NPC_DP_BEAM_STALKER:
if (Creature* creature = instance->GetCreature(guid))
{
creature->DespawnOrUnsummon();
}
break;
default:
break;
}
}
}
Events.ScheduleEvent(EVENT_WIPE_3, 2s);
break;
case EVENT_WIPE_3:
{
GuidSet encounterNPCSCopy = encounterNPCs;
for (ObjectGuid const& guid : encounterNPCSCopy)
{
if (Creature* creature = instance->GetCreature(guid))
{
creature->CastSpell(creature, SPELL_TELEPORT_VISUAL, true);
creature->DespawnOrUnsummon(1200ms, 0s);
}
}
break;
}
default:
break;
}
}
@@ -344,6 +535,8 @@ public:
protected:
EventMap Events;
std::array<ObjectGuid, MAX_PORTAL_LOCATIONS> _usedRiftPostions;
uint32 _timerToNextBoss;
};
};

View File

@@ -22,33 +22,8 @@
#include "ScriptedCreature.h"
#include "ScriptedGossip.h"
enum medivhSays
{
SAY_ENTER = 0,
SAY_DEATH = 5,
SAY_WIN = 6,
SAY_ORCS_ENTER = 7,
SAY_ORCS_ANSWER = 0
};
enum medivhSpells
{
SPELL_MANA_SHIELD = 31635,
SPELL_MEDIVH_CHANNEL = 31556,
SPELL_BLACK_CRYSTAL = 32563,
SPELL_PORTAL_CRYSTALS = 32564,
SPELL_BANISH_PURPLE = 32566,
SPELL_BANISH_GREEN = 32567,
SPELL_CORRUPT = 31326,
SPELL_CORRUPT_AEONUS = 37853,
};
enum medivhMisc
{
NPC_DP_EMITTER_STALKER = 18582,
NPC_DP_CRYSTAL_STALKER = 18553,
NPC_SHADOW_COUNCIL_ENFORCER = 17023,
GO_DARK_PORTAL = 185103,
@@ -120,6 +95,8 @@ struct npc_medivh_bm : public ScriptedAI
{
me->CastSpell(me, SPELL_MEDIVH_CHANNEL, false);
}
me->SetImmuneToNPC(false);
}
void JustSummoned(Creature* summon) override
@@ -156,7 +133,7 @@ struct npc_medivh_bm : public ScriptedAI
if (who->GetTypeId() == TYPEID_PLAYER && me->IsWithinDistInMap(who, 20.0f))
{
Talk(SAY_ENTER);
Talk(SAY_MEDIVH_ENTER);
_instance->SetData(DATA_MEDIVH, 1);
me->CastSpell(me, SPELL_MEDIVH_CHANNEL, false);
@@ -189,7 +166,6 @@ struct npc_medivh_bm : public ScriptedAI
{
me->SetRespawnTime(DAY);
events.Reset();
Talk(SAY_DEATH);
}
void UpdateAI(uint32 diff) override
@@ -216,7 +192,7 @@ struct npc_medivh_bm : public ScriptedAI
break;
case EVENT_OUTRO_1:
me->SetFacingTo(6.21f);
Talk(SAY_WIN);
Talk(SAY_MEDIVH_WIN);
events.ScheduleEvent(EVENT_OUTRO_2, 17000);
break;
case EVENT_OUTRO_2:
@@ -240,14 +216,14 @@ struct npc_medivh_bm : public ScriptedAI
events.ScheduleEvent(EVENT_OUTRO_7, 7000);
break;
case EVENT_OUTRO_7:
Talk(SAY_ORCS_ENTER);
Talk(SAY_MEDIVH_ORCS_ENTER);
events.ScheduleEvent(EVENT_OUTRO_8, 7000);
break;
case EVENT_OUTRO_8:
if (Creature* cr = me->FindNearestCreature(NPC_SHADOW_COUNCIL_ENFORCER, 20.0f))
{
cr->SetFacingTo(3.07f);
cr->AI()->Talk(SAY_ORCS_ANSWER);
cr->AI()->Talk(SAY_MEDIVH_ORCS_ANSWER);
}
break;
}
@@ -350,7 +326,7 @@ struct npc_time_rift : public NullCreatureAI
Creature* riftKeeper = ObjectAccessor::GetCreature(*me, _riftKeeperGUID);
if (!riftKeeper || !riftKeeper->IsAlive())
{
_instance->SetData(DATA_RIFT_KILLED, 1);
_instance->SetGuidData(DATA_RIFT_KILLED, me->GetGUID());
me->DespawnOrUnsummon(0);
break;

View File

@@ -77,19 +77,53 @@ enum CreatureIds
NPC_INFINITE_WHELP = 21818,
NPC_INFINITE_CRONOMANCER = 17892,
NPC_INFINITE_EXECUTIONER = 18994,
NPC_INFINITE_VANQUISHER = 18995
NPC_INFINITE_VANQUISHER = 18995,
NPC_DP_EMITTER_STALKER = 18582,
NPC_DP_CRYSTAL_STALKER = 18553,
NPC_DP_BEAM_STALKER = 18555
};
enum Misc
{
SPELL_RIFT_CHANNEL = 31387,
SPELL_TELEPORT_VISUAL = 7791,
EVENT_NEXT_PORTAL = 1,
EVENT_SUMMON_KEEPER = 2,
EVENT_SUMMON_KEEPER_1 = 2,
EVENT_SUMMON_KEEPER_2 = 3,
EVENT_SUMMON_KEEPER_3 = 4,
EVENT_SUMMON_KEEPER_4 = 5,
EVENT_WIPE_1 = 6,
EVENT_WIPE_2 = 7,
EVENT_WIPE_3 = 8,
ACTION_OUTRO = 1
};
enum medivhSays
{
SAY_MEDIVH_ENTER = 0,
SAY_MEDIVH_DEATH = 5,
SAY_MEDIVH_WIN = 6,
SAY_MEDIVH_ORCS_ENTER = 7,
SAY_MEDIVH_ORCS_ANSWER = 0
};
enum medivhSpells
{
SPELL_MANA_SHIELD = 31635,
SPELL_MEDIVH_CHANNEL = 31556,
SPELL_BLACK_CRYSTAL = 32563,
SPELL_PORTAL_CRYSTALS = 32564,
SPELL_BANISH_PURPLE = 32566,
SPELL_BANISH_GREEN = 32567,
SPELL_CORRUPT = 31326,
SPELL_CORRUPT_AEONUS = 37853,
};
template <class AI, class T>
inline AI* GetTheBlackMorassAI(T* obj)
{

View File

@@ -1061,6 +1061,7 @@ public:
{
data >> m_auiEncounter[0];
data >> m_auiEncounter[1];
data >> m_auiEncounter[2];
data >> m_auiEncounter[3];
data >> m_auiEncounter[4];
data >> m_auiEncounter[5];

View File

@@ -0,0 +1,152 @@
/*
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by the
* Free Software Foundation; either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "SpellScript.h"
#include "sethekk_halls.h"
enum Anzu
{
SAY_ANZU_INTRO1 = 0,
SAY_ANZU_INTRO2 = 1,
SAY_SUMMON = 2,
SPELL_PARALYZING_SCREECH = 40184,
SPELL_SPELL_BOMB = 40303,
SPELL_CYCLONE = 40321,
SPELL_BANISH_SELF = 42354,
SPELL_SHADOWFORM = 40973,
EVENT_SPELL_SCREECH = 1,
EVENT_SPELL_BOMB = 2,
EVENT_SPELL_CYCLONE = 3,
EVENT_ANZU_HEALTH1 = 4,
EVENT_ANZU_HEALTH2 = 5
};
struct boss_anzu : public BossAI
{
boss_anzu(Creature* creature) : BossAI(creature, DATA_ANZU)
{
talkTimer = 1;
me->ReplaceAllUnitFlags(UNIT_FLAG_NON_ATTACKABLE);
me->AddAura(SPELL_SHADOWFORM, me);
}
uint32 talkTimer;
void SummonedCreatureDies(Creature* summon, Unit*) override
{
summons.Despawn(summon);
summons.RemoveNotExisting();
if (summons.empty())
me->RemoveAurasDueToSpell(SPELL_BANISH_SELF);
}
void JustEngagedWith(Unit* /*who*/) override
{
_JustEngagedWith();
events.Reset();
events.ScheduleEvent(EVENT_SPELL_SCREECH, 14000);
events.ScheduleEvent(EVENT_SPELL_BOMB, 5000);
events.ScheduleEvent(EVENT_SPELL_CYCLONE, 8000);
events.ScheduleEvent(EVENT_ANZU_HEALTH1, 2000);
events.ScheduleEvent(EVENT_ANZU_HEALTH2, 2001);
}
void SummonBroods()
{
Talk(SAY_SUMMON);
me->CastSpell(me, SPELL_BANISH_SELF, true);
for (uint8 i = 0; i < 5; ++i)
me->SummonCreature(23132 /*NPC_BROOD_OF_ANZU*/, me->GetPositionX() + 20 * cos((float)i), me->GetPositionY() + 20 * std::sin((float)i), me->GetPositionZ() + 25.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 30000);
}
void UpdateAI(uint32 diff) override
{
if (talkTimer)
{
talkTimer += diff;
if (talkTimer >= 1000 && talkTimer < 10000)
{
Talk(SAY_ANZU_INTRO1);
talkTimer = 10000;
}
else if (talkTimer >= 16000)
{
me->ReplaceAllUnitFlags(UNIT_FLAG_NONE);
me->RemoveAurasDueToSpell(SPELL_SHADOWFORM);
Talk(SAY_ANZU_INTRO2);
talkTimer = 0;
}
}
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING | UNIT_STATE_STUNNED))
return;
switch (events.ExecuteEvent())
{
case EVENT_SPELL_SCREECH:
me->CastSpell(me, SPELL_PARALYZING_SCREECH, false);
events.RepeatEvent(23000);
events.DelayEvents(3000);
break;
case EVENT_SPELL_BOMB:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 50.0f, true))
me->CastSpell(target, SPELL_SPELL_BOMB, false);
events.RepeatEvent(urand(16000, 24500));
events.DelayEvents(3000);
break;
case EVENT_SPELL_CYCLONE:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 1, 45.0f, true))
me->CastSpell(target, SPELL_CYCLONE, false);
events.RepeatEvent(urand(22000, 27000));
events.DelayEvents(3000);
break;
case EVENT_ANZU_HEALTH1:
if (me->HealthBelowPct(66))
{
SummonBroods();
events.DelayEvents(10000);
return;
}
events.RepeatEvent(1000);
break;
case EVENT_ANZU_HEALTH2:
if (me->HealthBelowPct(33))
{
SummonBroods();
events.DelayEvents(10000);
return;
}
events.RepeatEvent(1000);
break;
}
DoMeleeAttackIfReady();
}
};
void AddSC_boss_anzu()
{
RegisterSethekkHallsCreatureAI(boss_anzu);
}

View File

@@ -17,6 +17,7 @@
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "SpellScript.h"
#include "sethekk_halls.h"
enum TailonkingIkiss
@@ -26,308 +27,150 @@ enum TailonkingIkiss
SAY_SLAY = 2,
SAY_DEATH = 3,
EMOTE_ARCANE_EXP = 4,
};
enum Spells
{
SPELL_BLINK = 38194,
SPELL_BLINK_TELEPORT = 38203,
SPELL_MANA_SHIELD = 38151,
SPELL_ARCANE_BUBBLE = 9438,
SPELL_SLOW = 35032,
SPELL_POLYMORPH_N = 38245, // Difficulty data
SPELL_POLYMORPH_H = 43309,
SPELL_ARCANE_VOLLEY_N = 35059, // Difficulty data
SPELL_ARCANE_VOLLEY_H = 40424,
SPELL_ARCANE_EXPLOSION_N = 38197, // Difficulty data
SPELL_ARCANE_EXPLOSION_H = 40425,
EVENT_SPELL_BLINK = 1,
EVENT_SPELL_POLYMORPH = 2,
EVENT_SPELL_SLOW = 3,
EVENT_SPELL_ARCANE_VOLLEY = 4,
EVENT_SPELL_ARCANE_EXPLO = 5,
EVENT_HEALTH_CHECK = 6,
EVENT_SPELL_BLINK_2 = 7
SPELL_POLYMORPH = 38245,
SPELL_ARCANE_VOLLEY = 35059,
SPELL_ARCANE_EXPLOSION = 38197,
};
class boss_talon_king_ikiss : public CreatureScript
struct boss_talon_king_ikiss : public BossAI
{
public:
boss_talon_king_ikiss() : CreatureScript("boss_talon_king_ikiss") { }
CreatureAI* GetAI(Creature* creature) const override
boss_talon_king_ikiss(Creature* creature) : BossAI(creature, DATA_IKISS), _spoken(false), _manaShield(false)
{
return GetSethekkHallsAI<boss_talon_king_ikissAI>(creature);
scheduler.SetValidator([this]
{
return !me->HasUnitState(UNIT_STATE_CASTING);
});
}
struct boss_talon_king_ikissAI : public ScriptedAI
void Reset() override
{
boss_talon_king_ikissAI(Creature* creature) : ScriptedAI(creature)
_Reset();
_spoken = false;
_manaShield = false;
}
void MoveInLineOfSight(Unit* who) override
{
if (!_spoken && who->IsPlayer())
{
Talk(SAY_INTRO);
_spoken = true;
}
EventMap events;
ScriptedAI::MoveInLineOfSight(who);
}
void JustEngagedWith(Unit* /*who*/) override
{
_JustEngagedWith();
Talk(SAY_AGGRO);
scheduler.Schedule(35s, [this](TaskContext context)
{
me->InterruptNonMeleeSpells(false);
DoCastAOE(SPELL_BLINK);
Talk(EMOTE_ARCANE_EXP);
context.Repeat(35s, 40s);
scheduler.Schedule(1s, [this](TaskContext)
{
DoCastAOE(SPELL_ARCANE_EXPLOSION);
DoCastSelf(SPELL_ARCANE_BUBBLE, true);
});
}).Schedule(5s, [this](TaskContext context)
{
DoCastAOE(SPELL_ARCANE_VOLLEY);
context.Repeat(7s, 12s);
}).Schedule(8s, [this](TaskContext context)
{
IsHeroic() ? DoCastRandomTarget(SPELL_POLYMORPH) : DoCastMaxThreat(SPELL_POLYMORPH);
context.Repeat(15s, 17500ms);
});
if (IsHeroic())
{
scheduler.Schedule(15s, 25s, [this](TaskContext context)
{
DoCastAOE(SPELL_SLOW);
context.Repeat(15s, 30s);
});
}
}
void JustDied(Unit* /*killer*/) override
{
_JustDied();
Talk(SAY_DEATH);
if (GameObject* coffer = instance->GetGameObject(DATA_GO_TALON_KING_COFFER))
{
coffer->RemoveGameObjectFlag(GO_FLAG_NOT_SELECTABLE | GO_FLAG_INTERACT_COND);
}
}
void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*damagetype*/, SpellSchoolMask /*damageSchoolMask*/) override
{
if (!_manaShield && me->HealthBelowPctDamaged(20, damage))
{
DoCast(me, SPELL_MANA_SHIELD);
_manaShield = true;
}
}
void KilledUnit(Unit* /*victim*/) override
{
if (urand(0, 1))
Talk(SAY_SLAY);
}
private:
bool _spoken;
void Reset() override
{
_spoken = false;
}
void MoveInLineOfSight(Unit* who) override
{
if (!_spoken && who->GetTypeId() == TYPEID_PLAYER)
{
Talk(SAY_INTRO);
_spoken = true;
}
ScriptedAI::MoveInLineOfSight(who);
}
void JustEngagedWith(Unit* /*who*/) override
{
Talk(SAY_AGGRO);
events.ScheduleEvent(EVENT_SPELL_BLINK, 35000);
events.ScheduleEvent(EVENT_SPELL_ARCANE_VOLLEY, 5000);
events.ScheduleEvent(EVENT_SPELL_POLYMORPH, 8000);
events.ScheduleEvent(EVENT_HEALTH_CHECK, 2000);
if (IsHeroic())
events.ScheduleEvent(EVENT_SPELL_SLOW, urand(15000, 25000));
}
void JustDied(Unit* /*killer*/) override
{
Talk(SAY_DEATH);
if (InstanceScript* instance = me->GetInstanceScript())
instance->SetData(DATA_IKISSDOOREVENT, DONE);
}
void KilledUnit(Unit* /*victim*/) override
{
if (urand(0, 1))
Talk(SAY_SLAY);
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case EVENT_SPELL_ARCANE_VOLLEY:
me->CastSpell(me, SPELL_ARCANE_VOLLEY_N, false);
events.RepeatEvent(urand(7000, 12000));
break;
case EVENT_SPELL_POLYMORPH:
if (Unit* target = (IsHeroic() ? SelectTarget(SelectTargetMethod::Random, 0) : SelectTarget(SelectTargetMethod::MaxThreat, 1)))
me->CastSpell(target, SPELL_POLYMORPH_N, false);
events.RepeatEvent(urand(15000, 17500));
break;
case EVENT_SPELL_SLOW:
me->CastSpell(me, SPELL_SLOW, false);
events.RepeatEvent(urand(15000, 30000));
break;
case EVENT_HEALTH_CHECK:
if (me->HealthBelowPct(20))
{
me->CastSpell(me, SPELL_MANA_SHIELD, false);
return;
}
events.RepeatEvent(1000);
break;
case EVENT_SPELL_BLINK:
Talk(EMOTE_ARCANE_EXP);
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0))
{
me->CastSpell(target, SPELL_BLINK, false);
me->NearTeleportTo(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), target->GetOrientation());
DoCast(target, SPELL_BLINK_TELEPORT);
}
events.RepeatEvent(urand(35000, 40000));
events.DelayEvents(500);
events.ScheduleEvent(EVENT_SPELL_BLINK_2, 0);
return;
case EVENT_SPELL_BLINK_2:
me->CastSpell(me, SPELL_ARCANE_EXPLOSION_N, false);
me->CastSpell(me, SPELL_ARCANE_BUBBLE, true);
break;
}
DoMeleeAttackIfReady();
}
};
bool _manaShield;
};
enum Anzu
// 38194 - Blink
class spell_talon_king_ikiss_blink : public SpellScript
{
SAY_ANZU_INTRO1 = 0,
SAY_ANZU_INTRO2 = 1,
SAY_SUMMON = 2,
PrepareSpellScript(spell_talon_king_ikiss_blink);
SPELL_PARALYZING_SCREECH = 40184,
SPELL_SPELL_BOMB = 40303,
SPELL_CYCLONE = 40321,
SPELL_BANISH_SELF = 42354,
SPELL_SHADOWFORM = 40973,
EVENT_SPELL_SCREECH = 1,
EVENT_SPELL_BOMB = 2,
EVENT_SPELL_CYCLONE = 3,
EVENT_ANZU_HEALTH1 = 4,
EVENT_ANZU_HEALTH2 = 5
};
class boss_anzu : public CreatureScript
{
public:
boss_anzu() : CreatureScript("boss_anzu") { }
CreatureAI* GetAI(Creature* creature) const override
bool Validate(SpellInfo const* /*spellInfo*/) override
{
return GetSethekkHallsAI<boss_anzuAI>(creature);
return sSpellMgr->GetSpellInfo(SPELL_BLINK);
}
struct boss_anzuAI : public ScriptedAI
void FilterTargets(std::list<WorldObject*>& targets)
{
boss_anzuAI(Creature* creature) : ScriptedAI(creature), summons(me)
uint8 maxSize = 1;
if (targets.size() > maxSize)
{
talkTimer = 1;
me->ReplaceAllUnitFlags(UNIT_FLAG_NON_ATTACKABLE);
me->AddAura(SPELL_SHADOWFORM, me);
Acore::Containers::RandomResize(targets, maxSize);
}
}
EventMap events;
SummonList summons;
uint32 talkTimer;
void HandleDummyHitTarget(SpellEffIndex effIndex)
{
PreventHitDefaultEffect(effIndex);
GetHitUnit()->CastSpell(GetCaster(), SPELL_BLINK_TELEPORT, true);
}
void Reset() override
{
summons.DespawnAll();
if (InstanceScript* instance = me->GetInstanceScript())
if (instance->GetData(TYPE_ANZU_ENCOUNTER) != DONE)
instance->SetData(TYPE_ANZU_ENCOUNTER, NOT_STARTED);
}
void JustSummoned(Creature* summon) override
{
summons.Summon(summon);
summon->AI()->AttackStart(me->GetVictim());
}
void SummonedCreatureDies(Creature* summon, Unit*) override
{
summons.Despawn(summon);
summons.RemoveNotExisting();
if (summons.empty())
me->RemoveAurasDueToSpell(SPELL_BANISH_SELF);
}
void JustEngagedWith(Unit* /*who*/) override
{
events.Reset();
events.ScheduleEvent(EVENT_SPELL_SCREECH, 14000);
events.ScheduleEvent(EVENT_SPELL_BOMB, 5000);
events.ScheduleEvent(EVENT_SPELL_CYCLONE, 8000);
events.ScheduleEvent(EVENT_ANZU_HEALTH1, 2000);
events.ScheduleEvent(EVENT_ANZU_HEALTH2, 2001);
if (InstanceScript* instance = me->GetInstanceScript())
instance->SetData(TYPE_ANZU_ENCOUNTER, IN_PROGRESS);
}
void JustDied(Unit* /*killer*/) override
{
if (InstanceScript* instance = me->GetInstanceScript())
instance->SetData(TYPE_ANZU_ENCOUNTER, DONE);
}
void SummonBroods()
{
Talk(SAY_SUMMON);
me->CastSpell(me, SPELL_BANISH_SELF, true);
for (uint8 i = 0; i < 5; ++i)
me->SummonCreature(23132 /*NPC_BROOD_OF_ANZU*/, me->GetPositionX() + 20 * cos((float)i), me->GetPositionY() + 20 * std::sin((float)i), me->GetPositionZ() + 25.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 30000);
}
void UpdateAI(uint32 diff) override
{
if (talkTimer)
{
talkTimer += diff;
if (talkTimer >= 1000 && talkTimer < 10000)
{
Talk(SAY_ANZU_INTRO1);
talkTimer = 10000;
}
else if (talkTimer >= 16000)
{
me->ReplaceAllUnitFlags(UNIT_FLAG_NONE);
me->RemoveAurasDueToSpell(SPELL_SHADOWFORM);
Talk(SAY_ANZU_INTRO2);
talkTimer = 0;
}
}
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING | UNIT_STATE_STUNNED))
return;
switch (events.ExecuteEvent())
{
case EVENT_SPELL_SCREECH:
me->CastSpell(me, SPELL_PARALYZING_SCREECH, false);
events.RepeatEvent(23000);
events.DelayEvents(3000);
break;
case EVENT_SPELL_BOMB:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 50.0f, true))
me->CastSpell(target, SPELL_SPELL_BOMB, false);
events.RepeatEvent(urand(16000, 24500));
events.DelayEvents(3000);
break;
case EVENT_SPELL_CYCLONE:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 1, 45.0f, true))
me->CastSpell(target, SPELL_CYCLONE, false);
events.RepeatEvent(urand(22000, 27000));
events.DelayEvents(3000);
break;
case EVENT_ANZU_HEALTH1:
if (me->HealthBelowPct(66))
{
SummonBroods();
events.DelayEvents(10000);
return;
}
events.RepeatEvent(1000);
break;
case EVENT_ANZU_HEALTH2:
if (me->HealthBelowPct(33))
{
SummonBroods();
events.DelayEvents(10000);
return;
}
events.RepeatEvent(1000);
break;
}
DoMeleeAttackIfReady();
}
};
void Register() override
{
OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_talon_king_ikiss_blink::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY);
OnEffectHitTarget += SpellEffectFn(spell_talon_king_ikiss_blink::HandleDummyHitTarget, EFFECT_0, SPELL_EFFECT_DUMMY);
}
};
void AddSC_boss_talon_king_ikiss()
{
new boss_talon_king_ikiss();
new boss_anzu();
RegisterSethekkHallsCreatureAI(boss_talon_king_ikiss);
RegisterSpellScript(spell_talon_king_ikiss_blink);
}

View File

@@ -19,6 +19,17 @@
#include "ScriptMgr.h"
#include "sethekk_halls.h"
DoorData const doorData[] =
{
{ GO_IKISS_DOOR, DATA_IKISS, DOOR_TYPE_PASSAGE },
{ 0, 0, DOOR_TYPE_ROOM } // END
};
ObjectData const gameObjectData[] =
{
{ GO_THE_TALON_KINGS_COFFER, DATA_GO_TALON_KING_COFFER }
};
class instance_sethekk_halls : public InstanceMapScript
{
public:
@@ -31,91 +42,20 @@ public:
struct instance_sethekk_halls_InstanceMapScript : public InstanceScript
{
instance_sethekk_halls_InstanceMapScript(Map* map) : InstanceScript(map) {}
uint32 AnzuEncounter;
ObjectGuid m_uiIkissDoorGUID;
ObjectGuid _talonKingsCofferGUID;
void Initialize() override
instance_sethekk_halls_InstanceMapScript(Map* map) : InstanceScript(map)
{
AnzuEncounter = NOT_STARTED;
SetHeaders(DataHeaders);
SetBossNumber(EncounterCount);
LoadDoorData(doorData);
LoadObjectData(nullptr, gameObjectData);
}
void OnCreatureCreate(Creature* creature) override
{
if (creature->GetEntry() == NPC_ANZU || creature->GetEntry() == NPC_VOICE_OF_THE_RAVEN_GOD)
if (AnzuEncounter >= IN_PROGRESS)
if (GetBossState(DATA_ANZU) == DONE)
creature->DespawnOrUnsummon(1);
}
void OnGameObjectCreate(GameObject* go) override
{
switch (go->GetEntry())
{
case GO_IKISS_DOOR:
m_uiIkissDoorGUID = go->GetGUID();
break;
case GO_THE_TALON_KINGS_COFFER:
_talonKingsCofferGUID = go->GetGUID();
break;
}
}
void SetData(uint32 type, uint32 data) override
{
switch (type)
{
case DATA_IKISSDOOREVENT:
if (data == DONE)
{
DoUseDoorOrButton(m_uiIkissDoorGUID, DAY * IN_MILLISECONDS);
if (GameObject* coffer = instance->GetGameObject(_talonKingsCofferGUID))
coffer->RemoveGameObjectFlag(GO_FLAG_NOT_SELECTABLE | GO_FLAG_INTERACT_COND);
}
break;
case TYPE_ANZU_ENCOUNTER:
AnzuEncounter = data;
SaveToDB();
break;
}
}
std::string GetSaveData() override
{
OUT_SAVE_INST_DATA;
std::ostringstream saveStream;
saveStream << "S H " << AnzuEncounter;
OUT_SAVE_INST_DATA_COMPLETE;
return saveStream.str();
}
void Load(const char* strIn) override
{
if (!strIn)
{
OUT_LOAD_INST_DATA_FAIL;
return;
}
OUT_LOAD_INST_DATA(strIn);
char dataHead1, dataHead2;
std::istringstream loadStream(strIn);
loadStream >> dataHead1 >> dataHead2;
if (dataHead1 == 'S' && dataHead2 == 'H')
{
loadStream >> AnzuEncounter;
if (AnzuEncounter == IN_PROGRESS)
AnzuEncounter = NOT_STARTED;
}
OUT_LOAD_INST_DATA_COMPLETE;
}
};
};

View File

@@ -20,14 +20,30 @@
#include "CreatureAIImpl.h"
#define DataHeaders "SH"
uint32 constexpr EncounterCount = 3;
#define SethekkHallsScriptName "instance_sethekk_halls"
enum Encounters
{
DATA_DARKWEAVER_SYTH,
DATA_ANZU,
DATA_IKISS,
};
enum eTypes
{
DATA_IKISSDOOREVENT = 1,
TYPE_ANZU_ENCOUNTER = 2,
};
enum InstanceData
{
DATA_GO_TALON_KING_COFFER
};
enum eIds
{
NPC_VOICE_OF_THE_RAVEN_GOD = 21851,
@@ -43,4 +59,6 @@ inline AI* GetSethekkHallsAI(T* obj)
return GetInstanceAI<AI>(obj, SethekkHallsScriptName);
}
#define RegisterSethekkHallsCreatureAI(ai_name) RegisterCreatureAIWithFactory(ai_name, GetSethekkHallsAI)
#endif

View File

@@ -38,6 +38,7 @@ public:
{
SetHeaders(DataHeader);
SetBossNumber(MAX_ENCOUNTER);
SetPersistentDataCount(MAX_DATA_INDEXES);
LoadDoorData(doorData);
_passageEncounter = 0;
@@ -149,7 +150,8 @@ public:
DoSummonAction(creature, player);
}
}
_passageEncounter++;
StorePersistentData(DATA_INDEX_PASSAGE_ENCOUNTER, _passageEncounter++);
SaveToDB();
}
}
@@ -186,16 +188,17 @@ public:
if (Creature* creature = instance->GetCreature(_pathaleonGUID))
creature->AI()->DoAction(1);
}
_passageEncounter++;
StorePersistentData(DATA_INDEX_PASSAGE_ENCOUNTER, _passageEncounter++);
SaveToDB();
}
}
}
}
void ReadSaveDataMore(std::istringstream& data) override
void ReadSaveDataMore(std::istringstream& /*data*/) override
{
data >> _passageEncounter;
_passageEncounter = GetPersistentData(DATA_INDEX_PASSAGE_ENCOUNTER);
if (_passageEncounter == ENCOUNTER_PASSAGE_DONE)
{
@@ -203,11 +206,6 @@ public:
}
}
void WriteSaveDataMore(std::ostringstream& data) override
{
data << _passageEncounter;
}
private:
ObjectGuid _pathaleonGUID;
uint32 _passageTimer;

View File

@@ -70,6 +70,12 @@ enum SpellIds
SPELL_TELEPORT_VISUAL = 35517
};
enum DataIndex
{
DATA_INDEX_PASSAGE_ENCOUNTER = 0,
MAX_DATA_INDEXES
};
template <class AI, class T>
inline AI* GetMechanarAI(T* obj)
{

View File

@@ -23,6 +23,7 @@ void AddSC_boss_nexusprince_shaffar(); //Auchindoun Mana Tombs
void AddSC_boss_pandemonius();
void AddSC_instance_mana_tombs();
void AddSC_boss_talon_king_ikiss(); //Auchindoun Sekketh Halls
void AddSC_boss_anzu();
void AddSC_instance_sethekk_halls();
void AddSC_instance_shadow_labyrinth(); //Auchindoun Shadow Labyrinth
void AddSC_boss_ambassador_hellmaw();
@@ -119,6 +120,7 @@ void AddOutlandScripts()
AddSC_boss_pandemonius();
AddSC_instance_mana_tombs();
AddSC_boss_talon_king_ikiss(); //Auchindoun Sekketh Halls
AddSC_boss_anzu();
AddSC_instance_sethekk_halls();
AddSC_instance_shadow_labyrinth(); //Auchindoun Shadow Labyrinth
AddSC_boss_ambassador_hellmaw();

View File

@@ -18,7 +18,7 @@
/* ScriptData
SDName: Shadowmoon_Valley
SD%Complete: 100
SDComment: Quest support: 10519, 10583, 10601, 10804, 10854, 10458, 10481, 10480, 10781, 10451. Vendor Drake Dealer Hurlunk.
SDComment: Quest support: 10519, 10583, 10601, 10804, 10854, 10458, 10481, 10480, 10781. Vendor Drake Dealer Hurlunk.
SDCategory: Shadowmoon Valley
EndScriptData */
@@ -29,7 +29,6 @@ npc_drake_dealer_hurlunk
npcs_flanis_swiftwing_and_kagrosh
npc_karynaku
npc_oronok_tornheart
npc_earthmender_wilda
npc_torloth_the_magnificent
npc_illidari_spawn
npc_lord_illidan_stormrage
@@ -811,176 +810,6 @@ public:
}
};
/*####
# npc_earthmender_wilda
####*/
enum Earthmender
{
SAY_WIL_START = 0,
SAY_WIL_AGGRO = 1,
SAY_WIL_PROGRESS1 = 2,
SAY_WIL_PROGRESS2 = 3,
SAY_WIL_FIND_EXIT = 4,
SAY_WIL_JUST_AHEAD = 5,
SAY_WIL_END = 6,
SPELL_CHAIN_LIGHTNING = 16006,
SPELL_EARTHBING_TOTEM = 15786,
SPELL_FROST_SHOCK = 12548,
SPELL_HEALING_WAVE = 12491,
QUEST_ESCAPE_COILSCAR = 10451,
NPC_COILSKAR_ASSASSIN = 21044
};
class npc_earthmender_wilda : public CreatureScript
{
public:
npc_earthmender_wilda() : CreatureScript("npc_earthmender_wilda") { }
bool OnQuestAccept(Player* player, Creature* creature, const Quest* quest) override
{
if (quest->GetQuestId() == QUEST_ESCAPE_COILSCAR)
{
creature->AI()->Talk(SAY_WIL_START, player);
creature->SetFaction(FACTION_EARTHEN_RING); //guessed
if (npc_earthmender_wildaAI* pEscortAI = CAST_AI(npc_earthmender_wilda::npc_earthmender_wildaAI, creature->AI()))
pEscortAI->Start(false, false, player->GetGUID(), quest);
}
return true;
}
CreatureAI* GetAI(Creature* creature) const override
{
return new npc_earthmender_wildaAI(creature);
}
struct npc_earthmender_wildaAI : public npc_escortAI
{
npc_earthmender_wildaAI(Creature* creature) : npc_escortAI(creature) { }
uint32 m_uiHealingTimer;
void Reset() override
{
m_uiHealingTimer = 0;
}
void WaypointReached(uint32 waypointId) override
{
Player* player = GetPlayerForEscort();
if (!player)
return;
switch (waypointId)
{
case 13:
Talk(SAY_WIL_PROGRESS1, player);
DoSpawnAssassin();
break;
case 14:
DoSpawnAssassin();
break;
case 15:
Talk(SAY_WIL_FIND_EXIT, player);
break;
case 19:
DoRandomSay();
break;
case 20:
DoSpawnAssassin();
break;
case 26:
DoRandomSay();
break;
case 27:
DoSpawnAssassin();
break;
case 33:
DoRandomSay();
break;
case 34:
DoSpawnAssassin();
break;
case 37:
DoRandomSay();
break;
case 38:
DoSpawnAssassin();
break;
case 39:
Talk(SAY_WIL_JUST_AHEAD, player);
break;
case 43:
DoRandomSay();
break;
case 44:
DoSpawnAssassin();
break;
case 50:
Talk(SAY_WIL_END, player);
player->GroupEventHappens(QUEST_ESCAPE_COILSCAR, me);
break;
}
}
void JustSummoned(Creature* summoned) override
{
if (summoned->GetEntry() == NPC_COILSKAR_ASSASSIN)
summoned->AI()->AttackStart(me);
}
//this is very unclear, random say without no real relevance to script/event
void DoRandomSay()
{
Talk(SAY_WIL_PROGRESS2);
}
void DoSpawnAssassin()
{
//unknown where they actually appear
DoSummon(NPC_COILSKAR_ASSASSIN, me, 15.0f, 5000, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT);
}
void JustEngagedWith(Unit* who) override
{
//don't always use
if (rand() % 5)
return;
//only aggro text if not player
if (who->GetTypeId() != TYPEID_PLAYER)
{
//appears to be random
if (urand(0, 1))
Talk(SAY_WIL_AGGRO);
}
}
void UpdateAI(uint32 uiDiff) override
{
npc_escortAI::UpdateAI(uiDiff);
if (!UpdateVictim())
return;
/// @todo add more abilities
if (!HealthAbovePct(30))
{
if (m_uiHealingTimer <= uiDiff)
{
DoCast(me, SPELL_HEALING_WAVE);
m_uiHealingTimer = 15000;
}
else
m_uiHealingTimer -= uiDiff;
}
}
};
};
/*#####
# Quest: Battle of the crimson watch
#####*/
@@ -1821,7 +1650,6 @@ void AddSC_shadowmoon_valley()
new npc_drake_dealer_hurlunk();
new npcs_flanis_swiftwing_and_kagrosh();
new npc_karynaku();
new npc_earthmender_wilda();
new npc_lord_illidan_stormrage();
new go_crystal_prison();
new npc_illidari_spawn();

View File

@@ -36,7 +36,6 @@ public:
MOCK_METHOD(WorldSession*, FindOfflineSession, (uint32 id), (const));
MOCK_METHOD(WorldSession*, FindOfflineSessionForCharacterGUID, (ObjectGuid::LowType guidLow),(const));
MOCK_METHOD(void, AddSession, (WorldSession* s), ());
MOCK_METHOD(void, SendAutoBroadcast, ());
MOCK_METHOD(bool, KickSession, (uint32 id), ());
MOCK_METHOD(void, UpdateMaxSessionCounters, ());
MOCK_METHOD(const SessionMap&, GetAllSessions, (), (const));
@@ -112,7 +111,7 @@ public:
MOCK_METHOD(LocaleConstant, GetAvailableDbcLocale, (LocaleConstant locale), (const));
MOCK_METHOD(void, LoadDBVersion, ());
MOCK_METHOD(char const *, GetDBVersion, (), (const));
MOCK_METHOD(void, LoadAutobroadcasts, ());
MOCK_METHOD(void, LoadMotd, ());
MOCK_METHOD(void, UpdateAreaDependentAuras, ());
MOCK_METHOD(uint32, GetCleaningFlags, (), (const));
MOCK_METHOD(void, SetCleaningFlags, (uint32 flags), ());