aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/server/database/CMakeLists.txt4
-rw-r--r--src/server/database/Database/Implementation/CharacterDatabase.cpp2
-rw-r--r--src/server/database/Updater/DBUpdater.cpp125
-rw-r--r--src/server/database/Updater/DBUpdater.h14
-rw-r--r--src/server/game/AI/CoreAI/UnitAI.cpp8
-rw-r--r--src/server/game/AI/CoreAI/UnitAI.h4
-rw-r--r--src/server/game/AI/ScriptedAI/ScriptedCreature.cpp2
-rw-r--r--src/server/game/AI/SmartScripts/SmartAI.cpp42
-rw-r--r--src/server/game/AI/SmartScripts/SmartAI.h4
-rw-r--r--src/server/game/AuctionHouse/AuctionHouseMgr.cpp6
-rw-r--r--src/server/game/Entities/Creature/Creature.cpp36
-rw-r--r--src/server/game/Entities/Creature/Creature.h10
-rw-r--r--src/server/game/Entities/Player/Player.cpp14
-rw-r--r--src/server/game/Entities/Player/Player.h7
-rw-r--r--src/server/game/Entities/Unit/Unit.cpp6
-rwxr-xr-xsrc/server/game/Entities/Vehicle/Vehicle.cpp6
-rw-r--r--src/server/game/Entities/Vehicle/VehicleDefines.h3
-rw-r--r--src/server/game/Handlers/DuelHandler.cpp3
-rw-r--r--src/server/game/Miscellaneous/Language.h4
-rw-r--r--src/server/game/Server/WorldSession.cpp26
-rw-r--r--src/server/game/Spells/SpellHistory.cpp17
-rw-r--r--src/server/game/Spells/SpellHistory.h1
-rw-r--r--src/server/game/Spells/SpellMgr.cpp4
-rw-r--r--src/server/game/Tickets/TicketMgr.cpp18
-rw-r--r--src/server/game/Tickets/TicketMgr.h5
-rw-r--r--src/server/game/World/World.cpp1
-rw-r--r--src/server/game/World/World.h1
-rw-r--r--src/server/scripts/Commands/cs_ticket.cpp5
-rw-r--r--src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_nefarian.cpp4
-rw-r--r--src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_vaelastrasz.cpp4
-rw-r--r--src/server/scripts/EasternKingdoms/Karazhan/karazhan.cpp37
-rw-r--r--src/server/scripts/EasternKingdoms/ScarletEnclave/chapter1.cpp42
-rw-r--r--src/server/scripts/EasternKingdoms/ZulAman/zulaman.cpp4
-rw-r--r--src/server/scripts/Kalimdor/BlackfathomDeeps/blackfathom_deeps.cpp2
-rw-r--r--src/server/scripts/Kalimdor/zone_azshara.cpp2
-rw-r--r--src/server/scripts/Kalimdor/zone_azuremyst_isle.cpp2
-rw-r--r--src/server/scripts/Northrend/IcecrownCitadel/boss_deathbringer_saurfang.cpp65
-rw-r--r--src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp6
-rw-r--r--src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp4
-rw-r--r--src/server/scripts/Northrend/Naxxramas/boss_noth.cpp284
-rw-r--r--src/server/scripts/Northrend/Ulduar/Ulduar/boss_yogg_saron.cpp4
-rw-r--r--src/server/scripts/Northrend/zone_grizzly_hills.cpp2
-rw-r--r--src/server/scripts/Northrend/zone_storm_peaks.cpp8
-rw-r--r--src/server/scripts/Northrend/zone_zuldrak.cpp4
-rw-r--r--src/server/scripts/Outland/BlackTemple/black_temple.cpp44
-rw-r--r--src/server/scripts/Outland/BlackTemple/boss_illidan.cpp2
-rw-r--r--src/server/scripts/Outland/BlackTemple/boss_shade_of_akama.cpp4
-rw-r--r--src/server/worldserver/worldserver.conf.dist8
48 files changed, 554 insertions, 356 deletions
diff --git a/src/server/database/CMakeLists.txt b/src/server/database/CMakeLists.txt
index 2375f18d7b5..19fa0ee0acf 100644
--- a/src/server/database/CMakeLists.txt
+++ b/src/server/database/CMakeLists.txt
@@ -8,7 +8,9 @@
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-find_package(MySQL REQUIRED)
+if (NOT MYSQL_FOUND)
+ message(SEND_ERROR "MySQL wasn't found on your system but it's required to build the servers!")
+endif()
if( USE_COREPCH )
include_directories(${CMAKE_CURRENT_BINARY_DIR})
diff --git a/src/server/database/Database/Implementation/CharacterDatabase.cpp b/src/server/database/Database/Implementation/CharacterDatabase.cpp
index b5b79a3e11d..14e7571dd42 100644
--- a/src/server/database/Database/Implementation/CharacterDatabase.cpp
+++ b/src/server/database/Database/Implementation/CharacterDatabase.cpp
@@ -337,7 +337,7 @@ void CharacterDatabaseConnection::DoPrepareStatements()
// GM Tickets
PrepareStatement(CHAR_SEL_GM_TICKETS, "SELECT id, playerGuid, name, description, createTime, mapId, posX, posY, posZ, lastModifiedTime, closedBy, assignedTo, comment, response, completed, escalated, viewed, needMoreHelp FROM gm_ticket", CONNECTION_SYNCH);
- PrepareStatement(CHAR_REP_GM_TICKET, "REPLACE INTO gm_ticket (id, playerGuid, name, description, createTime, mapId, posX, posY, posZ, lastModifiedTime, closedBy, assignedTo, comment, response, completed, escalated, viewed, needMoreHelp) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_REP_GM_TICKET, "REPLACE INTO gm_ticket (id, playerGuid, name, description, createTime, mapId, posX, posY, posZ, lastModifiedTime, closedBy, assignedTo, comment, response, completed, escalated, viewed, needMoreHelp, resolvedBy) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_GM_TICKET, "DELETE FROM gm_ticket WHERE id = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_PLAYER_GM_TICKETS, "DELETE FROM gm_ticket WHERE playerGuid = ?", CONNECTION_ASYNC);
diff --git a/src/server/database/Updater/DBUpdater.cpp b/src/server/database/Updater/DBUpdater.cpp
index ebdd6604fef..d90d71c5594 100644
--- a/src/server/database/Updater/DBUpdater.cpp
+++ b/src/server/database/Updater/DBUpdater.cpp
@@ -26,6 +26,8 @@
#include <iostream>
#include <unordered_map>
#include <boost/process.hpp>
+#include <boost/iostreams/stream.hpp>
+#include <boost/iostreams/copy.hpp>
#include <boost/iostreams/device/file_descriptor.hpp>
#include <boost/system/system_error.hpp>
@@ -33,24 +35,64 @@ using namespace boost::process;
using namespace boost::process::initializers;
using namespace boost::iostreams;
-template<class T>
-std::string DBUpdater<T>::GetSourceDirectory()
+std::string DBUpdaterUtil::GetMySqlCli()
{
- std::string const entry = sConfigMgr->GetStringDefault("Updates.SourcePath", "");
- if (!entry.empty())
- return entry;
+ if (!corrected_path().empty())
+ return corrected_path();
else
- return GitRevision::GetSourceDirectory();
+ {
+ std::string const entry = sConfigMgr->GetStringDefault("Updates.MySqlCLIPath", "");
+ if (!entry.empty())
+ return entry;
+ else
+ return GitRevision::GetMySQLExecutable();
+ }
+}
+
+bool DBUpdaterUtil::CheckExecutable()
+{
+ boost::filesystem::path exe(GetMySqlCli());
+ if (!exists(exe))
+ {
+ exe.clear();
+
+ try
+ {
+ exe = search_path("mysql");
+ }
+ catch (std::runtime_error&)
+ {
+ }
+
+ if (!exe.empty() && exists(exe))
+ {
+ // Correct the path to the cli
+ corrected_path() = absolute(exe).generic_string();
+ return true;
+ }
+
+ TC_LOG_FATAL("sql.updates", "Didn't find executeable mysql binary at \'%s\' or in path, correct the path in the *.conf (\"Updates.MySqlCLIPath\").",
+ absolute(exe).generic_string().c_str());
+
+ return false;
+ }
+ return true;
+}
+
+std::string& DBUpdaterUtil::corrected_path()
+{
+ static std::string path;
+ return path;
}
template<class T>
-std::string DBUpdater<T>::GetMySqlCli()
+std::string DBUpdater<T>::GetSourceDirectory()
{
- std::string const entry = sConfigMgr->GetStringDefault("Updates.MySqlCLIPath", "");
+ std::string const entry = sConfigMgr->GetStringDefault("Updates.SourcePath", "");
if (!entry.empty())
return entry;
else
- return GitRevision::GetMySQLExecutable();
+ return GitRevision::GetSourceDirectory();
}
// Auth Database
@@ -145,36 +187,6 @@ BaseLocation DBUpdater<T>::GetBaseLocationType()
}
template<class T>
-bool DBUpdater<T>::CheckExecutable()
-{
- DBUpdater<T>::Path const exe(DBUpdater<T>::GetMySqlCli());
- if (!exists(exe))
- {
- // Check for mysql in path
- std::vector<std::string> args = {"--version"};
- uint32 ret;
- try
- {
- child c = execute(run_exe("mysql"), set_args(args), throw_on_error(), close_stdout());
- ret = wait_for_exit(c);
- }
- catch (boost::system::system_error&)
- {
- ret = EXIT_FAILURE;
- }
-
- if (ret == EXIT_FAILURE)
- {
- TC_LOG_FATAL("sql.updates", "Didn't find executeable mysql binary at \'%s\', correct the path in the *.conf (\"Updates.MySqlCLIPath\").",
- absolute(exe).generic_string().c_str());
-
- return false;
- }
- }
- return true;
-}
-
-template<class T>
bool DBUpdater<T>::Create(DatabaseWorkerPool<T>& pool)
{
TC_LOG_INFO("sql.updates", "Database \"%s\" does not exist, do you want to create it? [yes (default) / no]: ",
@@ -222,7 +234,7 @@ bool DBUpdater<T>::Create(DatabaseWorkerPool<T>& pool)
template<class T>
bool DBUpdater<T>::Update(DatabaseWorkerPool<T>& pool)
{
- if (!DBUpdater<T>::CheckExecutable())
+ if (!DBUpdaterUtil::CheckExecutable())
return false;
TC_LOG_INFO("sql.updates", "Updating %s database...", DBUpdater<T>::GetTableName().c_str());
@@ -273,7 +285,7 @@ bool DBUpdater<T>::Populate(DatabaseWorkerPool<T>& pool)
return true;
}
- if (!DBUpdater<T>::CheckExecutable())
+ if (!DBUpdaterUtil::CheckExecutable())
return false;
TC_LOG_INFO("sql.updates", "Database %s is empty, auto populating it...", DBUpdater<T>::GetTableName().c_str());
@@ -346,7 +358,10 @@ void DBUpdater<T>::ApplyFile(DatabaseWorkerPool<T>& pool, std::string const& hos
std::string const& password, std::string const& port_or_socket, std::string const& database, Path const& path)
{
std::vector<std::string> args;
- args.reserve(7);
+ args.reserve(8);
+
+ // args[0] represents the program name
+ args.push_back("mysql");
// CLI Client connection info
args.push_back("-h" + host);
@@ -391,9 +406,29 @@ void DBUpdater<T>::ApplyFile(DatabaseWorkerPool<T>& pool, std::string const& hos
uint32 ret;
try
{
- child c = execute(run_exe(DBUpdater<T>::GetMySqlCli().empty() ? "mysql" :
- boost::filesystem::absolute(DBUpdater<T>::GetMySqlCli()).generic_string()),
- set_args(args), bind_stdin(source), throw_on_error());
+ boost::process::pipe outPipe = create_pipe();
+ boost::process::pipe errPipe = create_pipe();
+
+ child c = execute(run_exe(
+ boost::filesystem::absolute(DBUpdaterUtil::GetMySqlCli()).generic_string()),
+ set_args(args), bind_stdin(source), throw_on_error(),
+ bind_stdout(file_descriptor_sink(outPipe.sink, close_handle)),
+ bind_stderr(file_descriptor_sink(errPipe.sink, close_handle)));
+
+ file_descriptor_source mysqlOutfd(outPipe.source, close_handle);
+ file_descriptor_source mysqlErrfd(errPipe.source, close_handle);
+
+ stream<file_descriptor_source> mysqlOutStream(mysqlOutfd);
+ stream<file_descriptor_source> mysqlErrStream(mysqlErrfd);
+
+ std::stringstream out;
+ std::stringstream err;
+
+ copy(mysqlOutStream, out);
+ copy(mysqlErrStream, err);
+
+ TC_LOG_INFO("sql.updates", "%s", out.str().c_str());
+ TC_LOG_ERROR("sql.updates", "%s", err.str().c_str());
ret = wait_for_exit(c);
}
diff --git a/src/server/database/Updater/DBUpdater.h b/src/server/database/Updater/DBUpdater.h
index a2b12bed235..c8aa5d69fbc 100644
--- a/src/server/database/Updater/DBUpdater.h
+++ b/src/server/database/Updater/DBUpdater.h
@@ -54,6 +54,17 @@ struct UpdateResult
size_t archived;
};
+class DBUpdaterUtil
+{
+public:
+ static std::string GetMySqlCli();
+
+ static bool CheckExecutable();
+
+private:
+ static std::string& corrected_path();
+};
+
template <class T>
class DBUpdater
{
@@ -79,9 +90,6 @@ public:
static bool Populate(DatabaseWorkerPool<T>& pool);
private:
- static std::string GetMySqlCli();
- static bool CheckExecutable();
-
static QueryResult Retrieve(DatabaseWorkerPool<T>& pool, std::string const& query);
static void Apply(DatabaseWorkerPool<T>& pool, std::string const& query);
static void ApplyFile(DatabaseWorkerPool<T>& pool, Path const& path);
diff --git a/src/server/game/AI/CoreAI/UnitAI.cpp b/src/server/game/AI/CoreAI/UnitAI.cpp
index 5aa6ea8ea7a..f0d9d34db69 100644
--- a/src/server/game/AI/CoreAI/UnitAI.cpp
+++ b/src/server/game/AI/CoreAI/UnitAI.cpp
@@ -29,7 +29,15 @@
void UnitAI::AttackStart(Unit* victim)
{
if (victim && me->Attack(victim, true))
+ {
+ // Clear distracted state on attacking
+ if (me->HasUnitState(UNIT_STATE_DISTRACTED))
+ {
+ me->ClearUnitState(UNIT_STATE_DISTRACTED);
+ me->GetMotionMaster()->Clear();
+ }
me->GetMotionMaster()->MoveChase(victim);
+ }
}
void UnitAI::AttackStartCaster(Unit* victim, float dist)
diff --git a/src/server/game/AI/CoreAI/UnitAI.h b/src/server/game/AI/CoreAI/UnitAI.h
index 1939c96dac9..c93ed0db008 100644
--- a/src/server/game/AI/CoreAI/UnitAI.h
+++ b/src/server/game/AI/CoreAI/UnitAI.h
@@ -255,8 +255,8 @@ class UnitAI
static void FillAISpellInfo();
virtual void sGossipHello(Player* /*player*/) { }
- virtual void sGossipSelect(Player* /*player*/, uint32 /*sender*/, uint32 /*action*/) { }
- virtual void sGossipSelectCode(Player* /*player*/, uint32 /*sender*/, uint32 /*action*/, char const* /*code*/) { }
+ virtual void sGossipSelect(Player* /*player*/, uint32 /*menuId*/, uint32 /*gossipListId*/) { }
+ virtual void sGossipSelectCode(Player* /*player*/, uint32 /*menuId*/, uint32 /*gossipListId*/, char const* /*code*/) { }
virtual void sQuestAccept(Player* /*player*/, Quest const* /*quest*/) { }
virtual void sQuestSelect(Player* /*player*/, Quest const* /*quest*/) { }
virtual void sQuestReward(Player* /*player*/, Quest const* /*quest*/, uint32 /*opt*/) { }
diff --git a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp
index 8e2a82dbfd3..7b145268d22 100644
--- a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp
+++ b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp
@@ -470,6 +470,7 @@ void BossAI::_Reset()
if (!me->IsAlive())
return;
+ me->SetCombatPulseDelay(0);
me->ResetLootMode();
events.Reset();
summons.DespawnAll();
@@ -500,6 +501,7 @@ void BossAI::_EnterCombat()
instance->SetBossState(_bossId, IN_PROGRESS);
}
+ me->SetCombatPulseDelay(5);
me->setActive(true);
DoZoneInCombat();
ScheduleTasks();
diff --git a/src/server/game/AI/SmartScripts/SmartAI.cpp b/src/server/game/AI/SmartScripts/SmartAI.cpp
index 7c507ad59b6..46cf934356d 100644
--- a/src/server/game/AI/SmartScripts/SmartAI.cpp
+++ b/src/server/game/AI/SmartScripts/SmartAI.cpp
@@ -453,45 +453,15 @@ void SmartAI::MoveInLineOfSight(Unit* who)
GetScript()->OnMoveInLineOfSight(who);
- if (me->HasReactState(REACT_PASSIVE) || AssistPlayerInCombat(who))
+ if (AssistPlayerInCombat(who))
return;
- if (!CanAIAttack(who))
- return;
-
- if (!me->CanStartAttack(who, false))
- return;
-
- if (me->IsHostileTo(who))
- {
- float fAttackRadius = me->GetAttackDistance(who);
- if (me->IsWithinDistInMap(who, fAttackRadius) && me->IsWithinLOSInMap(who))
- {
- if (!me->GetVictim())
- {
- // Clear distracted state on combat
- if (me->HasUnitState(UNIT_STATE_DISTRACTED))
- {
- me->ClearUnitState(UNIT_STATE_DISTRACTED);
- me->GetMotionMaster()->Clear();
- }
-
- AttackStart(who);
- }
- else/* if (me->GetMap()->IsDungeon())*/
- {
- who->SetInCombatWith(me);
- me->AddThreat(who, 0.0f);
- }
- }
- }
+ CreatureAI::MoveInLineOfSight(who);
}
bool SmartAI::CanAIAttack(const Unit* /*who*/) const
{
- if (me->GetReactState() == REACT_PASSIVE)
- return false;
- return true;
+ return !(me->HasReactState(REACT_PASSIVE));
}
bool SmartAI::AssistPlayerInCombat(Unit* who)
@@ -728,12 +698,12 @@ void SmartAI::sGossipHello(Player* player)
GetScript()->ProcessEventsFor(SMART_EVENT_GOSSIP_HELLO, player);
}
-void SmartAI::sGossipSelect(Player* player, uint32 sender, uint32 action)
+void SmartAI::sGossipSelect(Player* player, uint32 menuId, uint32 gossipListId)
{
- GetScript()->ProcessEventsFor(SMART_EVENT_GOSSIP_SELECT, player, sender, action);
+ GetScript()->ProcessEventsFor(SMART_EVENT_GOSSIP_SELECT, player, menuId, gossipListId);
}
-void SmartAI::sGossipSelectCode(Player* /*player*/, uint32 /*sender*/, uint32 /*action*/, const char* /*code*/) { }
+void SmartAI::sGossipSelectCode(Player* /*player*/, uint32 /*menuId*/, uint32 /*gossipListId*/, const char* /*code*/) { }
void SmartAI::sQuestAccept(Player* player, Quest const* quest)
{
diff --git a/src/server/game/AI/SmartScripts/SmartAI.h b/src/server/game/AI/SmartScripts/SmartAI.h
index 1e287cd5b9e..ea03a821846 100644
--- a/src/server/game/AI/SmartScripts/SmartAI.h
+++ b/src/server/game/AI/SmartScripts/SmartAI.h
@@ -175,8 +175,8 @@ class SmartAI : public CreatureAI
void SetInvincibilityHpLevel(uint32 level) { mInvincibilityHpLevel = level; }
void sGossipHello(Player* player) override;
- void sGossipSelect(Player* player, uint32 sender, uint32 action) override;
- void sGossipSelectCode(Player* player, uint32 sender, uint32 action, const char* code) override;
+ void sGossipSelect(Player* player, uint32 menuId, uint32 gossipListId) override;
+ void sGossipSelectCode(Player* player, uint32 menuId, uint32 gossipListId, const char* code) override;
void sQuestAccept(Player* player, Quest const* quest) override;
//void sQuestSelect(Player* player, Quest const* quest) override;
void sQuestReward(Player* player, Quest const* quest, uint32 opt) override;
diff --git a/src/server/game/AuctionHouse/AuctionHouseMgr.cpp b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp
index 96a4fd42f5e..1809e306a45 100644
--- a/src/server/game/AuctionHouse/AuctionHouseMgr.cpp
+++ b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp
@@ -79,7 +79,7 @@ uint32 AuctionHouseMgr::GetAuctionDeposit(AuctionHouseEntry const* entry, uint32
uint32 MSV = pItem->GetTemplate()->SellPrice;
if (MSV <= 0)
- return AH_MINIMUM_DEPOSIT;
+ return AH_MINIMUM_DEPOSIT * sWorld->getRate(RATE_AUCTION_DEPOSIT);
float multiplier = CalculatePct(float(entry->depositPercent), 3);
uint32 timeHr = (((time / 60) / 60) / 12);
@@ -90,8 +90,8 @@ uint32 AuctionHouseMgr::GetAuctionDeposit(AuctionHouseEntry const* entry, uint32
TC_LOG_DEBUG("auctionHouse", "Multiplier: %f", multiplier);
TC_LOG_DEBUG("auctionHouse", "Deposit: %u", deposit);
- if (deposit < AH_MINIMUM_DEPOSIT)
- return AH_MINIMUM_DEPOSIT;
+ if (deposit < AH_MINIMUM_DEPOSIT * sWorld->getRate(RATE_AUCTION_DEPOSIT))
+ return AH_MINIMUM_DEPOSIT * sWorld->getRate(RATE_AUCTION_DEPOSIT);
else
return deposit;
}
diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp
index 7fe3628f893..c8a258ad386 100644
--- a/src/server/game/Entities/Creature/Creature.cpp
+++ b/src/server/game/Entities/Creature/Creature.cpp
@@ -138,7 +138,7 @@ bool ForcedDespawnDelayEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/)
Creature::Creature(bool isWorldObject): Unit(isWorldObject), MapObject(),
m_groupLootTimer(0), lootingGroupLowGUID(0), m_PlayerDamageReq(0),
m_lootRecipient(), m_lootRecipientGroup(0), _skinner(), _pickpocketLootRestore(0), m_corpseRemoveTime(0), m_respawnTime(0),
-m_respawnDelay(300), m_corpseDelay(60), m_respawnradius(0.0f), m_reactState(REACT_AGGRESSIVE),
+m_respawnDelay(300), m_corpseDelay(60), m_respawnradius(0.0f), m_combatPulseTime(0), m_combatPulseDelay(0), m_reactState(REACT_AGGRESSIVE),
m_defaultMovementType(IDLE_MOTION_TYPE), m_spawnId(0), m_equipmentId(0), m_originalEquipmentId(0), m_AlreadyCallAssistance(false),
m_AlreadySearchedAssistance(false), m_regenHealth(true), m_AI_locked(false), m_meleeDamageSchoolMask(SPELL_SCHOOL_MASK_NORMAL),
m_originalEntry(0), m_homePosition(), m_transportHomePosition(), m_creatureInfo(NULL), m_creatureData(NULL), m_waypointID(0), m_path_id(0), m_formation(NULL)
@@ -539,10 +539,44 @@ void Creature::Update(uint32 diff)
LastCharmerGUID.Clear();
}
+ // if periodic combat pulse is enabled and we are both in combat and in a dungeon, do this now
+ if (m_combatPulseDelay > 0 && IsInCombat() && GetMap()->IsDungeon())
+ {
+ if (diff > m_combatPulseTime)
+ m_combatPulseTime = 0;
+ else
+ m_combatPulseTime -= diff;
+
+ if (m_combatPulseTime == 0)
+ {
+ Map::PlayerList const &players = GetMap()->GetPlayers();
+ if (!players.isEmpty())
+ for (Map::PlayerList::const_iterator it = players.begin(); it != players.end(); ++it)
+ {
+ if (Player* player = it->GetSource())
+ {
+ if (player->IsGameMaster())
+ continue;
+
+ if (player->IsAlive() && this->IsHostileTo(player))
+ {
+ if (CanHaveThreatList())
+ AddThreat(player, 0.0f);
+ this->SetInCombatWith(player);
+ player->SetInCombatWith(this);
+ }
+ }
+ }
+
+ m_combatPulseTime = m_combatPulseDelay * IN_MILLISECONDS;
+ }
+ }
+
if (!IsInEvadeMode() && IsAIEnabled)
{
// do not allow the AI to be changed during update
m_AI_locked = true;
+
i_AI->UpdateAI(diff);
m_AI_locked = false;
}
diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h
index 3e381063772..79832b70f52 100644
--- a/src/server/game/Entities/Creature/Creature.h
+++ b/src/server/game/Entities/Creature/Creature.h
@@ -601,6 +601,14 @@ class Creature : public Unit, public GridObject<Creature>, public MapObject
float GetRespawnRadius() const { return m_respawnradius; }
void SetRespawnRadius(float dist) { m_respawnradius = dist; }
+ uint32 GetCombatPulseDelay() const { return m_combatPulseDelay; }
+ void SetCombatPulseDelay(uint32 delay) // (secs) interval at which the creature pulses the entire zone into combat (only works in dungeons)
+ {
+ m_combatPulseDelay = delay;
+ if (m_combatPulseTime == 0 || m_combatPulseTime > delay)
+ m_combatPulseTime = delay;
+ }
+
uint32 m_groupLootTimer; // (msecs)timer used for group loot
uint32 lootingGroupLowGUID; // used to find group which is looting corpse
@@ -686,6 +694,8 @@ class Creature : public Unit, public GridObject<Creature>, public MapObject
uint32 m_respawnDelay; // (secs) delay between corpse disappearance and respawning
uint32 m_corpseDelay; // (secs) delay between death and corpse disappearance
float m_respawnradius;
+ uint32 m_combatPulseTime; // (msecs) remaining time for next zone-in-combat pulse
+ uint32 m_combatPulseDelay; // (secs) how often the creature puts the entire zone in combat (only works in dungeons)
ReactStates m_reactState; // for AI, not charmInfo
void RegenerateMana();
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index e4452b84da4..1be3c2454b2 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -7610,6 +7610,20 @@ void Player::DuelComplete(DuelCompleteType type)
duel->opponent->SetGuidValue(PLAYER_DUEL_ARBITER, ObjectGuid::Empty);
duel->opponent->SetUInt32Value(PLAYER_DUEL_TEAM, 0);
+ if (sWorld->getBoolConfig(CONFIG_RESET_COOLDOWN_AFTER_DUEL) &&
+ type != DUEL_INTERRUPTED)
+ {
+ if (!HasCoolDownBeforeDuel())
+ RemoveArenaSpellCooldowns(true);
+ else
+ ChatHandler(GetSession()).PSendSysMessage(LANG_COOLDOWN_NOT_RESET_AFTER_DUEL);
+
+ if (!duel->opponent->HasCoolDownBeforeDuel())
+ duel->opponent->RemoveArenaSpellCooldowns(true);
+ else
+ ChatHandler(duel->opponent->GetSession()).PSendSysMessage(LANG_COOLDOWN_NOT_RESET_AFTER_DUEL);
+ }
+
delete duel->opponent->duel;
duel->opponent->duel = NULL;
delete duel;
diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h
index 2646d5e6ca0..0c1fdd8f03d 100644
--- a/src/server/game/Entities/Player/Player.h
+++ b/src/server/game/Entities/Player/Player.h
@@ -27,6 +27,7 @@
#include "PetDefines.h"
#include "QuestDef.h"
#include "SpellMgr.h"
+#include "SpellHistory.h"
#include "Unit.h"
#include <limits>
@@ -284,7 +285,7 @@ struct PvPInfo
struct DuelInfo
{
- DuelInfo() : initiator(NULL), opponent(NULL), startTimer(0), startTime(0), outOfBound(0), isMounted(false) { }
+ DuelInfo() : initiator(NULL), opponent(NULL), startTimer(0), startTime(0), outOfBound(0), isMounted(false), hasCoolDownBeforeDuel(false) { }
Player* initiator;
Player* opponent;
@@ -292,6 +293,7 @@ struct DuelInfo
time_t startTime;
time_t outOfBound;
bool isMounted;
+ bool hasCoolDownBeforeDuel;
};
struct Areas
@@ -1946,6 +1948,9 @@ class Player : public Unit, public GridObject<Player>
void SetHonorPoints(uint32 value);
void SetArenaPoints(uint32 value);
+ bool HasCoolDownBeforeDuel() const { return duel->hasCoolDownBeforeDuel; }
+ void UpdateHasCoolDownBeforeDuel() { duel->hasCoolDownBeforeDuel = GetSpellHistory()->GetArenaCooldownsSize() > 0; }
+
//End of PvP System
void SetDrunkValue(uint8 newDrunkValue, uint32 itemId = 0);
diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp
index f5bcd8f23e8..f085bab165a 100644
--- a/src/server/game/Entities/Unit/Unit.cpp
+++ b/src/server/game/Entities/Unit/Unit.cpp
@@ -16975,6 +16975,12 @@ void Unit::_EnterVehicle(Vehicle* vehicle, int8 seatId, AuraApplication const* a
}
}
+ // If vehicle flag for fixed position set (cannons), or if the following hardcoded units, then set state rooted
+ // 30236 | Argent Cannon
+ // 39759 | Tankbuster Cannon
+ if ((vehicle->GetVehicleInfo()->m_flags & VEHICLE_FLAG_FIXED_POSITION) || vehicle->GetBase()->GetEntry() == 30236 || vehicle->GetBase()->GetEntry() == 39759)
+ SetControlled(true, UNIT_STATE_ROOT);
+
ASSERT(!m_vehicle);
(void)vehicle->AddPassenger(this, seatId);
}
diff --git a/src/server/game/Entities/Vehicle/Vehicle.cpp b/src/server/game/Entities/Vehicle/Vehicle.cpp
index fcb74424eb1..982b931fd4f 100755
--- a/src/server/game/Entities/Vehicle/Vehicle.cpp
+++ b/src/server/game/Entities/Vehicle/Vehicle.cpp
@@ -192,6 +192,12 @@ void Vehicle::ApplyAllImmunities()
_me->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN, true);
}
+ // If vehicle flag for fixed position set (cannons), or if the following hardcoded units, then set state rooted
+ // 30236 | Argent Cannon
+ // 39759 | Tankbuster Cannon
+ if ((GetVehicleInfo()->m_flags & VEHICLE_FLAG_FIXED_POSITION) || GetBase()->GetEntry() == 30236 || GetBase()->GetEntry() == 39759)
+ _me->SetControlled(true, UNIT_STATE_ROOT);
+
// Different immunities for vehicles goes below
switch (GetVehicleInfo()->m_ID)
{
diff --git a/src/server/game/Entities/Vehicle/VehicleDefines.h b/src/server/game/Entities/Vehicle/VehicleDefines.h
index c00bc429f38..3726e9ddf8f 100644
--- a/src/server/game/Entities/Vehicle/VehicleDefines.h
+++ b/src/server/game/Entities/Vehicle/VehicleDefines.h
@@ -44,7 +44,8 @@ enum VehicleFlags
VEHICLE_FLAG_FULLSPEEDPITCHING = 0x00000020, // Sets MOVEFLAG2_FULLSPEEDPITCHING
VEHICLE_FLAG_CUSTOM_PITCH = 0x00000040, // If set use pitchMin and pitchMax from DBC, otherwise pitchMin = -pi/2, pitchMax = pi/2
VEHICLE_FLAG_ADJUST_AIM_ANGLE = 0x00000400, // Lua_IsVehicleAimAngleAdjustable
- VEHICLE_FLAG_ADJUST_AIM_POWER = 0x00000800 // Lua_IsVehicleAimPowerAdjustable
+ VEHICLE_FLAG_ADJUST_AIM_POWER = 0x00000800, // Lua_IsVehicleAimPowerAdjustable
+ VEHICLE_FLAG_FIXED_POSITION = 0x00200000 // Used for cannons, when they should be rooted
};
enum VehicleSpells
diff --git a/src/server/game/Handlers/DuelHandler.cpp b/src/server/game/Handlers/DuelHandler.cpp
index 2f39a91afbe..6e8ff5547d7 100644
--- a/src/server/game/Handlers/DuelHandler.cpp
+++ b/src/server/game/Handlers/DuelHandler.cpp
@@ -43,6 +43,9 @@ void WorldSession::HandleDuelAcceptedOpcode(WorldPacket& recvPacket)
TC_LOG_DEBUG("network", "Player 1 is: %u (%s)", player->GetGUID().GetCounter(), player->GetName().c_str());
TC_LOG_DEBUG("network", "Player 2 is: %u (%s)", plTarget->GetGUID().GetCounter(), plTarget->GetName().c_str());
+ player->UpdateHasCoolDownBeforeDuel();
+ plTarget->UpdateHasCoolDownBeforeDuel();
+
time_t now = time(NULL);
player->duel->startTimer = now;
plTarget->duel->startTimer = now;
diff --git a/src/server/game/Miscellaneous/Language.h b/src/server/game/Miscellaneous/Language.h
index a4ba9866064..d374c8c509a 100644
--- a/src/server/game/Miscellaneous/Language.h
+++ b/src/server/game/Miscellaneous/Language.h
@@ -1202,6 +1202,8 @@ enum TrinityStrings
LANG_BAN_ACCOUNT_YOUPERMBANNEDMESSAGE_WORLD = 11007,
LANG_NPCINFO_INHABIT_TYPE = 11008,
- LANG_NPCINFO_FLAGS_EXTRA = 11009
+ LANG_NPCINFO_FLAGS_EXTRA = 11009,
+
+ LANG_COOLDOWN_NOT_RESET_AFTER_DUEL = 11010
};
#endif
diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp
index 53da37bc730..45a816eda9b 100644
--- a/src/server/game/Server/WorldSession.cpp
+++ b/src/server/game/Server/WorldSession.cpp
@@ -840,13 +840,33 @@ void WorldSession::ReadMovementInfo(WorldPacket &data, MovementInfo* mi)
mi->RemoveMovementFlag((maskToRemove));
#endif
-
/*! This must be a packet spoofing attempt. MOVEMENTFLAG_ROOT sent from the client is not valid
in conjunction with any of the moving movement flags such as MOVEMENTFLAG_FORWARD.
It will freeze clients that receive this player's movement info.
*/
- REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_ROOT),
- MOVEMENTFLAG_ROOT);
+ // Only adjust movement flag removal for vehicles with the VEHICLE_FLAG_FIXED_POSITION flag, or the hard coded exceptions below:
+ // 30236 | Argent Cannon
+ // 39759 | Tankbuster Cannon
+ if (GetPlayer()->GetVehicleBase() && ((GetPlayer()->GetVehicle()->GetVehicleInfo()->m_flags & VEHICLE_FLAG_FIXED_POSITION) || GetPlayer()->GetVehicleBase()->GetEntry() == 30236 || GetPlayer()->GetVehicleBase()->GetEntry() == 39759))
+ {
+ // Actually players in rooted vehicles still send commands, don't clear root for these!
+ // Check specifically for the following conditions:
+ // MOVEMENTFLAG_ROOT + no other flags (0x800)
+ // MOVEMENTFLAG_ROOT + MOVEMENTFLAG_LEFT (0x810)
+ // MOVEMENTFLAG_ROOT + MOVEMENTFLAG_RIGHT (0x820)
+ // MOVEMENTFLAG_ROOT + MOVEMENTFLAG_PITCH_UP (0x840)
+ // MOVEMENTFLAG_ROOT + MOVEMENTFLAG_PITCH_DOWN (0x880)
+ // If none of these are true, clear the root
+ if (mi->HasMovementFlag(MOVEMENTFLAG_ROOT) && mi->HasMovementFlag(MOVEMENTFLAG_LEFT | MOVEMENTFLAG_RIGHT | MOVEMENTFLAG_PITCH_UP | MOVEMENTFLAG_PITCH_DOWN))
+ REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_ROOT),
+ MOVEMENTFLAG_MASK_MOVING);
+ }
+ else
+ {
+ // Only remove here for non vehicles
+ REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_ROOT),
+ MOVEMENTFLAG_ROOT);
+ }
//! Cannot hover without SPELL_AURA_HOVER
REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_HOVER) && !GetPlayer()->HasAuraType(SPELL_AURA_HOVER),
diff --git a/src/server/game/Spells/SpellHistory.cpp b/src/server/game/Spells/SpellHistory.cpp
index 09e3be690b1..208b4cf7ed9 100644
--- a/src/server/game/Spells/SpellHistory.cpp
+++ b/src/server/game/Spells/SpellHistory.cpp
@@ -637,6 +637,23 @@ void SpellHistory::BuildCooldownPacket(WorldPacket& data, uint8 flags, PacketCoo
}
}
+uint16 SpellHistory::GetArenaCooldownsSize()
+{
+ uint16 count = 0;
+
+ for (auto itr = _spellCooldowns.begin(); itr != _spellCooldowns.end();)
+ {
+ SpellInfo const* spellInfo = sSpellMgr->EnsureSpellInfo(itr->first);
+
+ if (spellInfo->RecoveryTime < 10 * MINUTE * IN_MILLISECONDS &&
+ spellInfo->CategoryRecoveryTime < 10 * MINUTE * IN_MILLISECONDS)
+ ++count;
+ ++itr;
+ }
+
+ return count;
+}
+
template void SpellHistory::LoadFromDB<Player>(PreparedQueryResult cooldownsResult);
template void SpellHistory::LoadFromDB<Pet>(PreparedQueryResult cooldownsResult);
template void SpellHistory::SaveToDB<Player>(SQLTransaction& trans);
diff --git a/src/server/game/Spells/SpellHistory.h b/src/server/game/Spells/SpellHistory.h
index f1533d57aef..4d0642d644e 100644
--- a/src/server/game/Spells/SpellHistory.h
+++ b/src/server/game/Spells/SpellHistory.h
@@ -117,6 +117,7 @@ public:
void BuildCooldownPacket(WorldPacket& data, uint8 flags, uint32 spellId, uint32 cooldown) const;
CooldownStorageType::size_type GetCooldownsSizeForPacket() const { return _spellCooldowns.size(); }
+ uint16 GetArenaCooldownsSize();
private:
Player* GetPlayerOwner() const;
diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp
index f27f9220aaa..55d0ad0b4dc 100644
--- a/src/server/game/Spells/SpellMgr.cpp
+++ b/src/server/game/Spells/SpellMgr.cpp
@@ -3105,11 +3105,13 @@ void SpellMgr::LoadSpellInfoCorrections()
case 28796: // Poison Bolt Volly - Faerlina
spellInfo->MaxAffectedTargets = 5;
break;
+ case 54835: // Curse of the Plaguebringer - Noth (H)
+ spellInfo->MaxAffectedTargets = 8;
+ break;
case 40827: // Sinful Beam
case 40859: // Sinister Beam
case 40860: // Vile Beam
case 40861: // Wicked Beam
- case 54835: // Curse of the Plaguebringer - Noth (H)
case 54098: // Poison Bolt Volly - Faerlina (H)
spellInfo->MaxAffectedTargets = 10;
break;
diff --git a/src/server/game/Tickets/TicketMgr.cpp b/src/server/game/Tickets/TicketMgr.cpp
index a51c9fb16d2..dbf66c6bd71 100644
--- a/src/server/game/Tickets/TicketMgr.cpp
+++ b/src/server/game/Tickets/TicketMgr.cpp
@@ -75,8 +75,8 @@ bool GmTicket::LoadFromDB(Field* fields)
void GmTicket::SaveToDB(SQLTransaction& trans) const
{
- // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
- // ticketId, guid, name, message, createTime, mapId, posX, posY, posZ, lastModifiedTime, closedBy, assignedTo, comment, completed, escalated, viewed
+ // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
+ // id, playerGuid, name, description, createTime, mapId, posX, posY, posZ, lastModifiedTime, closedBy, assignedTo, comment, response, completed, escalated, viewed, needMoreHelp, resolvedBy
uint8 index = 0;
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_GM_TICKET);
stmt->setUInt32( index, _id);
@@ -97,6 +97,7 @@ void GmTicket::SaveToDB(SQLTransaction& trans) const
stmt->setUInt8 (++index, uint8(_escalatedStatus));
stmt->setBool (++index, _viewed);
stmt->setBool (++index, _needMoreHelp);
+ stmt->setInt32 (++index, int32(_resolvedBy.GetCounter()));
CharacterDatabase.ExecuteOrAppend(trans, stmt);
}
@@ -361,6 +362,19 @@ void TicketMgr::CloseTicket(uint32 ticketId, ObjectGuid source)
}
}
+void TicketMgr::ResolveAndCloseTicket(uint32 ticketId, ObjectGuid source)
+{
+ if (GmTicket* ticket = GetTicket(ticketId))
+ {
+ SQLTransaction trans = SQLTransaction(nullptr);
+ ticket->SetClosedBy(source);
+ ticket->SetResolvedBy(source);
+ if (source)
+ --_openTicketCount;
+ ticket->SaveToDB(trans);
+ }
+}
+
void TicketMgr::RemoveTicket(uint32 ticketId)
{
if (GmTicket* ticket = GetTicket(ticketId))
diff --git a/src/server/game/Tickets/TicketMgr.h b/src/server/game/Tickets/TicketMgr.h
index e9892c22edc..f7d3178978f 100644
--- a/src/server/game/Tickets/TicketMgr.h
+++ b/src/server/game/Tickets/TicketMgr.h
@@ -119,6 +119,7 @@ public:
_escalatedStatus = TICKET_ASSIGNED;
}
void SetClosedBy(ObjectGuid value) { _closedBy = value; }
+ void SetResolvedBy(ObjectGuid value) { _resolvedBy = value; }
void SetCompleted() { _completed = true; }
void SetMessage(std::string const& message)
{
@@ -158,7 +159,8 @@ private:
std::string _message;
uint64 _createTime;
uint64 _lastModifiedTime;
- ObjectGuid _closedBy; // 0 = Open, -1 = Console, playerGuid = player abandoned ticket, other = GM who closed it.
+ ObjectGuid _closedBy; // 0 = Open, -1 = Console, playerGuid = player abandoned ticket or read the GM response message, other = GM who closed it.
+ ObjectGuid _resolvedBy; // 0 = Open, -1 = Resolved by Console, GM who resolved it by closing or completing the ticket.
ObjectGuid _assignedTo;
std::string _comment;
bool _completed;
@@ -216,6 +218,7 @@ public:
void AddTicket(GmTicket* ticket);
void CloseTicket(uint32 ticketId, ObjectGuid source);
+ void ResolveAndCloseTicket(uint32 ticketId, ObjectGuid source); // used when GM resolves a ticket by simply closing it
void RemoveTicket(uint32 ticketId);
bool GetStatus() const { return _status; }
diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp
index 2e61aecfb5f..85e79168b71 100644
--- a/src/server/game/World/World.cpp
+++ b/src/server/game/World/World.cpp
@@ -1190,6 +1190,7 @@ void World::LoadConfigSettings(bool reload)
m_int_configs[CONFIG_MAX_WHO] = sConfigMgr->GetIntDefault("MaxWhoListReturns", 49);
m_bool_configs[CONFIG_START_ALL_SPELLS] = sConfigMgr->GetBoolDefault("PlayerStart.AllSpells", false);
m_int_configs[CONFIG_HONOR_AFTER_DUEL] = sConfigMgr->GetIntDefault("HonorPointsAfterDuel", 0);
+ m_bool_configs[CONFIG_RESET_COOLDOWN_AFTER_DUEL] = sConfigMgr->GetBoolDefault("ResetCoolDownAfterDuel", 0);
m_bool_configs[CONFIG_START_ALL_EXPLORED] = sConfigMgr->GetBoolDefault("PlayerStart.MapsExplored", false);
m_bool_configs[CONFIG_START_ALL_REP] = sConfigMgr->GetBoolDefault("PlayerStart.AllReputation", false);
m_bool_configs[CONFIG_ALWAYS_MAXSKILL] = sConfigMgr->GetBoolDefault("AlwaysMaxWeaponSkill", false);
diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h
index 256b856cf73..1f71975cde2 100644
--- a/src/server/game/World/World.h
+++ b/src/server/game/World/World.h
@@ -161,6 +161,7 @@ enum WorldBoolConfigs
CONFIG_ALLOW_TRACK_BOTH_RESOURCES,
CONFIG_CALCULATE_CREATURE_ZONE_AREA_DATA,
CONFIG_CALCULATE_GAMEOBJECT_ZONE_AREA_DATA,
+ CONFIG_RESET_COOLDOWN_AFTER_DUEL,
BOOL_CONFIG_VALUE_COUNT
};
diff --git a/src/server/scripts/Commands/cs_ticket.cpp b/src/server/scripts/Commands/cs_ticket.cpp
index b609dfe18dd..f7634381fc9 100644
--- a/src/server/scripts/Commands/cs_ticket.cpp
+++ b/src/server/scripts/Commands/cs_ticket.cpp
@@ -153,7 +153,7 @@ public:
return true;
}
- sTicketMgr->CloseTicket(ticket->GetId(), player ? player->GetGUID() : ObjectGuid(uint64(-1)));
+ sTicketMgr->ResolveAndCloseTicket(ticket->GetId(), player ? player->GetGUID() : ObjectGuid(uint64(-1)));
sTicketMgr->UpdateLastChange();
std::string msg = ticket->FormatMessageString(*handler, player ? player->GetName().c_str() : "Console", NULL, NULL, NULL, NULL);
@@ -248,7 +248,10 @@ public:
if (Player* player = ticket->GetPlayer())
ticket->SendResponse(player->GetSession());
+ Player* gm = handler->GetSession() ? handler->GetSession()->GetPlayer() : nullptr;
+
SQLTransaction trans = SQLTransaction(NULL);
+ ticket->SetResolvedBy(gm ? gm->GetGUID() : ObjectGuid(uint64(-1)));
ticket->SetCompleted();
ticket->SaveToDB(trans);
diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_nefarian.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_nefarian.cpp
index 51e1bf67f16..a89571e8197 100644
--- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_nefarian.cpp
+++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_nefarian.cpp
@@ -367,9 +367,9 @@ public:
}
}
- void sGossipSelect(Player* player, uint32 sender, uint32 action) override
+ void sGossipSelect(Player* player, uint32 menuId, uint32 gossipListId) override
{
- if (sender == GOSSIP_ID && action == GOSSIP_OPTION_ID)
+ if (menuId == GOSSIP_ID && gossipListId == GOSSIP_OPTION_ID)
{
player->CLOSE_GOSSIP_MENU();
Talk(SAY_GAMESBEGIN_1);
diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_vaelastrasz.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_vaelastrasz.cpp
index 636554a0ae4..76ab4736275 100644
--- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_vaelastrasz.cpp
+++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_vaelastrasz.cpp
@@ -221,9 +221,9 @@ public:
DoMeleeAttackIfReady();
}
- void sGossipSelect(Player* player, uint32 sender, uint32 action) override
+ void sGossipSelect(Player* player, uint32 menuId, uint32 gossipListId) override
{
- if (sender == GOSSIP_ID && action == 0)
+ if (menuId == GOSSIP_ID && gossipListId == 0)
{
player->CLOSE_GOSSIP_MENU();
BeginSpeech(player);
diff --git a/src/server/scripts/EasternKingdoms/Karazhan/karazhan.cpp b/src/server/scripts/EasternKingdoms/Karazhan/karazhan.cpp
index f3c59654295..87945ccf916 100644
--- a/src/server/scripts/EasternKingdoms/Karazhan/karazhan.cpp
+++ b/src/server/scripts/EasternKingdoms/Karazhan/karazhan.cpp
@@ -25,7 +25,6 @@ EndScriptData */
/* ContentData
npc_barnes
-npc_berthold
npc_image_of_medivh
EndContentData */
@@ -416,41 +415,6 @@ public:
};
/*###
-# npc_berthold
-####*/
-
-#define GOSSIP_ITEM_TELEPORT "Teleport me to the Guardian's Library"
-
-class npc_berthold : public CreatureScript
-{
-public:
- npc_berthold() : CreatureScript("npc_berthold") { }
-
- bool OnGossipSelect(Player* player, Creature* /*creature*/, uint32 /*sender*/, uint32 action) override
- {
- player->PlayerTalkClass->ClearMenus();
- if (action == GOSSIP_ACTION_INFO_DEF + 1)
- player->CastSpell(player, SPELL_TELEPORT, true);
-
- player->CLOSE_GOSSIP_MENU();
- return true;
- }
-
- bool OnGossipHello(Player* player, Creature* creature) override
- {
- if (InstanceScript* instance = creature->GetInstanceScript())
- {
- // Check if Shade of Aran event is done
- if (instance->GetData(TYPE_ARAN) == DONE)
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_TELEPORT, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
- }
-
- player->SEND_GOSSIP_MENU(player->GetGossipTextId(creature), creature->GetGUID());
- return true;
- }
-};
-
-/*###
# npc_image_of_medivh
####*/
@@ -668,6 +632,5 @@ public:
void AddSC_karazhan()
{
new npc_barnes();
- new npc_berthold();
new npc_image_of_medivh();
}
diff --git a/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter1.cpp b/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter1.cpp
index b37d505d766..9747d31952b 100644
--- a/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter1.cpp
+++ b/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter1.cpp
@@ -729,13 +729,19 @@ class npc_dark_rider_of_acherus : public CreatureScript
## npc_salanar_the_horseman
######*/
-enum Spells_Salanar
+enum SalanarTheHorseman
{
- SPELL_REALM_OF_SHADOWS = 52693,
+ GOSSIP_SALANAR_MENU = 9739,
+ GOSSIP_SALANAR_OPTION = 0,
+ SALANAR_SAY = 0,
+ QUEST_INTO_REALM_OF_SHADOWS = 12687,
+ NPC_DARK_RIDER_OF_ACHERUS = 28654,
+ NPC_SALANAR_IN_REALM_OF_SHADOWS = 28788,
SPELL_EFFECT_STOLEN_HORSE = 52263,
SPELL_DELIVER_STOLEN_HORSE = 52264,
SPELL_CALL_DARK_RIDER = 52266,
- SPELL_EFFECT_OVERTAKE = 52349
+ SPELL_EFFECT_OVERTAKE = 52349,
+ SPELL_REALM_OF_SHADOWS = 52693
};
class npc_salanar_the_horseman : public CreatureScript
@@ -743,15 +749,19 @@ class npc_salanar_the_horseman : public CreatureScript
public:
npc_salanar_the_horseman() : CreatureScript("npc_salanar_the_horseman") { }
- CreatureAI* GetAI(Creature* creature) const override
- {
- return new npc_salanar_the_horsemanAI(creature);
- }
-
struct npc_salanar_the_horsemanAI : public ScriptedAI
{
npc_salanar_the_horsemanAI(Creature* creature) : ScriptedAI(creature) { }
+ void sGossipSelect(Player* player, uint32 menuId, uint32 gossipListId) override
+ {
+ if (menuId == GOSSIP_SALANAR_MENU && gossipListId == GOSSIP_SALANAR_OPTION)
+ {
+ player->CastSpell(player, SPELL_REALM_OF_SHADOWS, true);
+ player->PlayerTalkClass->SendCloseGossip();
+ }
+ }
+
void SpellHit(Unit* caster, const SpellInfo* spell) override
{
if (spell->Id == SPELL_DELIVER_STOLEN_HORSE)
@@ -766,7 +776,7 @@ public:
caster->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPELLCLICK);
caster->setFaction(35);
DoCast(caster, SPELL_CALL_DARK_RIDER, true);
- if (Creature* Dark_Rider = me->FindNearestCreature(28654, 15))
+ if (Creature* Dark_Rider = me->FindNearestCreature(NPC_DARK_RIDER_OF_ACHERUS, 15))
ENSURE_AI(npc_dark_rider_of_acherus::npc_dark_rider_of_acherusAI, Dark_Rider->AI())->InitDespawnHorse(caster);
}
}
@@ -784,10 +794,11 @@ public:
{
if (Player* player = charmer->ToPlayer())
{
- // for quest Into the Realm of Shadows(12687)
- if (me->GetEntry() == 28788 && player->GetQuestStatus(12687) == QUEST_STATUS_INCOMPLETE)
+ // for quest Into the Realm of Shadows(QUEST_INTO_REALM_OF_SHADOWS)
+ if (me->GetEntry() == NPC_SALANAR_IN_REALM_OF_SHADOWS && player->GetQuestStatus(QUEST_INTO_REALM_OF_SHADOWS) == QUEST_STATUS_INCOMPLETE)
{
- player->GroupEventHappens(12687, me);
+ player->GroupEventHappens(QUEST_INTO_REALM_OF_SHADOWS, me);
+ Talk(SALANAR_SAY);
charmer->RemoveAurasDueToSpell(SPELL_EFFECT_OVERTAKE);
if (Creature* creature = who->ToCreature())
{
@@ -796,14 +807,17 @@ public:
}
}
- if (player->HasAura(SPELL_REALM_OF_SHADOWS))
- player->RemoveAurasDueToSpell(SPELL_REALM_OF_SHADOWS);
+ player->RemoveAurasDueToSpell(SPELL_REALM_OF_SHADOWS);
}
}
}
}
};
+ CreatureAI* GetAI(Creature* creature) const override
+ {
+ return new npc_salanar_the_horsemanAI(creature);
+ }
};
/*######
diff --git a/src/server/scripts/EasternKingdoms/ZulAman/zulaman.cpp b/src/server/scripts/EasternKingdoms/ZulAman/zulaman.cpp
index 3b52e6775b2..1927b0f9829 100644
--- a/src/server/scripts/EasternKingdoms/ZulAman/zulaman.cpp
+++ b/src/server/scripts/EasternKingdoms/ZulAman/zulaman.cpp
@@ -268,9 +268,9 @@ class npc_harrison_jones : public CreatureScript
void EnterCombat(Unit* /*who*/) override { }
- void sGossipSelect(Player* player, uint32 sender, uint32 action) override
+ void sGossipSelect(Player* player, uint32 menuId, uint32 gossipListId) override
{
- if (me->GetCreatureTemplate()->GossipMenuId == sender && !action)
+ if (me->GetCreatureTemplate()->GossipMenuId == menuId && !gossipListId)
{
player->CLOSE_GOSSIP_MENU();
me->SetFacingToObject(player);
diff --git a/src/server/scripts/Kalimdor/BlackfathomDeeps/blackfathom_deeps.cpp b/src/server/scripts/Kalimdor/BlackfathomDeeps/blackfathom_deeps.cpp
index 8d9406adee1..ab7509c7a2b 100644
--- a/src/server/scripts/Kalimdor/BlackfathomDeeps/blackfathom_deeps.cpp
+++ b/src/server/scripts/Kalimdor/BlackfathomDeeps/blackfathom_deeps.cpp
@@ -226,7 +226,7 @@ public:
}
}
- void sGossipSelect(Player* player, uint32 /*sender*/, uint32 /*action*/) override
+ void sGossipSelect(Player* player, uint32 /*menuId*/, uint32 /*gossipListId*/) override
{
DoCast(player, SPELL_TELEPORT_DARNASSUS);
}
diff --git a/src/server/scripts/Kalimdor/zone_azshara.cpp b/src/server/scripts/Kalimdor/zone_azshara.cpp
index 6727aa69552..d6aa943123b 100644
--- a/src/server/scripts/Kalimdor/zone_azshara.cpp
+++ b/src/server/scripts/Kalimdor/zone_azshara.cpp
@@ -345,7 +345,7 @@ public:
}
}
- void sGossipSelect(Player* player, uint32 /*sender*/, uint32 /*action*/) override
+ void sGossipSelect(Player* player, uint32 /*menuId*/, uint32 /*gossipListId*/) override
{
player->CLOSE_GOSSIP_MENU();
me->CastSpell(player, SPELL_GIVE_SOUTHFURY_MOONSTONE, true);
diff --git a/src/server/scripts/Kalimdor/zone_azuremyst_isle.cpp b/src/server/scripts/Kalimdor/zone_azuremyst_isle.cpp
index 388834d1eff..52e32d3d37c 100644
--- a/src/server/scripts/Kalimdor/zone_azuremyst_isle.cpp
+++ b/src/server/scripts/Kalimdor/zone_azuremyst_isle.cpp
@@ -230,7 +230,7 @@ public:
Talk(ATTACK_YELL, who);
}
- void sGossipSelect(Player* player, uint32 /*sender*/, uint32 /*action*/) override
+ void sGossipSelect(Player* player, uint32 /*menuId*/, uint32 /*gossipListId*/) override
{
player->CLOSE_GOSSIP_MENU();
me->setFaction(FACTION_HOSTILE);
diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_deathbringer_saurfang.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_deathbringer_saurfang.cpp
index 943be27943d..365e0a1588d 100644
--- a/src/server/scripts/Northrend/IcecrownCitadel/boss_deathbringer_saurfang.cpp
+++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_deathbringer_saurfang.cpp
@@ -203,6 +203,9 @@ enum Misc
{
DATA_MADE_A_MESS = 45374613, // 4537, 4613 are achievement IDs
FACTION_SCOURGE = 974,
+
+ GOSSIP_MENU_MURADIN_BRONZEBEARD = 10934,
+ GOSSIP_MENU_HIGH_OVERLORD_SAURFANG = 10952
};
enum MovePoints
@@ -634,6 +637,15 @@ class npc_high_overlord_saurfang_icc : public CreatureScript
_events.Reset();
}
+ void sGossipSelect(Player* player, uint32 menuId, uint32 /*gossipListId*/) override
+ {
+ if (menuId == GOSSIP_MENU_HIGH_OVERLORD_SAURFANG)
+ {
+ player->PlayerTalkClass->SendCloseGossip();
+ DoAction(ACTION_START_EVENT);
+ }
+ }
+
void DoAction(int32 action) override
{
switch (action)
@@ -798,28 +810,6 @@ class npc_high_overlord_saurfang_icc : public CreatureScript
std::list<Creature*> _guardList;
};
- bool OnGossipHello(Player* player, Creature* creature) override
- {
- InstanceScript* instance = creature->GetInstanceScript();
- if (instance && instance->GetBossState(DATA_DEATHBRINGER_SAURFANG) != DONE)
- {
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, "We are ready to go, High Overlord. The Lich King must fall!", 631, -ACTION_START_EVENT);
- player->SEND_GOSSIP_MENU(DEFAULT_GOSSIP_MESSAGE, creature->GetGUID());
- }
-
- return true;
- }
-
- bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override
- {
- player->PlayerTalkClass->ClearMenus();
- player->CLOSE_GOSSIP_MENU();
- if (action == -ACTION_START_EVENT)
- creature->AI()->DoAction(ACTION_START_EVENT);
-
- return true;
- }
-
CreatureAI* GetAI(Creature* creature) const override
{
return GetIcecrownCitadelAI<npc_high_overlord_saurfangAI>(creature);
@@ -843,6 +833,15 @@ class npc_muradin_bronzebeard_icc : public CreatureScript
_events.Reset();
}
+ void sGossipSelect(Player* player, uint32 menuId, uint32 /*gossipListId*/) override
+ {
+ if (menuId == GOSSIP_MENU_MURADIN_BRONZEBEARD)
+ {
+ player->PlayerTalkClass->SendCloseGossip();
+ DoAction(ACTION_START_EVENT);
+ }
+ }
+
void DoAction(int32 action) override
{
switch (action)
@@ -946,28 +945,6 @@ class npc_muradin_bronzebeard_icc : public CreatureScript
std::list<Creature*> _guardList;
};
- bool OnGossipHello(Player* player, Creature* creature) override
- {
- InstanceScript* instance = creature->GetInstanceScript();
- if (instance && instance->GetBossState(DATA_DEATHBRINGER_SAURFANG) != DONE)
- {
- player->ADD_GOSSIP_ITEM(0, "Let it begin...", 631, -ACTION_START_EVENT + 1);
- player->SEND_GOSSIP_MENU(DEFAULT_GOSSIP_MESSAGE, creature->GetGUID());
- }
-
- return true;
- }
-
- bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override
- {
- player->PlayerTalkClass->ClearMenus();
- player->CLOSE_GOSSIP_MENU();
- if (action == -ACTION_START_EVENT + 1)
- creature->AI()->DoAction(ACTION_START_EVENT);
-
- return true;
- }
-
CreatureAI* GetAI(Creature* creature) const override
{
return GetIcecrownCitadelAI<npc_muradin_bronzebeard_iccAI>(creature);
diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp
index 78730b535aa..babd444288f 100644
--- a/src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp
+++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp
@@ -961,7 +961,7 @@ class npc_high_overlord_saurfang_igb : public CreatureScript
}
}
- void sGossipSelect(Player* /*player*/, uint32 /*sender*/, uint32 /*action*/) override
+ void sGossipSelect(Player* /*player*/, uint32 /*menuId*/, uint32 /*gossipListId*/) override
{
me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP);
me->GetTransport()->EnableMovement(true);
@@ -1229,7 +1229,7 @@ class npc_muradin_bronzebeard_igb : public CreatureScript
}
}
- void sGossipSelect(Player* /*player*/, uint32 /*sender*/, uint32 /*action*/) override
+ void sGossipSelect(Player* /*player*/, uint32 /*menuId*/, uint32 /*gossipListId*/) override
{
me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP);
me->GetTransport()->EnableMovement(true);
@@ -1394,7 +1394,7 @@ class npc_zafod_boombox : public CreatureScript
me->SetReactState(REACT_PASSIVE);
}
- void sGossipSelect(Player* player, uint32 /*sender*/, uint32 /*action*/) override
+ void sGossipSelect(Player* player, uint32 /*menuId*/, uint32 /*gossipListId*/) override
{
player->AddItem(ITEM_GOBLIN_ROCKET_PACK, 1);
player->PlayerTalkClass->SendCloseGossip();
diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp
index 6c512546a24..4fadc0bf8fa 100644
--- a/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp
+++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp
@@ -1199,9 +1199,9 @@ class npc_tirion_fordring_tft : public CreatureScript
SetEquipmentSlots(true); // remove glow on ashbringer
}
- void sGossipSelect(Player* /*player*/, uint32 sender, uint32 action) override
+ void sGossipSelect(Player* /*player*/, uint32 menuId, uint32 gossipListId) override
{
- if (me->GetCreatureTemplate()->GossipMenuId == sender && !action)
+ if (me->GetCreatureTemplate()->GossipMenuId == menuId && !gossipListId)
{
_events.SetPhase(PHASE_INTRO);
me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP);
diff --git a/src/server/scripts/Northrend/Naxxramas/boss_noth.cpp b/src/server/scripts/Northrend/Naxxramas/boss_noth.cpp
index f5e5b287571..810d446111a 100644
--- a/src/server/scripts/Northrend/Naxxramas/boss_noth.cpp
+++ b/src/server/scripts/Northrend/Naxxramas/boss_noth.cpp
@@ -19,51 +19,58 @@
#include "ScriptedCreature.h"
#include "naxxramas.h"
-enum Noth
+enum Phases
{
- SAY_AGGRO = 0,
- SAY_SUMMON = 1,
- SAY_SLAY = 2,
- SAY_DEATH = 3,
-
- SOUND_DEATH = 8848,
-
- SPELL_CURSE_PLAGUEBRINGER = 29213, // 25-man: 54835
- SPELL_CRIPPLE = 29212, // 25-man: 54814
- SPELL_TELEPORT = 29216,
-
- NPC_WARRIOR = 16984,
- NPC_CHAMPION = 16983,
- NPC_GUARDIAN = 16981
+ PHASE_NONE,
+ PHASE_GROUND,
+ PHASE_BALCONY
};
-#define SPELL_BLINK RAND(29208, 29209, 29210, 29211)
+enum Events
+{
+ EVENT_CURSE = 1, // curse of the plaguebringer
+ EVENT_BLINK, // blink (25m only)
+ EVENT_WARRIOR, // summon warriors during ground phase
+ EVENT_BALCONY, // become untargetable and begin balcony phase
+ EVENT_BALCONY_TELEPORT, // actually teleport to balcony, this is slightly delayed
+ EVENT_WAVE, // spawn wave during balcony phase
+ EVENT_GROUND, // end balcony phase and teleport to ground
+ EVENT_GROUND_ATTACKABLE // become attackable and aggressive again at start of ground phase, once again slightly delayed to prevent motionmaster weirdness
+};
-// Teleport position of Noth on his balcony
-Position const Teleport = { 2631.370f, -3529.680f, 274.040f, 6.277f };
+enum Talk
+{
+ SAY_AGGRO = 0,
+ SAY_SUMMON = 1,
+ SAY_SLAY = 2,
+ SAY_DEATH = 3,
-#define MAX_SUMMON_POS 5
+ EMOTE_SUMMON = 4, // ground phase
+ EMOTE_SUMMON_WAVE = 5, // balcony phase
+ EMOTE_TELEPORT_1 = 6, // ground to balcony
+ EMOTE_TELEPORT_2 = 7 // balcony to ground
+};
-Position const SummonPos[MAX_SUMMON_POS] =
+enum Spells
{
- { 2728.12f, -3544.43f, 261.91f, 6.04f },
- { 2729.05f, -3544.47f, 261.91f, 5.58f },
- { 2728.24f, -3465.08f, 264.20f, 3.56f },
- { 2704.11f, -3456.81f, 265.53f, 4.51f },
- { 2663.56f, -3464.43f, 262.66f, 5.20f }
+ SPELL_CURSE = 29213, // 25-man: 54835
+ SPELL_CRIPPLE = 29212, // 25-man: 54814
+
+ SPELL_TELEPORT = 29216, // ground to balcony
+ SPELL_TELEPORT_BACK = 29231 // balcony to ground
};
-enum Events
+enum Adds
{
- EVENT_NONE,
- EVENT_BERSERK,
- EVENT_CURSE,
- EVENT_BLINK,
- EVENT_WARRIOR,
- EVENT_BALCONY,
- EVENT_WAVE,
- EVENT_GROUND
+ N_WARRIOR_SPELLS = 3,
+ N_CHAMPION_SPELLS = 6,
+ N_GUARDIAN_SPELLS = 3
};
+const uint32 SummonWarriorSpells[N_WARRIOR_SPELLS] = { 29247, 29248, 29249 };
+const uint32 SummonChampionSpells[N_CHAMPION_SPELLS] = { 29238, 29255, 29257, 29258, 29262, 29267 };
+const uint32 SummonGuardianSpells[N_GUARDIAN_SPELLS] = { 29239, 29256, 29268 };
+
+#define SPELL_BLINK RAND(29208, 29209, 29210, 29211)
class boss_noth : public CreatureScript
{
@@ -72,16 +79,38 @@ public:
struct boss_nothAI : public BossAI
{
- boss_nothAI(Creature* creature) : BossAI(creature, BOSS_NOTH)
+ boss_nothAI(Creature* creature) : BossAI(creature, BOSS_NOTH), balconyCount(0), justBlinked(false)
{
- balconyCount = 0;
- waveCount = 0;
+ std::copy(SummonWarriorSpells, SummonWarriorSpells + N_WARRIOR_SPELLS, _SummonWarriorSpells);
+ std::copy(SummonChampionSpells, SummonChampionSpells + N_CHAMPION_SPELLS, _SummonChampionSpells);
+ std::copy(SummonGuardianSpells, SummonGuardianSpells + N_GUARDIAN_SPELLS, _SummonGuardianSpells);
+
+ events.SetPhase(PHASE_NONE);
+ }
+
+ void EnterEvadeMode() override
+ {
+ Reset(); // teleport back first
+ _EnterEvadeMode();
}
void Reset() override
{
- me->SetReactState(REACT_AGGRESSIVE);
- me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
+ if (!me->IsAlive())
+ return;
+
+ // in case we reset during balcony phase
+ if (events.IsInPhase(PHASE_BALCONY))
+ {
+ DoCastAOE(SPELL_TELEPORT_BACK);
+ me->SetReactState(REACT_AGGRESSIVE);
+ me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_NOT_SELECTABLE);
+ }
+
+ balconyCount = 0;
+ events.SetPhase(PHASE_NONE);
+ justBlinked = false;
+
_Reset();
}
@@ -89,31 +118,44 @@ public:
{
_EnterCombat();
Talk(SAY_AGGRO);
- balconyCount = 0;
EnterPhaseGround();
}
void EnterPhaseGround()
{
- me->SetReactState(REACT_AGGRESSIVE);
- me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
+ events.SetPhase(PHASE_GROUND);
+
DoZoneInCombat();
if (me->getThreatManager().isThreatListEmpty())
- EnterEvadeMode();
+ Reset();
else
{
- events.ScheduleEvent(EVENT_BALCONY, 110000);
- events.ScheduleEvent(EVENT_CURSE, 10000 + rand32() % 15000);
- events.ScheduleEvent(EVENT_WARRIOR, 30000);
+ uint8 secondsGround;
+ switch (balconyCount)
+ {
+ case 0:
+ secondsGround = 90;
+ break;
+ case 1:
+ secondsGround = 110;
+ break;
+ case 2:
+ default:
+ secondsGround = 180;
+ }
+ events.ScheduleEvent(EVENT_GROUND_ATTACKABLE, 2 * IN_MILLISECONDS, 0, PHASE_GROUND);
+ events.ScheduleEvent(EVENT_BALCONY, secondsGround * IN_MILLISECONDS, 0, PHASE_GROUND);
+ events.ScheduleEvent(EVENT_CURSE, urand(10,25) * IN_MILLISECONDS, 0, PHASE_GROUND);
+ events.ScheduleEvent(EVENT_WARRIOR, urand(20,30) * IN_MILLISECONDS, 0, PHASE_GROUND);
if (GetDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL)
- events.ScheduleEvent(EVENT_BLINK, urand(20000, 40000));
+ events.ScheduleEvent(EVENT_BLINK, urand(20,30) * IN_MILLISECONDS, 0, PHASE_GROUND);
}
}
- void KilledUnit(Unit* /*victim*/) override
+ void KilledUnit(Unit* victim) override
{
- if (!(rand32() % 5))
+ if(victim->GetTypeId() == TYPEID_PLAYER)
Talk(SAY_SLAY);
}
@@ -121,7 +163,7 @@ public:
{
summons.Summon(summon);
summon->setActive(true);
- summon->AI()->DoZoneInCombat();
+ summon->AI()->DoZoneInCombat(nullptr, 250.0f); // specify range to cover entire room - default 50yd is not enough
}
void JustDied(Unit* /*killer*/) override
@@ -130,10 +172,35 @@ public:
Talk(SAY_DEATH);
}
- void SummonUndead(uint32 entry, uint32 num)
+ void DamageTaken(Unit* /*who*/, uint32& damage) // prevent noth from somehow dying in the balcony phase
{
- for (uint32 i = 0; i < num; ++i)
- me->SummonCreature(entry, SummonPos[rand32() % MAX_SUMMON_POS], TEMPSUMMON_CORPSE_DESPAWN, 60000);
+ if (!events.IsInPhase(PHASE_BALCONY))
+ return;
+ if (damage < me->GetHealth())
+ return;
+
+ me->SetHealth(1u);
+ damage = 0u;
+ }
+
+ void HandleSummon(uint32* spellsList, const uint8 nSpells, uint8 num)
+ { // this ensures we do not spawn two mobs using the same spell (<=> in the same position) if we can help it
+ while (num)
+ for (uint8 it = 0; it < nSpells && num; ++it)
+ {
+ num--;
+ uint8 selected = urand(it, nSpells - 1);
+ DoCastAOE(spellsList[selected]);
+ if (selected != it) // shuffle the selected into the part of the array that is no longer being searched
+ std::swap(spellsList[selected], spellsList[it]);
+ }
+ }
+
+ void CastSummon(uint8 nWarrior, uint8 nChampion, uint8 nGuardian)
+ {
+ HandleSummon(_SummonWarriorSpells, N_WARRIOR_SPELLS, nWarrior);
+ HandleSummon(_SummonChampionSpells, N_CHAMPION_SPELLS, nChampion);
+ HandleSummon(_SummonGuardianSpells, N_GUARDIAN_SPELLS, nGuardian);
}
void UpdateAI(uint32 diff) override
@@ -151,72 +218,115 @@ public:
switch (eventId)
{
case EVENT_CURSE:
- DoCastAOE(SPELL_CURSE_PLAGUEBRINGER);
- events.ScheduleEvent(EVENT_CURSE, urand(50000, 60000));
- return;
+ {
+ DoCastAOE(SPELL_CURSE);
+ events.ScheduleEvent(EVENT_CURSE, urand(50, 70) * IN_MILLISECONDS, 0, PHASE_GROUND);
+ break;
+ }
case EVENT_WARRIOR:
Talk(SAY_SUMMON);
- SummonUndead(NPC_WARRIOR, RAID_MODE(2, 3));
- events.ScheduleEvent(EVENT_WARRIOR, 30000);
- return;
+ Talk(EMOTE_SUMMON);
+
+ CastSummon(RAID_MODE(2, 3), 0, 0);
+
+ events.ScheduleEvent(EVENT_WARRIOR, 40 * IN_MILLISECONDS, 0, PHASE_GROUND);
+ break;
case EVENT_BLINK:
DoCastAOE(SPELL_CRIPPLE, true);
DoCastAOE(SPELL_BLINK);
DoResetThreat();
- events.ScheduleEvent(EVENT_BLINK, 40000);
- return;
+ justBlinked = true;
+
+ events.ScheduleEvent(EVENT_BLINK, 40000, 0, PHASE_GROUND);
+ break;
case EVENT_BALCONY:
+ events.SetPhase(PHASE_BALCONY);
me->SetReactState(REACT_PASSIVE);
- me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
+ me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_NOT_SELECTABLE);
me->AttackStop();
+ me->StopMoving();
me->RemoveAllAuras();
- me->NearTeleportTo(Teleport.GetPositionX(), Teleport.GetPositionY(), Teleport.GetPositionZ(), Teleport.GetOrientation());
- events.Reset();
- events.ScheduleEvent(EVENT_WAVE, urand(2000, 5000));
- waveCount = 0;
- return;
+
+ events.ScheduleEvent(EVENT_BALCONY_TELEPORT, 3 * IN_MILLISECONDS, 0, PHASE_BALCONY);
+ events.ScheduleEvent(EVENT_WAVE, urand(5 * IN_MILLISECONDS, 8 * IN_MILLISECONDS), 0, PHASE_BALCONY);
+
+ uint8 secondsBalcony;
+ switch (balconyCount)
+ {
+ case 0:
+ secondsBalcony = 70;
+ break;
+ case 1:
+ secondsBalcony = 97;
+ break;
+ case 2:
+ default:
+ secondsBalcony = 120;
+ break;
+ }
+ events.ScheduleEvent(EVENT_GROUND, secondsBalcony * IN_MILLISECONDS, 0, PHASE_BALCONY);
+ break;
+ case EVENT_BALCONY_TELEPORT:
+ Talk(EMOTE_TELEPORT_1);
+ DoCastAOE(SPELL_TELEPORT);
+ break;
case EVENT_WAVE:
- Talk(SAY_SUMMON);
+ Talk(EMOTE_SUMMON_WAVE);
switch (balconyCount)
{
case 0:
- SummonUndead(NPC_CHAMPION, RAID_MODE(2, 4));
+ CastSummon(0, RAID_MODE(2, 4), 0);
break;
case 1:
- SummonUndead(NPC_CHAMPION, RAID_MODE(1, 2));
- SummonUndead(NPC_GUARDIAN, RAID_MODE(1, 2));
+ CastSummon(0, RAID_MODE(1, 2), RAID_MODE(1, 2));
break;
case 2:
- SummonUndead(NPC_GUARDIAN, RAID_MODE(2, 4));
+ CastSummon(0, 0, RAID_MODE(2, 4));
break;
default:
- SummonUndead(NPC_CHAMPION, RAID_MODE(5, 10));
- SummonUndead(NPC_GUARDIAN, RAID_MODE(5, 10));
+ CastSummon(0, RAID_MODE(5, 10), RAID_MODE(5, 10));
break;
}
- ++waveCount;
- events.ScheduleEvent(waveCount < 2 ? EVENT_WAVE : EVENT_GROUND, urand(30000, 45000));
- return;
+ events.ScheduleEvent(EVENT_WAVE, urand(30, 45) * IN_MILLISECONDS, 0, PHASE_BALCONY);
+ break;
case EVENT_GROUND:
- {
++balconyCount;
- float x, y, z, o;
- me->GetHomePosition(x, y, z, o);
- me->NearTeleportTo(x, y, z, o);
- events.ScheduleEvent(EVENT_BALCONY, 110000);
+
+ DoCastAOE(SPELL_TELEPORT_BACK);
+ Talk(EMOTE_TELEPORT_2);
+
EnterPhaseGround();
- return;
- }
+ break;
+ case EVENT_GROUND_ATTACKABLE:
+ me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_NOT_SELECTABLE);
+ me->SetReactState(REACT_AGGRESSIVE);
+ break;
}
}
- if (me->HasReactState(REACT_AGGRESSIVE))
- DoMeleeAttackIfReady();
+ if (events.IsInPhase(PHASE_GROUND))
+ {
+ /* workaround for movechase breaking after blinking
+ without this noth would just stand there unless his current target moves */
+ if (justBlinked && me->GetVictim() && !me->IsWithinMeleeRange(me->EnsureVictim()))
+ {
+ me->GetMotionMaster()->Clear();
+ me->GetMotionMaster()->MoveChase(me->EnsureVictim());
+ justBlinked = false;
+ }
+ else
+ DoMeleeAttackIfReady();
+ }
}
private:
- uint32 waveCount;
uint32 balconyCount;
+
+ bool justBlinked;
+
+ uint32 _SummonWarriorSpells[N_WARRIOR_SPELLS];
+ uint32 _SummonChampionSpells[N_CHAMPION_SPELLS];
+ uint32 _SummonGuardianSpells[N_GUARDIAN_SPELLS];
};
CreatureAI* GetAI(Creature* creature) const override
diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_yogg_saron.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_yogg_saron.cpp
index 59c28cd3a5a..6f07a88536d 100644
--- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_yogg_saron.cpp
+++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_yogg_saron.cpp
@@ -1508,9 +1508,9 @@ class npc_observation_ring_keeper : public CreatureScript
DoCast(SPELL_KEEPER_ACTIVE);
}
- void sGossipSelect(Player* player, uint32 sender, uint32 /*action*/) override
+ void sGossipSelect(Player* player, uint32 menuId, uint32 /*gossipListId*/) override
{
- if (sender != 10333)
+ if (menuId != 10333)
return;
me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP);
diff --git a/src/server/scripts/Northrend/zone_grizzly_hills.cpp b/src/server/scripts/Northrend/zone_grizzly_hills.cpp
index 83fd3859716..54b06260e78 100644
--- a/src/server/scripts/Northrend/zone_grizzly_hills.cpp
+++ b/src/server/scripts/Northrend/zone_grizzly_hills.cpp
@@ -762,7 +762,7 @@ public:
}
}
- void sGossipSelect(Player* player, uint32 /*sender*/, uint32 /*action*/) override
+ void sGossipSelect(Player* player, uint32 /*menuId*/, uint32 /*gossipListId*/) override
{
DoCast(player, SPELL_SUMMON_ASHWOOD_BRAND);
}
diff --git a/src/server/scripts/Northrend/zone_storm_peaks.cpp b/src/server/scripts/Northrend/zone_storm_peaks.cpp
index 25bf4826000..e86e907b550 100644
--- a/src/server/scripts/Northrend/zone_storm_peaks.cpp
+++ b/src/server/scripts/Northrend/zone_storm_peaks.cpp
@@ -85,13 +85,13 @@ public:
DoMeleeAttackIfReady();
}
- void sGossipSelect(Player* player, uint32 sender, uint32 action) override
+ void sGossipSelect(Player* player, uint32 menuId, uint32 gossipListId) override
{
- if (sender == GOSSIP_ID && action == GOSSIP_OPTION_ID)
+ if (menuId == GOSSIP_ID && gossipListId == GOSSIP_OPTION_ID)
{
player->CLOSE_GOSSIP_MENU();
me->setFaction(113);
- npc_escortAI::Start(true, true, player->GetGUID());
+ Start(true, true, player->GetGUID());
}
}
};
@@ -476,7 +476,7 @@ public:
objectCounter = 0;
}
- void sGossipSelect(Player* player, uint32 /*sender*/, uint32 /*action*/) override
+ void sGossipSelect(Player* player, uint32 /*menuId*/, uint32 /*gossipListId*/) override
{
player->CLOSE_GOSSIP_MENU();
playerGUID = player->GetGUID();
diff --git a/src/server/scripts/Northrend/zone_zuldrak.cpp b/src/server/scripts/Northrend/zone_zuldrak.cpp
index 91c796a6e69..e36930f5745 100644
--- a/src/server/scripts/Northrend/zone_zuldrak.cpp
+++ b/src/server/scripts/Northrend/zone_zuldrak.cpp
@@ -267,7 +267,7 @@ public:
return;
}
- void sGossipSelect(Player* player, uint32 /*sender*/, uint32 /*action*/) override
+ void sGossipSelect(Player* player, uint32 /*menuId*/, uint32 /*gossipListId*/) override
{
_events.ScheduleEvent(EVENT_RECRUIT_1, 100);
player->CLOSE_GOSSIP_MENU();
@@ -552,7 +552,7 @@ public:
}
}
- void sGossipSelect(Player* player, uint32 /*sender*/, uint32 /*action*/) override
+ void sGossipSelect(Player* player, uint32 /*menuId*/, uint32 /*gossipListId*/) override
{
player->CLOSE_GOSSIP_MENU();
DoCast(player, SPELL_ALCHEMIST_APPRENTICE_INVISBUFF);
diff --git a/src/server/scripts/Outland/BlackTemple/black_temple.cpp b/src/server/scripts/Outland/BlackTemple/black_temple.cpp
index 764cd9594ea..f044fcef5c7 100644
--- a/src/server/scripts/Outland/BlackTemple/black_temple.cpp
+++ b/src/server/scripts/Outland/BlackTemple/black_temple.cpp
@@ -15,22 +15,12 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-/*
-Name: Black_Temple
-Complete: 100%
-Comment: Spirit of Olum: Player Teleporter to Seer Kanai Teleport after defeating Naj'entus and Supremus.
-*/
-
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
-#include "ScriptedGossip.h"
#include "black_temple.h"
-#include "Player.h"
enum Spells
{
- // Spirit of Olum
- SPELL_TELEPORT = 41566,
// Wrathbone Flayer
SPELL_CLEAVE = 15496,
SPELL_IGNORED = 39544,
@@ -53,36 +43,6 @@ enum Events
};
// ########################################################
-// Spirit of Olum
-// ########################################################
-
-class npc_spirit_of_olum : public CreatureScript
-{
-public:
- npc_spirit_of_olum() : CreatureScript("npc_spirit_of_olum") { }
-
- struct npc_spirit_of_olumAI : public ScriptedAI
- {
- npc_spirit_of_olumAI(Creature* creature) : ScriptedAI(creature) { }
-
- void sGossipSelect(Player* player, uint32 /*sender*/, uint32 action) override
- {
- if (action == 1)
- {
- player->CLOSE_GOSSIP_MENU();
- player->InterruptNonMeleeSpells(false);
- player->CastSpell(player, SPELL_TELEPORT, false);
- }
- }
- };
-
- CreatureAI* GetAI(Creature* creature) const override
- {
- return new npc_spirit_of_olumAI(creature);
- }
-};
-
-// ########################################################
// Wrathbone Flayer
// ########################################################
@@ -123,7 +83,6 @@ public:
void UpdateAI(uint32 diff) override
{
-
if (!_enteredCombat)
{
_events.Update(diff);
@@ -215,12 +174,11 @@ public:
CreatureAI* GetAI(Creature* creature) const override
{
- return GetInstanceAI<npc_wrathbone_flayerAI>(creature);
+ return GetBlackTempleAI<npc_wrathbone_flayerAI>(creature);
}
};
void AddSC_black_temple()
{
- new npc_spirit_of_olum();
new npc_wrathbone_flayer();
}
diff --git a/src/server/scripts/Outland/BlackTemple/boss_illidan.cpp b/src/server/scripts/Outland/BlackTemple/boss_illidan.cpp
index 7cc460d4548..f8c031f69e7 100644
--- a/src/server/scripts/Outland/BlackTemple/boss_illidan.cpp
+++ b/src/server/scripts/Outland/BlackTemple/boss_illidan.cpp
@@ -1779,7 +1779,7 @@ public:
DoMeleeAttackIfReady();
}
- void sGossipSelect(Player* player, uint32 /*sender*/, uint32 /*action*/) override
+ void sGossipSelect(Player* player, uint32 /*menuId*/, uint32 /*gossipListId*/) override
{
player->CLOSE_GOSSIP_MENU();
EnterPhase(PHASE_CHANNEL);
diff --git a/src/server/scripts/Outland/BlackTemple/boss_shade_of_akama.cpp b/src/server/scripts/Outland/BlackTemple/boss_shade_of_akama.cpp
index e4369f0348d..25ff1bf9536 100644
--- a/src/server/scripts/Outland/BlackTemple/boss_shade_of_akama.cpp
+++ b/src/server/scripts/Outland/BlackTemple/boss_shade_of_akama.cpp
@@ -519,9 +519,9 @@ public:
DoMeleeAttackIfReady();
}
- void sGossipSelect(Player* player, uint32 /*sender*/, uint32 action) override
+ void sGossipSelect(Player* player, uint32 /*menuId*/, uint32 gossipListId) override
{
- if (action == 0)
+ if (gossipListId == 0)
{
player->CLOSE_GOSSIP_MENU();
StartChannel = true;
diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist
index b4a7e7a4864..1689f05966a 100644
--- a/src/server/worldserver/worldserver.conf.dist
+++ b/src/server/worldserver/worldserver.conf.dist
@@ -2605,6 +2605,14 @@ PlayerStart.MapsExplored = 0
HonorPointsAfterDuel = 0
#
+# ResetCoolDownAfterDuel
+# Description: Reset all cooldowns after duel, but only if player has no cooldowns before the duel.
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+
+ResetCoolDownAfterDuel = 0
+
+#
# AlwaysMaxWeaponSkill
# Description: Players will automatically gain max weapon/defense skill when logging in,
# or leveling.