mirror of
https://github.com/mod-playerbots/azerothcore-wotlk.git
synced 2026-01-24 22:26:22 +00:00
Merge branch 'master' into Playerbot
# Conflicts: # src/server/game/World/IWorld.h # src/server/game/World/World.h
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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();
|
||||
|
||||
142
src/server/game/Autobroadcast/AutobroadcastMgr.cpp
Normal file
142
src/server/game/Autobroadcast/AutobroadcastMgr.cpp
Normal 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);
|
||||
}
|
||||
54
src/server/game/Autobroadcast/AutobroadcastMgr.h
Normal file
54
src/server/game/Autobroadcast/AutobroadcastMgr.h
Normal 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_
|
||||
@@ -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; }
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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*/)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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...");
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -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:
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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];
|
||||
|
||||
152
src/server/scripts/Outland/Auchindoun/SethekkHalls/boss_anzu.cpp
Normal file
152
src/server/scripts/Outland/Auchindoun/SethekkHalls/boss_anzu.cpp
Normal 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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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), ());
|
||||
|
||||
Reference in New Issue
Block a user