aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/Debugging/Errors.cpp11
-rw-r--r--src/common/Debugging/Errors.h4
-rw-r--r--src/common/Utilities/Util.cpp7
-rw-r--r--src/common/Utilities/Util.h3
-rw-r--r--src/server/database/Database/DatabaseLoader.cpp10
-rw-r--r--src/server/database/Database/DatabaseWorkerPool.h2
-rw-r--r--src/server/game/AI/ScriptedAI/ScriptedCreature.cpp1
-rw-r--r--src/server/game/AI/ScriptedAI/ScriptedCreature.h3
-rw-r--r--src/server/game/AI/SmartScripts/SmartScript.cpp4
-rw-r--r--src/server/game/AI/SmartScripts/SmartScript.h2
-rw-r--r--src/server/game/Achievements/AchievementMgr.cpp2
-rw-r--r--src/server/game/AuctionHouse/AuctionHouseMgr.cpp123
-rw-r--r--src/server/game/AuctionHouse/AuctionHouseMgr.h11
-rw-r--r--src/server/game/Battlegrounds/BattlegroundQueue.cpp2
-rw-r--r--src/server/game/Conditions/ConditionMgr.cpp8
-rw-r--r--src/server/game/Conditions/ConditionMgr.h2
-rw-r--r--src/server/game/DataStores/DBCStructure.h2
-rw-r--r--src/server/game/DataStores/DBCfmt.h2
-rw-r--r--src/server/game/Entities/Creature/Creature.cpp44
-rw-r--r--src/server/game/Entities/Creature/Creature.h3
-rw-r--r--src/server/game/Entities/Item/Item.cpp1
-rw-r--r--src/server/game/Entities/Pet/Pet.cpp18
-rw-r--r--src/server/game/Entities/Pet/Pet.h2
-rw-r--r--src/server/game/Entities/Player/KillRewarder.cpp274
-rw-r--r--src/server/game/Entities/Player/KillRewarder.h59
-rw-r--r--src/server/game/Entities/Player/Player.cpp454
-rw-r--r--src/server/game/Entities/Player/Player.h110
-rw-r--r--src/server/game/Entities/Player/TradeData.cpp139
-rw-r--r--src/server/game/Entities/Player/TradeData.h81
-rw-r--r--src/server/game/Entities/Unit/Unit.cpp63
-rw-r--r--src/server/game/Entities/Unit/Unit.h10
-rw-r--r--src/server/game/Globals/ObjectMgr.cpp31
-rw-r--r--src/server/game/Groups/Group.cpp2
-rw-r--r--src/server/game/Handlers/AuctionHouseHandler.cpp7
-rw-r--r--src/server/game/Handlers/CharacterHandler.cpp10
-rw-r--r--src/server/game/Handlers/GroupHandler.cpp2
-rw-r--r--src/server/game/Handlers/MiscHandler.cpp2
-rw-r--r--src/server/game/Handlers/TradeHandler.cpp5
-rw-r--r--src/server/game/Miscellaneous/Language.h3
-rw-r--r--src/server/game/Quests/QuestDef.cpp12
-rw-r--r--src/server/game/Server/WorldSession.cpp7
-rw-r--r--src/server/game/Spells/Auras/SpellAuraEffects.cpp55
-rw-r--r--src/server/game/Spells/Spell.cpp14
-rw-r--r--src/server/game/Spells/SpellEffects.cpp2
-rw-r--r--src/server/game/Spells/SpellHistory.cpp134
-rw-r--r--src/server/game/Spells/SpellHistory.h3
-rw-r--r--src/server/game/Spells/SpellInfo.h2
-rw-r--r--src/server/game/Spells/SpellMgr.cpp34
-rw-r--r--src/server/game/Texts/CreatureTextMgr.cpp2
-rw-r--r--src/server/game/World/World.cpp9
-rw-r--r--src/server/game/World/World.h2
-rw-r--r--src/server/scripts/Commands/cs_wp.cpp6
-rw-r--r--src/server/scripts/EasternKingdoms/Karazhan/boss_midnight.cpp4
-rw-r--r--src/server/scripts/EasternKingdoms/Karazhan/boss_prince_malchezaar.cpp2
-rw-r--r--src/server/scripts/EasternKingdoms/MagistersTerrace/boss_felblood_kaelthas.cpp6
-rw-r--r--src/server/scripts/EasternKingdoms/MagistersTerrace/boss_selin_fireheart.cpp4
-rw-r--r--src/server/scripts/EasternKingdoms/ScarletEnclave/chapter5.cpp4
-rw-r--r--src/server/scripts/EasternKingdoms/ScarletMonastery/boss_headless_horseman.cpp4
-rw-r--r--src/server/scripts/EasternKingdoms/ScarletMonastery/instance_scarlet_monastery.cpp2
-rw-r--r--src/server/scripts/EasternKingdoms/Stratholme/stratholme.cpp4
-rw-r--r--src/server/scripts/EasternKingdoms/SunwellPlateau/boss_eredar_twins.cpp2
-rw-r--r--src/server/scripts/EasternKingdoms/SunwellPlateau/boss_kiljaeden.cpp4
-rw-r--r--src/server/scripts/EasternKingdoms/zone_western_plaguelands.cpp140
-rw-r--r--src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_anetheron.cpp2
-rw-r--r--src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_azgalor.cpp4
-rw-r--r--src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/boss_mal_ganis.cpp2
-rw-r--r--src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/culling_of_stratholme.cpp4
-rw-r--r--src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_cthun.cpp18
-rw-r--r--src/server/scripts/Kalimdor/zone_dustwallow_marsh.cpp2
-rw-r--r--src/server/scripts/Kalimdor/zone_moonglade.cpp59
-rw-r--r--src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_jedoga_shadowseeker.cpp2
-rw-r--r--src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/obsidian_sanctum.cpp6
-rw-r--r--src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp6
-rw-r--r--src/server/scripts/Northrend/IcecrownCitadel/boss_lady_deathwhisper.cpp4
-rw-r--r--src/server/scripts/Northrend/Naxxramas/boss_anubrekhan.cpp2
-rw-r--r--src/server/scripts/Northrend/Naxxramas/boss_maexxna.cpp150
-rw-r--r--src/server/scripts/Northrend/Naxxramas/boss_noth.cpp2
-rw-r--r--src/server/scripts/Northrend/Naxxramas/boss_razuvious.cpp196
-rw-r--r--src/server/scripts/Northrend/Naxxramas/boss_thaddius.cpp1372
-rw-r--r--src/server/scripts/Northrend/Naxxramas/instance_naxxramas.cpp47
-rw-r--r--src/server/scripts/Northrend/Naxxramas/naxxramas.h11
-rw-r--r--src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp5
-rw-r--r--src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp2
-rw-r--r--src/server/scripts/Northrend/Ulduar/Ulduar/boss_yogg_saron.cpp4
-rw-r--r--src/server/scripts/Northrend/zone_borean_tundra.cpp2
-rw-r--r--src/server/scripts/Northrend/zone_sholazar_basin.cpp2
-rw-r--r--src/server/scripts/Outland/Auchindoun/ShadowLabyrinth/boss_grandmaster_vorpil.cpp2
-rw-r--r--src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_lady_vashj.cpp2
-rw-r--r--src/server/scripts/Outland/TempestKeep/Eye/boss_kaelthas.cpp2
-rw-r--r--src/server/scripts/Pet/pet_mage.cpp185
-rw-r--r--src/server/scripts/Spells/spell_dk.cpp10
-rw-r--r--src/server/scripts/Spells/spell_holiday.cpp81
-rw-r--r--src/server/scripts/Spells/spell_hunter.cpp2
-rw-r--r--src/server/scripts/Spells/spell_item.cpp2
-rw-r--r--src/server/scripts/World/duel_reset.cpp79
-rw-r--r--src/server/worldserver/worldserver.conf.dist13
-rw-r--r--src/tools/map_extractor/CMakeLists.txt18
-rw-r--r--src/tools/map_extractor/System.cpp201
-rw-r--r--src/tools/map_extractor/loadlib.cpp8
-rw-r--r--src/tools/map_extractor/loadlib/loadlib.h30
-rw-r--r--src/tools/map_extractor/mpq_libmpq04.h6
-rw-r--r--src/tools/mmaps_generator/IntermediateValues.h1
-rw-r--r--src/tools/mmaps_generator/MapBuilder.cpp22
-rw-r--r--src/tools/mmaps_generator/MapBuilder.h16
-rw-r--r--src/tools/mmaps_generator/PathCommon.h5
-rw-r--r--src/tools/mmaps_generator/TerrainBuilder.cpp8
-rw-r--r--src/tools/mmaps_generator/TerrainBuilder.h11
-rw-r--r--src/tools/vmap4_extractor/adtfile.cpp10
-rw-r--r--src/tools/vmap4_extractor/mpq_libmpq04.h15
-rw-r--r--src/tools/vmap4_extractor/vmapexport.cpp50
-rw-r--r--src/tools/vmap4_extractor/wdtfile.cpp5
-rw-r--r--src/tools/vmap4_extractor/wdtfile.h13
-rw-r--r--src/tools/vmap4_extractor/wmo.cpp6
113 files changed, 3001 insertions, 1683 deletions
diff --git a/src/common/Debugging/Errors.cpp b/src/common/Debugging/Errors.cpp
index 45f130ceb3b..4c7e91a8219 100644
--- a/src/common/Debugging/Errors.cpp
+++ b/src/common/Debugging/Errors.cpp
@@ -59,10 +59,15 @@ void Assert(char const* file, int line, char const* function, char const* messag
exit(1);
}
-void Fatal(char const* file, int line, char const* function, char const* message)
+void Fatal(char const* file, int line, char const* function, char const* message, ...)
{
- fprintf(stderr, "\n%s:%i in %s FATAL ERROR:\n %s\n",
- file, line, function, message);
+ va_list args;
+ va_start(args, message);
+
+ fprintf(stderr, "\n%s:%i in %s FATAL ERROR:\n ", file, line, function);
+ vfprintf(stderr, message, args);
+ fprintf(stderr, "\n");
+ fflush(stderr);
std::this_thread::sleep_for(std::chrono::seconds(10));
*((volatile int*)NULL) = 0;
diff --git a/src/common/Debugging/Errors.h b/src/common/Debugging/Errors.h
index 3ceaf2c328f..9e526933acc 100644
--- a/src/common/Debugging/Errors.h
+++ b/src/common/Debugging/Errors.h
@@ -26,7 +26,7 @@ namespace Trinity
DECLSPEC_NORETURN void Assert(char const* file, int line, char const* function, char const* message) ATTR_NORETURN;
DECLSPEC_NORETURN void Assert(char const* file, int line, char const* function, char const* message, char const* format, ...) ATTR_NORETURN ATTR_PRINTF(5, 6);
- DECLSPEC_NORETURN void Fatal(char const* file, int line, char const* function, char const* message) ATTR_NORETURN;
+ DECLSPEC_NORETURN void Fatal(char const* file, int line, char const* function, char const* message, ...) ATTR_NORETURN ATTR_PRINTF(4, 5);
DECLSPEC_NORETURN void Error(char const* file, int line, char const* function, char const* message) ATTR_NORETURN;
@@ -45,7 +45,7 @@ namespace Trinity
#endif
#define WPAssert(cond, ...) ASSERT_BEGIN do { if (!(cond)) Trinity::Assert(__FILE__, __LINE__, __FUNCTION__, #cond, ##__VA_ARGS__); } while(0) ASSERT_END
-#define WPFatal(cond, msg) ASSERT_BEGIN do { if (!(cond)) Trinity::Fatal(__FILE__, __LINE__, __FUNCTION__, (msg)); } while(0) ASSERT_END
+#define WPFatal(cond, ...) ASSERT_BEGIN do { if (!(cond)) Trinity::Fatal(__FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__); } while(0) ASSERT_END
#define WPError(cond, msg) ASSERT_BEGIN do { if (!(cond)) Trinity::Error(__FILE__, __LINE__, __FUNCTION__, (msg)); } while(0) ASSERT_END
#define WPWarning(cond, msg) ASSERT_BEGIN do { if (!(cond)) Trinity::Warning(__FILE__, __LINE__, __FUNCTION__, (msg)); } while(0) ASSERT_END
#define WPAbort() ASSERT_BEGIN do { Trinity::Abort(__FILE__, __LINE__, __FUNCTION__); } while(0) ASSERT_END
diff --git a/src/common/Utilities/Util.cpp b/src/common/Utilities/Util.cpp
index 33c273fb05f..9f61dd12e4c 100644
--- a/src/common/Utilities/Util.cpp
+++ b/src/common/Utilities/Util.cpp
@@ -58,6 +58,13 @@ uint32 urand(uint32 min, uint32 max)
return GetRng()->URandom(min, max);
}
+uint32 urandms(uint32 min, uint32 max)
+{
+ ASSERT(max >= min);
+ ASSERT(INT_MAX/IN_MILLISECONDS >= max);
+ return GetRng()->URandom(min * IN_MILLISECONDS, max * IN_MILLISECONDS);
+}
+
float frand(float min, float max)
{
ASSERT(max >= min);
diff --git a/src/common/Utilities/Util.h b/src/common/Utilities/Util.h
index 6a872b44a60..b748e83408b 100644
--- a/src/common/Utilities/Util.h
+++ b/src/common/Utilities/Util.h
@@ -82,6 +82,9 @@ int32 irand(int32 min, int32 max);
/* Return a random number in the range min..max (inclusive). */
uint32 urand(uint32 min, uint32 max);
+/* Return a random millisecond value between min and max seconds. Functionally equivalent to urand(min*IN_MILLISECONDS, max*IN_MILLISECONDS). */
+uint32 urandms(uint32 min, uint32 max);
+
/* Return a random number in the range 0 .. UINT32_MAX. */
uint32 rand32();
diff --git a/src/server/database/Database/DatabaseLoader.cpp b/src/server/database/Database/DatabaseLoader.cpp
index 69e212cf7e1..39719e4c6a1 100644
--- a/src/server/database/Database/DatabaseLoader.cpp
+++ b/src/server/database/Database/DatabaseLoader.cpp
@@ -37,14 +37,14 @@ DatabaseLoader& DatabaseLoader::AddDatabase(DatabaseWorkerPool<T>& pool, std::st
std::string const dbString = sConfigMgr->GetStringDefault(name + "DatabaseInfo", "");
if (dbString.empty())
{
- TC_LOG_ERROR(_logger.c_str(), "Database %s not specified in configuration file!", name.c_str());
+ TC_LOG_ERROR(_logger, "Database %s not specified in configuration file!", name.c_str());
return false;
}
uint8 const asyncThreads = uint8(sConfigMgr->GetIntDefault(name + "Database.WorkerThreads", 1));
if (asyncThreads < 1 || asyncThreads > 32)
{
- TC_LOG_ERROR(_logger.c_str(), "%s database: invalid number of worker threads specified. "
+ TC_LOG_ERROR(_logger, "%s database: invalid number of worker threads specified. "
"Please pick a value between 1 and 32.", name.c_str());
return false;
}
@@ -85,7 +85,7 @@ DatabaseLoader& DatabaseLoader::AddDatabase(DatabaseWorkerPool<T>& pool, std::st
{
if (!DBUpdater<T>::Populate(pool))
{
- TC_LOG_ERROR(_logger.c_str(), "Could not populate the %s database, see log for details.", name.c_str());
+ TC_LOG_ERROR(_logger, "Could not populate the %s database, see log for details.", name.c_str());
return false;
}
return true;
@@ -95,7 +95,7 @@ DatabaseLoader& DatabaseLoader::AddDatabase(DatabaseWorkerPool<T>& pool, std::st
{
if (!DBUpdater<T>::Update(pool))
{
- TC_LOG_ERROR(_logger.c_str(), "Could not update the %s database, see log for details.", name.c_str());
+ TC_LOG_ERROR(_logger, "Could not update the %s database, see log for details.", name.c_str());
return false;
}
return true;
@@ -106,7 +106,7 @@ DatabaseLoader& DatabaseLoader::AddDatabase(DatabaseWorkerPool<T>& pool, std::st
{
if (!pool.PrepareStatements())
{
- TC_LOG_ERROR(_logger.c_str(), "Could not prepare statements of the %s database, see log for details.", name.c_str());
+ TC_LOG_ERROR(_logger, "Could not prepare statements of the %s database, see log for details.", name.c_str());
return false;
}
return true;
diff --git a/src/server/database/Database/DatabaseWorkerPool.h b/src/server/database/Database/DatabaseWorkerPool.h
index 32837daf5da..c7b5d8c8fea 100644
--- a/src/server/database/Database/DatabaseWorkerPool.h
+++ b/src/server/database/Database/DatabaseWorkerPool.h
@@ -67,6 +67,8 @@ class DatabaseWorkerPool
WPFatal(mysql_thread_safe(), "Used MySQL library isn't thread-safe.");
WPFatal(mysql_get_client_version() >= MIN_MYSQL_CLIENT_VERSION, "TrinityCore does not support MySQL versions below 5.1");
+ WPFatal(mysql_get_client_version() == MYSQL_VERSION_ID, "Used MySQL library version (%s) does not match the version used to compile TrinityCore (%s).",
+ mysql_get_client_info(), MYSQL_SERVER_VERSION);
}
~DatabaseWorkerPool()
diff --git a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp
index 7b145268d22..9a6742b5ebc 100644
--- a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp
+++ b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp
@@ -97,7 +97,6 @@ bool SummonList::HasEntry(uint32 entry) const
}
ScriptedAI::ScriptedAI(Creature* creature) : CreatureAI(creature),
- me(creature),
IsFleeing(false),
_evadeCheckCooldown(2500),
_isCombatMovementAllowed(true)
diff --git a/src/server/game/AI/ScriptedAI/ScriptedCreature.h b/src/server/game/AI/ScriptedAI/ScriptedCreature.h
index 7d7811d9e75..242acbcd44d 100644
--- a/src/server/game/AI/ScriptedAI/ScriptedCreature.h
+++ b/src/server/game/AI/ScriptedAI/ScriptedCreature.h
@@ -184,9 +184,6 @@ struct ScriptedAI : public CreatureAI
// Variables
// *************
- //Pointer to creature we are manipulating
- Creature* me;
-
//For fleeing
bool IsFleeing;
diff --git a/src/server/game/AI/SmartScripts/SmartScript.cpp b/src/server/game/AI/SmartScripts/SmartScript.cpp
index a1703dfa78f..7ca4274bc40 100644
--- a/src/server/game/AI/SmartScripts/SmartScript.cpp
+++ b/src/server/game/AI/SmartScripts/SmartScript.cpp
@@ -979,7 +979,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
{
if (me && !me->isDead())
{
- me->Kill(me);
+ me->KillSelf();
TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction: SMART_ACTION_DIE: Creature %u", me->GetGUID().GetCounter());
}
break;
@@ -1249,7 +1249,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
if (!IsUnit(*itr))
continue;
- (*itr)->ToUnit()->Kill((*itr)->ToUnit());
+ (*itr)->ToUnit()->KillSelf();
}
delete targets;
diff --git a/src/server/game/AI/SmartScripts/SmartScript.h b/src/server/game/AI/SmartScripts/SmartScript.h
index 6f9046520b0..1c4b9a51ece 100644
--- a/src/server/game/AI/SmartScripts/SmartScript.h
+++ b/src/server/game/AI/SmartScripts/SmartScript.h
@@ -248,7 +248,7 @@ class SmartScript
void DecPhase(int32 p = 1)
{
- if(mEventPhase > (uint32)p)
+ if (mEventPhase > (uint32)p)
mEventPhase -= (uint32)p;
else
mEventPhase = 0;
diff --git a/src/server/game/Achievements/AchievementMgr.cpp b/src/server/game/Achievements/AchievementMgr.cpp
index 2bacaff496f..a6359a353eb 100644
--- a/src/server/game/Achievements/AchievementMgr.cpp
+++ b/src/server/game/Achievements/AchievementMgr.cpp
@@ -1533,7 +1533,7 @@ void AchievementMgr::CompletedAchievement(AchievementEntry const* achievement)
//! Since no common attributes were found, (not even in titleRewardFlags field)
//! we explicitly check by ID. Maybe in the future we could move the achievement_reward
//! condition fields to the condition system.
- if (uint32 titleId = reward->titleId[achievement->ID == 1793 ? GetPlayer()->getGender() : (GetPlayer()->GetTeam() == ALLIANCE ? 0 : 1)])
+ if (uint32 titleId = reward->titleId[achievement->ID == 1793 ? GetPlayer()->GetByteValue(PLAYER_BYTES_3, 0) : (GetPlayer()->GetTeam() == ALLIANCE ? 0 : 1)])
if (CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(titleId))
GetPlayer()->SetTitle(titleEntry);
diff --git a/src/server/game/AuctionHouse/AuctionHouseMgr.cpp b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp
index 768def2ff4d..20d30704c13 100644
--- a/src/server/game/AuctionHouse/AuctionHouseMgr.cpp
+++ b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp
@@ -83,12 +83,23 @@ uint32 AuctionHouseMgr::GetAuctionDeposit(AuctionHouseEntry const* entry, uint32
float multiplier = CalculatePct(float(entry->depositPercent), 3);
uint32 timeHr = (((time / 60) / 60) / 12);
- uint32 deposit = uint32(((multiplier * MSV * count / 3) * timeHr * 3) * sWorld->getRate(RATE_AUCTION_DEPOSIT));
+ uint32 deposit = uint32(MSV * multiplier * sWorld->getRate(RATE_AUCTION_DEPOSIT));
+ float remainderbase = float(MSV * multiplier * sWorld->getRate(RATE_AUCTION_DEPOSIT)) - deposit;
+
+ deposit *= timeHr * count;
+
+ int i = count;
+ while (i > 0 && (remainderbase * i) != uint32(remainderbase * i))
+ i--;
+
+ if (i)
+ deposit += remainderbase * i * timeHr;
TC_LOG_DEBUG("auctionHouse", "MSV: %u", MSV);
TC_LOG_DEBUG("auctionHouse", "Items: %u", count);
TC_LOG_DEBUG("auctionHouse", "Multiplier: %f", multiplier);
TC_LOG_DEBUG("auctionHouse", "Deposit: %u", deposit);
+ TC_LOG_DEBUG("auctionHouse", "Deposit rm: %f", remainderbase * count);
if (deposit < AH_MINIMUM_DEPOSIT * sWorld->getRate(RATE_AUCTION_DEPOSIT))
return AH_MINIMUM_DEPOSIT * sWorld->getRate(RATE_AUCTION_DEPOSIT);
@@ -389,6 +400,116 @@ bool AuctionHouseMgr::RemoveAItem(ObjectGuid::LowType id, bool deleteItem)
return true;
}
+void AuctionHouseMgr::PendingAuctionAdd(Player* player, AuctionEntry* aEntry)
+{
+ PlayerAuctions* thisAH;
+ auto itr = pendingAuctionMap.find(player->GetGUID());
+ if (itr != pendingAuctionMap.end())
+ thisAH = itr->second.first;
+ else
+ {
+ thisAH = new PlayerAuctions;
+ pendingAuctionMap[player->GetGUID()] = AuctionPair(thisAH, 0);
+ }
+ thisAH->push_back(aEntry);
+}
+
+uint32 AuctionHouseMgr::PendingAuctionCount(const Player* player) const
+{
+ auto const itr = pendingAuctionMap.find(player->GetGUID());
+ if (itr != pendingAuctionMap.end())
+ return itr->second.first->size();
+
+ return 0;
+}
+
+void AuctionHouseMgr::PendingAuctionProcess(Player* player)
+{
+ auto iterMap = pendingAuctionMap.find(player->GetGUID());
+ if (iterMap == pendingAuctionMap.end())
+ return;
+
+ PlayerAuctions* thisAH = iterMap->second.first;
+
+ SQLTransaction trans = CharacterDatabase.BeginTransaction();
+
+ uint32 totalItems = 0;
+ for (auto itrAH = thisAH->begin(); itrAH != thisAH->end(); ++itrAH)
+ {
+ AuctionEntry* AH = (*itrAH);
+ totalItems += AH->itemCount;
+ }
+
+ uint32 totaldeposit = 0;
+ auto itr = (*thisAH->begin());
+
+ if (Item* item = GetAItem(itr->itemGUIDLow))
+ totaldeposit = GetAuctionDeposit(itr->auctionHouseEntry, itr->etime, item, totalItems);
+
+ uint32 depositremain = totaldeposit;
+ for (auto itr = thisAH->begin(); itr != thisAH->end(); ++itr)
+ {
+ AuctionEntry* AH = (*itr);
+
+ if (next(itr) == thisAH->end())
+ AH->deposit = depositremain;
+ else
+ {
+ AH->deposit = totaldeposit / thisAH->size();
+ depositremain -= AH->deposit;
+ }
+
+ AH->DeleteFromDB(trans);
+ AH->SaveToDB(trans);
+ }
+
+ CharacterDatabase.CommitTransaction(trans);
+ pendingAuctionMap.erase(player->GetGUID());
+ delete thisAH;
+ player->ModifyMoney(-int32(totaldeposit));
+}
+
+void AuctionHouseMgr::UpdatePendingAuctions()
+{
+ for (auto itr = pendingAuctionMap.begin(); itr != pendingAuctionMap.end();)
+ {
+ ObjectGuid playerGUID = itr->first;
+ if (Player* player = ObjectAccessor::FindConnectedPlayer(playerGUID))
+ {
+ // Check if there were auctions since last update process if not
+ if (PendingAuctionCount(player) == itr->second.second)
+ {
+ ++itr;
+ PendingAuctionProcess(player);
+ }
+ else
+ {
+ ++itr;
+ pendingAuctionMap[playerGUID].second = PendingAuctionCount(player);
+ }
+ }
+ else
+ {
+ // Expire any auctions that we couldn't get a deposit for
+ TC_LOG_WARN("auctionHouse", "Player %s was offline, unable to retrieve deposit!", playerGUID.ToString().c_str());
+ PlayerAuctions* thisAH = itr->second.first;
+ ++itr;
+ SQLTransaction trans = CharacterDatabase.BeginTransaction();
+ for (auto AHitr = thisAH->begin(); AHitr != thisAH->end();)
+ {
+ AuctionEntry* AH = (*AHitr);
+ ++AHitr;
+ AH->expire_time = time(NULL);
+ AH->DeleteFromDB(trans);
+ AH->SaveToDB(trans);
+ }
+ CharacterDatabase.CommitTransaction(trans);
+ pendingAuctionMap.erase(playerGUID);
+ delete thisAH;
+ }
+ }
+}
+
void AuctionHouseMgr::Update()
{
mHordeAuctions.Update();
diff --git a/src/server/game/AuctionHouse/AuctionHouseMgr.h b/src/server/game/AuctionHouse/AuctionHouseMgr.h
index 45cdba361f3..1f885837a44 100644
--- a/src/server/game/AuctionHouse/AuctionHouseMgr.h
+++ b/src/server/game/AuctionHouse/AuctionHouseMgr.h
@@ -22,6 +22,7 @@
#include "Common.h"
#include "DatabaseEnv.h"
#include "DBCStructure.h"
+#include <set>
class Item;
class Player;
@@ -82,6 +83,7 @@ struct AuctionEntry
time_t expire_time;
ObjectGuid::LowType bidder;
uint32 deposit; //deposit can be calculated only when creating auction
+ uint32 etime;
AuctionHouseEntry const* auctionHouseEntry; // in AuctionHouse.dbc
// helpers
@@ -151,6 +153,8 @@ class AuctionHouseMgr
}
typedef std::unordered_map<ObjectGuid::LowType, Item*> ItemMap;
+ typedef std::vector<AuctionEntry*> PlayerAuctions;
+ typedef std::pair<PlayerAuctions*, uint32> AuctionPair;
AuctionHouseObject* GetAuctionsMap(uint32 factionTemplateId);
AuctionHouseObject* GetAuctionsMapByHouseId(uint8 auctionHouseId);
@@ -184,7 +188,10 @@ class AuctionHouseMgr
void AddAItem(Item* it);
bool RemoveAItem(ObjectGuid::LowType id, bool deleteItem = false);
-
+ void PendingAuctionAdd(Player* player, AuctionEntry* aEntry);
+ uint32 PendingAuctionCount(const Player* player) const;
+ void PendingAuctionProcess(Player* player);
+ void UpdatePendingAuctions();
void Update();
private:
@@ -193,6 +200,8 @@ class AuctionHouseMgr
AuctionHouseObject mAllianceAuctions;
AuctionHouseObject mNeutralAuctions;
+ std::map<ObjectGuid, AuctionPair> pendingAuctionMap;
+
ItemMap mAitems;
};
diff --git a/src/server/game/Battlegrounds/BattlegroundQueue.cpp b/src/server/game/Battlegrounds/BattlegroundQueue.cpp
index 87e07e15191..223b71eb8c5 100644
--- a/src/server/game/Battlegrounds/BattlegroundQueue.cpp
+++ b/src/server/game/Battlegrounds/BattlegroundQueue.cpp
@@ -1069,7 +1069,7 @@ bool BGQueueRemoveEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/)
if (bgQueue.IsPlayerInvited(m_PlayerGuid, m_BgInstanceGUID, m_RemoveTime))
{
// track if player leaves the BG by not clicking enter button
- if (bg->isBattleground() && sWorld->getBoolConfig(CONFIG_BATTLEGROUND_TRACK_DESERTERS) &&
+ if (bg && bg->isBattleground() && sWorld->getBoolConfig(CONFIG_BATTLEGROUND_TRACK_DESERTERS) &&
(bg->GetStatus() == STATUS_IN_PROGRESS || bg->GetStatus() == STATUS_WAIT_JOIN))
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_DESERTER_TRACK);
diff --git a/src/server/game/Conditions/ConditionMgr.cpp b/src/server/game/Conditions/ConditionMgr.cpp
index d9fa125b64c..bae400adcc2 100644
--- a/src/server/game/Conditions/ConditionMgr.cpp
+++ b/src/server/game/Conditions/ConditionMgr.cpp
@@ -89,7 +89,7 @@ ConditionMgr::ConditionTypeInfo const ConditionMgr::StaticConditionTypeData[COND
{ "PhaseMask", true, false, false },
{ "Level", true, true, false },
{ "Quest Completed", true, false, false },
- { "Near Creature", true, true, false },
+ { "Near Creature", true, true, true },
{ "Near GameObject", true, true, false },
{ "Object Entry or Guid", true, true, true },
{ "Object TypeMask", true, false, false },
@@ -280,7 +280,7 @@ bool Condition::Meets(ConditionSourceInfo& sourceInfo) const
}
case CONDITION_NEAR_CREATURE:
{
- condMeets = GetClosestCreatureWithEntry(object, ConditionValue1, (float)ConditionValue2) ? true : false;
+ condMeets = GetClosestCreatureWithEntry(object, ConditionValue1, (float)ConditionValue2, bool(!ConditionValue3)) ? true : false;
break;
}
case CONDITION_NEAR_GAMEOBJECT:
@@ -324,7 +324,7 @@ bool Condition::Meets(ConditionSourceInfo& sourceInfo) const
Unit* unit = object->ToUnit();
if (toUnit && unit)
{
- switch (ConditionValue2)
+ switch (static_cast<RelationType>(ConditionValue2))
{
case RELATION_SELF:
condMeets = unit == toUnit;
@@ -344,6 +344,8 @@ bool Condition::Meets(ConditionSourceInfo& sourceInfo) const
case RELATION_CREATED_BY:
condMeets = unit->GetCreatorGUID() == toUnit->GetGUID();
break;
+ default:
+ break;
}
}
}
diff --git a/src/server/game/Conditions/ConditionMgr.h b/src/server/game/Conditions/ConditionMgr.h
index 81fe379f0db..43454aba049 100644
--- a/src/server/game/Conditions/ConditionMgr.h
+++ b/src/server/game/Conditions/ConditionMgr.h
@@ -59,7 +59,7 @@ enum ConditionTypes
CONDITION_PHASEMASK = 26, // phasemask 0 0 true if object is in phasemask
CONDITION_LEVEL = 27, // level ComparisonType 0 true if unit's level is equal to param1 (param2 can modify the statement)
CONDITION_QUEST_COMPLETE = 28, // quest_id 0 0 true if player has quest_id with all objectives complete, but not yet rewarded
- CONDITION_NEAR_CREATURE = 29, // creature entry distance 0 true if there is a creature of entry in range
+ CONDITION_NEAR_CREATURE = 29, // creature entry distance dead (0/1) true if there is a creature of entry in range
CONDITION_NEAR_GAMEOBJECT = 30, // gameobject entry distance 0 true if there is a gameobject of entry in range
CONDITION_OBJECT_ENTRY_GUID = 31, // TypeID entry guid true if object is type TypeID and the entry is 0 or matches entry of the object or matches guid of the object
CONDITION_TYPE_MASK = 32, // TypeMask 0 0 true if object is type object's TypeMask matches provided TypeMask
diff --git a/src/server/game/DataStores/DBCStructure.h b/src/server/game/DataStores/DBCStructure.h
index dc4ad55d9c2..a419864a194 100644
--- a/src/server/game/DataStores/DBCStructure.h
+++ b/src/server/game/DataStores/DBCStructure.h
@@ -809,7 +809,7 @@ struct CreatureModelDataEntry
{
uint32 Id;
uint32 Flags;
- //char* ModelPath[16]
+ char* ModelPath;
//uint32 Unk1;
float Scale; // Used in calculation of unit collision data
//int32 Unk2
diff --git a/src/server/game/DataStores/DBCfmt.h b/src/server/game/DataStores/DBCfmt.h
index c75c0fff625..e81db5924f6 100644
--- a/src/server/game/DataStores/DBCfmt.h
+++ b/src/server/game/DataStores/DBCfmt.h
@@ -42,7 +42,7 @@ char const CinematicSequencesEntryfmt[] = "nxxxxxxxxx";
char const CreatureDisplayInfofmt[] = "nixifxxxxxxxxxxx";
char const CreatureDisplayInfoExtrafmt[] = "diixxxxxxxxxxxxxxxxxx";
char const CreatureFamilyfmt[] = "nfifiiiiixssssssssssssssssxx";
-char const CreatureModelDatafmt[] = "nixxfxxxxxxxxxxffxxxxxxxxxxx";
+char const CreatureModelDatafmt[] = "nisxfxxxxxxxxxxffxxxxxxxxxxx";
char const CreatureSpellDatafmt[] = "niiiixxxx";
char const CreatureTypefmt[] = "nxxxxxxxxxxxxxxxxxx";
char const CurrencyTypesfmt[] = "xnxi";
diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp
index 9173d44ae86..6ba8fb44e09 100644
--- a/src/server/game/Entities/Creature/Creature.cpp
+++ b/src/server/game/Entities/Creature/Creature.cpp
@@ -103,6 +103,48 @@ uint32 CreatureTemplate::GetFirstValidModelId() const
return 0;
}
+uint32 CreatureTemplate::GetFirstInvisibleModel() const
+{
+ CreatureModelInfo const* modelInfo = sObjectMgr->GetCreatureModelInfo(Modelid1);
+ if (modelInfo && modelInfo->is_trigger)
+ return Modelid1;
+
+ modelInfo = sObjectMgr->GetCreatureModelInfo(Modelid2);
+ if (modelInfo && modelInfo->is_trigger)
+ return Modelid2;
+
+ modelInfo = sObjectMgr->GetCreatureModelInfo(Modelid3);
+ if (modelInfo && modelInfo->is_trigger)
+ return Modelid3;
+
+ modelInfo = sObjectMgr->GetCreatureModelInfo(Modelid4);
+ if (modelInfo && modelInfo->is_trigger)
+ return Modelid4;
+
+ return 11686;
+}
+
+uint32 CreatureTemplate::GetFirstVisibleModel() const
+{
+ CreatureModelInfo const* modelInfo = sObjectMgr->GetCreatureModelInfo(Modelid1);
+ if (modelInfo && !modelInfo->is_trigger)
+ return Modelid1;
+
+ modelInfo = sObjectMgr->GetCreatureModelInfo(Modelid2);
+ if (modelInfo && !modelInfo->is_trigger)
+ return Modelid2;
+
+ modelInfo = sObjectMgr->GetCreatureModelInfo(Modelid3);
+ if (modelInfo && !modelInfo->is_trigger)
+ return Modelid3;
+
+ modelInfo = sObjectMgr->GetCreatureModelInfo(Modelid4);
+ if (modelInfo && !modelInfo->is_trigger)
+ return Modelid4;
+
+ return 17519;
+}
+
bool AssistDelayEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/)
{
if (Unit* victim = ObjectAccessor::GetUnit(m_owner, m_victim))
@@ -440,8 +482,8 @@ bool Creature::UpdateEntry(uint32 entry, CreatureData const* data /*= nullptr*/)
ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_ATTACK_ME, true);
}
- LoadCreaturesAddon();
UpdateMovementFlags();
+ LoadCreaturesAddon();
return true;
}
diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h
index 9a41c8570ed..966944e099f 100644
--- a/src/server/game/Entities/Creature/Creature.h
+++ b/src/server/game/Entities/Creature/Creature.h
@@ -137,6 +137,8 @@ struct CreatureTemplate
uint32 ScriptID;
uint32 GetRandomValidModelId() const;
uint32 GetFirstValidModelId() const;
+ uint32 GetFirstInvisibleModel() const;
+ uint32 GetFirstVisibleModel() const;
// helpers
SkillType GetRequiredLootSkill() const
@@ -278,6 +280,7 @@ struct CreatureModelInfo
float combat_reach;
uint8 gender;
uint32 modelid_other_gender;
+ bool is_trigger;
};
// Benchmarked: Faster than std::map (insert/find)
diff --git a/src/server/game/Entities/Item/Item.cpp b/src/server/game/Entities/Item/Item.cpp
index 19cf9beff21..1b7914fd85f 100644
--- a/src/server/game/Entities/Item/Item.cpp
+++ b/src/server/game/Entities/Item/Item.cpp
@@ -28,6 +28,7 @@
#include "ConditionMgr.h"
#include "Player.h"
#include "WorldSession.h"
+#include "TradeData.h"
void AddItemsSetItem(Player* player, Item* item)
{
diff --git a/src/server/game/Entities/Pet/Pet.cpp b/src/server/game/Entities/Pet/Pet.cpp
index 0121194e327..a399600d2f2 100644
--- a/src/server/game/Entities/Pet/Pet.cpp
+++ b/src/server/game/Entities/Pet/Pet.cpp
@@ -52,7 +52,7 @@ Pet::Pet(Player* owner, PetType type) :
}
m_name = "Pet";
- m_regenTimer = PET_FOCUS_REGEN_INTERVAL;
+ m_focusRegenTimer = PET_FOCUS_REGEN_INTERVAL;
}
Pet::~Pet()
@@ -589,22 +589,22 @@ void Pet::Update(uint32 diff)
}
//regenerate focus for hunter pets or energy for deathknight's ghoul
- if (m_regenTimer)
+ if (m_focusRegenTimer)
{
- if (m_regenTimer > diff)
- m_regenTimer -= diff;
+ if (m_focusRegenTimer > diff)
+ m_focusRegenTimer -= diff;
else
{
switch (getPowerType())
{
case POWER_FOCUS:
Regenerate(POWER_FOCUS);
- m_regenTimer += PET_FOCUS_REGEN_INTERVAL - diff;
- if (!m_regenTimer) ++m_regenTimer;
+ m_focusRegenTimer += PET_FOCUS_REGEN_INTERVAL - diff;
+ if (!m_focusRegenTimer) ++m_focusRegenTimer;
// Reset if large diff (lag) causes focus to get 'stuck'
- if (m_regenTimer > PET_FOCUS_REGEN_INTERVAL)
- m_regenTimer = PET_FOCUS_REGEN_INTERVAL;
+ if (m_focusRegenTimer > PET_FOCUS_REGEN_INTERVAL)
+ m_focusRegenTimer = PET_FOCUS_REGEN_INTERVAL;
break;
@@ -615,7 +615,7 @@ void Pet::Update(uint32 diff)
// if (!m_regenTimer) ++m_regenTimer;
// break;
default:
- m_regenTimer = 0;
+ m_focusRegenTimer = 0;
break;
}
}
diff --git a/src/server/game/Entities/Pet/Pet.h b/src/server/game/Entities/Pet/Pet.h
index 85c1f83d4a7..c8e95a06a2c 100644
--- a/src/server/game/Entities/Pet/Pet.h
+++ b/src/server/game/Entities/Pet/Pet.h
@@ -152,7 +152,7 @@ class Pet : public Guardian
int32 m_duration; // time until unsummon (used mostly for summoned guardians and not used for controlled pets)
uint64 m_auraRaidUpdateMask;
bool m_loading;
- uint32 m_regenTimer;
+ uint32 m_focusRegenTimer;
DeclinedName *m_declinedname;
diff --git a/src/server/game/Entities/Player/KillRewarder.cpp b/src/server/game/Entities/Player/KillRewarder.cpp
new file mode 100644
index 00000000000..ad2f8f641ea
--- /dev/null
+++ b/src/server/game/Entities/Player/KillRewarder.cpp
@@ -0,0 +1,274 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "KillRewarder.h"
+#include "SpellAuraEffects.h"
+#include "Creature.h"
+#include "Formulas.h"
+#include "Group.h"
+#include "Guild.h"
+#include "GuildMgr.h"
+#include "InstanceScript.h"
+#include "Pet.h"
+#include "Player.h"
+
+ // == KillRewarder ====================================================
+ // KillRewarder encapsulates logic of rewarding player upon kill with:
+ // * XP;
+ // * honor;
+ // * reputation;
+ // * kill credit (for quest objectives).
+ // Rewarding is initiated in two cases: when player kills unit in Unit::Kill()
+ // and on battlegrounds in Battleground::RewardXPAtKill().
+ //
+ // Rewarding algorithm is:
+ // 1. Initialize internal variables to default values.
+ // 2. In case when player is in group, initialize variables necessary for group calculations:
+ // 2.1. _count - number of alive group members within reward distance;
+ // 2.2. _sumLevel - sum of levels of alive group members within reward distance;
+ // 2.3. _maxLevel - maximum level of alive group member within reward distance;
+ // 2.4. _maxNotGrayMember - maximum level of alive group member within reward distance,
+ // for whom victim is not gray;
+ // 2.5. _isFullXP - flag identifying that for all group members victim is not gray,
+ // so 100% XP will be rewarded (50% otherwise).
+ // 3. Reward killer (and group, if necessary).
+ // 3.1. If killer is in group, reward group.
+ // 3.1.1. Initialize initial XP amount based on maximum level of group member,
+ // for whom victim is not gray.
+ // 3.1.2. Alter group rate if group is in raid (not for battlegrounds).
+ // 3.1.3. Reward each group member (even dead) within reward distance (see 4. for more details).
+ // 3.2. Reward single killer (not group case).
+ // 3.2.1. Initialize initial XP amount based on killer's level.
+ // 3.2.2. Reward killer (see 4. for more details).
+ // 4. Reward player.
+ // 4.1. Give honor (player must be alive and not on BG).
+ // 4.2. Give XP.
+ // 4.2.1. If player is in group, adjust XP:
+ // * set to 0 if player's level is more than maximum level of not gray member;
+ // * cut XP in half if _isFullXP is false.
+ // 4.2.2. Apply auras modifying rewarded XP.
+ // 4.2.3. Give XP to player.
+ // 4.2.4. If player has pet, reward pet with XP (100% for single player, 50% for group case).
+ // 4.3. Give reputation (player must not be on BG).
+ // 4.4. Give kill credit (player must not be in group, or he must be alive or without corpse).
+ // 5. Credit instance encounter.
+
+KillRewarder::KillRewarder(Player* killer, Unit* victim, bool isBattleGround) :
+ // 1. Initialize internal variables to default values.
+ _killer(killer), _victim(victim), _group(killer->GetGroup()),
+ _groupRate(1.0f), _maxNotGrayMember(nullptr), _count(0), _sumLevel(0), _xp(0),
+ _isFullXP(false), _maxLevel(0), _isBattleGround(isBattleGround), _isPvP(false)
+{
+ // mark the credit as pvp if victim is player
+ if (victim->GetTypeId() == TYPEID_PLAYER)
+ _isPvP = true;
+ // or if its owned by player and its not a vehicle
+ else if (victim->GetCharmerOrOwnerGUID().IsPlayer())
+ _isPvP = !victim->IsVehicle();
+
+ _InitGroupData();
+}
+
+inline void KillRewarder::_InitGroupData()
+{
+ if (_group)
+ {
+ // 2. In case when player is in group, initialize variables necessary for group calculations:
+ for (GroupReference* itr = _group->GetFirstMember(); itr != nullptr; itr = itr->next())
+ if (Player* member = itr->GetSource())
+ if (member->IsAlive() && member->IsAtGroupRewardDistance(_victim))
+ {
+ const uint8 lvl = member->getLevel();
+ // 2.1. _count - number of alive group members within reward distance;
+ ++_count;
+ // 2.2. _sumLevel - sum of levels of alive group members within reward distance;
+ _sumLevel += lvl;
+ // 2.3. _maxLevel - maximum level of alive group member within reward distance;
+ if (_maxLevel < lvl)
+ _maxLevel = lvl;
+ // 2.4. _maxNotGrayMember - maximum level of alive group member within reward distance,
+ // for whom victim is not gray;
+ uint32 grayLevel = Trinity::XP::GetGrayLevel(lvl);
+ if (_victim->getLevel() > grayLevel && (!_maxNotGrayMember || _maxNotGrayMember->getLevel() < lvl))
+ _maxNotGrayMember = member;
+ }
+ // 2.5. _isFullXP - flag identifying that for all group members victim is not gray,
+ // so 100% XP will be rewarded (50% otherwise).
+ _isFullXP = _maxNotGrayMember && (_maxLevel == _maxNotGrayMember->getLevel());
+ }
+ else
+ _count = 1;
+}
+
+inline void KillRewarder::_InitXP(Player* player)
+{
+ // Get initial value of XP for kill.
+ // XP is given:
+ // * on battlegrounds;
+ // * otherwise, not in PvP;
+ // * not if killer is on vehicle.
+ if (_isBattleGround || (!_isPvP && !_killer->GetVehicle()))
+ _xp = Trinity::XP::Gain(player, _victim, _isBattleGround);
+}
+
+inline void KillRewarder::_RewardHonor(Player* player)
+{
+ // Rewarded player must be alive.
+ if (player->IsAlive())
+ player->RewardHonor(_victim, _count, -1, true);
+}
+
+inline void KillRewarder::_RewardXP(Player* player, float rate)
+{
+ uint32 xp(_xp);
+ if (_group)
+ {
+ // 4.2.1. If player is in group, adjust XP:
+ // * set to 0 if player's level is more than maximum level of not gray member;
+ // * cut XP in half if _isFullXP is false.
+ if (_maxNotGrayMember && player->IsAlive() &&
+ _maxNotGrayMember->getLevel() >= player->getLevel())
+ xp = _isFullXP ?
+ uint32(xp * rate) : // Reward FULL XP if all group members are not gray.
+ uint32(xp * rate / 2) + 1; // Reward only HALF of XP if some of group members are gray.
+ else
+ xp = 0;
+ }
+ if (xp)
+ {
+ // 4.2.2. Apply auras modifying rewarded XP (SPELL_AURA_MOD_XP_PCT).
+ for (auto const& aura : player->GetAuraEffectsByType(SPELL_AURA_MOD_XP_PCT))
+ AddPct(xp, aura->GetAmount());
+
+ // 4.2.3. Give XP to player.
+ player->GiveXP(xp, _victim, _groupRate);
+ if (Pet* pet = player->GetPet())
+ // 4.2.4. If player has pet, reward pet with XP (100% for single player, 50% for group case).
+ pet->GivePetXP(_group ? xp / 2 : xp);
+ }
+}
+
+inline void KillRewarder::_RewardReputation(Player* player, float rate)
+{
+ // 4.3. Give reputation (player must not be on BG).
+ // Even dead players and corpses are rewarded.
+ player->RewardReputation(_victim, rate);
+}
+
+inline void KillRewarder::_RewardKillCredit(Player* player)
+{
+ // 4.4. Give kill credit (player must not be in group, or he must be alive or without corpse).
+ if (!_group || player->IsAlive() || !player->GetCorpse())
+ if (Creature* target = _victim->ToCreature())
+ {
+ player->KilledMonster(target->GetCreatureTemplate(), target->GetGUID());
+ player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE_TYPE, target->GetCreatureType(), 1, target);
+ }
+}
+
+void KillRewarder::_RewardPlayer(Player* player, bool isDungeon)
+{
+ // 4. Reward player.
+ if (!_isBattleGround)
+ {
+ // 4.1. Give honor (player must be alive and not on BG).
+ _RewardHonor(player);
+ // 4.1.1 Send player killcredit for quests with PlayerSlain
+ if (_victim->GetTypeId() == TYPEID_PLAYER)
+ player->KilledPlayerCredit();
+ }
+ // Give XP only in PvE or in battlegrounds.
+ // Give reputation and kill credit only in PvE.
+ if (!_isPvP || _isBattleGround)
+ {
+ float const rate = _group ?
+ _groupRate * float(player->getLevel()) / _sumLevel : // Group rate depends on summary level.
+ 1.0f; // Personal rate is 100%.
+ if (_xp)
+ // 4.2. Give XP.
+ _RewardXP(player, rate);
+ if (!_isBattleGround)
+ {
+ // If killer is in dungeon then all members receive full reputation at kill.
+ _RewardReputation(player, isDungeon ? 1.0f : rate);
+ _RewardKillCredit(player);
+ }
+ }
+}
+
+void KillRewarder::_RewardGroup()
+{
+ if (_maxLevel)
+ {
+ if (_maxNotGrayMember)
+ // 3.1.1. Initialize initial XP amount based on maximum level of group member,
+ // for whom victim is not gray.
+ _InitXP(_maxNotGrayMember);
+ // To avoid unnecessary calculations and calls,
+ // proceed only if XP is not ZERO or player is not on battleground
+ // (battleground rewards only XP, that's why).
+ if (!_isBattleGround || _xp)
+ {
+ bool const isDungeon = !_isPvP && sMapStore.LookupEntry(_killer->GetMapId())->IsDungeon();
+ if (!_isBattleGround)
+ {
+ // 3.1.2. Alter group rate if group is in raid (not for battlegrounds).
+ bool const isRaid = !_isPvP && sMapStore.LookupEntry(_killer->GetMapId())->IsRaid() && _group->isRaidGroup();
+ _groupRate = Trinity::XP::xp_in_group_rate(_count, isRaid);
+ }
+
+ // 3.1.3. Reward each group member (even dead or corpse) within reward distance.
+ for (GroupReference* itr = _group->GetFirstMember(); itr != nullptr; itr = itr->next())
+ {
+ if (Player* member = itr->GetSource())
+ {
+ if (member->IsAtGroupRewardDistance(_victim))
+ {
+ _RewardPlayer(member, isDungeon);
+ member->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_SPECIAL_PVP_KILL, 1, 0, _victim);
+ }
+ }
+ }
+ }
+ }
+}
+
+void KillRewarder::Reward()
+{
+ // 3. Reward killer (and group, if necessary).
+ if (_group)
+ // 3.1. If killer is in group, reward group.
+ _RewardGroup();
+ else
+ {
+ // 3.2. Reward single killer (not group case).
+ // 3.2.1. Initialize initial XP amount based on killer's level.
+ _InitXP(_killer);
+ // To avoid unnecessary calculations and calls,
+ // proceed only if XP is not ZERO or player is not on battleground
+ // (battleground rewards only XP, that's why).
+ if (!_isBattleGround || _xp)
+ // 3.2.2. Reward killer.
+ _RewardPlayer(_killer, false);
+ }
+
+ // 5. Credit instance encounter.
+ if (Creature* victim = _victim->ToCreature())
+ if (victim->IsDungeonBoss())
+ if (InstanceScript* instance = _victim->GetInstanceScript())
+ instance->UpdateEncounterState(ENCOUNTER_CREDIT_KILL_CREATURE, _victim->GetEntry(), _victim);
+}
diff --git a/src/server/game/Entities/Player/KillRewarder.h b/src/server/game/Entities/Player/KillRewarder.h
new file mode 100644
index 00000000000..577a8ffea20
--- /dev/null
+++ b/src/server/game/Entities/Player/KillRewarder.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef KillRewarder_h__
+#define KillRewarder_h__
+
+#include "Define.h"
+
+class Player;
+class Unit;
+class Group;
+
+class KillRewarder
+{
+public:
+ KillRewarder(Player* killer, Unit* victim, bool isBattleGround);
+
+ void Reward();
+
+private:
+ void _InitXP(Player* player);
+ void _InitGroupData();
+
+ void _RewardHonor(Player* player);
+ void _RewardXP(Player* player, float rate);
+ void _RewardReputation(Player* player, float rate);
+ void _RewardKillCredit(Player* player);
+ void _RewardPlayer(Player* player, bool isDungeon);
+ void _RewardGroup();
+
+ Player* _killer;
+ Unit* _victim;
+ Group* _group;
+ float _groupRate;
+ Player* _maxNotGrayMember;
+ uint32 _count;
+ uint32 _sumLevel;
+ uint32 _xp;
+ bool _isFullXP;
+ uint8 _maxLevel;
+ bool _isBattleGround;
+ bool _isPvP;
+};
+
+#endif // KillRewarder_h__
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index 3d11e786d52..ceab7d06c96 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -48,6 +48,7 @@
#include "GuildMgr.h"
#include "InstanceSaveMgr.h"
#include "InstanceScript.h"
+#include "KillRewarder.h"
#include "LFGMgr.h"
#include "Language.h"
#include "Log.h"
@@ -289,374 +290,6 @@ std::ostringstream& operator<< (std::ostringstream& ss, PlayerTaxi const& taxi)
return ss;
}
-//== TradeData =================================================
-
-TradeData* TradeData::GetTraderData() const
-{
- return m_trader->GetTradeData();
-}
-
-Item* TradeData::GetItem(TradeSlots slot) const
-{
- return m_items[slot] ? m_player->GetItemByGuid(m_items[slot]) : NULL;
-}
-
-bool TradeData::HasItem(ObjectGuid itemGuid) const
-{
- for (uint8 i = 0; i < TRADE_SLOT_COUNT; ++i)
- if (m_items[i] == itemGuid)
- return true;
-
- return false;
-}
-
-TradeSlots TradeData::GetTradeSlotForItem(ObjectGuid itemGuid) const
-{
- for (uint8 i = 0; i < TRADE_SLOT_COUNT; ++i)
- if (m_items[i] == itemGuid)
- return TradeSlots(i);
-
- return TRADE_SLOT_INVALID;
-}
-
-Item* TradeData::GetSpellCastItem() const
-{
- return m_spellCastItem ? m_player->GetItemByGuid(m_spellCastItem) : NULL;
-}
-
-void TradeData::SetItem(TradeSlots slot, Item* item, bool update /*= false*/)
-{
- ObjectGuid itemGuid;
- if (item)
- itemGuid = item->GetGUID();
-
- if (m_items[slot] == itemGuid && !update)
- return;
-
- m_items[slot] = itemGuid;
-
- SetAccepted(false);
- GetTraderData()->SetAccepted(false);
-
- Update();
-
- // need remove possible trader spell applied to changed item
- if (slot == TRADE_SLOT_NONTRADED)
- GetTraderData()->SetSpell(0);
-
- // need remove possible player spell applied (possible move reagent)
- SetSpell(0);
-}
-
-void TradeData::SetSpell(uint32 spell_id, Item* castItem /*= NULL*/)
-{
- ObjectGuid itemGuid = castItem ? castItem->GetGUID() : ObjectGuid::Empty;
-
- if (m_spell == spell_id && m_spellCastItem == itemGuid)
- return;
-
- m_spell = spell_id;
- m_spellCastItem = itemGuid;
-
- SetAccepted(false);
- GetTraderData()->SetAccepted(false);
-
- Update(true); // send spell info to item owner
- Update(false); // send spell info to caster self
-}
-
-void TradeData::SetMoney(uint32 money)
-{
- if (m_money == money)
- return;
-
- if (!m_player->HasEnoughMoney(money))
- {
- TradeStatusInfo info;
- info.Status = TRADE_STATUS_CLOSE_WINDOW;
- info.Result = EQUIP_ERR_NOT_ENOUGH_MONEY;
- m_player->GetSession()->SendTradeStatus(info);
- return;
- }
-
- m_money = money;
-
- SetAccepted(false);
- GetTraderData()->SetAccepted(false);
-
- Update(true);
-}
-
-void TradeData::Update(bool forTarget /*= true*/)
-{
- if (forTarget)
- m_trader->GetSession()->SendUpdateTrade(true); // player state for trader
- else
- m_player->GetSession()->SendUpdateTrade(false); // player state for player
-}
-
-void TradeData::SetAccepted(bool state, bool crosssend /*= false*/)
-{
- m_accepted = state;
-
- if (!state)
- {
- TradeStatusInfo info;
- info.Status = TRADE_STATUS_BACK_TO_TRADE;
- if (crosssend)
- m_trader->GetSession()->SendTradeStatus(info);
- else
- m_player->GetSession()->SendTradeStatus(info);
- }
-}
-
-// == KillRewarder ====================================================
-// KillRewarder incapsulates logic of rewarding player upon kill with:
-// * XP;
-// * honor;
-// * reputation;
-// * kill credit (for quest objectives).
-// Rewarding is initiated in two cases: when player kills unit in Unit::Kill()
-// and on battlegrounds in Battleground::RewardXPAtKill().
-//
-// Rewarding algorithm is:
-// 1. Initialize internal variables to default values.
-// 2. In case when player is in group, initialize variables necessary for group calculations:
-// 2.1. _count - number of alive group members within reward distance;
-// 2.2. _sumLevel - sum of levels of alive group members within reward distance;
-// 2.3. _maxLevel - maximum level of alive group member within reward distance;
-// 2.4. _maxNotGrayMember - maximum level of alive group member within reward distance,
-// for whom victim is not gray;
-// 2.5. _isFullXP - flag identifying that for all group members victim is not gray,
-// so 100% XP will be rewarded (50% otherwise).
-// 3. Reward killer (and group, if necessary).
-// 3.1. If killer is in group, reward group.
-// 3.1.1. Initialize initial XP amount based on maximum level of group member,
-// for whom victim is not gray.
-// 3.1.2. Alter group rate if group is in raid (not for battlegrounds).
-// 3.1.3. Reward each group member (even dead) within reward distance (see 4. for more details).
-// 3.2. Reward single killer (not group case).
-// 3.2.1. Initialize initial XP amount based on killer's level.
-// 3.2.2. Reward killer (see 4. for more details).
-// 4. Reward player.
-// 4.1. Give honor (player must be alive and not on BG).
-// 4.2. Give XP.
-// 4.2.1. If player is in group, adjust XP:
-// * set to 0 if player's level is more than maximum level of not gray member;
-// * cut XP in half if _isFullXP is false.
-// 4.2.2. Apply auras modifying rewarded XP.
-// 4.2.3. Give XP to player.
-// 4.2.4. If player has pet, reward pet with XP (100% for single player, 50% for group case).
-// 4.3. Give reputation (player must not be on BG).
-// 4.4. Give kill credit (player must not be in group, or he must be alive or without corpse).
-// 5. Credit instance encounter.
-KillRewarder::KillRewarder(Player* killer, Unit* victim, bool isBattleGround) :
- // 1. Initialize internal variables to default values.
- _killer(killer), _victim(victim), _group(killer->GetGroup()),
- _groupRate(1.0f), _maxNotGrayMember(NULL), _count(0), _sumLevel(0), _xp(0),
- _isFullXP(false), _maxLevel(0), _isBattleGround(isBattleGround), _isPvP(false)
-{
- // mark the credit as pvp if victim is player
- if (victim->GetTypeId() == TYPEID_PLAYER)
- _isPvP = true;
- // or if its owned by player and its not a vehicle
- else if (victim->GetCharmerOrOwnerGUID().IsPlayer())
- _isPvP = !victim->IsVehicle();
-
- _InitGroupData();
-}
-
-inline void KillRewarder::_InitGroupData()
-{
- if (_group)
- {
- // 2. In case when player is in group, initialize variables necessary for group calculations:
- for (GroupReference* itr = _group->GetFirstMember(); itr != NULL; itr = itr->next())
- if (Player* member = itr->GetSource())
- if (member->IsAlive() && member->IsAtGroupRewardDistance(_victim))
- {
- const uint8 lvl = member->getLevel();
- // 2.1. _count - number of alive group members within reward distance;
- ++_count;
- // 2.2. _sumLevel - sum of levels of alive group members within reward distance;
- _sumLevel += lvl;
- // 2.3. _maxLevel - maximum level of alive group member within reward distance;
- if (_maxLevel < lvl)
- _maxLevel = lvl;
- // 2.4. _maxNotGrayMember - maximum level of alive group member within reward distance,
- // for whom victim is not gray;
- uint32 grayLevel = Trinity::XP::GetGrayLevel(lvl);
- if (_victim->getLevel() > grayLevel && (!_maxNotGrayMember || _maxNotGrayMember->getLevel() < lvl))
- _maxNotGrayMember = member;
- }
- // 2.5. _isFullXP - flag identifying that for all group members victim is not gray,
- // so 100% XP will be rewarded (50% otherwise).
- _isFullXP = _maxNotGrayMember && (_maxLevel == _maxNotGrayMember->getLevel());
- }
- else
- _count = 1;
-}
-
-inline void KillRewarder::_InitXP(Player* player)
-{
- // Get initial value of XP for kill.
- // XP is given:
- // * on battlegrounds;
- // * otherwise, not in PvP;
- // * not if killer is on vehicle.
- if (_isBattleGround || (!_isPvP && !_killer->GetVehicle()))
- _xp = Trinity::XP::Gain(player, _victim, _isBattleGround);
-}
-
-inline void KillRewarder::_RewardHonor(Player* player)
-{
- // Rewarded player must be alive.
- if (player->IsAlive())
- player->RewardHonor(_victim, _count, -1, true);
-}
-
-inline void KillRewarder::_RewardXP(Player* player, float rate)
-{
- uint32 xp(_xp);
- if (_group)
- {
- // 4.2.1. If player is in group, adjust XP:
- // * set to 0 if player's level is more than maximum level of not gray member;
- // * cut XP in half if _isFullXP is false.
- if (_maxNotGrayMember && player->IsAlive() &&
- _maxNotGrayMember->getLevel() >= player->getLevel())
- xp = _isFullXP ?
- uint32(xp * rate) : // Reward FULL XP if all group members are not gray.
- uint32(xp * rate / 2) + 1; // Reward only HALF of XP if some of group members are gray.
- else
- xp = 0;
- }
- if (xp)
- {
- // 4.2.2. Apply auras modifying rewarded XP (SPELL_AURA_MOD_XP_PCT).
- Unit::AuraEffectList const& auras = player->GetAuraEffectsByType(SPELL_AURA_MOD_XP_PCT);
- for (Unit::AuraEffectList::const_iterator i = auras.begin(); i != auras.end(); ++i)
- AddPct(xp, (*i)->GetAmount());
-
- // 4.2.3. Give XP to player.
- player->GiveXP(xp, _victim, _groupRate);
- if (Pet* pet = player->GetPet())
- // 4.2.4. If player has pet, reward pet with XP (100% for single player, 50% for group case).
- pet->GivePetXP(_group ? xp / 2 : xp);
- }
-}
-
-inline void KillRewarder::_RewardReputation(Player* player, float rate)
-{
- // 4.3. Give reputation (player must not be on BG).
- // Even dead players and corpses are rewarded.
- player->RewardReputation(_victim, rate);
-}
-
-inline void KillRewarder::_RewardKillCredit(Player* player)
-{
- // 4.4. Give kill credit (player must not be in group, or he must be alive or without corpse).
- if (!_group || player->IsAlive() || !player->GetCorpse())
- if (Creature* target = _victim->ToCreature())
- {
- player->KilledMonster(target->GetCreatureTemplate(), target->GetGUID());
- player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE_TYPE, target->GetCreatureType(), 1, target);
- }
-}
-
-void KillRewarder::_RewardPlayer(Player* player, bool isDungeon)
-{
- // 4. Reward player.
- if (!_isBattleGround)
- {
- // 4.1. Give honor (player must be alive and not on BG).
- _RewardHonor(player);
- // 4.1.1 Send player killcredit for quests with PlayerSlain
- if (_victim->GetTypeId() == TYPEID_PLAYER)
- player->KilledPlayerCredit();
- }
- // Give XP only in PvE or in battlegrounds.
- // Give reputation and kill credit only in PvE.
- if (!_isPvP || _isBattleGround)
- {
- const float rate = _group ?
- _groupRate * float(player->getLevel()) / _sumLevel : // Group rate depends on summary level.
- 1.0f; // Personal rate is 100%.
- if (_xp)
- // 4.2. Give XP.
- _RewardXP(player, rate);
- if (!_isBattleGround)
- {
- // If killer is in dungeon then all members receive full reputation at kill.
- _RewardReputation(player, isDungeon ? 1.0f : rate);
- _RewardKillCredit(player);
- }
- }
-}
-
-void KillRewarder::_RewardGroup()
-{
- if (_maxLevel)
- {
- if (_maxNotGrayMember)
- // 3.1.1. Initialize initial XP amount based on maximum level of group member,
- // for whom victim is not gray.
- _InitXP(_maxNotGrayMember);
- // To avoid unnecessary calculations and calls,
- // proceed only if XP is not ZERO or player is not on battleground
- // (battleground rewards only XP, that's why).
- if (!_isBattleGround || _xp)
- {
- const bool isDungeon = !_isPvP && sMapStore.LookupEntry(_killer->GetMapId())->IsDungeon();
- if (!_isBattleGround)
- {
- // 3.1.2. Alter group rate if group is in raid (not for battlegrounds).
- const bool isRaid = !_isPvP && sMapStore.LookupEntry(_killer->GetMapId())->IsRaid() && _group->isRaidGroup();
- _groupRate = Trinity::XP::xp_in_group_rate(_count, isRaid);
- }
-
- // 3.1.3. Reward each group member (even dead or corpse) within reward distance.
- for (GroupReference* itr = _group->GetFirstMember(); itr != NULL; itr = itr->next())
- {
- if (Player* member = itr->GetSource())
- {
- if (member->IsAtGroupRewardDistance(_victim))
- {
- _RewardPlayer(member, isDungeon);
- member->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_SPECIAL_PVP_KILL, 1, 0, _victim);
- }
- }
- }
- }
- }
-}
-
-void KillRewarder::Reward()
-{
- // 3. Reward killer (and group, if necessary).
- if (_group)
- // 3.1. If killer is in group, reward group.
- _RewardGroup();
- else
- {
- // 3.2. Reward single killer (not group case).
- // 3.2.1. Initialize initial XP amount based on killer's level.
- _InitXP(_killer);
- // To avoid unnecessary calculations and calls,
- // proceed only if XP is not ZERO or player is not on battleground
- // (battleground rewards only XP, that's why).
- if (!_isBattleGround || _xp)
- // 3.2.2. Reward killer.
- _RewardPlayer(_killer, false);
- }
-
- // 5. Credit instance encounter.
- if (Creature* victim = _victim->ToCreature())
- if (victim->IsDungeonBoss())
- if (InstanceScript* instance = _victim->GetInstanceScript())
- instance->UpdateEncounterState(ENCOUNTER_CREDIT_KILL_CREATURE, _victim->GetEntry(), _victim);
-}
-
Player::Player(WorldSession* session): Unit(true)
{
m_speakTime = 0;
@@ -902,6 +535,8 @@ Player::Player(WorldSession* session): Unit(true)
SetPendingBind(0, 0);
_activeCheats = CHEAT_NONE;
+ healthBeforeDuel = 0;
+ manaBeforeDuel = 0;
m_achievementMgr = new AchievementMgr(this);
m_reputationMgr = new ReputationMgr(this);
}
@@ -1900,6 +1535,7 @@ void Player::Update(uint32 p_time)
//because we don't want player's ghost teleported from graveyard
if (IsHasDelayedTeleport() && IsAlive())
TeleportTo(m_teleport_dest, m_teleport_options);
+
}
void Player::setDeathState(DeathState s)
@@ -2514,6 +2150,17 @@ void Player::RemoveFromWorld()
}
}
+bool Player::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index) const
+{
+ // players are immune to taunt (the aura and the spell effect)
+ if (spellInfo->Effects[index].IsAura(SPELL_AURA_MOD_TAUNT))
+ return true;
+ if (spellInfo->Effects[index].IsEffect(SPELL_EFFECT_ATTACK_ME))
+ return true;
+
+ return Unit::IsImmunedToSpellEffect(spellInfo, index);
+}
+
void Player::RegenerateAll()
{
//if (m_regenTimer <= 500)
@@ -2926,6 +2573,11 @@ void Player::SetGameMaster(bool on)
UpdateObjectVisibility();
}
+bool Player::CanBeGameMaster() const
+{
+ return GetSession()->HasPermission(rbac::RBAC_PERM_COMMAND_GM);
+}
+
void Player::SetGMVisible(bool on)
{
if (on)
@@ -5124,7 +4776,7 @@ Corpse* Player::CreateCorpse()
// prevent existence 2 corpse for player
SpawnCorpseBones();
- uint32 _uf, _pb, _pb2, _cfb1, _cfb2;
+ uint32 _pb, _pb2, _cfb1, _cfb2;
Corpse* corpse = new Corpse((m_ExtraFlags & PLAYER_EXTRA_PVP_DEATH) ? CORPSE_RESURRECTABLE_PVP : CORPSE_RESURRECTABLE_PVE);
SetPvPDeath(false);
@@ -5137,18 +4789,16 @@ Corpse* Player::CreateCorpse()
_corpseLocation.WorldRelocate(*this);
- _uf = GetUInt32Value(UNIT_FIELD_BYTES_0);
_pb = GetUInt32Value(PLAYER_BYTES);
_pb2 = GetUInt32Value(PLAYER_BYTES_2);
- uint8 race = (uint8)(_uf);
uint8 skin = (uint8)(_pb);
uint8 face = (uint8)(_pb >> 8);
uint8 hairstyle = (uint8)(_pb >> 16);
uint8 haircolor = (uint8)(_pb >> 24);
uint8 facialhair = (uint8)(_pb2);
- _cfb1 = ((0x00) | (race << 8) | (getGender() << 16) | (skin << 24));
+ _cfb1 = ((0x00) | (getRace() << 8) | (GetByteValue(PLAYER_BYTES_3, 0) << 16) | (skin << 24));
_cfb2 = ((face) | (hairstyle << 8) | (haircolor << 16) | (facialhair << 24));
corpse->SetUInt32Value(CORPSE_FIELD_BYTES_1, _cfb1);
@@ -6187,8 +5837,6 @@ void Player::UpdateCombatSkills(Unit* victim, WeaponAttackType attType, bool def
uint8 plevel = getLevel(); // if defense than victim == attacker
uint8 greylevel = Trinity::XP::GetGrayLevel(plevel);
uint8 moblevel = victim->getLevelForTarget(this);
- if (moblevel < greylevel)
- return;
if (moblevel > plevel + 5)
moblevel = plevel + 5;
@@ -13592,36 +13240,25 @@ void Player::TradeCancel(bool sendback)
void Player::UpdateSoulboundTradeItems()
{
- if (m_itemSoulboundTradeable.empty())
- return;
-
// also checks for garbage data
- for (ItemDurationList::iterator itr = m_itemSoulboundTradeable.begin(); itr != m_itemSoulboundTradeable.end();)
+ for (GuidUnorderedSet::iterator itr = m_itemSoulboundTradeable.begin(); itr != m_itemSoulboundTradeable.end();)
{
- ASSERT(*itr);
- if ((*itr)->GetOwnerGUID() != GetGUID())
- {
- m_itemSoulboundTradeable.erase(itr++);
- continue;
- }
- if ((*itr)->CheckSoulboundTradeExpire())
- {
- m_itemSoulboundTradeable.erase(itr++);
- continue;
- }
- ++itr;
+ Item* item = GetItemByGuid(*itr);
+ if (!item || item->GetOwnerGUID() != GetGUID() || item->CheckSoulboundTradeExpire())
+ itr = m_itemSoulboundTradeable.erase(itr);
+ else
+ ++itr;
}
}
void Player::AddTradeableItem(Item* item)
{
- m_itemSoulboundTradeable.push_back(item);
+ m_itemSoulboundTradeable.insert(item->GetGUID());
}
-/// @todo should never allow an item to be added to m_itemSoulboundTradeable twice
void Player::RemoveTradeableItem(Item* item)
{
- m_itemSoulboundTradeable.remove(item);
+ m_itemSoulboundTradeable.erase(item->GetGUID());
}
void Player::UpdateItemDuration(uint32 time, bool realtimeonly)
@@ -15927,8 +15564,9 @@ bool Player::GetQuestRewardStatus(uint32 quest_id) const
if (qInfo->IsSeasonal() && !qInfo->IsRepeatable())
{
uint16 eventId = sGameEventMgr->GetEventIdForQuest(qInfo);
- if (m_seasonalquests.find(eventId) != m_seasonalquests.end())
- return m_seasonalquests.find(eventId)->second.find(quest_id) != m_seasonalquests.find(eventId)->second.end();
+ auto seasonalQuestItr = m_seasonalquests.find(eventId);
+ if (seasonalQuestItr != m_seasonalquests.end())
+ return seasonalQuestItr->second.find(quest_id) != seasonalQuestItr->second.end();
return false;
}
@@ -15956,7 +15594,8 @@ QuestStatus Player::GetQuestStatus(uint32 quest_id) const
if (qInfo->IsSeasonal() && !qInfo->IsRepeatable())
{
uint16 eventId = sGameEventMgr->GetEventIdForQuest(qInfo);
- if (m_seasonalquests.find(eventId) == m_seasonalquests.end() || m_seasonalquests.find(eventId)->second.find(quest_id) == m_seasonalquests.find(eventId)->second.end())
+ auto seasonalQuestItr = m_seasonalquests.find(eventId);
+ if (seasonalQuestItr == m_seasonalquests.end() || seasonalQuestItr->second.find(quest_id) == seasonalQuestItr->second.end())
return QUEST_STATUS_NONE;
}
@@ -19037,17 +18676,28 @@ bool Player::CheckInstanceLoginValid(Map* map)
{
// cannot be in raid instance without a group
if (!GetGroup())
- return false;
+ return IsInstanceLoginGameMasterException();
}
else
{
// cannot be in normal instance without a group and more players than 1 in instance
if (!GetGroup() && map->GetPlayersCountExceptGMs() > 1)
- return false;
+ return IsInstanceLoginGameMasterException();
}
// do checks for satisfy accessreqs, instance full, encounter in progress (raid), perm bind group != perm bind player
- return sMapMgr->CanPlayerEnter(map->GetId(), this, true);
+ return sMapMgr->CanPlayerEnter(map->GetId(), this, true) || IsInstanceLoginGameMasterException();
+}
+
+bool Player::IsInstanceLoginGameMasterException() const
+{
+ if (CanBeGameMaster())
+ {
+ ChatHandler(GetSession()).SendSysMessage(LANG_INSTANCE_LOGIN_GAMEMASTER_EXCEPTION);
+ return true;
+ }
+ else
+ return false;
}
bool Player::CheckInstanceCount(uint32 instanceId) const
@@ -21432,7 +21082,7 @@ void Player::InitDisplayIds()
return;
}
- uint8 gender = getGender();
+ uint8 gender = GetByteValue(PLAYER_BYTES_3, 0);
switch (gender)
{
case GENDER_FEMALE:
@@ -24182,7 +23832,7 @@ void Player::SetViewpoint(WorldObject* target, bool apply)
// farsight dynobj or puppet may be very far away
UpdateVisibilityOf(target);
- if (target->isType(TYPEMASK_UNIT) && !GetVehicle())
+ if (target->isType(TYPEMASK_UNIT) && target != GetVehicleBase())
((Unit*)target)->AddPlayerToVision(this);
}
else
@@ -24195,7 +23845,7 @@ void Player::SetViewpoint(WorldObject* target, bool apply)
return;
}
- if (target->isType(TYPEMASK_UNIT) && !GetVehicle())
+ if (target->isType(TYPEMASK_UNIT) && target != GetVehicleBase())
((Unit*)target)->RemovePlayerFromVision(this);
//must immediately set seer back otherwise may crash
diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h
index 7c0700fa244..26944de9770 100644
--- a/src/server/game/Entities/Player/Player.h
+++ b/src/server/game/Entities/Player/Player.h
@@ -29,6 +29,7 @@
#include "SpellMgr.h"
#include "SpellHistory.h"
#include "Unit.h"
+#include "TradeData.h"
#include <limits>
#include <string>
@@ -687,14 +688,6 @@ struct ItemPosCount
};
typedef std::vector<ItemPosCount> ItemPosCountVec;
-enum TradeSlots
-{
- TRADE_SLOT_COUNT = 7,
- TRADE_SLOT_TRADED_COUNT = 6,
- TRADE_SLOT_NONTRADED = 6,
- TRADE_SLOT_INVALID = -1
-};
-
enum TransferAbortReason
{
TRANSFER_ABORT_NONE = 0x00,
@@ -1007,88 +1000,6 @@ struct TradeStatusInfo
uint8 Slot;
};
-class TradeData
-{
- public: // constructors
- TradeData(Player* player, Player* trader) :
- m_player(player), m_trader(trader), m_accepted(false), m_acceptProccess(false),
- m_money(0), m_spell(0), m_spellCastItem() { }
-
- Player* GetTrader() const { return m_trader; }
- TradeData* GetTraderData() const;
-
- Item* GetItem(TradeSlots slot) const;
- bool HasItem(ObjectGuid itemGuid) const;
- TradeSlots GetTradeSlotForItem(ObjectGuid itemGuid) const;
- void SetItem(TradeSlots slot, Item* item, bool update = false);
-
- uint32 GetSpell() const { return m_spell; }
- void SetSpell(uint32 spell_id, Item* castItem = NULL);
-
- Item* GetSpellCastItem() const;
- bool HasSpellCastItem() const { return !m_spellCastItem.IsEmpty(); }
-
- uint32 GetMoney() const { return m_money; }
- void SetMoney(uint32 money);
-
- bool IsAccepted() const { return m_accepted; }
- void SetAccepted(bool state, bool crosssend = false);
-
- bool IsInAcceptProcess() const { return m_acceptProccess; }
- void SetInAcceptProcess(bool state) { m_acceptProccess = state; }
-
- private: // internal functions
-
- void Update(bool for_trader = true);
-
- private: // fields
-
- Player* m_player; // Player who own of this TradeData
- Player* m_trader; // Player who trade with m_player
-
- bool m_accepted; // m_player press accept for trade list
- bool m_acceptProccess; // one from player/trader press accept and this processed
-
- uint32 m_money; // m_player place money to trade
-
- uint32 m_spell; // m_player apply spell to non-traded slot item
- ObjectGuid m_spellCastItem; // applied spell cast by item use
-
- ObjectGuid m_items[TRADE_SLOT_COUNT]; // traded items from m_player side including non-traded slot
-};
-
-class KillRewarder
-{
-public:
- KillRewarder(Player* killer, Unit* victim, bool isBattleGround);
-
- void Reward();
-
-private:
- void _InitXP(Player* player);
- void _InitGroupData();
-
- void _RewardHonor(Player* player);
- void _RewardXP(Player* player, float rate);
- void _RewardReputation(Player* player, float rate);
- void _RewardKillCredit(Player* player);
- void _RewardPlayer(Player* player, bool isDungeon);
- void _RewardGroup();
-
- Player* _killer;
- Unit* _victim;
- Group* _group;
- float _groupRate;
- Player* _maxNotGrayMember;
- uint32 _count;
- uint32 _sumLevel;
- uint32 _xp;
- bool _isFullXP;
- uint8 _maxLevel;
- bool _isBattleGround;
- bool _isPvP;
-};
-
class Player : public Unit, public GridObject<Player>
{
friend class WorldSession;
@@ -1123,6 +1034,8 @@ class Player : public Unit, public GridObject<Player>
static bool BuildEnumData(PreparedQueryResult result, WorldPacket* data);
+ bool IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index) const override;
+
void SetInWater(bool apply);
bool IsInWater() const override { return m_isInWater; }
@@ -1161,6 +1074,7 @@ class Player : public Unit, public GridObject<Player>
bool isAcceptWhispers() const { return (m_ExtraFlags & PLAYER_EXTRA_ACCEPT_WHISPERS) != 0; }
void SetAcceptWhispers(bool on) { if (on) m_ExtraFlags |= PLAYER_EXTRA_ACCEPT_WHISPERS; else m_ExtraFlags &= ~PLAYER_EXTRA_ACCEPT_WHISPERS; }
bool IsGameMaster() const { return (m_ExtraFlags & PLAYER_EXTRA_GM_ON) != 0; }
+ bool CanBeGameMaster() const;
void SetGameMaster(bool on);
bool isGMChat() const { return (m_ExtraFlags & PLAYER_EXTRA_GM_CHAT) != 0; }
void SetGMChat(bool on) { if (on) m_ExtraFlags |= PLAYER_EXTRA_GM_CHAT; else m_ExtraFlags &= ~PLAYER_EXTRA_GM_CHAT; }
@@ -1325,7 +1239,7 @@ class Player : public Unit, public GridObject<Player>
float GetReputationPriceDiscount(Creature const* creature) const;
- Player* GetTrader() const { return m_trade ? m_trade->GetTrader() : NULL; }
+ Player* GetTrader() const { return m_trade ? m_trade->GetTrader() : nullptr; }
TradeData* GetTradeData() const { return m_trade; }
void TradeCancel(bool sendback);
@@ -1947,6 +1861,12 @@ class Player : public Unit, public GridObject<Player>
void SetHonorPoints(uint32 value);
void SetArenaPoints(uint32 value);
+ // duel health and mana reset methods
+ void SaveHealthBeforeDuel() { healthBeforeDuel = GetHealth(); }
+ void SaveManaBeforeDuel() { manaBeforeDuel = GetPower(POWER_MANA); }
+ void RestoreHealthAfterDuel() { SetHealth(healthBeforeDuel); }
+ void RestoreManaAfterDuel() { SetPower(POWER_MANA, manaBeforeDuel); }
+
//End of PvP System
void SetDrunkValue(uint8 newDrunkValue, uint32 itemId = 0);
@@ -2474,7 +2394,7 @@ class Player : public Unit, public GridObject<Player>
EnchantDurationList m_enchantDuration;
ItemDurationList m_itemDuration;
- ItemDurationList m_itemSoulboundTradeable;
+ GuidUnorderedSet m_itemSoulboundTradeable;
void ResetTimeSync();
void SendTimeSync();
@@ -2583,6 +2503,8 @@ class Player : public Unit, public GridObject<Player>
bool IsHasDelayedTeleport() const { return m_bHasDelayedTeleport; }
void SetDelayedTeleportFlag(bool setting) { m_bHasDelayedTeleport = setting; }
void ScheduleDelayedOperation(uint32 operation) { if (operation < DELAYED_END) m_DelayedOperations |= operation; }
+
+ bool IsInstanceLoginGameMasterException() const;
MapReference m_mapRef;
@@ -2630,6 +2552,10 @@ class Player : public Unit, public GridObject<Player>
uint32 _activeCheats;
+ // variables to save health and mana before duel and restore them after duel
+ uint32 healthBeforeDuel;
+ uint32 manaBeforeDuel;
+
WorldLocation _corpseLocation;
};
diff --git a/src/server/game/Entities/Player/TradeData.cpp b/src/server/game/Entities/Player/TradeData.cpp
new file mode 100644
index 00000000000..bbbd1c81773
--- /dev/null
+++ b/src/server/game/Entities/Player/TradeData.cpp
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "TradeData.h"
+#include "Player.h"
+#include "WorldSession.h"
+
+TradeData* TradeData::GetTraderData() const
+{
+ return _trader->GetTradeData();
+}
+
+Item* TradeData::GetItem(TradeSlots slot) const
+{
+ return !_items[slot].IsEmpty() ? _player->GetItemByGuid(_items[slot]) : nullptr;
+}
+
+bool TradeData::HasItem(ObjectGuid itemGuid) const
+{
+ for (uint8 i = 0; i < TRADE_SLOT_COUNT; ++i)
+ if (_items[i] == itemGuid)
+ return true;
+
+ return false;
+}
+
+TradeSlots TradeData::GetTradeSlotForItem(ObjectGuid itemGuid) const
+{
+ for (uint8 i = 0; i < TRADE_SLOT_COUNT; ++i)
+ if (_items[i] == itemGuid)
+ return TradeSlots(i);
+
+ return TRADE_SLOT_INVALID;
+}
+
+Item* TradeData::GetSpellCastItem() const
+{
+ return !_spellCastItem.IsEmpty() ? _player->GetItemByGuid(_spellCastItem) : nullptr;
+}
+
+void TradeData::SetItem(TradeSlots slot, Item* item, bool update /*= false*/)
+{
+ ObjectGuid itemGuid;
+ if (item)
+ itemGuid = item->GetGUID();
+
+ if (_items[slot] == itemGuid && !update)
+ return;
+
+ _items[slot] = itemGuid;
+
+ SetAccepted(false);
+ GetTraderData()->SetAccepted(false);
+
+ Update();
+
+ // need remove possible trader spell applied to changed item
+ if (slot == TRADE_SLOT_NONTRADED)
+ GetTraderData()->SetSpell(0);
+
+ // need remove possible player spell applied (possible move reagent)
+ SetSpell(0);
+}
+
+void TradeData::SetSpell(uint32 spell_id, Item* castItem /*= nullptr*/)
+{
+ ObjectGuid itemGuid = castItem ? castItem->GetGUID() : ObjectGuid::Empty;
+
+ if (_spell == spell_id && _spellCastItem == itemGuid)
+ return;
+
+ _spell = spell_id;
+ _spellCastItem = itemGuid;
+
+ SetAccepted(false);
+ GetTraderData()->SetAccepted(false);
+
+ Update(true); // send spell info to item owner
+ Update(false); // send spell info to caster self
+}
+
+void TradeData::SetMoney(uint32 money)
+{
+ if (_money == money)
+ return;
+
+ if (!_player->HasEnoughMoney(money))
+ {
+ TradeStatusInfo info;
+ info.Status = TRADE_STATUS_CLOSE_WINDOW;
+ info.Result = EQUIP_ERR_NOT_ENOUGH_MONEY;
+ _player->GetSession()->SendTradeStatus(info);
+ return;
+ }
+
+ _money = money;
+
+ SetAccepted(false);
+ GetTraderData()->SetAccepted(false);
+
+ Update(true);
+}
+
+void TradeData::Update(bool forTrader /*= true*/) const
+{
+ if (forTrader)
+ _trader->GetSession()->SendUpdateTrade(true); // player state for trader
+ else
+ _player->GetSession()->SendUpdateTrade(false); // player state for player
+}
+
+void TradeData::SetAccepted(bool state, bool forTrader /*= false*/)
+{
+ _accepted = state;
+
+ if (!state)
+ {
+ TradeStatusInfo info;
+ info.Status = TRADE_STATUS_BACK_TO_TRADE;
+ if (forTrader)
+ _trader->GetSession()->SendTradeStatus(info);
+ else
+ _player->GetSession()->SendTradeStatus(info);
+ }
+}
diff --git a/src/server/game/Entities/Player/TradeData.h b/src/server/game/Entities/Player/TradeData.h
new file mode 100644
index 00000000000..cfaf066bde0
--- /dev/null
+++ b/src/server/game/Entities/Player/TradeData.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef TradeData_h__
+#define TradeData_h__
+
+#include "ObjectGuid.h"
+
+enum TradeSlots
+{
+ TRADE_SLOT_COUNT = 7,
+ TRADE_SLOT_TRADED_COUNT = 6,
+ TRADE_SLOT_NONTRADED = 6,
+ TRADE_SLOT_INVALID = -1
+};
+
+class Item;
+class Player;
+
+class TradeData
+{
+public:
+ TradeData(Player* player, Player* trader) :
+ _player(player), _trader(trader), _accepted(false), _acceptProccess(false),
+ _money(0), _spell(0), _spellCastItem() { }
+
+ Player* GetTrader() const { return _trader; }
+ TradeData* GetTraderData() const;
+
+ Item* GetItem(TradeSlots slot) const;
+ bool HasItem(ObjectGuid itemGuid) const;
+ TradeSlots GetTradeSlotForItem(ObjectGuid itemGuid) const;
+ void SetItem(TradeSlots slot, Item* item, bool update = false);
+
+ uint32 GetSpell() const { return _spell; }
+ void SetSpell(uint32 spell_id, Item* castItem = nullptr);
+
+ Item* GetSpellCastItem() const;
+ bool HasSpellCastItem() const { return !_spellCastItem.IsEmpty(); }
+
+ uint32 GetMoney() const { return _money; }
+ void SetMoney(uint32 money);
+
+ bool IsAccepted() const { return _accepted; }
+ void SetAccepted(bool state, bool forTrader = false);
+
+ bool IsInAcceptProcess() const { return _acceptProccess; }
+ void SetInAcceptProcess(bool state) { _acceptProccess = state; }
+
+private:
+ void Update(bool for_trader = true) const;
+
+ Player* _player; // Player who own of this TradeData
+ Player* _trader; // Player who trade with _player
+
+ bool _accepted; // _player press accept for trade list
+ bool _acceptProccess; // one from player/trader press accept and this processed
+
+ uint32 _money; // _player place money to trade
+
+ uint32 _spell; // _player apply spell to non-traded slot item
+ ObjectGuid _spellCastItem; // applied spell cast by item use
+
+ ObjectGuid _items[TRADE_SLOT_COUNT]; // traded items from _player side including non-traded slot
+};
+
+#endif // TradeData_h__
diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp
index 6b234df5860..27f6d5f614d 100644
--- a/src/server/game/Entities/Unit/Unit.cpp
+++ b/src/server/game/Entities/Unit/Unit.cpp
@@ -7475,8 +7475,8 @@ bool Unit::HandleAuraProc(Unit* victim, uint32 damage, Aura* triggeredByAura, Sp
uint32 stack = triggeredByAura->GetStackAmount();
int32 const mod = (GetMap()->GetSpawnMode() & 1) ? 1500 : 1250;
int32 dmg = 0;
- for (uint8 i = 1; i < stack; ++i)
- dmg += mod * stack;
+ for (uint8 i = 1; i <= stack; ++i)
+ dmg += mod * i;
if (Unit* caster = triggeredByAura->GetCaster())
caster->CastCustomSpell(70701, SPELLVALUE_BASE_POINT0, dmg);
break;
@@ -8291,6 +8291,7 @@ bool Unit::HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* trigg
case 15337: // Improved Spirit Tap (Rank 1)
case 15338: // Improved Spirit Tap (Rank 2)
{
+ ASSERT(procSpell);
if (procSpell->SpellFamilyFlags[0] & 0x800000)
if ((procSpell->Id != 58381) || !roll_chance_i(50))
return false;
@@ -8552,7 +8553,7 @@ bool Unit::HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* trigg
// try detect target manually if not set
if (target == NULL)
- target = !(procFlags & (PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_POS | PROC_FLAG_DONE_SPELL_NONE_DMG_CLASS_POS)) && triggerEntry && triggerEntry->IsPositive() ? this : victim;
+ target = !(procFlags & (PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_POS | PROC_FLAG_DONE_SPELL_NONE_DMG_CLASS_POS)) && triggerEntry->IsPositive() ? this : victim;
if (basepoints0)
CastCustomSpell(target, trigger_spell_id, &basepoints0, NULL, NULL, true, castItem, triggeredByAura);
@@ -9648,7 +9649,7 @@ Unit* Unit::GetMagicHitRedirectTarget(Unit* victim, SpellInfo const* spellInfo)
&& _IsValidAttackTarget(magnet, spellInfo))
{
/// @todo handle this charge drop by proc in cast phase on explicit target
- if (victim && spellInfo->Speed > 0.0f)
+ if (spellInfo->Speed > 0.0f)
{
// Set up missile speed based delay
uint32 delay = uint32(std::floor(std::max<float>(victim->GetDistance(this), 5.0f) / spellInfo->Speed * 1000.0f));
@@ -12987,9 +12988,6 @@ void Unit::ModSpellCastTime(SpellInfo const* spellInfo, int32 & castTime, Spell*
if (!spellInfo || castTime < 0)
return;
- if (spellInfo->IsChanneled() && !spellInfo->HasAttribute(SPELL_ATTR5_HASTE_AFFECT_DURATION))
- return;
-
// called from caster
if (Player* modOwner = GetSpellModOwner())
modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_CASTING_TIME, castTime, spell);
@@ -13003,6 +13001,25 @@ void Unit::ModSpellCastTime(SpellInfo const* spellInfo, int32 & castTime, Spell*
castTime = 500;
}
+void Unit::ModSpellDurationTime(SpellInfo const* spellInfo, int32 & duration, Spell* spell)
+{
+ if (!spellInfo || duration < 0)
+ return;
+
+ if (spellInfo->IsChanneled() && !spellInfo->HasAttribute(SPELL_ATTR5_HASTE_AFFECT_DURATION))
+ return;
+
+ // called from caster
+ if (Player* modOwner = GetSpellModOwner())
+ modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_CASTING_TIME, duration, spell);
+
+ if (!(spellInfo->HasAttribute(SPELL_ATTR0_ABILITY) || spellInfo->HasAttribute(SPELL_ATTR0_TRADESPELL) || spellInfo->HasAttribute(SPELL_ATTR3_NO_DONE_BONUS)) &&
+ ((GetTypeId() == TYPEID_PLAYER && spellInfo->SpellFamilyName) || GetTypeId() == TYPEID_UNIT))
+ duration = int32(float(duration) * GetFloatValue(UNIT_MOD_CAST_SPEED));
+ else if (spellInfo->HasAttribute(SPELL_ATTR0_REQ_AMMO) && !spellInfo->HasAttribute(SPELL_ATTR2_AUTOREPEAT_FLAG))
+ duration = int32(float(duration) * m_modAttackSpeedPct[RANGED_ATTACK]);
+}
+
DiminishingLevels Unit::GetDiminishing(DiminishingGroup group)
{
for (Diminishing::iterator i = m_Diminishing.begin(); i != m_Diminishing.end(); ++i)
@@ -13772,7 +13789,7 @@ void CharmInfo::InitPossessCreateSpells()
break;
}
- for (uint32 i = 0; i < CREATURE_MAX_SPELLS; ++i)
+ for (uint8 i = 0; i < CREATURE_MAX_SPELLS; ++i)
{
uint32 spellId = _unit->ToCreature()->m_spells[i];
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
@@ -13781,7 +13798,7 @@ void CharmInfo::InitPossessCreateSpells()
if (spellInfo->IsPassive())
_unit->CastSpell(_unit, spellInfo, true);
else
- AddSpellToActionBar(spellInfo, ACT_PASSIVE);
+ AddSpellToActionBar(spellInfo, ACT_PASSIVE, i % MAX_UNIT_ACTION_BAR_INDEX);
}
}
}
@@ -13839,11 +13856,12 @@ void CharmInfo::InitCharmCreateSpells()
}
}
-bool CharmInfo::AddSpellToActionBar(SpellInfo const* spellInfo, ActiveStates newstate)
+bool CharmInfo::AddSpellToActionBar(SpellInfo const* spellInfo, ActiveStates newstate, uint8 preferredSlot)
{
uint32 spell_id = spellInfo->Id;
uint32 first_id = spellInfo->GetFirstRankSpell()->Id;
+ ASSERT(preferredSlot < MAX_UNIT_ACTION_BAR_INDEX);
// new spell rank can be already listed
for (uint8 i = 0; i < MAX_UNIT_ACTION_BAR_INDEX; ++i)
{
@@ -13860,9 +13878,10 @@ bool CharmInfo::AddSpellToActionBar(SpellInfo const* spellInfo, ActiveStates new
// or use empty slot in other case
for (uint8 i = 0; i < MAX_UNIT_ACTION_BAR_INDEX; ++i)
{
- if (!PetActionBar[i].GetAction() && PetActionBar[i].IsActionBarForSpell())
+ uint8 j = (preferredSlot + i) % MAX_UNIT_ACTION_BAR_INDEX;
+ if (!PetActionBar[j].GetAction() && PetActionBar[j].IsActionBarForSpell())
{
- SetActionBar(i, spell_id, newstate == ACT_DECIDE ? spellInfo->IsAutocastable() ? ACT_DISABLED : ACT_PASSIVE : newstate);
+ SetActionBar(j, spell_id, newstate == ACT_DECIDE ? spellInfo->IsAutocastable() ? ACT_DISABLED : ACT_PASSIVE : newstate);
return true;
}
}
@@ -15802,7 +15821,7 @@ void Unit::SetFeared(bool apply)
}
if (Player* player = ToPlayer())
- if(!player->HasUnitState(UNIT_STATE_POSSESSED))
+ if (!player->HasUnitState(UNIT_STATE_POSSESSED))
player->SetClientControl(this, !apply);
}
@@ -16023,7 +16042,7 @@ void Unit::RemoveCharmedBy(Unit* charmer)
// Vehicle should not attack its passenger after he exists the seat
if (type != CHARM_TYPE_VEHICLE)
- LastCharmerGUID = charmer->GetGUID();
+ LastCharmerGUID = ASSERT_NOTNULL(charmer)->GetGUID();
}
// If charmer still exists
@@ -17772,22 +17791,8 @@ void Unit::BuildValuesUpdate(uint8 updateType, ByteBuffer* data, Player* target)
}
if (cinfo->flags_extra & CREATURE_FLAG_EXTRA_TRIGGER)
- {
if (target->IsGameMaster())
- {
- if (cinfo->Modelid1)
- displayId = cinfo->Modelid1; // Modelid1 is a visible model for gms
- else
- displayId = 17519; // world visible trigger's model
- }
- else
- {
- if (cinfo->Modelid2)
- displayId = cinfo->Modelid2; // Modelid2 is an invisible model for players
- else
- displayId = 11686; // world invisible trigger's model
- }
- }
+ displayId = cinfo->GetFirstVisibleModel();
}
fieldBuffer << uint32(displayId);
diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h
index 39b1c34be55..2ddbe398f76 100644
--- a/src/server/game/Entities/Unit/Unit.h
+++ b/src/server/game/Entities/Unit/Unit.h
@@ -898,6 +898,12 @@ public:
uint32 GetHitMask() const { return _hitMask; }
SpellInfo const* GetSpellInfo() const { return NULL; }
+ SpellInfo const* EnsureSpellInfo() const
+ {
+ SpellInfo const* spellInfo = GetSpellInfo();
+ ASSERT(spellInfo);
+ return spellInfo;
+ }
SpellSchoolMask GetSchoolMask() const { return SPELL_SCHOOL_MASK_NONE; }
DamageInfo* GetDamageInfo() const { return _damageInfo; }
@@ -1119,7 +1125,7 @@ struct CharmInfo
void InitEmptyActionBar(bool withAttack = true);
//return true if successful
- bool AddSpellToActionBar(SpellInfo const* spellInfo, ActiveStates newstate = ACT_DECIDE);
+ bool AddSpellToActionBar(SpellInfo const* spellInfo, ActiveStates newstate = ACT_DECIDE, uint8 preferredSlot = 0);
bool RemoveSpellFromActionBar(uint32 spell_id);
void LoadPetActionBar(const std::string& data);
void BuildActionBar(WorldPacket* data);
@@ -1395,6 +1401,7 @@ class Unit : public WorldObject
void DealDamageMods(Unit* victim, uint32 &damage, uint32* absorb);
uint32 DealDamage(Unit* victim, uint32 damage, CleanDamage const* cleanDamage = NULL, DamageEffectType damagetype = DIRECT_DAMAGE, SpellSchoolMask damageSchoolMask = SPELL_SCHOOL_MASK_NORMAL, SpellInfo const* spellProto = NULL, bool durabilityLoss = true);
void Kill(Unit* victim, bool durabilityLoss = true);
+ void KillSelf(bool durabilityLoss = true) { Kill(this, durabilityLoss); }
int32 DealHeal(Unit* victim, uint32 addhealth);
void ProcDamageAndSpell(Unit* victim, uint32 procAttacker, uint32 procVictim, uint32 procEx, uint32 amount, WeaponAttackType attType = BASE_ATTACK, SpellInfo const* procSpell = NULL, SpellInfo const* procAura = NULL);
@@ -1982,6 +1989,7 @@ class Unit : public WorldObject
int32 CalcSpellDuration(SpellInfo const* spellProto);
int32 ModSpellDuration(SpellInfo const* spellProto, Unit const* target, int32 duration, bool positive, uint32 effectMask);
void ModSpellCastTime(SpellInfo const* spellProto, int32& castTime, Spell* spell = NULL);
+ void ModSpellDurationTime(SpellInfo const* spellProto, int32& castTime, Spell* spell = NULL);
float CalculateLevelPenalty(SpellInfo const* spellProto) const;
void addFollower(FollowerReference* pRef) { m_FollowingRefManager.insertFirst(pRef); }
diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp
index 046ce422cd2..6f5368ed150 100644
--- a/src/server/game/Globals/ObjectMgr.cpp
+++ b/src/server/game/Globals/ObjectMgr.cpp
@@ -1236,7 +1236,11 @@ uint32 ObjectMgr::ChooseDisplayId(CreatureTemplate const* cinfo, CreatureData co
if (data && data->displayid)
return data->displayid;
- return cinfo->GetRandomValidModelId();
+ if (!(cinfo->flags_extra & CREATURE_FLAG_EXTRA_TRIGGER))
+ return cinfo->GetRandomValidModelId();
+
+ // Triggers by default receive the invisible model
+ return cinfo->GetFirstInvisibleModel();
}
void ObjectMgr::ChooseCreatureFlags(const CreatureTemplate* cinfo, uint32& npcflag, uint32& unit_flags, uint32& dynamicflags, const CreatureData* data /*= NULL*/)
@@ -1301,6 +1305,12 @@ void ObjectMgr::LoadCreatureModelInfo()
Field* fields = result->Fetch();
uint32 modelId = fields[0].GetUInt32();
+ CreatureDisplayInfoEntry const* creatureDisplay = sCreatureDisplayInfoStore.LookupEntry(modelId);
+ if (!creatureDisplay)
+ {
+ TC_LOG_ERROR("sql.sql", "Table `creature_model_info` has model for nonexistent display id (%u).", modelId);
+ continue;
+ }
CreatureModelInfo& modelInfo = _creatureModelStore[modelId];
@@ -1308,12 +1318,10 @@ void ObjectMgr::LoadCreatureModelInfo()
modelInfo.combat_reach = fields[2].GetFloat();
modelInfo.gender = fields[3].GetUInt8();
modelInfo.modelid_other_gender = fields[4].GetUInt32();
+ modelInfo.is_trigger = false;
// Checks
- if (!sCreatureDisplayInfoStore.LookupEntry(modelId))
- TC_LOG_ERROR("sql.sql", "Table `creature_model_info` has model for nonexistent display id (%u).", modelId);
-
if (modelInfo.gender > GENDER_NONE)
{
TC_LOG_ERROR("sql.sql", "Table `creature_model_info` has wrong gender (%u) for display id (%u).", uint32(modelInfo.gender), modelId);
@@ -1329,6 +1337,9 @@ void ObjectMgr::LoadCreatureModelInfo()
if (modelInfo.combat_reach < 0.1f)
modelInfo.combat_reach = DEFAULT_COMBAT_REACH;
+ if (CreatureModelDataEntry const* modelData = sCreatureModelDataStore.LookupEntry(creatureDisplay->ModelId))
+ modelInfo.is_trigger = strstr(modelData->ModelPath, "InvisibleStalker") != nullptr;
+
++count;
}
while (result->NextRow());
@@ -2347,6 +2358,12 @@ void ObjectMgr::LoadItemTemplates()
itemTemplate.ContainerSlots = uint32(fields[26].GetUInt8());
itemTemplate.StatsCount = uint32(fields[27].GetUInt8());
+ if (itemTemplate.StatsCount > MAX_ITEM_PROTO_STATS)
+ {
+ TC_LOG_ERROR("sql.sql", "Item (Entry: %u) has too large value in statscount (%u), replace by hardcoded limit (%u).", entry, itemTemplate.StatsCount, MAX_ITEM_PROTO_STATS);
+ itemTemplate.StatsCount = MAX_ITEM_PROTO_STATS;
+ }
+
for (uint8 i = 0; i < itemTemplate.StatsCount; ++i)
{
itemTemplate.ItemStat[i].ItemStatType = uint32(fields[28 + i*2].GetUInt8());
@@ -2594,12 +2611,6 @@ void ObjectMgr::LoadItemTemplates()
itemTemplate.ContainerSlots = MAX_BAG_SIZE;
}
- if (itemTemplate.StatsCount > MAX_ITEM_PROTO_STATS)
- {
- TC_LOG_ERROR("sql.sql", "Item (Entry: %u) has too large value in statscount (%u), replace by hardcoded limit (%u).", entry, itemTemplate.StatsCount, MAX_ITEM_PROTO_STATS);
- itemTemplate.StatsCount = MAX_ITEM_PROTO_STATS;
- }
-
for (uint8 j = 0; j < itemTemplate.StatsCount; ++j)
{
// for ItemStatValue != 0
diff --git a/src/server/game/Groups/Group.cpp b/src/server/game/Groups/Group.cpp
index 5f500156f58..4f8accee69c 100644
--- a/src/server/game/Groups/Group.cpp
+++ b/src/server/game/Groups/Group.cpp
@@ -539,7 +539,7 @@ bool Group::RemoveMember(ObjectGuid guid, const RemoveMethod& method /*= GROUP_R
}
// Reevaluate group enchanter if the leaving player had enchanting skill or the player is offline
- if ((player && player->GetSkillValue(SKILL_ENCHANTING)) || !player)
+ if (!player || player->GetSkillValue(SKILL_ENCHANTING))
ResetMaxEnchantingLevel();
// Remove player from loot rolls
diff --git a/src/server/game/Handlers/AuctionHouseHandler.cpp b/src/server/game/Handlers/AuctionHouseHandler.cpp
index 8ecf86680ca..a9680ff69bb 100644
--- a/src/server/game/Handlers/AuctionHouseHandler.cpp
+++ b/src/server/game/Handlers/AuctionHouseHandler.cpp
@@ -303,18 +303,21 @@ void WorldSession::HandleAuctionSellItem(WorldPacket& recvData)
AH->buyout = buyout;
AH->expire_time = time(NULL) + auctionTime;
AH->deposit = deposit;
+ AH->etime = etime;
AH->auctionHouseEntry = auctionHouseEntry;
TC_LOG_INFO("network", "CMSG_AUCTION_SELL_ITEM: Player %s (guid %d) is selling item %s entry %u (guid %d) with count %u with initial bid %u with buyout %u and with time %u (in sec) in auctionhouse %u",
_player->GetName().c_str(), _player->GetGUID().GetCounter(), item->GetTemplate()->Name1.c_str(), item->GetEntry(), item->GetGUID().GetCounter(), item->GetCount(), bid, buyout, auctionTime, AH->GetHouseId());
sAuctionMgr->AddAItem(item);
auctionHouse->AddAuction(AH);
+ sAuctionMgr->PendingAuctionAdd(_player, AH);
_player->MoveItemFromInventory(item->GetBagSlot(), item->GetSlot(), true);
SQLTransaction trans = CharacterDatabase.BeginTransaction();
item->DeleteFromInventoryDB(trans);
item->SaveToDB(trans);
+
AH->SaveToDB(trans);
_player->SaveInventoryAndGoldToDB(trans);
CharacterDatabase.CommitTransaction(trans);
@@ -351,12 +354,14 @@ void WorldSession::HandleAuctionSellItem(WorldPacket& recvData)
AH->buyout = buyout;
AH->expire_time = time(NULL) + auctionTime;
AH->deposit = deposit;
+ AH->etime = etime;
AH->auctionHouseEntry = auctionHouseEntry;
TC_LOG_INFO("network", "CMSG_AUCTION_SELL_ITEM: Player %s (guid %d) is selling item %s entry %u (guid %d) with count %u with initial bid %u with buyout %u and with time %u (in sec) in auctionhouse %u",
_player->GetName().c_str(), _player->GetGUID().GetCounter(), newItem->GetTemplate()->Name1.c_str(), newItem->GetEntry(), newItem->GetGUID().GetCounter(), newItem->GetCount(), bid, buyout, auctionTime, AH->GetHouseId());
sAuctionMgr->AddAItem(newItem);
auctionHouse->AddAuction(AH);
+ sAuctionMgr->PendingAuctionAdd(_player, AH);
for (uint32 j = 0; j < itemsCount; ++j)
{
@@ -396,8 +401,6 @@ void WorldSession::HandleAuctionSellItem(WorldPacket& recvData)
GetPlayer()->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_CREATE_AUCTION, 1);
}
-
- _player->ModifyMoney(-int32(deposit));
}
//this function is called when client bids or buys out auction
diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp
index e5b32f548f9..60ec60443d6 100644
--- a/src/server/game/Handlers/CharacterHandler.cpp
+++ b/src/server/game/Handlers/CharacterHandler.cpp
@@ -642,7 +642,7 @@ void WorldSession::HandleCharCreateCallback(PreparedQueryResult result, Characte
TC_LOG_INFO("entities.player.character", "Account: %d (IP: %s) Create Character:[%s] (GUID: %u)", GetAccountId(), GetRemoteAddress().c_str(), createInfo->Name.c_str(), newChar.GetGUID().GetCounter());
sScriptMgr->OnPlayerCreate(&newChar);
- sWorld->AddCharacterInfo(newChar.GetGUID(), GetAccountId(), newChar.GetName(), newChar.getGender(), newChar.getRace(), newChar.getClass(), newChar.getLevel());
+ sWorld->AddCharacterInfo(newChar.GetGUID(), GetAccountId(), newChar.GetName(), newChar.GetByteValue(PLAYER_BYTES_3, 0), newChar.getRace(), newChar.getClass(), newChar.getLevel());
newChar.CleanupsBeforeDelete();
delete createInfo;
@@ -1242,20 +1242,20 @@ void WorldSession::HandleAlterAppearance(WorldPacket& recvData)
BarberShopStyleEntry const* bs_hair = sBarberShopStyleStore.LookupEntry(Hair);
- if (!bs_hair || bs_hair->type != 0 || bs_hair->race != _player->getRace() || bs_hair->gender != _player->getGender())
+ if (!bs_hair || bs_hair->type != 0 || bs_hair->race != _player->getRace() || bs_hair->gender != _player->GetByteValue(PLAYER_BYTES_3, 0))
return;
BarberShopStyleEntry const* bs_facialHair = sBarberShopStyleStore.LookupEntry(FacialHair);
- if (!bs_facialHair || bs_facialHair->type != 2 || bs_facialHair->race != _player->getRace() || bs_facialHair->gender != _player->getGender())
+ if (!bs_facialHair || bs_facialHair->type != 2 || bs_facialHair->race != _player->getRace() || bs_facialHair->gender != _player->GetByteValue(PLAYER_BYTES_3, 0))
return;
BarberShopStyleEntry const* bs_skinColor = sBarberShopStyleStore.LookupEntry(SkinColor);
- if (bs_skinColor && (bs_skinColor->type != 3 || bs_skinColor->race != _player->getRace() || bs_skinColor->gender != _player->getGender()))
+ if (bs_skinColor && (bs_skinColor->type != 3 || bs_skinColor->race != _player->getRace() || bs_skinColor->gender != _player->GetByteValue(PLAYER_BYTES_3, 0)))
return;
- if (!Player::ValidateAppearance(_player->getRace(), _player->getClass(), _player->getGender(), bs_hair->hair_id, Color, _player->GetByteValue(PLAYER_BYTES, 1), bs_facialHair->hair_id, bs_skinColor ? bs_skinColor->hair_id : _player->GetByteValue(PLAYER_BYTES, 0)))
+ if (!Player::ValidateAppearance(_player->getRace(), _player->getClass(), _player->GetByteValue(PLAYER_BYTES_3, 0), bs_hair->hair_id, Color, _player->GetByteValue(PLAYER_BYTES, 1), bs_facialHair->hair_id, bs_skinColor ? bs_skinColor->hair_id : _player->GetByteValue(PLAYER_BYTES, 0)))
return;
GameObject* go = _player->FindNearestGameObjectOfType(GAMEOBJECT_TYPE_BARBER_CHAIR, 5.0f);
diff --git a/src/server/game/Handlers/GroupHandler.cpp b/src/server/game/Handlers/GroupHandler.cpp
index 30764fea2a3..9f225df8427 100644
--- a/src/server/game/Handlers/GroupHandler.cpp
+++ b/src/server/game/Handlers/GroupHandler.cpp
@@ -991,7 +991,7 @@ void WorldSession::HandleRequestPartyMemberStatsOpcode(WorldPacket &recvData)
data.put<uint64>(maskPos, auraMask); // GROUP_UPDATE_FLAG_AURAS
if (updateFlags & GROUP_UPDATE_FLAG_PET_GUID)
- data << uint64(pet->GetGUID());
+ data << uint64(ASSERT_NOTNULL(pet)->GetGUID());
data << std::string(pet ? pet->GetName() : ""); // GROUP_UPDATE_FLAG_PET_NAME
data << uint16(pet ? pet->GetDisplayId() : 0); // GROUP_UPDATE_FLAG_PET_MODEL_ID
diff --git a/src/server/game/Handlers/MiscHandler.cpp b/src/server/game/Handlers/MiscHandler.cpp
index 2eebbfc1004..6dcd3242a6c 100644
--- a/src/server/game/Handlers/MiscHandler.cpp
+++ b/src/server/game/Handlers/MiscHandler.cpp
@@ -283,7 +283,7 @@ void WorldSession::HandleWhoOpcode(WorldPacket& recvData)
continue;
uint32 pzoneid = target->GetZoneId();
- uint8 gender = target->getGender();
+ uint8 gender = target->GetByteValue(PLAYER_BYTES_3, 0);
bool z_show = true;
for (uint32 i = 0; i < zones_count; ++i)
diff --git a/src/server/game/Handlers/TradeHandler.cpp b/src/server/game/Handlers/TradeHandler.cpp
index fbfd16ae1c6..1bb21971935 100644
--- a/src/server/game/Handlers/TradeHandler.cpp
+++ b/src/server/game/Handlers/TradeHandler.cpp
@@ -28,6 +28,7 @@
#include "SocialMgr.h"
#include "Language.h"
#include "AccountMgr.h"
+#include "TradeData.h"
void WorldSession::SendTradeStatus(TradeStatusInfo const& info)
{
@@ -454,6 +455,8 @@ void WorldSession::HandleAcceptTradeOpcode(WorldPacket& /*recvPacket*/)
SendTradeStatus(myCanCompleteInfo);
my_trade->SetAccepted(false);
his_trade->SetAccepted(false);
+ delete my_spell;
+ delete his_spell;
return;
}
else if (hisCanCompleteInfo.Result != EQUIP_ERR_OK)
@@ -466,6 +469,8 @@ void WorldSession::HandleAcceptTradeOpcode(WorldPacket& /*recvPacket*/)
trader->GetSession()->SendTradeStatus(hisCanCompleteInfo);
my_trade->SetAccepted(false);
his_trade->SetAccepted(false);
+ delete my_spell;
+ delete his_spell;
return;
}
diff --git a/src/server/game/Miscellaneous/Language.h b/src/server/game/Miscellaneous/Language.h
index de117905b3d..85971396619 100644
--- a/src/server/game/Miscellaneous/Language.h
+++ b/src/server/game/Miscellaneous/Language.h
@@ -1202,7 +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_INSTANCE_LOGIN_GAMEMASTER_EXCEPTION = 11010
};
#endif
diff --git a/src/server/game/Quests/QuestDef.cpp b/src/server/game/Quests/QuestDef.cpp
index 58bc4915d3f..a3f662f2ade 100644
--- a/src/server/game/Quests/QuestDef.cpp
+++ b/src/server/game/Quests/QuestDef.cpp
@@ -127,6 +127,18 @@ Quest::Quest(Field* questRecord)
for (int i = 0; i < QUEST_REWARD_CHOICES_COUNT; ++i)
if (RewardChoiceItemId[i])
++_rewChoiceItemsCount;
+
+ for (int i = 0; i < QUEST_EMOTE_COUNT; ++i)
+ DetailsEmote[i] = 0;
+
+ for (int i = 0; i < QUEST_EMOTE_COUNT; ++i)
+ DetailsEmoteDelay[i] = 0;
+
+ for (int i = 0; i < QUEST_EMOTE_COUNT; ++i)
+ OfferRewardEmote[i] = 0;
+
+ for (int i = 0; i < QUEST_EMOTE_COUNT; ++i)
+ OfferRewardEmoteDelay[i] = 0;
}
void Quest::LoadQuestDetails(Field* fields)
diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp
index b1a043c4b5b..6ae63f2efa3 100644
--- a/src/server/game/Server/WorldSession.cpp
+++ b/src/server/game/Server/WorldSession.cpp
@@ -341,7 +341,7 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater)
LogUnexpectedOpcode(packet, "STATUS_TRANSFER", "the player has not logged in yet");
else if (_player->IsInWorld())
LogUnexpectedOpcode(packet, "STATUS_TRANSFER", "the player is still in world");
- else if(AntiDOS.EvaluateOpcode(*packet, currentTime))
+ else if (AntiDOS.EvaluateOpcode(*packet, currentTime))
{
sScriptMgr->OnPacketReceive(this, *packet);
(this->*opHandle.handler)(*packet);
@@ -858,9 +858,8 @@ void WorldSession::ReadMovementInfo(WorldPacket &data, MovementInfo* mi)
// 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);
+ REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_ROOT) && mi->HasMovementFlag(MOVEMENTFLAG_LEFT | MOVEMENTFLAG_RIGHT | MOVEMENTFLAG_PITCH_UP | MOVEMENTFLAG_PITCH_DOWN),
+ MOVEMENTFLAG_MASK_MOVING);
}
else
{
diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp
index 5e70d953be9..01f27a38e9c 100644
--- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp
+++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp
@@ -541,10 +541,7 @@ void AuraEffect::CalculatePeriodic(Unit* caster, bool create, bool load)
{
// Haste modifies periodic time of channeled spells
if (m_spellInfo->IsChanneled())
- {
- if (m_spellInfo->HasAttribute(SPELL_ATTR5_HASTE_AFFECT_DURATION))
- caster->ModSpellCastTime(m_spellInfo, m_amplitude);
- }
+ caster->ModSpellDurationTime(m_spellInfo, m_amplitude);
// and periodic time of auras affected by SPELL_AURA_PERIODIC_HASTE
else if (caster->HasAuraTypeWithAffectMask(SPELL_AURA_PERIODIC_HASTE, m_spellInfo) || m_spellInfo->HasAttribute(SPELL_ATTR5_HASTE_AFFECT_DURATION))
m_amplitude = int32(m_amplitude * caster->GetFloatValue(UNIT_MOD_CAST_SPEED));
@@ -1866,43 +1863,43 @@ void AuraEffect::HandleAuraTransform(AuraApplication const* aurApp, uint8 mode,
{
// Blood Elf
case RACE_BLOODELF:
- target->SetDisplayId(target->getGender() == GENDER_MALE ? 17829 : 17830);
+ target->SetDisplayId(target->getGender() == GENDER_FEMALE ? 17830 : 17829);
break;
// Orc
case RACE_ORC:
- target->SetDisplayId(target->getGender() == GENDER_MALE ? 10139 : 10140);
+ target->SetDisplayId(target->getGender() == GENDER_FEMALE ? 10140 : 10139);
break;
// Troll
case RACE_TROLL:
- target->SetDisplayId(target->getGender() == GENDER_MALE ? 10135 : 10134);
+ target->SetDisplayId(target->getGender() == GENDER_FEMALE ? 10134 : 10135);
break;
// Tauren
case RACE_TAUREN:
- target->SetDisplayId(target->getGender() == GENDER_MALE ? 10136 : 10147);
+ target->SetDisplayId(target->getGender() == GENDER_FEMALE ? 10147 : 10136);
break;
// Undead
case RACE_UNDEAD_PLAYER:
- target->SetDisplayId(target->getGender() == GENDER_MALE ? 10146 : 10145);
+ target->SetDisplayId(target->getGender() == GENDER_FEMALE ? 10145 : 10146);
break;
// Draenei
case RACE_DRAENEI:
- target->SetDisplayId(target->getGender() == GENDER_MALE ? 17827 : 17828);
+ target->SetDisplayId(target->getGender() == GENDER_FEMALE ? 17828 : 17827);
break;
// Dwarf
case RACE_DWARF:
- target->SetDisplayId(target->getGender() == GENDER_MALE ? 10141 : 10142);
+ target->SetDisplayId(target->getGender() == GENDER_FEMALE ? 10142 : 10141);
break;
// Gnome
case RACE_GNOME:
- target->SetDisplayId(target->getGender() == GENDER_MALE ? 10148 : 10149);
+ target->SetDisplayId(target->getGender() == GENDER_FEMALE ? 10149 : 10148);
break;
// Human
case RACE_HUMAN:
- target->SetDisplayId(target->getGender() == GENDER_MALE ? 10137 : 10138);
+ target->SetDisplayId(target->getGender() == GENDER_FEMALE ? 10138 : 10137);
break;
// Night Elf
case RACE_NIGHTELF:
- target->SetDisplayId(target->getGender() == GENDER_MALE ? 10143 : 10144);
+ target->SetDisplayId(target->getGender() == GENDER_FEMALE ? 10144 : 10143);
break;
default:
break;
@@ -4646,17 +4643,10 @@ void AuraEffect::HandleAuraDummy(AuraApplication const* aurApp, uint8 mode, bool
{
if (caster)
{
- switch (caster->getGender())
- {
- case GENDER_FEMALE:
- caster->CastSpell(target, 37095, true, NULL, this); // Blood Elf Disguise
- break;
- case GENDER_MALE:
- caster->CastSpell(target, 37093, true, NULL, this);
- break;
- default:
- break;
- }
+ if (caster->getGender() == GENDER_FEMALE)
+ caster->CastSpell(target, 37095, true, NULL, this); // Blood Elf Disguise
+ else
+ caster->CastSpell(target, 37093, true, NULL, this);
}
break;
}
@@ -4686,15 +4676,10 @@ void AuraEffect::HandleAuraDummy(AuraApplication const* aurApp, uint8 mode, bool
case 46354: // Blood Elf Illusion
if (caster)
{
- switch (caster->getGender())
- {
- case GENDER_FEMALE:
- caster->CastSpell(target, 46356, true, NULL, this);
- break;
- case GENDER_MALE:
- caster->CastSpell(target, 46355, true, NULL, this);
- break;
- }
+ if (caster->getGender() == GENDER_FEMALE)
+ caster->CastSpell(target, 46356, true, NULL, this);
+ else
+ caster->CastSpell(target, 46355, true, NULL, this);
}
break;
case 46361: // Reinforced Net
@@ -5577,7 +5562,7 @@ void AuraEffect::HandlePeriodicTriggerSpellAuraTick(Unit* target, Unit* caster)
case 31347:
{
target->CastSpell(target, 31350, true, NULL, this);
- target->Kill(target);
+ target->KillSelf();
return;
}
// Spellcloth
diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp
index 76eefa0bb0e..4f3b60d6f37 100644
--- a/src/server/game/Spells/Spell.cpp
+++ b/src/server/game/Spells/Spell.cpp
@@ -53,6 +53,7 @@
#include "SpellHistory.h"
#include "Battlefield.h"
#include "BattlefieldMgr.h"
+#include "TradeData.h"
extern pEffect SpellEffects[TOTAL_SPELL_EFFECTS];
@@ -2428,9 +2429,8 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target)
{
m_caster->CombatStart(unit, !m_spellInfo->HasAttribute(SPELL_ATTR3_NO_INITIAL_AGGRO));
- if (m_spellInfo->HasAttribute(SPELL_ATTR0_CU_AURA_CC))
- if (!unit->IsStandState())
- unit->SetStandState(UNIT_STAND_STATE_STAND);
+ if (!unit->IsStandState())
+ unit->SetStandState(UNIT_STAND_STATE_STAND);
}
if (spellHitTarget)
@@ -2514,8 +2514,8 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit* unit, uint32 effectMask, bool scaleA
if (m_caster->_IsValidAttackTarget(unit, m_spellInfo))
{
unit->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_HITBYSPELL);
- /// @todo This is a hack. But we do not know what types of stealth should be interrupted by CC
- if (m_spellInfo->HasAttribute(SPELL_ATTR0_CU_AURA_CC) && unit->IsControlledByPlayer())
+
+ if (!m_spellInfo->HasAttribute(SPELL_ATTR0_CU_DONT_BREAK_STEALTH))
unit->RemoveAurasByType(SPELL_AURA_MOD_STEALTH);
}
else if (m_caster->IsFriendlyTo(unit))
@@ -2624,7 +2624,7 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit* unit, uint32 effectMask, bool scaleA
// Haste modifies duration of channeled spells
if (m_spellInfo->IsChanneled())
- m_originalCaster->ModSpellCastTime(aurSpellInfo, duration, this);
+ m_originalCaster->ModSpellDurationTime(aurSpellInfo, duration, this);
// and duration of auras affected by SPELL_AURA_PERIODIC_HASTE
else if (m_originalCaster->HasAuraTypeWithAffectMask(SPELL_AURA_PERIODIC_HASTE, aurSpellInfo) || m_spellInfo->HasAttribute(SPELL_ATTR5_HASTE_AFFECT_DURATION))
duration = int32(duration * m_originalCaster->GetFloatValue(UNIT_MOD_CAST_SPEED));
@@ -3258,7 +3258,7 @@ void Spell::handle_immediate()
modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_DURATION, duration);
// Apply haste mods
- m_caster->ModSpellCastTime(m_spellInfo, duration, this);
+ m_caster->ModSpellDurationTime(m_spellInfo, duration, this);
m_spellState = SPELL_STATE_CASTING;
m_caster->AddInterruptMask(m_spellInfo->ChannelInterruptFlags);
diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp
index b345c5affea..b3ab72dcbff 100644
--- a/src/server/game/Spells/SpellEffects.cpp
+++ b/src/server/game/Spells/SpellEffects.cpp
@@ -5638,7 +5638,7 @@ void Spell::EffectGameObjectDamage(SpellEffIndex /*effIndex*/)
FactionTemplateEntry const* casterFaction = caster->GetFactionTemplateEntry();
FactionTemplateEntry const* targetFaction = sFactionTemplateStore.LookupEntry(gameObjTarget->GetUInt32Value(GAMEOBJECT_FACTION));
// Do not allow to damage GO's of friendly factions (ie: Wintergrasp Walls/Ulduar Storm Beacons)
- if ((casterFaction && targetFaction && !casterFaction->IsFriendlyTo(*targetFaction)) || !targetFaction)
+ if (!targetFaction || (casterFaction && targetFaction && !casterFaction->IsFriendlyTo(*targetFaction)))
gameObjTarget->ModifyHealth(-damage, caster, GetSpellInfo()->Id);
}
diff --git a/src/server/game/Spells/SpellHistory.cpp b/src/server/game/Spells/SpellHistory.cpp
index d2cf03dc936..faad54d0af7 100644
--- a/src/server/game/Spells/SpellHistory.cpp
+++ b/src/server/game/Spells/SpellHistory.cpp
@@ -41,6 +41,7 @@ struct SpellHistory::PersistenceHelper<Player>
if (!sSpellMgr->GetSpellInfo(*spellId))
return false;
+ cooldownEntry->SpellId = *spellId;
cooldownEntry->CooldownEnd = Clock::from_time_t(time_t(fields[2].GetUInt32()));
cooldownEntry->ItemId = fields[1].GetUInt32();
cooldownEntry->CategoryId = fields[3].GetUInt32();
@@ -72,6 +73,7 @@ struct SpellHistory::PersistenceHelper<Pet>
if (!sSpellMgr->GetSpellInfo(*spellId))
return false;
+ cooldownEntry->SpellId = *spellId;
cooldownEntry->CooldownEnd = Clock::from_time_t(time_t(fields[1].GetUInt32()));
cooldownEntry->ItemId = 0;
cooldownEntry->CategoryId = fields[2].GetUInt32();
@@ -280,32 +282,7 @@ void SpellHistory::StartCooldown(SpellInfo const* spellInfo, uint32 itemId, Spel
int32 cooldown = -1;
int32 categoryCooldown = -1;
- // some special item spells without correct cooldown in SpellInfo
- // cooldown information stored in item prototype
- if (itemId)
- {
- if (ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId))
- {
- for (uint8 idx = 0; idx < MAX_ITEM_PROTO_SPELLS; ++idx)
- {
- if (uint32(proto->Spells[idx].SpellId) == spellInfo->Id)
- {
- categoryId = proto->Spells[idx].SpellCategory;
- cooldown = proto->Spells[idx].SpellCooldown;
- categoryCooldown = proto->Spells[idx].SpellCategoryCooldown;
- break;
- }
- }
- }
- }
-
- // if no cooldown found above then base at DBC data
- if (cooldown < 0 && categoryCooldown < 0)
- {
- categoryId = spellInfo->GetCategory();
- cooldown = spellInfo->RecoveryTime;
- categoryCooldown = spellInfo->CategoryRecoveryTime;
- }
+ GetCooldownDurations(spellInfo, itemId, &cooldown, &categoryId, &categoryCooldown);
Clock::time_point curTime = Clock::now();
Clock::time_point catrecTime;
@@ -381,23 +358,39 @@ void SpellHistory::StartCooldown(SpellInfo const* spellInfo, uint32 itemId, Spel
void SpellHistory::SendCooldownEvent(SpellInfo const* spellInfo, uint32 itemId /*= 0*/, Spell* spell /*= nullptr*/, bool startCooldown /*= true*/)
{
- // start cooldowns at server side, if any
- if (startCooldown)
- StartCooldown(spellInfo, itemId, spell);
-
// Send activate cooldown timer (possible 0) at client side
if (Player* player = GetPlayerOwner())
{
+ uint32 category = spellInfo->GetCategory();
+ GetCooldownDurations(spellInfo, itemId, nullptr, &category, nullptr);
+
+ auto categoryItr = _categoryCooldowns.find(category);
+ if (categoryItr != _categoryCooldowns.end() && categoryItr->second->SpellId != spellInfo->Id)
+ {
+ WorldPacket data(SMSG_COOLDOWN_EVENT, 4 + 8);
+ data << uint32(categoryItr->second->SpellId);
+ data << uint64(_owner->GetGUID());
+ player->SendDirectMessage(&data);
+
+ if (startCooldown)
+ StartCooldown(sSpellMgr->EnsureSpellInfo(categoryItr->second->SpellId), itemId, spell);
+ }
+
WorldPacket data(SMSG_COOLDOWN_EVENT, 4 + 8);
data << uint32(spellInfo->Id);
data << uint64(_owner->GetGUID());
player->SendDirectMessage(&data);
}
+
+ // start cooldowns at server side, if any
+ if (startCooldown)
+ StartCooldown(spellInfo, itemId, spell);
}
void SpellHistory::AddCooldown(uint32 spellId, uint32 itemId, Clock::time_point cooldownEnd, uint32 categoryId, Clock::time_point categoryEnd, bool onHold /*= false*/)
{
CooldownEntry& cooldownEntry = _spellCooldowns[spellId];
+ cooldownEntry.SpellId = spellId;
cooldownEntry.CooldownEnd = cooldownEnd;
cooldownEntry.ItemId = itemId;
cooldownEntry.CategoryId = categoryId;
@@ -478,21 +471,7 @@ bool SpellHistory::HasCooldown(SpellInfo const* spellInfo, uint32 itemId /*= 0*/
return true;
uint32 category = 0;
- if (ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(itemId))
- {
- for (uint32 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i)
- {
- if (uint32(itemTemplate->Spells[i].SpellId) == spellInfo->Id)
- {
- category = itemTemplate->Spells[i].SpellCategory;
- break;
- }
- }
- }
-
- if (!category)
- category = spellInfo->GetCategory();
-
+ GetCooldownDurations(spellInfo, itemId, nullptr, &category, nullptr);
if (!category)
return false;
@@ -651,6 +630,48 @@ void SpellHistory::BuildCooldownPacket(WorldPacket& data, uint8 flags, PacketCoo
}
}
+void SpellHistory::GetCooldownDurations(SpellInfo const* spellInfo, uint32 itemId, int32* cooldown, uint32* categoryId, int32* categoryCooldown)
+{
+ ASSERT(cooldown || categoryId || categoryCooldown);
+ int32 tmpCooldown = -1;
+ uint32 tmpCategoryId = 0;
+ int32 tmpCategoryCooldown = -1;
+
+ // some special item spells without correct cooldown in SpellInfo
+ // cooldown information stored in item prototype
+ if (itemId)
+ {
+ if (ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId))
+ {
+ for (uint8 idx = 0; idx < MAX_ITEM_PROTO_SPELLS; ++idx)
+ {
+ if (uint32(proto->Spells[idx].SpellId) == spellInfo->Id)
+ {
+ tmpCooldown = proto->Spells[idx].SpellCooldown;
+ tmpCategoryId = proto->Spells[idx].SpellCategory;
+ tmpCategoryCooldown = proto->Spells[idx].SpellCategoryCooldown;
+ break;
+ }
+ }
+ }
+ }
+
+ // if no cooldown found above then base at DBC data
+ if (tmpCooldown < 0 && tmpCategoryCooldown < 0)
+ {
+ tmpCooldown = spellInfo->RecoveryTime;
+ tmpCategoryId = spellInfo->GetCategory();
+ tmpCategoryCooldown = spellInfo->CategoryRecoveryTime;
+ }
+
+ if (cooldown)
+ *cooldown = tmpCooldown;
+ if (categoryId)
+ *categoryId = tmpCategoryId;
+ if (categoryCooldown)
+ *categoryCooldown = tmpCategoryCooldown;
+}
+
void SpellHistory::SaveCooldownStateBeforeDuel()
{
_spellCooldownsBeforeDuel = _spellCooldowns;
@@ -671,19 +692,12 @@ void SpellHistory::RestoreCooldownStateAfterDuel()
_spellCooldownsBeforeDuel[itr->first] = _spellCooldowns[itr->first];
}
- _spellCooldowns = _spellCooldownsBeforeDuel;
-
- // update the client: clear all cooldowns
- std::vector<int32> resetCooldowns;
- resetCooldowns.reserve(_spellCooldowns.size());
-
- for (auto itr = _spellCooldowns.begin(); itr != _spellCooldowns.end(); ++itr)
- resetCooldowns.push_back(itr->first);
-
- if (resetCooldowns.empty())
- return;
-
- SendClearCooldowns(resetCooldowns);
+ // check for spell with onHold active before and during the duel
+ for (auto itr = _spellCooldownsBeforeDuel.begin(); itr != _spellCooldownsBeforeDuel.end(); ++itr)
+ {
+ if (!itr->second.OnHold && !_spellCooldowns[itr->first].OnHold)
+ _spellCooldowns[itr->first] = _spellCooldownsBeforeDuel[itr->first];
+ }
// update the client: restore old cooldowns
PacketCooldowns cooldowns;
@@ -694,14 +708,14 @@ void SpellHistory::RestoreCooldownStateAfterDuel()
uint32 cooldownDuration = itr->second.CooldownEnd > now ? std::chrono::duration_cast<std::chrono::milliseconds>(itr->second.CooldownEnd - now).count() : 0;
// cooldownDuration must be between 0 and 10 minutes in order to avoid any visual bugs
- if (cooldownDuration == 0 || cooldownDuration > 10 * MINUTE * IN_MILLISECONDS)
+ if (cooldownDuration <= 0 || cooldownDuration > 10 * MINUTE * IN_MILLISECONDS || itr->second.OnHold)
continue;
cooldowns[itr->first] = cooldownDuration;
}
WorldPacket data;
- BuildCooldownPacket(data, SPELL_COOLDOWN_FLAG_NONE, cooldowns);
+ BuildCooldownPacket(data, SPELL_COOLDOWN_FLAG_INCLUDE_EVENT_COOLDOWNS, cooldowns);
player->SendDirectMessage(&data);
}
}
diff --git a/src/server/game/Spells/SpellHistory.h b/src/server/game/Spells/SpellHistory.h
index 6a1da28f08f..db65cd50c3e 100644
--- a/src/server/game/Spells/SpellHistory.h
+++ b/src/server/game/Spells/SpellHistory.h
@@ -38,6 +38,7 @@ public:
struct CooldownEntry
{
+ uint32 SpellId = 0;
Clock::time_point CooldownEnd;
uint32 ItemId = 0;
uint32 CategoryId = 0;
@@ -135,6 +136,8 @@ private:
typedef std::unordered_map<uint32, uint32> PacketCooldowns;
void BuildCooldownPacket(WorldPacket& data, uint8 flags, PacketCooldowns const& cooldowns) const;
+ static void GetCooldownDurations(SpellInfo const* spellInfo, uint32 itemId, int32* cooldown, uint32* categoryId, int32* categoryCooldown);
+
Unit* _owner;
CooldownStorageType _spellCooldowns;
CooldownStorageType _spellCooldownsBeforeDuel;
diff --git a/src/server/game/Spells/SpellInfo.h b/src/server/game/Spells/SpellInfo.h
index 241e7df9544..d042f926d02 100644
--- a/src/server/game/Spells/SpellInfo.h
+++ b/src/server/game/Spells/SpellInfo.h
@@ -176,7 +176,7 @@ enum SpellCustomAttributes
SPELL_ATTR0_CU_CONE_LINE = 0x00000004,
SPELL_ATTR0_CU_SHARE_DAMAGE = 0x00000008,
SPELL_ATTR0_CU_NO_INITIAL_THREAT = 0x00000010,
- SPELL_ATTR0_CU_AURA_CC = 0x00000040,
+ SPELL_ATTR0_CU_DONT_BREAK_STEALTH = 0x00000040,
SPELL_ATTR0_CU_DIRECT_DAMAGE = 0x00000100,
SPELL_ATTR0_CU_CHARGE = 0x00000200,
SPELL_ATTR0_CU_PICKPOCKET = 0x00000400,
diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp
index 6305d0a456f..debe4ac3cbf 100644
--- a/src/server/game/Spells/SpellMgr.cpp
+++ b/src/server/game/Spells/SpellMgr.cpp
@@ -969,10 +969,10 @@ bool SpellMgr::CanSpellTriggerProcOnEvent(SpellProcEntry const& procEntry, ProcE
// check spell family name/flags (if set) for spells
if (eventInfo.GetTypeMask() & (PERIODIC_PROC_FLAG_MASK | SPELL_PROC_FLAG_MASK | PROC_FLAG_DONE_TRAP_ACTIVATION))
{
- if (procEntry.spellFamilyName && eventInfo.GetSpellInfo() && (procEntry.spellFamilyName != eventInfo.GetSpellInfo()->SpellFamilyName))
+ if (procEntry.spellFamilyName && eventInfo.GetSpellInfo() && (procEntry.spellFamilyName != eventInfo.EnsureSpellInfo()->SpellFamilyName))
return false;
- if (procEntry.spellFamilyMask && eventInfo.GetSpellInfo() && !(procEntry.spellFamilyMask & eventInfo.GetSpellInfo()->SpellFamilyFlags))
+ if (procEntry.spellFamilyMask && eventInfo.GetSpellInfo() && !(procEntry.spellFamilyMask & eventInfo.EnsureSpellInfo()->SpellFamilyFlags))
return false;
}
@@ -2801,14 +2801,6 @@ void SpellMgr::LoadSpellInfoCustomAttributes()
{
switch (spellInfo->Effects[j].ApplyAuraName)
{
- case SPELL_AURA_MOD_POSSESS:
- case SPELL_AURA_MOD_CONFUSE:
- case SPELL_AURA_MOD_CHARM:
- case SPELL_AURA_AOE_CHARM:
- case SPELL_AURA_MOD_FEAR:
- case SPELL_AURA_MOD_STUN:
- spellInfo->AttributesCu |= SPELL_ATTR0_CU_AURA_CC;
- break;
case SPELL_AURA_PERIODIC_HEAL:
case SPELL_AURA_PERIODIC_DAMAGE:
case SPELL_AURA_PERIODIC_DAMAGE_PERCENT:
@@ -2901,22 +2893,6 @@ void SpellMgr::LoadSpellInfoCustomAttributes()
if (spellInfo->SpellVisual[0] == 3879)
spellInfo->AttributesCu |= SPELL_ATTR0_CU_CONE_BACK;
- switch (spellInfo->SpellFamilyName)
- {
- case SPELLFAMILY_WARRIOR:
- // Shout
- if (spellInfo->SpellFamilyFlags[0] & 0x20000 || spellInfo->SpellFamilyFlags[1] & 0x20)
- spellInfo->AttributesCu |= SPELL_ATTR0_CU_AURA_CC;
- break;
- case SPELLFAMILY_DRUID:
- // Roar
- if (spellInfo->SpellFamilyFlags[0] & 0x8)
- spellInfo->AttributesCu |= SPELL_ATTR0_CU_AURA_CC;
- break;
- default:
- break;
- }
-
spellInfo->_InitializeExplicitTargetMask();
}
@@ -2947,6 +2923,11 @@ void SpellMgr::LoadSpellInfoCorrections()
spellInfo->Speed = SPEED_CHARGE;
break;
}
+
+ // Passive talent auras cannot target pets
+ if (spellInfo->IsPassive() && GetTalentSpellCost(i))
+ if (spellInfo->Effects[j].TargetA.GetTarget() == TARGET_UNIT_PET)
+ spellInfo->Effects[j].TargetA = SpellImplicitTargetInfo(TARGET_UNIT_CASTER);
}
if (spellInfo->ActiveIconID == 2158) // flight
@@ -3072,6 +3053,7 @@ void SpellMgr::LoadSpellInfoCorrections()
case 52479: // Gift of the Harvester
case 48246: // Ball of Flame
case 36327: // Shoot Arcane Explosion Arrow
+ case 55479: // Force Obedience
spellInfo->MaxAffectedTargets = 1;
break;
case 36384: // Skartax Purple Beam
diff --git a/src/server/game/Texts/CreatureTextMgr.cpp b/src/server/game/Texts/CreatureTextMgr.cpp
index 9f75d917aea..034fe96306e 100644
--- a/src/server/game/Texts/CreatureTextMgr.cpp
+++ b/src/server/game/Texts/CreatureTextMgr.cpp
@@ -293,7 +293,7 @@ uint32 CreatureTextMgr::SendChat(Creature* source, uint8 textGroup, WorldObject
SendChatPacket(finalSource, builder, finalType, whisperTarget, range, team, gmOnly);
}
- if (isEqualChanced || (!isEqualChanced && totalChance == 100.0f))
+ if (isEqualChanced || totalChance == 100.0f)
SetRepeatId(source, textGroup, iter->id);
return iter->duration;
diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp
index bb4de29ad56..bcfd105f3c6 100644
--- a/src/server/game/World/World.cpp
+++ b/src/server/game/World/World.cpp
@@ -1183,6 +1183,7 @@ void World::LoadConfigSettings(bool reload)
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_DUEL_COOLDOWNS] = sConfigMgr->GetBoolDefault("ResetDuelCooldowns", false);
+ m_bool_configs[CONFIG_RESET_DUEL_HEALTH_MANA] = sConfigMgr->GetBoolDefault("ResetDuelHealthMana", false);
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);
@@ -1781,6 +1782,7 @@ void World::SetInitialWorldSettings()
m_timers[WUPDATE_WEATHERS].SetInterval(1*IN_MILLISECONDS);
m_timers[WUPDATE_AUCTIONS].SetInterval(MINUTE*IN_MILLISECONDS);
+ m_timers[WUPDATE_AUCTIONS_PENDING].SetInterval(250);
m_timers[WUPDATE_UPTIME].SetInterval(m_int_configs[CONFIG_UPTIME_UPDATE]*MINUTE*IN_MILLISECONDS);
//Update "uptime" table based on configuration entry in minutes.
m_timers[WUPDATE_CORPSES].SetInterval(20 * MINUTE * IN_MILLISECONDS);
@@ -2051,6 +2053,13 @@ void World::Update(uint32 diff)
sAuctionMgr->Update();
}
+ if (m_timers[WUPDATE_AUCTIONS_PENDING].Passed())
+ {
+ m_timers[WUPDATE_AUCTIONS_PENDING].Reset();
+
+ sAuctionMgr->UpdatePendingAuctions();
+ }
+
/// <li> Handle AHBot operations
if (m_timers[WUPDATE_AHBOT].Passed())
{
diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h
index e3dc3b55681..c76b11276cc 100644
--- a/src/server/game/World/World.h
+++ b/src/server/game/World/World.h
@@ -69,6 +69,7 @@ enum ShutdownExitCode
enum WorldTimers
{
WUPDATE_AUCTIONS,
+ WUPDATE_AUCTIONS_PENDING,
WUPDATE_WEATHERS,
WUPDATE_UPTIME,
WUPDATE_CORPSES,
@@ -164,6 +165,7 @@ enum WorldBoolConfigs
CONFIG_CALCULATE_CREATURE_ZONE_AREA_DATA,
CONFIG_CALCULATE_GAMEOBJECT_ZONE_AREA_DATA,
CONFIG_RESET_DUEL_COOLDOWNS,
+ CONFIG_RESET_DUEL_HEALTH_MANA,
BOOL_CONFIG_VALUE_COUNT
};
diff --git a/src/server/scripts/Commands/cs_wp.cpp b/src/server/scripts/Commands/cs_wp.cpp
index b35e71dbd50..30ee8254f72 100644
--- a/src/server/scripts/Commands/cs_wp.cpp
+++ b/src/server/scripts/Commands/cs_wp.cpp
@@ -374,6 +374,12 @@ public:
if (show == "del")
{
+ if (!arg_id)
+ {
+ handler->SendSysMessage("|cffff33ffERROR: Waypoint script guid not present.|r");
+ return true;
+ }
+
id = atoi(arg_id);
stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_WAYPOINT_SCRIPT_ID_BY_GUID);
diff --git a/src/server/scripts/EasternKingdoms/Karazhan/boss_midnight.cpp b/src/server/scripts/EasternKingdoms/Karazhan/boss_midnight.cpp
index 0b5c3302890..96c7c5d8102 100644
--- a/src/server/scripts/EasternKingdoms/Karazhan/boss_midnight.cpp
+++ b/src/server/scripts/EasternKingdoms/Karazhan/boss_midnight.cpp
@@ -108,7 +108,7 @@ public:
{
Talk(SAY_DEATH);
if (Unit* midnight = ObjectAccessor::GetUnit(*me, Midnight))
- midnight->Kill(midnight);
+ midnight->KillSelf();
}
void UpdateAI(uint32 diff) override;
@@ -264,7 +264,7 @@ void boss_attumen::boss_attumenAI::UpdateAI(uint32 diff)
}
Midnight.Clear();
me->SetVisible(false);
- me->Kill(me);
+ me->KillSelf();
} else ResetTimer -= diff;
}
diff --git a/src/server/scripts/EasternKingdoms/Karazhan/boss_prince_malchezaar.cpp b/src/server/scripts/EasternKingdoms/Karazhan/boss_prince_malchezaar.cpp
index 0abba3dfff4..d11c4a6e584 100644
--- a/src/server/scripts/EasternKingdoms/Karazhan/boss_prince_malchezaar.cpp
+++ b/src/server/scripts/EasternKingdoms/Karazhan/boss_prince_malchezaar.cpp
@@ -295,7 +295,7 @@ public:
{
Unit* axe = ObjectAccessor::GetUnit(*me, axes[i]);
if (axe && axe->IsAlive())
- axe->Kill(axe);
+ axe->KillSelf();
axes[i].Clear();
}
}
diff --git a/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_felblood_kaelthas.cpp b/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_felblood_kaelthas.cpp
index a95ac9aaa20..e4cc7c0e84a 100644
--- a/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_felblood_kaelthas.cpp
+++ b/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_felblood_kaelthas.cpp
@@ -473,7 +473,7 @@ public:
if (FlameStrikeTimer <= diff)
{
DoCast(me, SPELL_FLAMESTRIKE1_NORMAL, true);
- me->Kill(me);
+ me->KillSelf();
} else FlameStrikeTimer -= diff;
}
};
@@ -636,7 +636,7 @@ public:
if (HatchTimer <= diff)
{
me->SummonCreature(CREATURE_PHOENIX, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 60000);
- me->Kill(me);
+ me->KillSelf();
} else HatchTimer -= diff;
}
};
@@ -675,7 +675,7 @@ public:
void UpdateAI(uint32 diff) override
{
if (DespawnTimer <= diff)
- me->Kill(me);
+ me->KillSelf();
else
DespawnTimer -= diff;
diff --git a/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_selin_fireheart.cpp b/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_selin_fireheart.cpp
index f57de5316ab..480fd8848f5 100644
--- a/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_selin_fireheart.cpp
+++ b/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_selin_fireheart.cpp
@@ -139,7 +139,7 @@ class boss_selin_fireheart : public CreatureScript
for (Creature* crystal : Crystals)
{
if (crystal && crystal->IsAlive())
- crystal->Kill(crystal);
+ crystal->KillSelf();
}
}
@@ -218,7 +218,7 @@ class boss_selin_fireheart : public CreatureScript
Creature* CrystalChosen = ObjectAccessor::GetCreature(*me, CrystalGUID);
if (CrystalChosen && CrystalChosen->IsAlive())
- CrystalChosen->Kill(CrystalChosen);
+ CrystalChosen->KillSelf();
CrystalGUID.Clear();
diff --git a/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter5.cpp b/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter5.cpp
index 2714d65774f..ce8b094bb83 100644
--- a/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter5.cpp
+++ b/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter5.cpp
@@ -1218,7 +1218,7 @@ public:
if (Creature* temp = ObjectAccessor::GetCreature(*me, uiLichKingGUID)) // Lich king disappears here
{
temp->AI()->Talk(EMOTE_LIGHT_OF_DAWN17);
- temp->Kill(temp);
+ temp->KillSelf();
}
JumpToNextStep(10000);
break;
@@ -1633,7 +1633,7 @@ public:
if (temp->IsAlive())
{
temp->SetVisible(false);
- temp->Kill(temp);
+ temp->KillSelf();
}
}
};
diff --git a/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_headless_horseman.cpp b/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_headless_horseman.cpp
index a78ad740c97..bac4e450a03 100644
--- a/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_headless_horseman.cpp
+++ b/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_headless_horseman.cpp
@@ -359,8 +359,8 @@ public:
{
die = false;
if (Unit* body = ObjectAccessor::GetUnit(*me, bodyGUID))
- body->Kill(body);
- me->Kill(me);
+ body->KillSelf();
+ me->KillSelf();
}
else wait -= diff;
}
diff --git a/src/server/scripts/EasternKingdoms/ScarletMonastery/instance_scarlet_monastery.cpp b/src/server/scripts/EasternKingdoms/ScarletMonastery/instance_scarlet_monastery.cpp
index ed9f7ccb6dd..c41b1d87d61 100644
--- a/src/server/scripts/EasternKingdoms/ScarletMonastery/instance_scarlet_monastery.cpp
+++ b/src/server/scripts/EasternKingdoms/ScarletMonastery/instance_scarlet_monastery.cpp
@@ -121,7 +121,7 @@ class instance_scarlet_monastery : public InstanceMapScript
{
Creature* add = instance->GetCreature(guid);
if (add && add->IsAlive())
- add->Kill(add);
+ add->KillSelf();
}
HorsemanAdds.clear();
HandleGameObject(PumpkinShrineGUID, false);
diff --git a/src/server/scripts/EasternKingdoms/Stratholme/stratholme.cpp b/src/server/scripts/EasternKingdoms/Stratholme/stratholme.cpp
index ba99df8cdd5..60dd27eed90 100644
--- a/src/server/scripts/EasternKingdoms/Stratholme/stratholme.cpp
+++ b/src/server/scripts/EasternKingdoms/Stratholme/stratholme.cpp
@@ -170,7 +170,7 @@ public:
{
if (Player* player = temp->ToPlayer())
player->KilledMonsterCredit(NPC_RESTLESS, me->GetGUID());
- me->Kill(me);
+ me->KillSelf();
}
}
else
@@ -248,7 +248,7 @@ public:
if (Tagged)
{
if (Die_Timer <= diff)
- me->Kill(me);
+ me->KillSelf();
else Die_Timer -= diff;
}
diff --git a/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_eredar_twins.cpp b/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_eredar_twins.cpp
index 31ca716e0fb..77e0e680f4a 100644
--- a/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_eredar_twins.cpp
+++ b/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_eredar_twins.cpp
@@ -698,7 +698,7 @@ public:
if (KillTimer <= diff)
{
- me->Kill(me);
+ me->KillSelf();
KillTimer = 9999999;
} else KillTimer -= diff;
diff --git a/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_kiljaeden.cpp b/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_kiljaeden.cpp
index 4d544ccdeab..e3deac506b0 100644
--- a/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_kiljaeden.cpp
+++ b/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_kiljaeden.cpp
@@ -1112,7 +1112,7 @@ public:
else if (me->IsWithinDistInMap(me->GetVictim(), 3)) // Explode if it's close enough to it's target
{
DoCastVictim(SPELL_FELFIRE_FISSION);
- me->Kill(me);
+ me->KillSelf();
}
}
};
@@ -1172,7 +1172,7 @@ public:
uiTimer = 5000;
break;
case 3:
- me->Kill(me);
+ me->KillSelf();
me->RemoveCorpse();
break;
}
diff --git a/src/server/scripts/EasternKingdoms/zone_western_plaguelands.cpp b/src/server/scripts/EasternKingdoms/zone_western_plaguelands.cpp
index 9cecbc31665..9af23f17dca 100644
--- a/src/server/scripts/EasternKingdoms/zone_western_plaguelands.cpp
+++ b/src/server/scripts/EasternKingdoms/zone_western_plaguelands.cpp
@@ -277,145 +277,6 @@ public:
};
/*######
-## npc_anchorite_truuen
-######*/
-
-enum Truuen
-{
- NPC_GHOST_UTHER = 17233,
- NPC_THEL_DANIS = 1854,
- NPC_GHOUL = 1791, //ambush
-
- QUEST_TOMB_LIGHTBRINGER = 9446,
-
- SAY_WP_0 = 0, //Beware! We are attacked!
- SAY_WP_1 = 1, //It must be the purity of the Mark of the Lightbringer that is drawing forth the Scourge to attack us. We must proceed with caution lest we be overwhelmed!
- SAY_WP_2 = 2, //This land truly needs to be cleansed by the Light! Let us continue on to the tomb. It isn't far now...
- SAY_WP_3 = 0, //Be welcome, friends!
- SAY_WP_4 = 0, //Thank you for coming here in remembrance of me. Your efforts in recovering that symbol, while unnecessary, are certainly touching to an old man's heart.
- SAY_WP_5 = 1, //Please, rise my friend. Keep the Blessing as a symbol of the strength of the Light and how heroes long gone might once again rise in each of us to inspire.
- SAY_WP_6 = 2 //Thank you my friend for making this possible. This is a day that I shall never forget! I think I will stay a while. Please return to High Priestess MacDonnell at the camp. I know that she'll be keenly interested to know of what has transpired here.
-};
-
-class npc_anchorite_truuen : public CreatureScript
-{
-public:
- npc_anchorite_truuen() : CreatureScript("npc_anchorite_truuen") { }
-
- bool OnQuestAccept(Player* player, Creature* creature, Quest const* quest) override
- {
- if (quest->GetQuestId() == QUEST_TOMB_LIGHTBRINGER)
- {
- npc_escortAI* pEscortAI = ENSURE_AI(npc_anchorite_truuen::npc_anchorite_truuenAI, creature->AI());
- pEscortAI->Start(true, true, player->GetGUID());
- }
- return false;
- }
-
- CreatureAI* GetAI(Creature* creature) const override
- {
- return new npc_anchorite_truuenAI(creature);
- }
-
- struct npc_anchorite_truuenAI : public npc_escortAI
- {
- npc_anchorite_truuenAI(Creature* creature) : npc_escortAI(creature)
- {
- Initialize();
- }
-
- void Initialize()
- {
- m_uiChatTimer = 7000;
- }
-
- uint32 m_uiChatTimer;
-
- ObjectGuid UghostGUID;
-
- void Reset() override
- {
- Initialize();
- }
-
- void JustSummoned(Creature* summoned) override
- {
- if (summoned->GetEntry() == NPC_GHOUL)
- summoned->AI()->AttackStart(me);
- }
-
- void WaypointReached(uint32 waypointId) override
- {
- Player* player = GetPlayerForEscort();
-
- switch (waypointId)
- {
- case 8:
- Talk(SAY_WP_0);
- me->SummonCreature(NPC_GHOUL, me->GetPositionX()+7.0f, me->GetPositionY()+7.0f, me->GetPositionZ(), 0.0f, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 90000);
- me->SummonCreature(NPC_GHOUL, me->GetPositionX()+5.0f, me->GetPositionY()+5.0f, me->GetPositionZ(), 0.0f, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 90000);
- break;
- case 9:
- Talk(SAY_WP_1);
- break;
- case 14:
- me->SummonCreature(NPC_GHOUL, me->GetPositionX()+7.0f, me->GetPositionY()+7.0f, me->GetPositionZ(), 0.0f, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 90000);
- me->SummonCreature(NPC_GHOUL, me->GetPositionX()+5.0f, me->GetPositionY()+5.0f, me->GetPositionZ(), 0.0f, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 90000);
- me->SummonCreature(NPC_GHOUL, me->GetPositionX()+10.0f, me->GetPositionY()+10.0f, me->GetPositionZ(), 0.0f, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 90000);
- me->SummonCreature(NPC_GHOUL, me->GetPositionX()+8.0f, me->GetPositionY()+8.0f, me->GetPositionZ(), 0.0f, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 90000);
- break;
- case 15:
- Talk(SAY_WP_2);
- break;
- case 21:
- if (Creature* Theldanis = GetClosestCreatureWithEntry(me, NPC_THEL_DANIS, 150))
- Theldanis->AI()->Talk(SAY_WP_3);
- break;
- case 23:
- if (Creature* Ughost = me->SummonCreature(NPC_GHOST_UTHER, 971.86f, -1825.42f, 81.99f, 0.0f, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 30000))
- {
- UghostGUID = Ughost->GetGUID();
- Ughost->SetDisableGravity(true);
- Ughost->AI()->Talk(SAY_WP_4, me);
- }
- m_uiChatTimer = 4000;
- break;
- case 24:
- if (Creature* Ughost = ObjectAccessor::GetCreature(*me, UghostGUID))
- Ughost->AI()->Talk(SAY_WP_5, me);
- m_uiChatTimer = 4000;
- break;
- case 25:
- if (Creature* Ughost = ObjectAccessor::GetCreature(*me, UghostGUID))
- Ughost->AI()->Talk(SAY_WP_6, me);
- m_uiChatTimer = 4000;
- break;
- case 26:
- if (player)
- player->GroupEventHappens(QUEST_TOMB_LIGHTBRINGER, me);
- break;
- }
- }
-
- void EnterCombat(Unit* /*who*/) override { }
-
- void JustDied(Unit* /*killer*/) override
- {
- if (Player* player = GetPlayerForEscort())
- player->FailQuest(QUEST_TOMB_LIGHTBRINGER);
- }
-
- void UpdateAI(uint32 uiDiff) override
- {
- npc_escortAI::UpdateAI(uiDiff);
- DoMeleeAttackIfReady();
- if (HasEscortState(STATE_ESCORT_ESCORTING))
- m_uiChatTimer = 6000;
- }
- };
-};
-
-/*######
##
######*/
@@ -425,5 +286,4 @@ void AddSC_western_plaguelands()
new npc_myranda_the_hag();
new npc_the_scourge_cauldron();
new npc_andorhal_tower();
- new npc_anchorite_truuen();
}
diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_anetheron.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_anetheron.cpp
index 254e3a41d6b..d0a9b233454 100644
--- a/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_anetheron.cpp
+++ b/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_anetheron.cpp
@@ -235,7 +235,7 @@ public:
if (AnetheronGUID)
{
Creature* boss = ObjectAccessor::GetCreature(*me, AnetheronGUID);
- if (!boss || (boss && boss->isDead()))
+ if (!boss || boss->isDead())
{
me->setDeathState(JUST_DIED);
me->RemoveCorpse();
diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_azgalor.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_azgalor.cpp
index 08942e3393f..8bfdee067b2 100644
--- a/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_azgalor.cpp
+++ b/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/boss_azgalor.cpp
@@ -200,7 +200,6 @@ public:
CrippleTimer = 50000;
WarstompTimer = 10000;
CheckTimer = 5000;
- instance = creature->GetInstanceScript();
AzgalorGUID = instance->GetGuidData(DATA_AZGALOR);
}
@@ -208,7 +207,6 @@ public:
uint32 WarstompTimer;
uint32 CheckTimer;
ObjectGuid AzgalorGUID;
- InstanceScript* instance;
void Reset() override
{
@@ -248,7 +246,7 @@ public:
if (AzgalorGUID)
{
Creature* boss = ObjectAccessor::GetCreature(*me, AzgalorGUID);
- if (!boss || (boss && boss->isDead()))
+ if (!boss || boss->isDead())
{
me->setDeathState(JUST_DIED);
me->RemoveCorpse();
diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/boss_mal_ganis.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/boss_mal_ganis.cpp
index 70ecaad3d8d..c39fbc90795 100644
--- a/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/boss_mal_ganis.cpp
+++ b/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/boss_mal_ganis.cpp
@@ -218,7 +218,7 @@ public:
break;
case 5:
me->SetVisible(false);
- me->Kill(me);
+ me->KillSelf();
break;
}
diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/culling_of_stratholme.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/culling_of_stratholme.cpp
index b5d2931377f..a73a6b8d998 100644
--- a/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/culling_of_stratholme.cpp
+++ b/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/culling_of_stratholme.cpp
@@ -817,7 +817,7 @@ public:
cityman1->AI()->Talk(SAY_PHASE204);
cityman1->SetTarget(me->GetGUID());
if (Creature* cityman0 = ObjectAccessor::GetCreature(*me, citymenGUID[0]))
- cityman0->Kill(cityman0);
+ cityman0->KillSelf();
me->SetTarget(citymenGUID[1]);
}
JumpToNextStep(0);
@@ -829,7 +829,7 @@ public:
break;
case 33:
if (Creature* cityman1 = ObjectAccessor::GetCreature(*me, citymenGUID[1]))
- cityman1->Kill(cityman1);
+ cityman1->KillSelf();
JumpToNextStep(1000);
break;
case 34:
diff --git a/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_cthun.cpp b/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_cthun.cpp
index 8e84f1b4265..6016893a785 100644
--- a/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_cthun.cpp
+++ b/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_cthun.cpp
@@ -917,7 +917,7 @@ public:
void JustDied(Unit* /*killer*/) override
{
if (Unit* p = ObjectAccessor::GetUnit(*me, Portal))
- p->Kill(p);
+ p->KillSelf();
}
void Reset() override
@@ -943,7 +943,7 @@ public:
//KillSelfTimer
if (KillSelfTimer <= diff)
{
- me->Kill(me);
+ me->KillSelf();
return;
} else KillSelfTimer -= diff;
@@ -997,7 +997,7 @@ public:
void JustDied(Unit* /*killer*/) override
{
if (Unit* p = ObjectAccessor::GetUnit(*me, Portal))
- p->Kill(p);
+ p->KillSelf();
}
void Reset() override
@@ -1025,7 +1025,7 @@ public:
if (EvadeTimer <= diff)
{
if (Unit* p = ObjectAccessor::GetUnit(*me, Portal))
- p->Kill(p);
+ p->KillSelf();
//Dissapear and reappear at new position
me->SetVisible(false);
@@ -1033,7 +1033,7 @@ public:
Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0);
if (!target)
{
- me->Kill(me);
+ me->KillSelf();
return;
}
@@ -1113,7 +1113,7 @@ public:
void JustDied(Unit* /*killer*/) override
{
if (Unit* p = ObjectAccessor::GetUnit(*me, Portal))
- p->Kill(p);
+ p->KillSelf();
}
void Reset() override
@@ -1142,7 +1142,7 @@ public:
if (EvadeTimer <= diff)
{
if (Unit* p = ObjectAccessor::GetUnit(*me, Portal))
- p->Kill(p);
+ p->KillSelf();
//Dissapear and reappear at new position
me->SetVisible(false);
@@ -1150,7 +1150,7 @@ public:
Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0);
if (!target)
{
- me->Kill(me);
+ me->KillSelf();
return;
}
@@ -1231,7 +1231,7 @@ public:
void JustDied(Unit* /*killer*/) override
{
if (Unit* p = ObjectAccessor::GetUnit(*me, Portal))
- p->Kill(p);
+ p->KillSelf();
}
void Reset() override
diff --git a/src/server/scripts/Kalimdor/zone_dustwallow_marsh.cpp b/src/server/scripts/Kalimdor/zone_dustwallow_marsh.cpp
index b0800918765..fba0956ccfc 100644
--- a/src/server/scripts/Kalimdor/zone_dustwallow_marsh.cpp
+++ b/src/server/scripts/Kalimdor/zone_dustwallow_marsh.cpp
@@ -352,7 +352,7 @@ class spell_ooze_zap_channel_end : public SpellScriptLoader
PreventHitDefaultEffect(effIndex);
if (Player* player = GetCaster()->ToPlayer())
player->CastSpell(player, SPELL_OOZE_CHANNEL_CREDIT, true);
- GetHitUnit()->Kill(GetHitUnit());
+ GetHitUnit()->KillSelf();
}
void Register() override
diff --git a/src/server/scripts/Kalimdor/zone_moonglade.cpp b/src/server/scripts/Kalimdor/zone_moonglade.cpp
index 163620230ef..c7decd3e286 100644
--- a/src/server/scripts/Kalimdor/zone_moonglade.cpp
+++ b/src/server/scripts/Kalimdor/zone_moonglade.cpp
@@ -108,64 +108,6 @@ public:
};
/*######
-## npc_great_bear_spirit
-######*/
-
-#define GOSSIP_BEAR1 "What do you represent, spirit?"
-#define GOSSIP_BEAR2 "I seek to understand the importance of strength of the body."
-#define GOSSIP_BEAR3 "I seek to understand the importance of strength of the heart."
-#define GOSSIP_BEAR4 "I have heard your words, Great Bear Spirit, and I understand. I now seek your blessings to fully learn the way of the Claw."
-
-class npc_great_bear_spirit : public CreatureScript
-{
-public:
- npc_great_bear_spirit() : CreatureScript("npc_great_bear_spirit") { }
-
- bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override
- {
- player->PlayerTalkClass->ClearMenus();
- switch (action)
- {
- case GOSSIP_ACTION_INFO_DEF:
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_BEAR2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
- player->SEND_GOSSIP_MENU(4721, creature->GetGUID());
- break;
- case GOSSIP_ACTION_INFO_DEF + 1:
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_BEAR3, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2);
- player->SEND_GOSSIP_MENU(4733, creature->GetGUID());
- break;
- case GOSSIP_ACTION_INFO_DEF + 2:
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_BEAR4, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3);
- player->SEND_GOSSIP_MENU(4734, creature->GetGUID());
- break;
- case GOSSIP_ACTION_INFO_DEF + 3:
- player->SEND_GOSSIP_MENU(4735, creature->GetGUID());
- if (player->GetQuestStatus(5929) == QUEST_STATUS_INCOMPLETE)
- player->AreaExploredOrEventHappens(5929);
- if (player->GetQuestStatus(5930) == QUEST_STATUS_INCOMPLETE)
- player->AreaExploredOrEventHappens(5930);
- break;
- }
- return true;
- }
-
- bool OnGossipHello(Player* player, Creature* creature) override
- {
- //ally or horde quest
- if (player->GetQuestStatus(5929) == QUEST_STATUS_INCOMPLETE || player->GetQuestStatus(5930) == QUEST_STATUS_INCOMPLETE)
- {
- player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_BEAR1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF);
- player->SEND_GOSSIP_MENU(4719, creature->GetGUID());
- }
- else
- player->SEND_GOSSIP_MENU(4718, creature->GetGUID());
-
- return true;
- }
-
-};
-
-/*######
## npc_silva_filnaveth
######*/
@@ -712,7 +654,6 @@ public:
void AddSC_moonglade()
{
new npc_bunthen_plainswind();
- new npc_great_bear_spirit();
new npc_silva_filnaveth();
new npc_clintar_spirit();
new npc_omen();
diff --git a/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_jedoga_shadowseeker.cpp b/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_jedoga_shadowseeker.cpp
index 5890b9cbec7..240e8a7b957 100644
--- a/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_jedoga_shadowseeker.cpp
+++ b/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_jedoga_shadowseeker.cpp
@@ -433,7 +433,7 @@ public:
{
ENSURE_AI(boss_jedoga_shadowseeker::boss_jedoga_shadowseekerAI, boss->AI())->bOpFerok = true;
ENSURE_AI(boss_jedoga_shadowseeker::boss_jedoga_shadowseekerAI, boss->AI())->bOpFerokFail = false;
- me->Kill(me);
+ me->KillSelf();
}
}
break;
diff --git a/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/obsidian_sanctum.cpp b/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/obsidian_sanctum.cpp
index eac84d1f6c9..3b12104a90b 100644
--- a/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/obsidian_sanctum.cpp
+++ b/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/obsidian_sanctum.cpp
@@ -338,14 +338,14 @@ struct dummy_dragonAI : public ScriptedAI
if (instance->GetBossState(DATA_SARTHARION) != IN_PROGRESS)
instance->SetBossState(DATA_SHADRON, DONE);
if (Creature* acolyte = me->FindNearestCreature(NPC_ACOLYTE_OF_SHADRON, 100.0f))
- acolyte->Kill(acolyte);
+ acolyte->KillSelf();
break;
case NPC_VESPERON:
spellId = SPELL_POWER_OF_VESPERON;
if (instance->GetBossState(DATA_SARTHARION) != IN_PROGRESS)
instance->SetBossState(DATA_VESPERON, DONE);
if (Creature* acolyte = me->FindNearestCreature(NPC_ACOLYTE_OF_VESPERON, 100.0f))
- acolyte->Kill(acolyte);
+ acolyte->KillSelf();
break;
}
@@ -948,7 +948,7 @@ public:
//DoCastVictim(57620, true);
//DoCastVictim(57874, true);
me->RemoveAllAuras();
- me->Kill(me);
+ me->KillSelf();
}
}
diff --git a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp
index 0b8fa7459a8..6bc49de5319 100644
--- a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp
+++ b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp
@@ -360,11 +360,11 @@ class boss_halion : public CreatureScript
if (Creature* twilightHalion = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_TWILIGHT_HALION)))
if (twilightHalion->IsAlive())
- twilightHalion->Kill(twilightHalion);
+ twilightHalion->KillSelf();
if (Creature* controller = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_HALION_CONTROLLER)))
if (controller->IsAlive())
- controller->Kill(controller);
+ controller->KillSelf();
}
Position const* GetMeteorStrikePosition() const { return &_meteorStrikePos; }
@@ -523,7 +523,7 @@ class boss_twilight_halion : public CreatureScript
if (Creature* controller = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_HALION_CONTROLLER)))
if (controller->IsAlive())
- controller->Kill(controller);
+ controller->KillSelf();
instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me);
}
diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_lady_deathwhisper.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_lady_deathwhisper.cpp
index b2aa6dadd84..78a279b94fd 100644
--- a/src/server/scripts/Northrend/IcecrownCitadel/boss_lady_deathwhisper.cpp
+++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_lady_deathwhisper.cpp
@@ -831,7 +831,7 @@ class npc_vengeful_shade : public CreatureScript
case SPELL_VENGEFUL_BLAST_25N:
case SPELL_VENGEFUL_BLAST_10H:
case SPELL_VENGEFUL_BLAST_25H:
- me->Kill(me);
+ me->KillSelf();
break;
default:
break;
@@ -1019,7 +1019,7 @@ class spell_cultist_dark_martyrdom : public SpellScriptLoader
if (Unit* owner = GetCaster()->ToTempSummon()->GetSummoner())
owner->GetAI()->SetGUID(GetCaster()->GetGUID(), GUID_CULTIST);
- GetCaster()->Kill(GetCaster());
+ GetCaster()->KillSelf();
GetCaster()->SetDisplayId(uint32(GetCaster()->GetEntry() == NPC_CULT_FANATIC ? 38009 : 38010));
}
diff --git a/src/server/scripts/Northrend/Naxxramas/boss_anubrekhan.cpp b/src/server/scripts/Northrend/Naxxramas/boss_anubrekhan.cpp
index 47569a6b85f..11fe5f41455 100644
--- a/src/server/scripts/Northrend/Naxxramas/boss_anubrekhan.cpp
+++ b/src/server/scripts/Northrend/Naxxramas/boss_anubrekhan.cpp
@@ -197,7 +197,7 @@ public:
if (!guardCorpses.empty())
{
if (ObjectGuid target = Trinity::Containers::SelectRandomContainerElement(guardCorpses))
- if(Creature* creatureTarget = ObjectAccessor::GetCreature(*me, target))
+ if (Creature* creatureTarget = ObjectAccessor::GetCreature(*me, target))
{
creatureTarget->CastSpell(creatureTarget, SPELL_SUMMON_CORPSE_SCARABS_MOB, true, nullptr, nullptr, me->GetGUID());
creatureTarget->AI()->Talk(EMOTE_SCARAB);
diff --git a/src/server/scripts/Northrend/Naxxramas/boss_maexxna.cpp b/src/server/scripts/Northrend/Naxxramas/boss_maexxna.cpp
index e52731d003e..0d938122f28 100644
--- a/src/server/scripts/Northrend/Naxxramas/boss_maexxna.cpp
+++ b/src/server/scripts/Northrend/Naxxramas/boss_maexxna.cpp
@@ -18,6 +18,7 @@
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "PassiveAI.h"
+#include "SpellScript.h"
#include "naxxramas.h"
enum Spells
@@ -28,6 +29,14 @@ enum Spells
SPELL_NECROTIC_POISON = 28776,
SPELL_FRENZY = 54123
};
+#define SPELL_FRENZY_HELPER RAID_MODE(54123,54124)
+
+enum Emotes
+{
+ EMOTE_SPIDERS = 0,
+ EMOTE_WEB_WRAP = 1,
+ EMOTE_WEB_SPRAY = 2
+};
enum Creatures
{
@@ -35,12 +44,16 @@ enum Creatures
NPC_SPIDERLING = 17055,
};
-#define MAX_POS_WRAP 3
-const Position PosWrap[MAX_POS_WRAP] =
+#define MAX_WRAP_POSITION 7
+const Position WrapPositions[MAX_WRAP_POSITION] =
{
- {3546.796f, -3869.082f, 296.450f, 0.0f},
- {3531.271f, -3847.424f, 299.450f, 0.0f},
- {3497.067f, -3843.384f, 302.384f, 0.0f},
+ {3453.818f, -3854.651f, 308.7581f, 4.362833f},
+ {3535.042f, -3842.383f, 300.795f, 3.179324f},
+ {3538.399f, -3846.088f, 299.964f, 4.310297f},
+ {3548.464f, -3854.676f, 298.6075f, 4.546609f},
+ {3557.663f, -3870.123f, 297.5027f, 3.756433f},
+ {3560.546f, -3879.353f, 297.4843f, 2.508937f},
+ {3562.535f, -3892.507f, 298.532f, 6.022466f},
};
enum Events
@@ -51,7 +64,24 @@ enum Events
EVENT_POISON,
EVENT_WRAP,
EVENT_SUMMON,
- EVENT_FRENZY,
+};
+
+const float WEB_WRAP_MOVE_SPEED = 20.0f;
+
+struct WebTargetSelector : public std::unary_function<Unit*, bool>
+{
+ WebTargetSelector(Unit* maexxna) : _maexxna(maexxna) {}
+ bool operator()(Unit const* target) const
+ {
+ if (_maexxna->GetVictim() == target) // never target tank
+ return false;
+ if (target->HasAura(SPELL_WEB_WRAP)) // never target targets that are already webbed
+ return false;
+ return true;
+ }
+
+ private:
+ const Unit* _maexxna;
};
class boss_maexxna : public CreatureScript
@@ -66,27 +96,22 @@ public:
struct boss_maexxnaAI : public BossAI
{
- boss_maexxnaAI(Creature* creature) : BossAI(creature, BOSS_MAEXXNA)
- {
- Initialize();
- }
+ boss_maexxnaAI(Creature* creature) : BossAI(creature, BOSS_MAEXXNA) { }
- void Initialize()
+ void EnterCombat(Unit* /*who*/) override
{
- enraged = false;
+ _EnterCombat();
+ events.ScheduleEvent(EVENT_WRAP, 20 * IN_MILLISECONDS);
+ events.ScheduleEvent(EVENT_SPRAY, 40 * IN_MILLISECONDS);
+ events.ScheduleEvent(EVENT_SHOCK, urandms(5, 10));
+ events.ScheduleEvent(EVENT_POISON, urandms(10, 15));
+ events.ScheduleEvent(EVENT_SUMMON, 30 * IN_MILLISECONDS);
}
- bool enraged;
-
- void EnterCombat(Unit* /*who*/) override
+ void Reset() override
{
- _EnterCombat();
- Initialize();
- events.ScheduleEvent(EVENT_WRAP, 20000);
- events.ScheduleEvent(EVENT_SPRAY, 40000);
- events.ScheduleEvent(EVENT_SHOCK, urand(5000, 10000));
- events.ScheduleEvent(EVENT_POISON, urand(10000, 15000));
- events.ScheduleEvent(EVENT_SUMMON, 30000);
+ _Reset();
+ instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_WEB_WRAP);
}
void UpdateAI(uint32 diff) override
@@ -94,10 +119,9 @@ public:
if (!UpdateVictim() || !CheckInRoom())
return;
- if (!enraged && HealthBelowPct(30))
+ if (HealthBelowPct(30) && !me->HasAura(SPELL_FRENZY_HELPER))
{
- enraged = true;
- events.ScheduleEvent(EVENT_FRENZY, 0); // will be cast immediately
+ DoCast(SPELL_FRENZY);
}
events.Update(diff);
@@ -107,41 +131,49 @@ public:
switch (eventId)
{
case EVENT_WRAP:
- /// @todo Add missing text
- for (uint8 i = 0; i < RAID_MODE(1, 2); ++i)
+ {
+ std::list<Unit*> targets;
+ SelectTargetList(targets, WebTargetSelector(me), RAID_MODE(1, 2), SELECT_TARGET_RANDOM);
+ if (!targets.empty())
{
- if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1, 0, true, -SPELL_WEB_WRAP))
+ Talk(EMOTE_WEB_WRAP);
+ int8 wrapPos = -1;
+ for (Unit* target : targets)
{
+ if (wrapPos == -1) // allow all positions on the first target
+ wrapPos = urand(0, MAX_WRAP_POSITION - 1);
+ else // on subsequent iterations, only allow positions that are not equal to the previous one (this is sufficient since we should only have two targets at most, ever)
+ wrapPos = (wrapPos + urand(1, MAX_WRAP_POSITION - 1)) % MAX_WRAP_POSITION;
+
target->RemoveAura(sSpellMgr->GetSpellIdForDifficulty(SPELL_WEB_SPRAY, me));
- uint8 pos = rand32() % MAX_POS_WRAP;
- target->GetMotionMaster()->MoveJump(PosWrap[pos].GetPositionX(), PosWrap[pos].GetPositionY(), PosWrap[pos].GetPositionZ(), 20, 20);
- if (Creature* wrap = DoSummon(NPC_WEB_WRAP, PosWrap[pos], 0, TEMPSUMMON_CORPSE_DESPAWN))
- wrap->AI()->SetGUID(target->GetGUID());
+ if (Creature* wrap = DoSummon(NPC_WEB_WRAP, WrapPositions[wrapPos], 70 * IN_MILLISECONDS, TEMPSUMMON_TIMED_DESPAWN))
+ {
+ wrap->AI()->SetGUID(target->GetGUID()); // handles application of debuff
+ target->GetMotionMaster()->MoveJump(WrapPositions[wrapPos], WEB_WRAP_MOVE_SPEED, WEB_WRAP_MOVE_SPEED); // move after stun to avoid stun cancelling move
+ }
}
}
events.ScheduleEvent(EVENT_WRAP, 40000);
break;
+ }
case EVENT_SPRAY:
+ Talk(EMOTE_WEB_SPRAY);
DoCastAOE(SPELL_WEB_SPRAY);
events.ScheduleEvent(EVENT_SPRAY, 40000);
break;
case EVENT_SHOCK:
DoCastAOE(SPELL_POISON_SHOCK);
- events.ScheduleEvent(EVENT_SHOCK, urand(10000, 20000));
+ events.ScheduleEvent(EVENT_SHOCK, urandms(10, 20));
break;
case EVENT_POISON:
DoCastVictim(SPELL_NECROTIC_POISON);
- events.ScheduleEvent(EVENT_POISON, urand(10000, 20000));
- break;
- case EVENT_FRENZY:
- DoCast(me, SPELL_FRENZY, true);
- events.ScheduleEvent(EVENT_FRENZY, 600000);
+ events.ScheduleEvent(EVENT_POISON, urandms(10, 20));
break;
case EVENT_SUMMON:
- /// @todo Add missing text
+ Talk(EMOTE_SPIDERS);
uint8 amount = urand(8, 10);
for (uint8 i = 0; i < amount; ++i)
- DoSummon(NPC_SPIDERLING, me, 0, TEMPSUMMON_CORPSE_DESPAWN);
+ DoSummon(NPC_SPIDERLING, me, 4.0f, 5 * IN_MILLISECONDS, TEMPSUMMON_CORPSE_TIMED_DESPAWN);
events.ScheduleEvent(EVENT_SUMMON, 40000);
break;
}
@@ -165,23 +197,49 @@ public:
struct npc_webwrapAI : public NullCreatureAI
{
- npc_webwrapAI(Creature* creature) : NullCreatureAI(creature) { }
+ npc_webwrapAI(Creature* creature) : NullCreatureAI(creature), visibleTimer(0) { }
ObjectGuid victimGUID;
+ uint32 visibleTimer;
+
+ void InitializeAI() override
+ {
+ me->SetVisible(false);
+ }
void SetGUID(ObjectGuid guid, int32 /*param*/) override
{
+ if (!guid)
+ return;
victimGUID = guid;
- if (me->m_spells[0] && victimGUID)
- if (Unit* victim = ObjectAccessor::GetUnit(*me, victimGUID))
- victim->CastSpell(victim, me->m_spells[0], true, NULL, NULL, me->GetGUID());
+ if (Unit* victim = ObjectAccessor::GetUnit(*me, victimGUID))
+ {
+ visibleTimer = (me->GetDistance2d(victim)/WEB_WRAP_MOVE_SPEED + 0.5f) * IN_MILLISECONDS;
+ victim->CastSpell(victim, SPELL_WEB_WRAP, true, NULL, NULL, me->GetGUID());
+ }
+ }
+
+ void UpdateAI(uint32 diff) override
+ {
+ if (!visibleTimer)
+ return;
+
+ if (diff >= visibleTimer)
+ {
+ visibleTimer = 0;
+ me->SetVisible(true);
+ }
+ else
+ visibleTimer -= diff;
}
void JustDied(Unit* /*killer*/) override
{
- if (me->m_spells[0] && victimGUID)
+ if (victimGUID)
if (Unit* victim = ObjectAccessor::GetUnit(*me, victimGUID))
- victim->RemoveAurasDueToSpell(me->m_spells[0], me->GetGUID());
+ victim->RemoveAurasDueToSpell(SPELL_WEB_WRAP, me->GetGUID());
+
+ me->DespawnOrUnsummon(5 * IN_MILLISECONDS);
}
};
diff --git a/src/server/scripts/Northrend/Naxxramas/boss_noth.cpp b/src/server/scripts/Northrend/Naxxramas/boss_noth.cpp
index 106661b70bf..7b3a9f8ac74 100644
--- a/src/server/scripts/Northrend/Naxxramas/boss_noth.cpp
+++ b/src/server/scripts/Northrend/Naxxramas/boss_noth.cpp
@@ -155,7 +155,7 @@ public:
void KilledUnit(Unit* victim) override
{
- if(victim->GetTypeId() == TYPEID_PLAYER)
+ if (victim->GetTypeId() == TYPEID_PLAYER)
Talk(SAY_SLAY);
}
diff --git a/src/server/scripts/Northrend/Naxxramas/boss_razuvious.cpp b/src/server/scripts/Northrend/Naxxramas/boss_razuvious.cpp
index 1683667a02a..f94f7b227bf 100644
--- a/src/server/scripts/Northrend/Naxxramas/boss_razuvious.cpp
+++ b/src/server/scripts/Northrend/Naxxramas/boss_razuvious.cpp
@@ -17,43 +17,40 @@
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
+#include "SpellInfo.h"
#include "naxxramas.h"
-//Razuvious - NO TEXT sound only
-//8852 aggro01 - Hah hah, I'm just getting warmed up!
-//8853 aggro02 Stand and fight!
-//8854 aggro03 Show me what you've got!
-//8861 slay1 - You should've stayed home!
-//8863 slay2-
-//8858 cmmnd3 - You disappoint me, students!
-//8855 cmmnd1 - Do as I taught you!
-//8856 cmmnd2 - Show them no mercy!
-//8859 cmmnd4 - The time for practice is over! Show me what you've learned!
-//8861 Sweep the leg! Do you have a problem with that?
-//8860 death - An honorable... death...
-//8947 - Aggro Mixed? - ?
-
-#define SOUND_AGGRO RAND(8852, 8853, 8854)
-#define SOUND_SLAY RAND(8861, 8863)
-#define SOUND_COMMND RAND(8855, 8856, 8858, 8859, 8861)
-#define SOUND_DEATH 8860
-#define SOUND_AGGROMIX 8847
+enum Yells
+{
+ SAY_AGGRO = 0,
+ SAY_SLAY = 1,
+ SAY_TAUNTED = 2,
+ SAY_DEATH = 3
+};
enum Spells
{
- SPELL_UNBALANCING_STRIKE = 26613,
- SPELL_DISRUPTING_SHOUT = 29107,
- SPELL_JAGGED_KNIFE = 55550,
- SPELL_HOPELESS = 29125
+ SPELL_UNBALANCING_STRIKE = 26613,
+ SPELL_DISRUPTING_SHOUT = 29107,
+ SPELL_JAGGED_KNIFE = 55550,
+ SPELL_HOPELESS = 29125,
+ SPELL_UNDERSTUDY_TAUNT = 29060,
+ SPELL_UNDERSTUDY_BLOOD_STRIKE = 61696,
+ SPELL_FORCE_OBEDIENCE = 55479
};
enum Events
{
- EVENT_NONE,
+ EVENT_ATTACK = 1,
EVENT_STRIKE,
EVENT_SHOUT,
- EVENT_KNIFE,
- EVENT_COMMAND,
+ EVENT_KNIFE
+};
+
+enum SummonGroups
+{
+ SUMMON_GROUP_10MAN = 1,
+ SUMMON_GROUP_25MAN = 2
};
class boss_razuvious : public CreatureScript
@@ -70,36 +67,60 @@ public:
{
boss_razuviousAI(Creature* creature) : BossAI(creature, BOSS_RAZUVIOUS) { }
- void KilledUnit(Unit* /*victim*/) override
+ void SummonAdds()
{
- if (!(rand32() % 3))
- DoPlaySoundToSet(me, SOUND_SLAY);
+ me->SummonCreatureGroup(SUMMON_GROUP_10MAN);
+ if (Is25ManRaid())
+ me->SummonCreatureGroup(SUMMON_GROUP_25MAN);
}
- void DamageTaken(Unit* pDone_by, uint32& uiDamage) override
+ void InitializeAI() override
{
- // Damage done by the controlled Death Knight understudies should also count toward damage done by players
- if (pDone_by->GetTypeId() == TYPEID_UNIT && (pDone_by->GetEntry() == 16803 || pDone_by->GetEntry() == 29941))
+ if (!me->isDead())
{
- me->LowerPlayerDamageReq(uiDamage);
+ Reset();
+ SummonAdds();
}
}
+ void JustReachedHome() override
+ {
+ _JustReachedHome();
+ SummonAdds();
+ me->GetMotionMaster()->Initialize();
+ }
+
+ void KilledUnit(Unit* victim) override
+ {
+ if (victim->GetTypeId() == TYPEID_PLAYER || (victim->GetTypeId() == TYPEID_UNIT && victim->GetEntry() == NPC_DK_UNDERSTUDY))
+ Talk(SAY_SLAY);
+ }
+
+ void SpellHit(Unit* caster, SpellInfo const* spell) override
+ {
+ if (spell->Id == SPELL_UNDERSTUDY_TAUNT)
+ Talk(SAY_TAUNTED, caster);
+ }
+
void JustDied(Unit* /*killer*/) override
{
- _JustDied();
- DoPlaySoundToSet(me, SOUND_DEATH);
- me->CastSpell(me, SPELL_HOPELESS, true); /// @todo this may affect other creatures
+ Talk(SAY_DEATH);
+ DoCastAOE(SPELL_HOPELESS, true);
+
+ events.Reset();
+ instance->SetBossState(BOSS_RAZUVIOUS, DONE);
}
void EnterCombat(Unit* /*who*/) override
{
_EnterCombat();
- DoPlaySoundToSet(me, SOUND_AGGRO);
- events.ScheduleEvent(EVENT_STRIKE, 30000);
- events.ScheduleEvent(EVENT_SHOUT, 25000);
- events.ScheduleEvent(EVENT_COMMAND, 40000);
- events.ScheduleEvent(EVENT_KNIFE, 10000);
+ me->StopMoving();
+ summons.DoZoneInCombat();
+ Talk(SAY_AGGRO);
+ events.ScheduleEvent(EVENT_ATTACK, 7 * IN_MILLISECONDS);
+ events.ScheduleEvent(EVENT_STRIKE, 21 * IN_MILLISECONDS);
+ events.ScheduleEvent(EVENT_SHOUT, 16 * IN_MILLISECONDS);
+ events.ScheduleEvent(EVENT_KNIFE, 10 * IN_MILLISECONDS);
}
void UpdateAI(uint32 diff) override
@@ -113,33 +134,112 @@ public:
{
switch (eventId)
{
+ case EVENT_ATTACK:
+ SetCombatMovement(true);
+ if (Unit* victim = me->GetVictim())
+ me->GetMotionMaster()->MoveChase(victim);
+ break;
case EVENT_STRIKE:
DoCastVictim(SPELL_UNBALANCING_STRIKE);
- events.ScheduleEvent(EVENT_STRIKE, 30000);
+ events.ScheduleEvent(EVENT_STRIKE, 6 * IN_MILLISECONDS);
return;
case EVENT_SHOUT:
DoCastAOE(SPELL_DISRUPTING_SHOUT);
- events.ScheduleEvent(EVENT_SHOUT, 25000);
+ events.ScheduleEvent(EVENT_SHOUT, 16 * IN_MILLISECONDS);
return;
case EVENT_KNIFE:
if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 45.0f))
DoCast(target, SPELL_JAGGED_KNIFE);
- events.ScheduleEvent(EVENT_KNIFE, 10000);
- return;
- case EVENT_COMMAND:
- DoPlaySoundToSet(me, SOUND_COMMND);
- events.ScheduleEvent(EVENT_COMMAND, 40000);
+ events.ScheduleEvent(EVENT_KNIFE, urandms(10,15));
return;
}
}
DoMeleeAttackIfReady();
}
+
+ void Reset() override
+ {
+ SetCombatMovement(false);
+ _Reset();
+ }
};
};
+class npc_dk_understudy : public CreatureScript
+{
+ public:
+ npc_dk_understudy() : CreatureScript("npc_dk_understudy") { }
+
+ struct npc_dk_understudyAI : public ScriptedAI
+ {
+ npc_dk_understudyAI(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()), bloodStrikeTimer(0) { }
+
+ void EnterCombat(Unit* /*who*/) override
+ {
+ me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_NONE);
+ if (Creature* razuvious = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_RAZUVIOUS)))
+ razuvious->AI()->DoZoneInCombat(nullptr, 250.0f);
+ }
+
+ void JustReachedHome() override
+ {
+ if (_instance->GetBossState(BOSS_RAZUVIOUS) == DONE)
+ me->DespawnOrUnsummon();
+ else
+ ScriptedAI::JustReachedHome();
+ }
+
+ void UpdateAI(uint32 diff) override
+ {
+ if (!me->isPossessedByPlayer() && !UpdateVictim())
+ return;
+
+ if (!me->isPossessedByPlayer())
+ {
+ if (diff < bloodStrikeTimer)
+ bloodStrikeTimer -= diff;
+ else
+ DoCastVictim(SPELL_UNDERSTUDY_BLOOD_STRIKE);
+ }
+
+ DoMeleeAttackIfReady();
+ }
+
+ void OnCharmed(bool apply) override
+ {
+ ScriptedAI::OnCharmed(apply);
+ if (apply)
+ {
+ if (!me->IsInCombat())
+ EnterCombat(nullptr);
+ me->StopMoving();
+ me->SetReactState(REACT_PASSIVE);
+ _charmer = me->GetCharmerGUID();
+ }
+ else
+ {
+ me->SetReactState(REACT_AGGRESSIVE);
+ if (Unit* charmer = ObjectAccessor::GetUnit(*me, _charmer))
+ me->AddThreat(charmer, 100000.0f);
+ DoZoneInCombat(nullptr, 250.0f);
+ }
+ }
+ private:
+ InstanceScript* const _instance;
+ ObjectGuid _charmer;
+ uint32 bloodStrikeTimer;
+ };
+
+ CreatureAI* GetAI(Creature* creature) const override
+ {
+ return GetInstanceAI<npc_dk_understudyAI>(creature);
+ }
+};
+
void AddSC_boss_razuvious()
{
new boss_razuvious();
+ new npc_dk_understudy();
}
diff --git a/src/server/scripts/Northrend/Naxxramas/boss_thaddius.cpp b/src/server/scripts/Northrend/Naxxramas/boss_thaddius.cpp
index d74fd5a03f8..5d3b038d41d 100644
--- a/src/server/scripts/Northrend/Naxxramas/boss_thaddius.cpp
+++ b/src/server/scripts/Northrend/Naxxramas/boss_thaddius.cpp
@@ -19,48 +19,104 @@
#include "ScriptedCreature.h"
#include "SpellScript.h"
#include "Player.h"
+#include "ObjectGuid.h"
#include "naxxramas.h"
-//Stalagg
-enum StalaggYells
+
+enum Phases
+{
+ PHASE_NOT_ENGAGED = 1,
+ PHASE_PETS,
+ PHASE_TRANSITION,
+ PHASE_THADDIUS,
+ PHASE_RESETTING
+};
+
+enum AIActions
{
- SAY_STAL_AGGRO = 0,
- SAY_STAL_SLAY = 1,
- SAY_STAL_DEATH = 2
+ ACTION_RESET_ENCOUNTER_TIMER = -1, // sent from instance AI
+ ACTION_BEGIN_RESET_ENCOUNTER = 0, // sent from thaddius to pets to trigger despawn and encounter reset
+ ACTION_RESET_ENCOUNTER, // sent from thaddius to pets to trigger respawn and full reset
+ ACTION_FEUGEN_DIED, // sent from respective pet to thaddius to indicate death
+ ACTION_STALAGG_DIED, // ^
+ ACTION_FEUGEN_RESET, // pet to thaddius
+ ACTION_STALAGG_RESET, // ^
+ ACTION_FEUGEN_AGGRO, // pet to thaddius on combat start
+ ACTION_STALAGG_AGGRO, // ^
+ ACTION_FEUGEN_REVIVING_FX, // thaddius to pet when pet reports its death
+ ACTION_STALAGG_REVIVING_FX, // ^
+ ACTION_FEUGEN_REVIVED, // thaddius to pet when pet should revive
+ ACTION_STALAGG_REVIVED, // ^
+ ACTION_TRANSITION, // thaddius to pets when transition starts (coil overload anim)
+ ACTION_TRANSITION_2, // thaddius to pets to make the coils shock him
+ ACTION_TRANSITION_3, // thaddius to pets to disable coil GO after spawn
+
+ ACTION_POLARITY_CROSSED // triggers achievement failure, sent from spellscript
};
-enum StalagSpells
+enum Events
{
- SPELL_POWERSURGE = 28134,
- SPELL_MAGNETIC_PULL = 28338,
- SPELL_STALAGG_TESLA = 28097
+ EVENT_SHIFT = 1, // polarity shift
+ EVENT_SHIFT_TALK, // polarity shift yell (hack? couldn't find any event for cast finish)
+ EVENT_CHAIN, // chain lightning
+ EVENT_BERSERK, // enrage timer
+ EVENT_REVIVE_FEUGEN, // timer until feugen is revived (if stalagg still lives)
+ EVENT_REVIVE_STALAGG, // timer until stalagg is revived (if feugen still lives)
+ EVENT_TRANSITION_1, // timer until overload emote
+ EVENT_TRANSITION_2, // timer until thaddius gets zapped by the coils
+ EVENT_TRANSITION_3, // timer until thaddius engages
+ EVENT_ENABLE_BALL_LIGHTNING // grace period after thaddius aggro after which he starts being a baller (e.g. tossing ball lightning at out of range targets)
};
-//Feugen
-enum FeugenYells
+enum Misc
{
- SAY_FEUG_AGGRO = 0,
- SAY_FEUG_SLAY = 1,
- SAY_FEUG_DEATH = 2
+ MAX_POLARITY_10M = 5,
+ MAX_POLARITY_25M = 13,
+
+ DATA_POLARITY_CROSSED = 1,
+};
+
+// Feugen & Stalagg
+enum PetYells
+{
+ SAY_STALAGG_AGGRO = 0,
+ SAY_STALAGG_SLAY = 1,
+ SAY_STALAGG_DEATH = 2,
+
+ SAY_FEUGEN_AGGRO = 0,
+ SAY_FEUGEN_SLAY = 1,
+ SAY_FEUGEN_DEATH = 2,
+
+ EMOTE_FEIGN_DEATH = 3,
+ EMOTE_FEIGN_REVIVE = 4,
+
+ EMOTE_TESLA_LINK_BREAKS = 0,
+ EMOTE_TESLA_OVERLOAD = 1
};
-enum FeugenSpells
+enum PetSpells
{
- SPELL_STATICFIELD = 28135,
- SPELL_FEUGEN_TESLA = 28109
+ SPELL_STALAGG_POWERSURGE = 28134,
+ //SPELL_STALAGG_TESLA = 28097,
+ SPELL_STALAGG_TESLA_PERIODIC = 28098,
+ SPELL_STALAGG_CHAIN_VISUAL = 28096,
+
+ SPELL_FEUGEN_STATICFIELD = 28135,
+ //SPELL_FEUGEN_TESLA = 28109,
+ SPELL_FEUGEN_TESLA_PERIODIC = 28110,
+ SPELL_FEUGEN_CHAIN_VISUAL = 28111,
+
+ SPELL_MAGNETIC_PULL = 54517,
+ SPELL_MAGNETIC_PULL_EFFECT = 28337,
+
+ SPELL_TESLA_SHOCK = 28099
};
-// Thaddius DoAction
-enum ThaddiusActions
+enum PetMisc
{
- ACTION_FEUGEN_RESET,
- ACTION_FEUGEN_DIED,
- ACTION_STALAGG_RESET,
- ACTION_STALAGG_DIED
+ OVERLOAD_DISTANCE = 28
};
-//generic
-#define C_TESLA_COIL 16218 //the coils (emotes "Tesla Coil overloads!")
//Thaddius
enum ThaddiusYells
@@ -70,34 +126,31 @@ enum ThaddiusYells
SAY_SLAY = 2,
SAY_ELECT = 3,
SAY_DEATH = 4,
- SAY_SCREAM = 5
+ SAY_SCREAM = 5,
+
+ EMOTE_POLARITY_SHIFTED = 6
};
enum ThaddiusSpells
{
- SPELL_POLARITY_SHIFT = 28089,
- SPELL_BALL_LIGHTNING = 28299,
- SPELL_CHAIN_LIGHTNING = 28167,
- SPELL_BERSERK = 27680,
- SPELL_POSITIVE_CHARGE = 28062,
- SPELL_POSITIVE_CHARGE_STACK = 29659,
- SPELL_NEGATIVE_CHARGE = 28085,
- SPELL_NEGATIVE_CHARGE_STACK = 29660,
- SPELL_POSITIVE_POLARITY = 28059,
- SPELL_NEGATIVE_POLARITY = 28084,
-};
+ SPELL_THADDIUS_INACTIVE_VISUAL = 28160,
+ SPELL_THADDIUS_SPARK_VISUAL = 28136,
+ SPELL_SHOCK_VISUAL = 28159,
-enum Events
-{
- EVENT_NONE,
- EVENT_SHIFT,
- EVENT_CHAIN,
- EVENT_BERSERK,
-};
+ SPELL_BALL_LIGHTNING = 28299,
+ SPELL_CHAIN_LIGHTNING = 28167,
+ SPELL_BERSERK = 27680,
-enum Achievement
-{
- DATA_POLARITY_SWITCH = 76047605,
+ // polarity handling
+ SPELL_POLARITY_SHIFT = 28089,
+
+ SPELL_POSITIVE_CHARGE_APPLY = 28059,
+ SPELL_POSITIVE_CHARGE_TICK = 28062,
+ SPELL_POSITIVE_CHARGE_AMP = 29659,
+
+ SPELL_NEGATIVE_CHARGE_APPLY = 28084,
+ SPELL_NEGATIVE_CHARGE_TICK = 28085,
+ SPELL_NEGATIVE_CHARGE_AMP = 29660,
};
class boss_thaddius : public CreatureScript
@@ -112,166 +165,276 @@ public:
struct boss_thaddiusAI : public BossAI
{
- boss_thaddiusAI(Creature* creature) : BossAI(creature, BOSS_THADDIUS)
- {
- // init is a bit tricky because thaddius shall track the life of both adds, but not if there was a wipe
- // and, in particular, if there was a crash after both adds were killed (should not respawn)
-
- // Moreover, the adds may not yet be spawn. So just track down the status if mob is spawn
- // and each mob will send its status at reset (meaning that it is alive)
- checkFeugenAlive = false;
- if (Creature* pFeugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN)))
- checkFeugenAlive = pFeugen->IsAlive();
+ public:
+ boss_thaddiusAI(Creature* creature) : BossAI(creature, BOSS_THADDIUS), stalaggAlive(true), feugenAlive(true), ballLightningEnabled(false), shockingEligibility(true)
+ {
+ if (instance->GetBossState(BOSS_THADDIUS) != DONE)
+ {
+ events.SetPhase(PHASE_NOT_ENGAGED);
+ SetCombatMovement(false);
- checkStalaggAlive = false;
- if (Creature* pStalagg = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_STALAGG)))
- checkStalaggAlive = pStalagg->IsAlive();
+ // initialize everything properly, and ensure that the coils are loaded by the time we initialize
+ BeginResetEncounter(true);
+ }
+ }
- if (!checkFeugenAlive && !checkStalaggAlive)
+ void KilledUnit(Unit* victim) override
{
- me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_STUNNED);
- me->SetReactState(REACT_AGGRESSIVE);
+ if (victim->GetTypeId() == TYPEID_PLAYER)
+ Talk(SAY_SLAY);
}
- else
+
+ void Reset() override
{
- me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_STUNNED);
- me->SetReactState(REACT_PASSIVE);
+ if (events.IsInPhase(PHASE_TRANSITION) || (events.IsInPhase(PHASE_THADDIUS) && me->IsAlive()))
+ BeginResetEncounter();
}
- polaritySwitch = false;
- uiAddsTimer = 0;
- }
-
- bool checkStalaggAlive;
- bool checkFeugenAlive;
- bool polaritySwitch;
- uint32 uiAddsTimer;
-
- void KilledUnit(Unit* /*victim*/) override
- {
- if (!(rand32() % 5))
- Talk(SAY_SLAY);
- }
-
- void JustDied(Unit* /*killer*/) override
- {
- _JustDied();
- Talk(SAY_DEATH);
- }
+ void JustDied(Unit* /*killer*/) override
+ {
+ _JustDied();
+ me->setActive(false);
+ if (Creature* stalagg = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_STALAGG)))
+ stalagg->setActive(false);
+ if (Creature* feugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN)))
+ feugen->setActive(false);
+ Talk(SAY_DEATH);
+ }
- void DoAction(int32 action) override
- {
- switch (action)
+ void DoAction(int32 action) override
{
- case ACTION_FEUGEN_RESET:
- checkFeugenAlive = true;
- break;
- case ACTION_FEUGEN_DIED:
- checkFeugenAlive = false;
- break;
- case ACTION_STALAGG_RESET:
- checkStalaggAlive = true;
- break;
- case ACTION_STALAGG_DIED:
- checkStalaggAlive = false;
- break;
+ switch (action)
+ {
+ case ACTION_RESET_ENCOUNTER_TIMER:
+ if (events.IsInPhase(PHASE_RESETTING))
+ ResetEncounter();
+ break;
+ case ACTION_FEUGEN_RESET:
+ case ACTION_STALAGG_RESET:
+ if (!events.IsInPhase(PHASE_NOT_ENGAGED) && !events.IsInPhase(PHASE_RESETTING))
+ BeginResetEncounter();
+ break;
+ case ACTION_FEUGEN_AGGRO:
+ case ACTION_STALAGG_AGGRO:
+ if (events.IsInPhase(PHASE_RESETTING))
+ return BeginResetEncounter();
+ if (!events.IsInPhase(PHASE_NOT_ENGAGED))
+ return;
+ events.SetPhase(PHASE_PETS);
+
+ shockingEligibility = true;
+
+ if (!instance->CheckRequiredBosses(BOSS_THADDIUS))
+ return BeginResetEncounter();
+ instance->SetBossState(BOSS_THADDIUS, IN_PROGRESS);
+
+ me->setActive(true);
+ if (Creature* stalagg = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_STALAGG)))
+ stalagg->setActive(true);
+ if (Creature* feugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN)))
+ feugen->setActive(true);
+ break;
+ case ACTION_FEUGEN_DIED:
+ if (Creature* feugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN)))
+ feugen->AI()->DoAction(ACTION_FEUGEN_REVIVING_FX);
+ feugenAlive = false;
+ if (stalaggAlive)
+ events.ScheduleEvent(EVENT_REVIVE_FEUGEN, 5 * IN_MILLISECONDS, 0, PHASE_PETS);
+ else
+ Transition();
+
+ break;
+ case ACTION_STALAGG_DIED:
+ if (Creature* stalagg = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_STALAGG)))
+ stalagg->AI()->DoAction(ACTION_STALAGG_REVIVING_FX);
+ stalaggAlive = false;
+ if (feugenAlive)
+ events.ScheduleEvent(EVENT_REVIVE_STALAGG, 5 * IN_MILLISECONDS, 0, PHASE_PETS);
+ else
+ Transition();
+
+ break;
+
+ case ACTION_POLARITY_CROSSED:
+ shockingEligibility = false;
+ break;
+ default:
+ break;
+ }
}
- if (!checkFeugenAlive && !checkStalaggAlive)
+ uint32 GetData(uint32 id) const override
{
- me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_STUNNED);
- // REACT_AGGRESSIVE only reset when he takes damage.
- DoZoneInCombat();
+ return (id == DATA_POLARITY_CROSSED && shockingEligibility) ? 1u : 0u;
}
- else
+
+ void Transition() // initiate transition between pet phase and thaddius phase
{
- me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_STUNNED);
- me->SetReactState(REACT_PASSIVE);
+ events.SetPhase(PHASE_TRANSITION);
+
+ me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
+
+ events.ScheduleEvent(EVENT_TRANSITION_1, 10 * IN_MILLISECONDS, 0, PHASE_TRANSITION);
+ events.ScheduleEvent(EVENT_TRANSITION_2, 12 * IN_MILLISECONDS, 0, PHASE_TRANSITION);
+ events.ScheduleEvent(EVENT_TRANSITION_3, 14 * IN_MILLISECONDS, 0, PHASE_TRANSITION);
}
- }
- void EnterCombat(Unit* /*who*/) override
- {
- _EnterCombat();
- Talk(SAY_AGGRO);
- events.ScheduleEvent(EVENT_SHIFT, 30000);
- events.ScheduleEvent(EVENT_CHAIN, urand(10000, 20000));
- events.ScheduleEvent(EVENT_BERSERK, 360000);
- }
+ void BeginResetEncounter(bool initial = false)
+ {
+ if (instance->GetBossState(BOSS_THADDIUS) == DONE)
+ return;
+ if (events.IsInPhase(PHASE_RESETTING))
+ return;
- void DamageTaken(Unit* /*pDoneBy*/, uint32 & /*uiDamage*/) override
- {
- me->SetReactState(REACT_AGGRESSIVE);
- }
+ if (initial) // signal shorter spawn timer to instance script
+ instance->SetBossState(BOSS_THADDIUS, SPECIAL);
+ instance->ProcessEvent(me, EVENT_THADDIUS_BEGIN_RESET);
+ instance->SetBossState(BOSS_THADDIUS, NOT_STARTED);
- void SetData(uint32 id, uint32 data) override
- {
- if (id == DATA_POLARITY_SWITCH)
- polaritySwitch = data ? true : false;
- }
+ // remove polarity shift debuffs on reset
+ instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_POSITIVE_CHARGE_APPLY);
+ instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_NEGATIVE_CHARGE_APPLY);
- uint32 GetData(uint32 id) const override
- {
- if (id != DATA_POLARITY_SWITCH)
- return 0;
+ me->DespawnOrUnsummon();
- return uint32(polaritySwitch);
- }
+ me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_STUNNED);
+ events.SetPhase(PHASE_RESETTING);
+ if (Creature* feugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN)))
+ feugen->AI()->DoAction(ACTION_BEGIN_RESET_ENCOUNTER);
+ if (Creature* stalagg = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_STALAGG)))
+ stalagg->AI()->DoAction(ACTION_BEGIN_RESET_ENCOUNTER);
- void UpdateAI(uint32 diff) override
- {
- if (checkFeugenAlive && checkStalaggAlive)
- uiAddsTimer = 0;
+ me->setActive(false);
+ }
- if (checkStalaggAlive != checkFeugenAlive)
+ void ResetEncounter()
{
- uiAddsTimer += diff;
- if (uiAddsTimer > 5000)
- {
- if (!checkStalaggAlive)
- {
- if (Creature* pStalagg = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_STALAGG)))
- pStalagg->Respawn();
- }
- else
- {
- if (Creature* pFeugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN)))
- pFeugen->Respawn();
- }
- }
- }
+ feugenAlive = true;
+ stalaggAlive = true;
- if (!UpdateVictim())
- return;
+ me->Respawn(true);
+ _Reset();
+ events.SetPhase(PHASE_NOT_ENGAGED);
- events.Update(diff);
+ me->CastSpell(me, SPELL_THADDIUS_INACTIVE_VISUAL, true);
- if (me->HasUnitState(UNIT_STATE_CASTING))
- return;
+ if (Creature* feugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN)))
+ feugen->AI()->DoAction(ACTION_RESET_ENCOUNTER);
+ if (Creature* stalagg = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_STALAGG)))
+ stalagg->AI()->DoAction(ACTION_RESET_ENCOUNTER);
+ }
- while (uint32 eventId = events.ExecuteEvent())
+ void UpdateAI(uint32 diff) override
{
- switch (eventId)
+ if (events.IsInPhase(PHASE_NOT_ENGAGED))
+ return;
+ if (events.IsInPhase(PHASE_THADDIUS) && !UpdateVictim())
+ return;
+
+ events.Update(diff);
+ while (uint32 eventId = events.ExecuteEvent())
{
- case EVENT_SHIFT:
- DoCastAOE(SPELL_POLARITY_SHIFT);
- events.ScheduleEvent(EVENT_SHIFT, 30000);
- return;
- case EVENT_CHAIN:
- DoCastVictim(SPELL_CHAIN_LIGHTNING);
- events.ScheduleEvent(EVENT_CHAIN, urand(10000, 20000));
- return;
- case EVENT_BERSERK:
- DoCast(me, SPELL_BERSERK);
- return;
+ switch (eventId)
+ {
+ case EVENT_REVIVE_FEUGEN:
+ feugenAlive = true;
+ if (Creature* feugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN)))
+ feugen->AI()->DoAction(ACTION_FEUGEN_REVIVED);
+ break;
+ case EVENT_REVIVE_STALAGG:
+ stalaggAlive = true;
+ if (Creature* stalagg = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_STALAGG)))
+ stalagg->AI()->DoAction(ACTION_STALAGG_REVIVED);
+ break;
+ case EVENT_TRANSITION_1: // tesla coils overload
+ if (Creature* feugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN)))
+ feugen->AI()->DoAction(ACTION_TRANSITION);
+ if (Creature* stalagg = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_STALAGG)))
+ stalagg->AI()->DoAction(ACTION_TRANSITION);
+ break;
+ case EVENT_TRANSITION_2: // tesla coils shock thaddius
+ me->CastSpell(me, SPELL_THADDIUS_SPARK_VISUAL, true);
+ if (Creature* feugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN)))
+ feugen->AI()->DoAction(ACTION_TRANSITION_2);
+ if (Creature* stalagg = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_STALAGG)))
+ stalagg->AI()->DoAction(ACTION_TRANSITION_2);
+ break;
+ case EVENT_TRANSITION_3: // thaddius becomes active
+ me->CastSpell(me, SPELL_THADDIUS_SPARK_VISUAL, true);
+ ballLightningEnabled = false;
+ me->RemoveAura(SPELL_THADDIUS_INACTIVE_VISUAL);
+ me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED);
+ me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC);
+
+ DoZoneInCombat();
+ if (Unit* closest = SelectTarget(SELECT_TARGET_NEAREST, 0, 500.0f))
+ AttackStart(closest);
+ else // if there is no nearest target, then there is no target, meaning we should reset
+ return BeginResetEncounter();
+
+ if (Creature* feugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN)))
+ feugen->AI()->DoAction(ACTION_TRANSITION_3);
+ if (Creature* stalagg = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_STALAGG)))
+ stalagg->AI()->DoAction(ACTION_TRANSITION_3);
+
+ events.SetPhase(PHASE_THADDIUS);
+
+ Talk(SAY_AGGRO);
+
+ events.ScheduleEvent(EVENT_ENABLE_BALL_LIGHTNING, 5 * IN_MILLISECONDS, 0, PHASE_THADDIUS);
+ events.ScheduleEvent(EVENT_SHIFT, 10 * IN_MILLISECONDS, 0, PHASE_THADDIUS);
+ events.ScheduleEvent(EVENT_CHAIN, urand(10, 20) * IN_MILLISECONDS, 0, PHASE_THADDIUS);
+ events.ScheduleEvent(EVENT_BERSERK, 6 * MINUTE * IN_MILLISECONDS, 0, PHASE_THADDIUS);
+
+ break;
+ case EVENT_ENABLE_BALL_LIGHTNING:
+ ballLightningEnabled = true;
+ break;
+ case EVENT_SHIFT:
+ me->CastStop(); // shift overrides all other spells
+ DoCastAOE(SPELL_POLARITY_SHIFT);
+ events.ScheduleEvent(EVENT_SHIFT_TALK, 3 * IN_MILLISECONDS, PHASE_THADDIUS);
+ events.ScheduleEvent(EVENT_SHIFT, 30 * IN_MILLISECONDS, PHASE_THADDIUS);
+ break;
+ case EVENT_SHIFT_TALK:
+ Talk(SAY_ELECT);
+ Talk(EMOTE_POLARITY_SHIFTED);
+ break;
+ case EVENT_CHAIN:
+ if (me->FindCurrentSpellBySpellId(SPELL_POLARITY_SHIFT)) // delay until shift is over
+ events.ScheduleEvent(EVENT_CHAIN, 3 * IN_MILLISECONDS, 0, PHASE_THADDIUS);
+ else
+ {
+ me->CastStop();
+ DoCastVictim(SPELL_CHAIN_LIGHTNING);
+ events.ScheduleEvent(EVENT_CHAIN, urand(10, 20) * IN_MILLISECONDS, PHASE_THADDIUS);
+ }
+ break;
+ case EVENT_BERSERK:
+ me->CastStop();
+ DoCast(me, SPELL_BERSERK);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (events.IsInPhase(PHASE_THADDIUS))
+ {
+ if (me->IsWithinMeleeRange(me->GetVictim()))
+ DoMeleeAttackIfReady();
+ else
+ if (ballLightningEnabled)
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM))
+ DoCast(target, SPELL_BALL_LIGHTNING);
}
}
- if (events.GetTimer() > 15000 && !me->IsWithinMeleeRange(me->GetVictim()))
- DoCastVictim(SPELL_BALL_LIGHTNING);
- else
- DoMeleeAttackIfReady();
- }
+ private:
+ bool stalaggAlive;
+ bool feugenAlive;
+ bool ballLightningEnabled;
+ bool shockingEligibility;
};
};
@@ -288,88 +451,257 @@ public:
struct npc_stalaggAI : public ScriptedAI
{
- npc_stalaggAI(Creature* creature) : ScriptedAI(creature)
- {
- Initialize();
- instance = creature->GetInstanceScript();
- }
+ public:
+ npc_stalaggAI(Creature* creature) : ScriptedAI(creature), _myCoil(ObjectGuid::Empty), _myCoilGO(ObjectGuid::Empty), isOverloading(false), refreshBeam(false), isFeignDeath(false)
+ {
+ Initialize();
+ instance = creature->GetInstanceScript();
+ }
- void Initialize()
- {
- powerSurgeTimer = urand(20000, 25000);
- magneticPullTimer = 20000;
- }
+ void Initialize()
+ {
+ if (GameObject* coil = myCoilGO())
+ coil->SetGoState(GO_STATE_ACTIVE);
- InstanceScript* instance;
+ // if the encounter reset while feigning death
+ me->SetStandState(UNIT_STAND_STATE_STAND);
+ me->SetReactState(REACT_AGGRESSIVE);
+ me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
+ isOverloading = false;
+ isFeignDeath = false;
- uint32 powerSurgeTimer;
- uint32 magneticPullTimer;
+ // force tesla coil state refresh
+ refreshBeam = true;
- void Reset() override
- {
- if (Creature* pThaddius = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_THADDIUS)))
- if (pThaddius->AI())
- pThaddius->AI()->DoAction(ACTION_STALAGG_RESET);
- Initialize();
- }
+ powerSurgeTimer = 10 * IN_MILLISECONDS;
+ }
- void KilledUnit(Unit* /*victim*/) override
- {
- if (!(rand32() % 5))
- Talk(SAY_STAL_SLAY);
- }
+ void Reset() override
+ {
+ if (isFeignDeath || !me->IsAlive())
+ return;
+ if (Creature* thaddius = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_THADDIUS)))
+ thaddius->AI()->DoAction(ACTION_STALAGG_RESET);
+ }
- void EnterCombat(Unit* /*who*/) override
- {
- Talk(SAY_STAL_AGGRO);
- DoCast(SPELL_STALAGG_TESLA);
- }
+ void BeginResetEncounter()
+ {
+ if (GameObject* coil = myCoilGO())
+ coil->SetGoState(GO_STATE_READY);
+ me->DespawnOrUnsummon();
+ me->setActive(false);
+ }
- void JustDied(Unit* /*killer*/) override
- {
- Talk(SAY_STAL_DEATH);
- if (Creature* pThaddius = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_THADDIUS)))
- if (pThaddius->AI())
- pThaddius->AI()->DoAction(ACTION_STALAGG_DIED);
- }
+ void ResetEncounter()
+ {
+ me->Respawn(true);
+ Initialize();
+ }
- void UpdateAI(uint32 uiDiff) override
- {
- if (!UpdateVictim())
- return;
+ void DoAction(int32 action) override
+ {
+ switch (action)
+ {
+ case ACTION_BEGIN_RESET_ENCOUNTER:
+ BeginResetEncounter();
+ break;
+ case ACTION_RESET_ENCOUNTER:
+ ResetEncounter();
+ break;
+ case ACTION_STALAGG_REVIVING_FX:
+ break;
+ case ACTION_STALAGG_REVIVED:
+ if (!isFeignDeath)
+ break;
+
+ me->SetFullHealth();
+ me->SetStandState(UNIT_STAND_STATE_STAND);
+ me->SetReactState(REACT_AGGRESSIVE);
+ me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
+ Talk(EMOTE_FEIGN_REVIVE);
+ isFeignDeath = false;
+
+ refreshBeam = true; // force beam refresh
+
+ if (Creature* feugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN)))
+ if (feugen->GetVictim())
+ {
+ me->AddThreat(feugen->EnsureVictim(), 0.0f);
+ me->SetInCombatWith(feugen->EnsureVictim());
+ }
+ break;
+ case ACTION_TRANSITION:
+ me->KillSelf(); // true death
+ me->DespawnOrUnsummon();
+
+ if (Creature* coil = myCoil())
+ {
+ coil->CastStop();
+ coil->AI()->Talk(EMOTE_TESLA_OVERLOAD);
+ }
+ break;
+ case ACTION_TRANSITION_2:
+ if (Creature* coil = myCoil())
+ if (Creature* thaddius = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_THADDIUS)))
+ coil->CastSpell(thaddius, SPELL_SHOCK_VISUAL);
+ break;
+ case ACTION_TRANSITION_3:
+ if (GameObject* coil = myCoilGO())
+ coil->SetGoState(GO_STATE_READY);
+ break;
+ default:
+ break;
+ }
+ }
- if (magneticPullTimer <= uiDiff)
+ void KilledUnit(Unit* victim) override
{
- if (Creature* pFeugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN)))
+ if (victim->GetTypeId() == TYPEID_PLAYER)
+ Talk(SAY_STALAGG_SLAY);
+ }
+
+ void EnterCombat(Unit* who) override
+ {
+ Talk(SAY_STALAGG_AGGRO);
+
+ if (Creature* thaddius = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_THADDIUS)))
+ thaddius->AI()->DoAction(ACTION_STALAGG_AGGRO);
+
+ if (Creature* feugen = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FEUGEN)))
+ if (!feugen->IsInCombat())
+ {
+ feugen->AddThreat(who, 0.0f);
+ feugen->SetInCombatWith(who);
+ }
+ }
+
+ void DamageTaken(Unit* /*who*/, uint32& damage) override
+ {
+ if (damage < me->GetHealth())
+ return;
+
+ if (isFeignDeath) // don't take damage while feigning death
{
- Unit* pStalaggVictim = me->GetVictim();
- Unit* pFeugenVictim = pFeugen->GetVictim();
+ damage = 0;
+ return;
+ }
+
+ isFeignDeath = true;
+ isOverloading = false;
+
+ Talk(EMOTE_FEIGN_DEATH);
+ if (Creature* thaddius = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_THADDIUS)))
+ thaddius->AI()->DoAction(ACTION_STALAGG_DIED);
+
+ me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
+ me->RemoveAllAuras();
+ me->SetReactState(REACT_PASSIVE);
+ me->AttackStop();
+ me->StopMoving();
+ me->SetStandState(UNIT_STAND_STATE_DEAD);
- if (pFeugenVictim && pStalaggVictim)
+ damage = 0;
+
+ // force beam refresh as we just removed auras
+ refreshBeam = true;
+ }
+
+ void SpellHit(Unit* caster, SpellInfo const* spell) override
+ {
+ if (!caster)
+ return;
+ if (spell->Id != SPELL_STALAGG_TESLA_PERIODIC)
+ return;
+ if (!isFeignDeath && me->IsInCombat() && !me->GetHomePosition().IsInDist(me, OVERLOAD_DISTANCE))
+ {
+ if (!isOverloading)
{
- // magnetic pull is not working. So just jump.
+ isOverloading = true;
+ caster->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC);
+ if (Creature* creatureCaster = caster->ToCreature())
+ creatureCaster->AI()->Talk(EMOTE_TESLA_LINK_BREAKS);
+ me->RemoveAura(SPELL_STALAGG_CHAIN_VISUAL);
+ }
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM))
+ {
+ caster->CastStop(SPELL_TESLA_SHOCK);
+ caster->CastSpell(target, SPELL_TESLA_SHOCK,true);
+ }
+ }
+ else if (isOverloading || refreshBeam)
+ {
+ isOverloading = false;
+ refreshBeam = false;
+ caster->CastStop();
+ caster->CastSpell(me, SPELL_STALAGG_CHAIN_VISUAL, true);
+ caster->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC);
+ }
+ }
- // reset aggro to be sure that feugen will not follow the jump
- pFeugen->getThreatManager().modifyThreatPercent(pFeugenVictim, -100);
- pFeugenVictim->JumpTo(me, 0.3f);
+ void UpdateAI(uint32 uiDiff) override
+ {
+ if (!isFeignDeath)
+ if (!UpdateVictim())
+ return;
- me->getThreatManager().modifyThreatPercent(pStalaggVictim, -100);
- pStalaggVictim->JumpTo(pFeugen, 0.3f);
+ if (powerSurgeTimer <= uiDiff)
+ {
+ if (isFeignDeath) // delay until potential revive
+ powerSurgeTimer = 0u;
+ else
+ {
+ DoCast(me, SPELL_STALAGG_POWERSURGE);
+ powerSurgeTimer = urand(25, 30) * IN_MILLISECONDS;
}
}
+ else
+ powerSurgeTimer -= uiDiff;
- magneticPullTimer = 20000;
+ if (!isFeignDeath)
+ DoMeleeAttackIfReady();
}
- else magneticPullTimer -= uiDiff;
- if (powerSurgeTimer <= uiDiff)
+ private:
+ Creature* myCoil()
{
- DoCast(me, SPELL_POWERSURGE);
- powerSurgeTimer = urand(15000, 20000);
- } else powerSurgeTimer -= uiDiff;
+ Creature* coil = nullptr;
+ if (_myCoil)
+ coil = ObjectAccessor::GetCreature(*me, _myCoil);
+ if (!coil)
+ {
+ coil = me->FindNearestCreature(NPC_TESLA, 1000.0f, true);
+ if (coil)
+ {
+ _myCoil = coil->GetGUID();
+ coil->SetReactState(REACT_PASSIVE);
+ }
+ }
+ return coil;
+ }
- DoMeleeAttackIfReady();
- }
+ GameObject* myCoilGO()
+ {
+ GameObject* coil = nullptr;
+ if (_myCoilGO)
+ coil = ObjectAccessor::GetGameObject(*me, _myCoilGO);
+ if (!coil)
+ {
+ coil = me->FindNearestGameObject(GO_CONS_NOX_TESLA_STALAGG, 1000.0f);
+ if (coil)
+ _myCoilGO = coil->GetGUID();
+ }
+ return coil;
+ }
+
+ InstanceScript* instance;
+
+ uint32 powerSurgeTimer;
+
+ ObjectGuid _myCoil;
+ ObjectGuid _myCoilGO;
+ bool isOverloading;
+ bool refreshBeam;
+ bool isFeignDeath;
};
};
@@ -386,142 +718,381 @@ public:
struct npc_feugenAI : public ScriptedAI
{
- npc_feugenAI(Creature* creature) : ScriptedAI(creature)
- {
- Initialize();
- instance = creature->GetInstanceScript();
- }
-
- void Initialize()
- {
- staticFieldTimer = 5000;
- }
+ public:
+ npc_feugenAI(Creature* creature) : ScriptedAI(creature), _myCoil(ObjectGuid::Empty), _myCoilGO(ObjectGuid::Empty), isOverloading(false), refreshBeam(false), isFeignDeath(false)
+ {
+ Initialize();
+ instance = creature->GetInstanceScript();
+ }
- InstanceScript* instance;
+ void Initialize()
+ {
+ if (GameObject* coil = myCoilGO())
+ coil->SetGoState(GO_STATE_ACTIVE);
- uint32 staticFieldTimer;
+ // if the encounter reset while feigning death
+ me->SetStandState(UNIT_STAND_STATE_STAND);
+ me->SetReactState(REACT_AGGRESSIVE);
+ me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
+ isOverloading = false;
+ isFeignDeath = false;
- void Reset() override
- {
- if (Creature* pThaddius = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_THADDIUS)))
- if (pThaddius->AI())
- pThaddius->AI()->DoAction(ACTION_FEUGEN_RESET);
- Initialize();
- }
+ // force coil state to refresh
+ refreshBeam = true;
- void KilledUnit(Unit* /*victim*/) override
- {
- if (!(rand32() % 5))
- Talk(SAY_FEUG_SLAY);
- }
+ staticFieldTimer = 6 * IN_MILLISECONDS;
+ magneticPullTimer = 20 * IN_MILLISECONDS;
+ }
- void EnterCombat(Unit* /*who*/) override
- {
- Talk(SAY_FEUG_AGGRO);
- DoCast(SPELL_FEUGEN_TESLA);
- }
+ void Reset() override
+ {
+ if (isFeignDeath || !me->IsAlive())
+ return;
+ if (Creature* thaddius = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_THADDIUS)))
+ thaddius->AI()->DoAction(ACTION_FEUGEN_RESET);
+ }
- void JustDied(Unit* /*killer*/) override
- {
- Talk(SAY_FEUG_DEATH);
- if (Creature* pThaddius = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_THADDIUS)))
- if (pThaddius->AI())
- pThaddius->AI()->DoAction(ACTION_FEUGEN_DIED);
- }
+ void BeginResetEncounter()
+ {
+ if (GameObject* coil = myCoilGO())
+ coil->SetGoState(GO_STATE_READY);
+ me->DespawnOrUnsummon();
+ me->setActive(false);
+ }
- void UpdateAI(uint32 uiDiff) override
- {
- if (!UpdateVictim())
- return;
+ void ResetEncounter()
+ {
+ me->Respawn(true);
+ Initialize();
+ }
- if (staticFieldTimer <= uiDiff)
+ void DoAction(int32 action) override
{
- DoCast(me, SPELL_STATICFIELD);
- staticFieldTimer = 5000;
- } else staticFieldTimer -= uiDiff;
+ switch (action)
+ {
+ case ACTION_BEGIN_RESET_ENCOUNTER:
+ BeginResetEncounter();
+ break;
+ case ACTION_RESET_ENCOUNTER:
+ ResetEncounter();
+ break;
+ case ACTION_FEUGEN_REVIVING_FX:
+ break;
+ case ACTION_FEUGEN_REVIVED:
+ if (!isFeignDeath)
+ break;
+
+ me->SetFullHealth();
+ me->SetStandState(UNIT_STAND_STATE_STAND);
+ me->SetReactState(REACT_AGGRESSIVE);
+ me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
+ Talk(EMOTE_FEIGN_REVIVE);
+ isFeignDeath = false;
+
+ refreshBeam = true; // force beam refresh
+
+ if (Creature* stalagg = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_STALAGG)))
+ if (stalagg->GetVictim())
+ {
+ me->AddThreat(stalagg->EnsureVictim(), 0.0f);
+ me->SetInCombatWith(stalagg->EnsureVictim());
+ }
+ staticFieldTimer = 6 * IN_MILLISECONDS;
+ magneticPullTimer = 30 * IN_MILLISECONDS;
+ break;
+ case ACTION_TRANSITION:
+ me->KillSelf(); // true death this time around
+ me->DespawnOrUnsummon();
+
+ if (Creature* coil = myCoil())
+ {
+ coil->CastStop();
+ coil->AI()->Talk(EMOTE_TESLA_OVERLOAD);
+ }
+ break;
+ case ACTION_TRANSITION_2:
+ if (Creature* coil = myCoil())
+ if (Creature* thaddius = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_THADDIUS)))
+ coil->CastSpell(thaddius, SPELL_SHOCK_VISUAL);
+ break;
+ case ACTION_TRANSITION_3:
+ if (GameObject* coil = myCoilGO())
+ coil->SetGoState(GO_STATE_READY);
+ default:
+ break;
+ }
+ }
- DoMeleeAttackIfReady();
- }
- };
+ void KilledUnit(Unit* victim) override
+ {
+ if (victim->GetTypeId() == TYPEID_PLAYER)
+ Talk(SAY_FEUGEN_SLAY);
+ }
-};
+ void EnterCombat(Unit* who) override
+ {
+ Talk(SAY_FEUGEN_AGGRO);
-class spell_thaddius_pos_neg_charge : public SpellScriptLoader
-{
- public:
- spell_thaddius_pos_neg_charge() : SpellScriptLoader("spell_thaddius_pos_neg_charge") { }
+ if (Creature* thaddius = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_THADDIUS)))
+ thaddius->AI()->DoAction(ACTION_STALAGG_AGGRO);
- class spell_thaddius_pos_neg_charge_SpellScript : public SpellScript
- {
- PrepareSpellScript(spell_thaddius_pos_neg_charge_SpellScript);
+ if (Creature* stalagg = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_STALAGG)))
+ if (!stalagg->IsInCombat())
+ {
+ stalagg->AddThreat(who, 0.0f);
+ stalagg->SetInCombatWith(who);
+ }
+ }
- bool Validate(SpellInfo const* /*spell*/) override
+ void DamageTaken(Unit* /*who*/, uint32& damage) override
{
- if (!sSpellMgr->GetSpellInfo(SPELL_POSITIVE_CHARGE))
- return false;
- if (!sSpellMgr->GetSpellInfo(SPELL_POSITIVE_CHARGE_STACK))
- return false;
- if (!sSpellMgr->GetSpellInfo(SPELL_NEGATIVE_CHARGE))
- return false;
- if (!sSpellMgr->GetSpellInfo(SPELL_NEGATIVE_CHARGE_STACK))
- return false;
- return true;
+ if (damage < me->GetHealth())
+ return;
+
+ if (isFeignDeath) // don't take damage while feigning death
+ {
+ damage = 0;
+ return;
+ }
+
+ isFeignDeath = true;
+ isOverloading = false;
+
+ Talk(EMOTE_FEIGN_DEATH);
+ if (Creature* thaddius = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_THADDIUS)))
+ thaddius->AI()->DoAction(ACTION_FEUGEN_DIED);
+
+ me->RemoveAllAuras();
+ me->SetReactState(REACT_PASSIVE);
+ me->AttackStop();
+ me->StopMoving();
+ me->SetStandState(UNIT_STAND_STATE_DEAD);
+ me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
+
+ damage = 0;
+
+ // force beam refresh as we just removed auras
+ refreshBeam = true;
}
- bool Load() override
+ void SpellHit(Unit* caster, SpellInfo const* spell) override
{
- return GetCaster()->GetTypeId() == TYPEID_UNIT;
+ if (!caster)
+ return;
+ if (spell->Id != SPELL_FEUGEN_TESLA_PERIODIC)
+ return;
+ if (!isFeignDeath && me->IsInCombat() && !me->GetHomePosition().IsInDist(me, OVERLOAD_DISTANCE))
+ {
+ if (!isOverloading)
+ {
+ isOverloading = true;
+ caster->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC);
+ if (Creature* creatureCaster = caster->ToCreature())
+ creatureCaster->AI()->Talk(EMOTE_TESLA_LINK_BREAKS);
+ me->RemoveAura(SPELL_STALAGG_CHAIN_VISUAL);
+ }
+ if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
+ {
+ caster->CastStop(SPELL_TESLA_SHOCK);
+ caster->CastSpell(target, SPELL_TESLA_SHOCK,true);
+ }
+ }
+ else if (isOverloading || refreshBeam)
+ {
+ isOverloading = false;
+ refreshBeam = false;
+ caster->CastStop();
+ caster->CastSpell(me, SPELL_FEUGEN_CHAIN_VISUAL, true);
+ caster->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC);
+ }
}
- void HandleTargets(std::list<WorldObject*>& targets)
+ void UpdateAI(uint32 uiDiff) override
{
- uint8 count = 0;
- for (std::list<WorldObject*>::iterator ihit = targets.begin(); ihit != targets.end(); ++ihit)
- if ((*ihit)->GetGUID() != GetCaster()->GetGUID())
- if (Player* target = (*ihit)->ToPlayer())
- if (target->HasAura(GetTriggeringSpell()->Id))
- ++count;
+ if (isFeignDeath)
+ return;
+ if (!UpdateVictim())
+ return;
- if (count)
+ if (magneticPullTimer <= uiDiff)
+ {
+ DoCast(me, SPELL_MAGNETIC_PULL);
+ magneticPullTimer = 20 * IN_MILLISECONDS;
+ }
+ else magneticPullTimer -= uiDiff;
+
+ if (staticFieldTimer <= uiDiff)
{
- uint32 spellId = 0;
+ DoCast(me, SPELL_FEUGEN_STATICFIELD);
+ staticFieldTimer = 6 * IN_MILLISECONDS;
+ }
+ else staticFieldTimer -= uiDiff;
- if (GetSpellInfo()->Id == SPELL_POSITIVE_CHARGE)
- spellId = SPELL_POSITIVE_CHARGE_STACK;
- else // if (GetSpellInfo()->Id == SPELL_NEGATIVE_CHARGE)
- spellId = SPELL_NEGATIVE_CHARGE_STACK;
+ DoMeleeAttackIfReady();
+ }
- GetCaster()->SetAuraStack(spellId, GetCaster(), count);
+ private:
+ Creature* myCoil()
+ {
+ Creature* coil = nullptr;
+ if (_myCoil)
+ coil = ObjectAccessor::GetCreature(*me, _myCoil);
+ if (!coil)
+ {
+ coil = me->FindNearestCreature(NPC_TESLA, 1000.0f, true);
+ if (coil)
+ {
+ _myCoil = coil->GetGUID();
+ coil->SetReactState(REACT_PASSIVE);
+ }
}
+ return coil;
+ }
+
+ GameObject* myCoilGO()
+ {
+ GameObject* coil = nullptr;
+ if (_myCoilGO)
+ coil = ObjectAccessor::GetGameObject(*me, _myCoilGO);
+ if (!coil)
+ {
+ coil = me->FindNearestGameObject(GO_CONS_NOX_TESLA_FEUGEN, 1000.0f);
+ if (coil)
+ _myCoilGO = coil->GetGUID();
+ }
+ return coil;
+ }
+ InstanceScript* instance;
+
+ uint32 magneticPullTimer;
+ uint32 staticFieldTimer;
+
+ ObjectGuid _myCoil;
+ ObjectGuid _myCoilGO;
+
+ bool isOverloading;
+ bool refreshBeam;
+ bool isFeignDeath;
+ };
+};
+
+class npc_tesla : public CreatureScript
+{
+public:
+ npc_tesla() : CreatureScript("npc_tesla") { }
+
+ CreatureAI* GetAI(Creature* creature) const override
+ {
+ return GetInstanceAI<npc_teslaAI>(creature);
+ }
+
+ struct npc_teslaAI : public ScriptedAI
+ {
+ npc_teslaAI(Creature* creature) : ScriptedAI(creature) { }
+
+ void EnterEvadeMode() override { } // never stop casting due to evade
+ void UpdateAI(uint32 /*diff*/) override { } // never do anything unless told
+ void EnterCombat(Unit* /*who*/) override { }
+ void DamageTaken(Unit* /*who*/, uint32& damage) override { damage = 0; } // no, you can't kill it
+ };
+};
+
+class spell_thaddius_polarity_charge : public SpellScriptLoader
+{
+ public:
+ spell_thaddius_polarity_charge() : SpellScriptLoader("spell_thaddius_polarity_charge") { }
+
+ class spell_thaddius_polarity_charge_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_thaddius_polarity_charge_SpellScript);
+
+ bool Validate(SpellInfo const* /*spell*/) override
+ {
+ return (
+ sSpellMgr->GetSpellInfo(SPELL_POLARITY_SHIFT) &&
+ sSpellMgr->GetSpellInfo(SPELL_POSITIVE_CHARGE_APPLY) &&
+ sSpellMgr->GetSpellInfo(SPELL_POSITIVE_CHARGE_TICK) &&
+ sSpellMgr->GetSpellInfo(SPELL_POSITIVE_CHARGE_AMP) &&
+ sSpellMgr->GetSpellInfo(SPELL_NEGATIVE_CHARGE_APPLY) &&
+ sSpellMgr->GetSpellInfo(SPELL_NEGATIVE_CHARGE_TICK) &&
+ sSpellMgr->GetSpellInfo(SPELL_NEGATIVE_CHARGE_AMP)
+ );
}
- void HandleDamage(SpellEffIndex /*effIndex*/)
+ void HandleTargets(std::list<WorldObject*>& targetList)
{
if (!GetTriggeringSpell())
return;
- Unit* target = GetHitUnit();
- Unit* caster = GetCaster();
+ uint32 triggeringId = GetTriggeringSpell()->Id;
+ uint32 ampId;
+ switch (triggeringId)
+ {
+ case SPELL_POSITIVE_CHARGE_APPLY:
+ ampId = SPELL_POSITIVE_CHARGE_AMP;
+ break;
+ case SPELL_NEGATIVE_CHARGE_APPLY:
+ ampId = SPELL_NEGATIVE_CHARGE_AMP;
+ break;
+ default:
+ return;
+ }
- if (target->HasAura(GetTriggeringSpell()->Id))
- SetHitDamage(0);
- else
+ uint8 maxStacks = 0;
+ if (GetCaster())
+ switch (GetCaster()->GetMap()->GetDifficulty())
+ {
+ case RAID_DIFFICULTY_10MAN_NORMAL:
+ maxStacks = MAX_POLARITY_10M;
+ break;
+ case RAID_DIFFICULTY_25MAN_NORMAL:
+ maxStacks = MAX_POLARITY_25M;
+ break;
+ default:
+ break;
+ }
+
+ uint8 stacksCount = 1; // do we get a stack for our own debuff?
+ std::list<WorldObject*>::iterator it = targetList.begin();
+ while(it != targetList.end())
+ {
+ if ((*it)->GetTypeId() != TYPEID_PLAYER)
+ {
+ it = targetList.erase(it);
+ continue;
+ }
+ if ((*it)->ToPlayer()->HasAura(triggeringId))
+ {
+ it = targetList.erase(it);
+ if (stacksCount < maxStacks)
+ stacksCount++;
+ continue;
+ }
+
+ // this guy will get hit - achievement failure trigger
+ if (Creature* thaddius = (*it)->FindNearestCreature(NPC_THADDIUS,200.0f))
+ thaddius->AI()->DoAction(ACTION_POLARITY_CROSSED);
+
+ ++it;
+ }
+
+ if (GetCaster() && GetCaster()->ToPlayer())
{
- if (target->GetTypeId() == TYPEID_PLAYER && caster->IsAIEnabled)
- caster->ToCreature()->AI()->SetData(DATA_POLARITY_SWITCH, 1);
+ if (!GetCaster()->ToPlayer()->HasAura(ampId))
+ GetCaster()->ToPlayer()->AddAura(ampId, GetCaster());
+ GetCaster()->ToPlayer()->SetAuraStack(ampId, GetCaster(), stacksCount);
}
}
void Register() override
{
- OnEffectHitTarget += SpellEffectFn(spell_thaddius_pos_neg_charge_SpellScript::HandleDamage, EFFECT_0, SPELL_EFFECT_SCHOOL_DAMAGE);
- OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_thaddius_pos_neg_charge_SpellScript::HandleTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ALLY);
+ OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_thaddius_polarity_charge_SpellScript::HandleTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ALLY);
}
};
SpellScript* GetSpellScript() const override
{
- return new spell_thaddius_pos_neg_charge_SpellScript();
+ return new spell_thaddius_polarity_charge_SpellScript;
}
};
@@ -536,16 +1107,33 @@ class spell_thaddius_polarity_shift : public SpellScriptLoader
bool Validate(SpellInfo const* /*spell*/) override
{
- if (!sSpellMgr->GetSpellInfo(SPELL_POSITIVE_POLARITY) || !sSpellMgr->GetSpellInfo(SPELL_NEGATIVE_POLARITY))
- return false;
- return true;
+ return (
+ sSpellMgr->GetSpellInfo(SPELL_POLARITY_SHIFT) &&
+ sSpellMgr->GetSpellInfo(SPELL_POSITIVE_CHARGE_APPLY) &&
+ sSpellMgr->GetSpellInfo(SPELL_POSITIVE_CHARGE_TICK) &&
+ sSpellMgr->GetSpellInfo(SPELL_POSITIVE_CHARGE_AMP) &&
+ sSpellMgr->GetSpellInfo(SPELL_NEGATIVE_CHARGE_APPLY) &&
+ sSpellMgr->GetSpellInfo(SPELL_NEGATIVE_CHARGE_TICK) &&
+ sSpellMgr->GetSpellInfo(SPELL_NEGATIVE_CHARGE_AMP)
+ );
}
- void HandleDummy(SpellEffIndex /* effIndex */)
+ void HandleDummy(SpellEffIndex /*effIndex*/)
{
- Unit* caster = GetCaster();
if (Unit* target = GetHitUnit())
- target->CastSpell(target, roll_chance_i(50) ? SPELL_POSITIVE_POLARITY : SPELL_NEGATIVE_POLARITY, true, NULL, NULL, caster->GetGUID());
+ if (target->GetTypeId() == TYPEID_PLAYER)
+ {
+ if (roll_chance_i(50))
+ { // positive
+ target->CastSpell(target, SPELL_POSITIVE_CHARGE_APPLY, true);
+ target->RemoveAura(SPELL_POSITIVE_CHARGE_AMP);
+ }
+ else
+ { // negative
+ target->CastSpell(target, SPELL_NEGATIVE_CHARGE_APPLY, true);
+ target->RemoveAura(SPELL_NEGATIVE_CHARGE_AMP);
+ }
+ }
}
void Register() override
@@ -560,23 +1148,131 @@ class spell_thaddius_polarity_shift : public SpellScriptLoader
}
};
-class achievement_polarity_switch : public AchievementCriteriaScript
+class spell_thaddius_magnetic_pull : public SpellScriptLoader
{
public:
- achievement_polarity_switch() : AchievementCriteriaScript("achievement_polarity_switch") { }
+ spell_thaddius_magnetic_pull() : SpellScriptLoader("spell_thaddius_magnetic_pull") { };
+
+ class spell_thaddius_magnetic_pull_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_thaddius_magnetic_pull_SpellScript);
+
+ bool Validate(SpellInfo const* /*spell*/) override
+ {
+ return sSpellMgr->GetSpellInfo(SPELL_MAGNETIC_PULL) ? true : false;
+ }
+
+ void HandleCast() // only feugen ever casts this according to wowhead data
+ {
+ Unit* feugen = GetCaster();
+ if (!feugen || feugen->GetEntry() != NPC_FEUGEN)
+ return;
+
+ Unit* stalagg = ObjectAccessor::GetCreature(*feugen, feugen->GetInstanceScript()->GetGuidData(DATA_STALAGG));
+ if (!stalagg)
+ return;
+
+ Unit* feugenTank = feugen->GetVictim();
+ Unit* stalaggTank = stalagg->GetVictim();
+
+ if (!feugenTank || !stalaggTank)
+ return;
+
+ ThreatManager& feugenThreat = feugen->getThreatManager();
+ ThreatManager& stalaggThreat = stalagg->getThreatManager();
+
+ if (feugenTank == stalaggTank) // special behavior if the tanks are the same (taken from retail)
+ {
+ float feugenTankThreat = feugenThreat.getThreat(feugenTank);
+ float stalaggTankThreat = stalaggThreat.getThreat(stalaggTank);
+
+ feugenThreat.addThreat(feugenTank, stalaggTankThreat - feugenTankThreat);
+ stalaggThreat.addThreat(stalaggTank, feugenTankThreat - stalaggTankThreat);
+
+ feugen->CastSpell(stalaggTank, SPELL_MAGNETIC_PULL_EFFECT, true);
+ }
+ else // normal case, two tanks
+ {
+ float feugenTankThreat = feugenThreat.getThreat(feugenTank);
+ float feugenOtherThreat = feugenThreat.getThreat(stalaggTank);
+ float stalaggTankThreat = stalaggThreat.getThreat(stalaggTank);
+ float stalaggOtherThreat = stalaggThreat.getThreat(feugenTank);
+
+ // set the two entries in feugen's threat table to be equal to the ones in stalagg's
+ feugenThreat.addThreat(stalaggTank, stalaggTankThreat - feugenOtherThreat);
+ feugenThreat.addThreat(feugenTank, stalaggOtherThreat - feugenTankThreat);
+
+ // set the two entries in stalagg's threat table to be equal to the ones in feugen's
+ stalaggThreat.addThreat(feugenTank, feugenTankThreat - stalaggOtherThreat);
+ stalaggThreat.addThreat(stalaggTank, feugenOtherThreat - stalaggTankThreat);
+
+ // pull the two tanks across
+ feugenTank->CastSpell(stalaggTank, SPELL_MAGNETIC_PULL_EFFECT, true);
+ stalaggTank->CastSpell(feugenTank, SPELL_MAGNETIC_PULL_EFFECT, true);
+
+ // and make both attack their respective new tanks
+ if (feugen->GetAI())
+ feugen->GetAI()->AttackStart(stalaggTank);
+ if (stalagg->GetAI())
+ stalagg->GetAI()->AttackStart(feugenTank);
+ }
+ }
+
+ void Register() override
+ {
+ OnCast += SpellCastFn(spell_thaddius_magnetic_pull_SpellScript::HandleCast);
+ }
+ };
+
+ SpellScript* GetSpellScript() const override
+ {
+ return new spell_thaddius_magnetic_pull_SpellScript();
+ }
+};
+
+class at_thaddius_entrance : public AreaTriggerScript
+{
+ public:
+ at_thaddius_entrance() : AreaTriggerScript("at_thaddius_entrance") { }
+
+ bool OnTrigger(Player* player, AreaTriggerEntry const* /*areaTrigger*/) override
+ {
+ InstanceScript* instance = player->GetInstanceScript();
+ if (!instance || instance->GetData(DATA_HAD_THADDIUS_GREET) || instance->GetBossState(BOSS_THADDIUS) == DONE)
+ return true;
+
+ if (Creature* thaddius = ObjectAccessor::GetCreature(*player, instance->GetGuidData(DATA_THADDIUS)))
+ thaddius->AI()->Talk(SAY_GREET);
+ instance->SetData(DATA_HAD_THADDIUS_GREET, 1u);
+
+ return true;
+ }
+};
+
+class achievement_thaddius_shocking : public AchievementCriteriaScript
+{
+ public:
+ achievement_thaddius_shocking() : AchievementCriteriaScript("achievement_thaddius_shocking") { }
bool OnCheck(Player* /*source*/, Unit* target) override
{
- return target && target->GetAI()->GetData(DATA_POLARITY_SWITCH);
+ return target && target->GetAI() && target->GetAI()->GetData(DATA_POLARITY_CROSSED);
}
};
+
void AddSC_boss_thaddius()
{
new boss_thaddius();
new npc_stalagg();
new npc_feugen();
- new spell_thaddius_pos_neg_charge();
+ new npc_tesla();
+
+ new spell_thaddius_polarity_charge();
new spell_thaddius_polarity_shift();
- new achievement_polarity_switch();
+ new spell_thaddius_magnetic_pull();
+
+ new at_thaddius_entrance();
+
+ new achievement_thaddius_shocking();
}
diff --git a/src/server/scripts/Northrend/Naxxramas/instance_naxxramas.cpp b/src/server/scripts/Northrend/Naxxramas/instance_naxxramas.cpp
index 9b10fab2d62..0e572835a51 100644
--- a/src/server/scripts/Northrend/Naxxramas/instance_naxxramas.cpp
+++ b/src/server/scripts/Northrend/Naxxramas/instance_naxxramas.cpp
@@ -60,7 +60,6 @@ DoorData const doorData[] =
MinionData const minionData[] =
{
- { NPC_DK_UNDERSTUDY, BOSS_RAZUVIOUS },
{ NPC_SIR, BOSS_HORSEMEN },
{ NPC_THANE, BOSS_HORSEMEN },
{ NPC_LADY, BOSS_HORSEMEN },
@@ -127,6 +126,7 @@ class instance_naxxramas : public InstanceMapScript
AbominationCount = 0;
hadAnubRekhanGreet = false;
hadFaerlinaGreet = false;
+ hadThaddiusGreet = false;
CurrentWingTaunt = SAY_KELTHUZAD_FIRST_WING_TAUNT;
playerDied = 0;
@@ -142,6 +142,9 @@ class instance_naxxramas : public InstanceMapScript
case NPC_FAERLINA:
FaerlinaGUID = creature->GetGUID();
break;
+ case NPC_RAZUVIOUS:
+ RazuviousGUID = creature->GetGUID();
+ break;
case NPC_THANE:
ThaneGUID = creature->GetGUID();
break;
@@ -154,12 +157,12 @@ class instance_naxxramas : public InstanceMapScript
case NPC_SIR:
SirGUID = creature->GetGUID();
break;
- case NPC_THADDIUS:
- ThaddiusGUID = creature->GetGUID();
- break;
case NPC_HEIGAN:
HeiganGUID = creature->GetGUID();
break;
+ case NPC_THADDIUS:
+ ThaddiusGUID = creature->GetGUID();
+ break;
case NPC_FEUGEN:
FeugenGUID = creature->GetGUID();
break;
@@ -187,6 +190,19 @@ class instance_naxxramas : public InstanceMapScript
AddMinion(creature, false);
}
+ void ProcessEvent(WorldObject* /*source*/, uint32 eventId) override
+ {
+ switch (eventId)
+ {
+ case EVENT_THADDIUS_BEGIN_RESET:
+ if (GetBossState(BOSS_THADDIUS) == SPECIAL) // this is the initial spawn, we want a shorter spawn time
+ events.ScheduleEvent(EVENT_THADDIUS_RESET, 5 * IN_MILLISECONDS);
+ else
+ events.ScheduleEvent(EVENT_THADDIUS_RESET, 30 * IN_MILLISECONDS);
+ break;
+ }
+ }
+
void OnGameObjectCreate(GameObject* go) override
{
if (go->GetGOInfo()->displayId == 6785 || go->GetGOInfo()->displayId == 1287)
@@ -329,6 +345,9 @@ class instance_naxxramas : public InstanceMapScript
case DATA_HAD_FAERLINA_GREET:
hadFaerlinaGreet = (value == 1u);
break;
+ case DATA_HAD_THADDIUS_GREET:
+ hadThaddiusGreet = (value == 1u);
+ break;
default:
break;
}
@@ -341,9 +360,11 @@ class instance_naxxramas : public InstanceMapScript
case DATA_ABOMINATION_KILLED:
return AbominationCount;
case DATA_HAD_ANUBREKHAN_GREET:
- return (uint32)hadAnubRekhanGreet;
+ return hadAnubRekhanGreet ? 1u : 0u;
case DATA_HAD_FAERLINA_GREET:
- return (uint32)hadFaerlinaGreet;
+ return hadFaerlinaGreet ? 1u : 0u;
+ case DATA_HAD_THADDIUS_GREET:
+ return hadThaddiusGreet ? 1u : 0u;
default:
break;
}
@@ -359,6 +380,8 @@ class instance_naxxramas : public InstanceMapScript
return AnubRekhanGUID;
case DATA_FAERLINA:
return FaerlinaGUID;
+ case DATA_RAZUVIOUS:
+ return RazuviousGUID;
case DATA_THANE:
return ThaneGUID;
case DATA_LADY:
@@ -367,14 +390,14 @@ class instance_naxxramas : public InstanceMapScript
return BaronGUID;
case DATA_SIR:
return SirGUID;
- case DATA_THADDIUS:
- return ThaddiusGUID;
case DATA_HEIGAN:
return HeiganGUID;
case DATA_FEUGEN:
return FeugenGUID;
case DATA_STALAGG:
return StalaggGUID;
+ case DATA_THADDIUS:
+ return ThaddiusGUID;
case DATA_KELTHUZAD:
return KelthuzadGUID;
case DATA_KELTHUZAD_PORTAL01:
@@ -543,6 +566,11 @@ class instance_naxxramas : public InstanceMapScript
kelthuzad->AI()->Talk(SAY_DIALOGUE_SAPPHIRON_KELTHUZAD4);
HandleGameObject(KelthuzadDoorGUID, true);
break;
+ case EVENT_THADDIUS_RESET:
+ if (GetBossState(BOSS_THADDIUS) != DONE)
+ if (Creature* thaddius = instance->GetCreature(ThaddiusGUID))
+ thaddius->AI()->DoAction(-1);
+ break;
default:
break;
}
@@ -628,6 +656,8 @@ class instance_naxxramas : public InstanceMapScript
ObjectGuid HeiganGUID;
/* The Military Quarter */
+ // Instructor Razuvious
+ ObjectGuid RazuviousGUID;
// Gothik the Harvester
ObjectGuid GothikGateGUID;
// The Four Horsemen
@@ -657,6 +687,7 @@ class instance_naxxramas : public InstanceMapScript
uint8 AbominationCount;
bool hadAnubRekhanGreet;
bool hadFaerlinaGreet;
+ bool hadThaddiusGreet;
uint8 CurrentWingTaunt;
/* The Immortal / The Undying */
diff --git a/src/server/scripts/Northrend/Naxxramas/naxxramas.h b/src/server/scripts/Northrend/Naxxramas/naxxramas.h
index 6289b707411..e4d15cf84ba 100644
--- a/src/server/scripts/Northrend/Naxxramas/naxxramas.h
+++ b/src/server/scripts/Northrend/Naxxramas/naxxramas.h
@@ -47,8 +47,8 @@ enum Data
DATA_GOTHIK_GATE,
DATA_SAPPHIRON_BIRTH,
DATA_HAD_ANUBREKHAN_GREET,
-
DATA_HAD_FAERLINA_GREET,
+ DATA_HAD_THADDIUS_GREET,
DATA_HORSEMEN0,
DATA_HORSEMEN1,
@@ -66,6 +66,7 @@ enum Data64
{
DATA_ANUBREKHAN,
DATA_FAERLINA,
+ DATA_RAZUVIOUS,
DATA_THANE,
DATA_LADY,
DATA_BARON,
@@ -87,14 +88,16 @@ enum CreaturesIds
{
NPC_ANUBREKHAN = 15956,
NPC_FAERLINA = 15953,
+ NPC_RAZUVIOUS = 16061,
NPC_THANE = 16064,
NPC_LADY = 16065,
NPC_BARON = 30549,
NPC_SIR = 16063,
- NPC_THADDIUS = 15928,
NPC_HEIGAN = 15936,
+ NPC_THADDIUS = 15928,
NPC_FEUGEN = 15930,
NPC_STALAGG = 15929,
+ NPC_TESLA = 16218,
NPC_SAPPHIRON = 15989,
NPC_KEL_THUZAD = 15990,
NPC_CRYPT_GUARD = 16573,
@@ -174,6 +177,10 @@ enum InstanceEvents
EVENT_DIALOGUE_GOTHIK_KORTHAZZ2,
EVENT_DIALOGUE_GOTHIK_RIVENDARE2,
+ // Thaddius AI requesting timed encounter (re-)spawn
+ EVENT_THADDIUS_BEGIN_RESET,
+ EVENT_THADDIUS_RESET,
+
// Dialogue that happens after each wing.
EVENT_KELTHUZAD_WING_TAUNT,
diff --git a/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp b/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp
index cc9af9413e5..33ffea4993b 100644
--- a/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp
+++ b/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp
@@ -1244,8 +1244,9 @@ public:
++_wpCount;
}
else if (Vehicle* hoverDisk = me->GetVehicleKit())
- if (Unit* lordPassenger = hoverDisk->GetPassenger(0))
- lordPassenger->ToCreature()->AI()->DoAction(ACTION_SET_DISK_VICTIM_CHASE);
+ if (Unit* passenger = hoverDisk->GetPassenger(0))
+ if (Creature* lordPassenger = passenger->ToCreature())
+ lordPassenger->AI()->DoAction(ACTION_SET_DISK_VICTIM_CHASE);
}
private:
diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp
index bd4fee60c42..5c852cd7f26 100644
--- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp
+++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp
@@ -701,7 +701,7 @@ class boss_leviathan_mk_ii : public CreatureScript
{
me->CastStop();
if (Unit* turret = me->GetVehicleKit()->GetPassenger(3))
- turret->Kill(turret);
+ turret->KillSelf();
me->SetSpeed(MOVE_RUN, 1.5f, true);
me->GetMotionMaster()->MovePoint(WP_MKII_P1_IDLE, VehicleRelocation[WP_MKII_P1_IDLE]);
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 6f07a88536d..8545d5b21eb 100644
--- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_yogg_saron.cpp
+++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_yogg_saron.cpp
@@ -2423,7 +2423,7 @@ class spell_yogg_saron_squeeze : public SpellScriptLoader // 64125
{
if (Unit* vehicle = GetTarget()->GetVehicleBase())
if (vehicle->IsAlive())
- vehicle->Kill(vehicle); // should tentacle die or just release its target?
+ vehicle->KillSelf(); // should tentacle die or just release its target?
}
void Register() override
@@ -2905,7 +2905,7 @@ class spell_yogg_saron_insane : public SpellScriptLoader // 63120
void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
{
if (GetTarget()->IsAlive())
- GetTarget()->Kill(GetTarget());
+ GetTarget()->KillSelf();
}
void Register() override
diff --git a/src/server/scripts/Northrend/zone_borean_tundra.cpp b/src/server/scripts/Northrend/zone_borean_tundra.cpp
index 4cdfa653224..6ef9abe2267 100644
--- a/src/server/scripts/Northrend/zone_borean_tundra.cpp
+++ b/src/server/scripts/Northrend/zone_borean_tundra.cpp
@@ -1326,7 +1326,7 @@ public:
arlos->AI()->Talk(SAY_ARLOS_1);
arlos->AI()->Talk(SAY_ARLOS_2);
leryssa->AI()->Talk(SAY_LERYSSA_1);
- arlos->Kill(arlos, false);
+ arlos->KillSelf(false);
leryssa->RemoveAura(SPELL_STUN);
leryssa->ClearUnitState(UNIT_STATE_STUNNED);
leryssa->SetWalk(false);
diff --git a/src/server/scripts/Northrend/zone_sholazar_basin.cpp b/src/server/scripts/Northrend/zone_sholazar_basin.cpp
index 6e4a8df6f25..5d47e7fa4ec 100644
--- a/src/server/scripts/Northrend/zone_sholazar_basin.cpp
+++ b/src/server/scripts/Northrend/zone_sholazar_basin.cpp
@@ -778,7 +778,7 @@ public:
wilhelm->AI()->Talk(SAY_WILHELM_MISS);
drostan->AI()->Talk(SAY_DROSTAN_REPLY_MISS);
- bird->Kill(bird);
+ bird->KillSelf();
crunchy->GetMotionMaster()->MovePoint(0, bird->GetPositionX(), bird->GetPositionY(),
bird->GetMap()->GetWaterOrGroundLevel(bird->GetPositionX(), bird->GetPositionY(), bird->GetPositionZ()));
/// @todo Make crunchy perform emote eat when he reaches the bird
diff --git a/src/server/scripts/Outland/Auchindoun/ShadowLabyrinth/boss_grandmaster_vorpil.cpp b/src/server/scripts/Outland/Auchindoun/ShadowLabyrinth/boss_grandmaster_vorpil.cpp
index b16a9630335..04d3a20d945 100644
--- a/src/server/scripts/Outland/Auchindoun/ShadowLabyrinth/boss_grandmaster_vorpil.cpp
+++ b/src/server/scripts/Outland/Auchindoun/ShadowLabyrinth/boss_grandmaster_vorpil.cpp
@@ -257,7 +257,7 @@ class npc_voidtraveler : public CreatureScript
{
DoCastAOE(SPELL_EMPOWERING_SHADOWS, true);
DoCast(me, SPELL_SHADOW_NOVA, true);
- me->Kill(me);
+ me->KillSelf();
return;
}
me->GetMotionMaster()->MoveFollow(Vorpil, 0, 0);
diff --git a/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_lady_vashj.cpp b/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_lady_vashj.cpp
index 787b191e23d..0292ed65697 100644
--- a/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_lady_vashj.cpp
+++ b/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_lady_vashj.cpp
@@ -633,7 +633,7 @@ public:
}
if (Creature* vashj = ObjectAccessor::GetCreature(*me, VashjGUID))
if (!vashj->IsInCombat() || ENSURE_AI(boss_lady_vashj::boss_lady_vashjAI, vashj->AI())->Phase != 2 || vashj->isDead())
- me->Kill(me);
+ me->KillSelf();
Move = 1000;
} else Move -= diff;
}
diff --git a/src/server/scripts/Outland/TempestKeep/Eye/boss_kaelthas.cpp b/src/server/scripts/Outland/TempestKeep/Eye/boss_kaelthas.cpp
index 872269fd19f..2d99fd9bacf 100644
--- a/src/server/scripts/Outland/TempestKeep/Eye/boss_kaelthas.cpp
+++ b/src/server/scripts/Outland/TempestKeep/Eye/boss_kaelthas.cpp
@@ -1286,7 +1286,7 @@ class npc_kael_flamestrike : public CreatureScript
DoCast(me, SPELL_FLAME_STRIKE_DMG);
}
else
- me->Kill(me);
+ me->KillSelf();
KillSelf = true;
Timer = 1000;
diff --git a/src/server/scripts/Pet/pet_mage.cpp b/src/server/scripts/Pet/pet_mage.cpp
index 0d6353cfb86..14d2687c298 100644
--- a/src/server/scripts/Pet/pet_mage.cpp
+++ b/src/server/scripts/Pet/pet_mage.cpp
@@ -24,11 +24,25 @@
#include "ScriptedCreature.h"
#include "CombatAI.h"
#include "Pet.h"
+#include "PetAI.h"
+#include "Cell.h"
+#include "CellImpl.h"
+#include "GridNotifiers.h"
+#include "GridNotifiersImpl.h"
enum MageSpells
{
SPELL_MAGE_CLONE_ME = 45204,
- SPELL_MAGE_MASTERS_THREAT_LIST = 58838
+ SPELL_MAGE_MASTERS_THREAT_LIST = 58838,
+ SPELL_MAGE_FROST_BOLT = 59638,
+ SPELL_MAGE_FIRE_BLAST = 59637
+};
+
+enum MirrorImageTimers
+{
+ TIMER_MIRROR_IMAGE_INIT = 0,
+ TIMER_MIRROR_IMAGE_FROST_BOLT = 4000,
+ TIMER_MIRROR_IMAGE_FIRE_BLAST = 6000
};
class npc_pet_mage_mirror_image : public CreatureScript
@@ -40,20 +54,184 @@ class npc_pet_mage_mirror_image : public CreatureScript
{
npc_pet_mage_mirror_imageAI(Creature* creature) : CasterAI(creature) { }
+ void Init()
+ {
+ Unit* owner = me->GetCharmerOrOwner();
+
+ std::list<Unit*> targets;
+ Trinity::AnyUnfriendlyUnitInObjectRangeCheck u_check(me, me, 30.0f);
+ Trinity::UnitListSearcher<Trinity::AnyUnfriendlyUnitInObjectRangeCheck> searcher(me, targets, u_check);
+ me->VisitNearbyObject(40.0f, searcher);
+
+ Unit* highestThreatUnit = nullptr;
+ float highestThreat = 0.0f;
+ Unit* nearestPlayer = nullptr;
+ for (std::list<Unit*>::const_iterator iter = targets.begin(); iter != targets.end(); ++iter)
+ {
+ // Consider only units without CC
+ if (!(*iter)->HasBreakableByDamageCrowdControlAura((*iter)))
+ {
+ // Take first found unit
+ if (!highestThreatUnit && (*iter)->GetTypeId() != TYPEID_PLAYER)
+ {
+ highestThreatUnit = (*iter);
+ continue;
+ }
+ if (!nearestPlayer && ((*iter)->GetTypeId() == TYPEID_PLAYER))
+ {
+ nearestPlayer = (*iter);
+ continue;
+ }
+ // else compare best fit unit with current unit
+ ThreatContainer::StorageType triggers = (*iter)->getThreatManager().getThreatList();
+ for (ThreatContainer::StorageType::const_iterator trig_citr = triggers.begin(); trig_citr != triggers.end(); ++trig_citr)
+ {
+ // Try to find threat referenced to owner
+ if ((*trig_citr)->getTarget() == owner)
+ {
+ // Check if best fit hostile unit hs lower threat than this current unit
+ if (highestThreat < (*trig_citr)->getThreat())
+ {
+ // If so, update best fit unit
+ highestThreat = (*trig_citr)->getThreat();
+ highestThreatUnit = (*iter);
+ break;
+ }
+ }
+ }
+ // In case no unit with threat was found so far, always check for nearest unit (only for players)
+ if ((*iter)->GetTypeId() == TYPEID_PLAYER)
+ {
+ // If this player is closer than the previous one, update it
+ if (me->GetDistance((*iter)->GetPosition()) < me->GetDistance(nearestPlayer->GetPosition()))
+ nearestPlayer = (*iter);
+ }
+ }
+ }
+ // Prioritize units with threat referenced to owner
+ if (highestThreat > 0.0f && highestThreatUnit)
+ me->Attack(highestThreatUnit, false);
+ // If there is no such target, try to attack nearest hostile unit if such exists
+ else if (nearestPlayer)
+ me->Attack(nearestPlayer, false);
+ }
+
+ bool IsInThreatList(Unit* target)
+ {
+ Unit* owner = me->GetCharmerOrOwner();
+
+ std::list<Unit*> targets;
+ Trinity::AnyUnfriendlyUnitInObjectRangeCheck u_check(me, me, 30.0f);
+ Trinity::UnitListSearcher<Trinity::AnyUnfriendlyUnitInObjectRangeCheck> searcher(me, targets, u_check);
+ me->VisitNearbyObject(40.0f, searcher);
+
+ for (std::list<Unit*>::const_iterator iter = targets.begin(); iter != targets.end(); ++iter)
+ {
+ if ((*iter) == target)
+ {
+ // Consider only units without CC
+ if (!(*iter)->HasBreakableByDamageCrowdControlAura((*iter)))
+ {
+ ThreatContainer::StorageType triggers = (*iter)->getThreatManager().getThreatList();
+ for (ThreatContainer::StorageType::const_iterator trig_citr = triggers.begin(); trig_citr != triggers.end(); ++trig_citr)
+ {
+ // Try to find threat referenced to owner
+ if ((*trig_citr)->getTarget() == owner)
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
void InitializeAI() override
{
CasterAI::InitializeAI();
Unit* owner = me->GetOwner();
if (!owner)
return;
- // Inherit Master's Threat List (not yet implemented)
- owner->CastSpell((Unit*)NULL, SPELL_MAGE_MASTERS_THREAT_LIST, true);
+
// here mirror image casts on summoner spell (not present in client dbc) 49866
// here should be auras (not present in client dbc): 35657, 35658, 35659, 35660 selfcast by mirror images (stats related?)
// Clone Me!
owner->CastSpell(me, SPELL_MAGE_CLONE_ME, false);
}
+ void EnterCombat(Unit* who) override
+ {
+ if (me->GetVictim() && !me->GetVictim()->HasBreakableByDamageCrowdControlAura(me))
+ {
+ me->CastSpell(who, SPELL_MAGE_FIRE_BLAST, false);
+ events.ScheduleEvent(SPELL_MAGE_FROST_BOLT, TIMER_MIRROR_IMAGE_INIT);
+ events.ScheduleEvent(SPELL_MAGE_FIRE_BLAST, TIMER_MIRROR_IMAGE_FIRE_BLAST);
+ }
+ else
+ EnterEvadeMode();
+ }
+
+ void Reset() override
+ {
+ events.Reset();
+ }
+
+ void UpdateAI(uint32 diff) override
+ {
+ Unit* owner = me->GetCharmerOrOwner();
+ Unit* target = owner->getAttackerForHelper();
+
+ events.Update(diff);
+
+ // prevent CC interrupts by images
+ if (me->GetVictim() && me->EnsureVictim()->HasBreakableByDamageCrowdControlAura(me))
+ {
+ me->InterruptNonMeleeSpells(false);
+ return;
+ }
+
+ if (me->HasUnitState(UNIT_STATE_CASTING))
+ return;
+
+ if (!owner)
+ return;
+
+ // assign target if image doesnt have any or the target is not actual
+ if (!target || me->GetVictim() != target)
+ {
+ Unit* ownerTarget = nullptr;
+ if (Player* owner = me->GetCharmerOrOwner()->ToPlayer())
+ ownerTarget = owner->GetSelectedUnit();
+
+ // recognize which victim will be choosen
+ if (ownerTarget && ownerTarget->GetTypeId() == TYPEID_PLAYER)
+ {
+ if (!ownerTarget->HasBreakableByDamageCrowdControlAura(ownerTarget))
+ me->Attack(ownerTarget, false);
+ }
+ else if (ownerTarget && (ownerTarget->GetTypeId() != TYPEID_PLAYER) && IsInThreatList(ownerTarget))
+ {
+ if (!ownerTarget->HasBreakableByDamageCrowdControlAura(ownerTarget))
+ me->Attack(ownerTarget, false);
+ }
+ else
+ Init();
+ }
+
+ if (uint32 spellId = events.ExecuteEvent())
+ {
+ if (spellId == SPELL_MAGE_FROST_BOLT)
+ {
+ events.ScheduleEvent(SPELL_MAGE_FROST_BOLT, TIMER_MIRROR_IMAGE_FROST_BOLT);
+ DoCastVictim(spellId);
+ }
+ else if (spellId == SPELL_MAGE_FIRE_BLAST)
+ {
+ DoCastVictim(spellId);
+ events.ScheduleEvent(SPELL_MAGE_FIRE_BLAST, TIMER_MIRROR_IMAGE_FIRE_BLAST);
+ }
+ }
+ }
+
// Do not reload Creature templates on evade mode enter - prevent visual lost
void EnterEvadeMode() override
{
@@ -68,6 +246,7 @@ class npc_pet_mage_mirror_image : public CreatureScript
me->GetMotionMaster()->Clear(false);
me->GetMotionMaster()->MoveFollow(owner, PET_FOLLOW_DIST, me->GetFollowAngle(), MOTION_SLOT_ACTIVE);
}
+ Init();
}
};
diff --git a/src/server/scripts/Spells/spell_dk.cpp b/src/server/scripts/Spells/spell_dk.cpp
index cd0052c24bc..88271dc7139 100644
--- a/src/server/scripts/Spells/spell_dk.cpp
+++ b/src/server/scripts/Spells/spell_dk.cpp
@@ -1343,6 +1343,15 @@ class spell_dk_raise_dead : public SpellScriptLoader
GetCaster()->CastSpell(targets, spellInfo, NULL, TRIGGERED_FULL_MASK);
}
+ void OverrideCooldown()
+ {
+ // Because the ghoul is summoned by one of triggered spells SendCooldownEvent is not sent for this spell
+ // but the client has locked it by itself so we need some link between this spell and the real spell summoning.
+ // Luckily such link already exists - spell category
+ // This starts infinite category cooldown which can later be used by SendCooldownEvent to send packet for this spell
+ GetCaster()->GetSpellHistory()->StartCooldown(GetSpellInfo(), 0, nullptr, true);
+ }
+
void Register() override
{
OnCheckCast += SpellCheckCastFn(spell_dk_raise_dead_SpellScript::CheckCast);
@@ -1351,6 +1360,7 @@ class spell_dk_raise_dead : public SpellScriptLoader
OnCast += SpellCastFn(spell_dk_raise_dead_SpellScript::ConsumeReagents);
OnEffectHitTarget += SpellEffectFn(spell_dk_raise_dead_SpellScript::HandleRaiseDead, EFFECT_1, SPELL_EFFECT_SCRIPT_EFFECT);
OnEffectHitTarget += SpellEffectFn(spell_dk_raise_dead_SpellScript::HandleRaiseDead, EFFECT_2, SPELL_EFFECT_DUMMY);
+ AfterCast += SpellCastFn(spell_dk_raise_dead_SpellScript::OverrideCooldown);
}
private:
diff --git a/src/server/scripts/Spells/spell_holiday.cpp b/src/server/scripts/Spells/spell_holiday.cpp
index a1b218010aa..e29f87a5ff9 100644
--- a/src/server/scripts/Spells/spell_holiday.cpp
+++ b/src/server/scripts/Spells/spell_holiday.cpp
@@ -277,6 +277,86 @@ class spell_hallow_end_tricky_treat : public SpellScriptLoader
}
};
+// Hallowed wands
+enum HallowendData
+{
+ //wand spells
+ SPELL_HALLOWED_WAND_PIRATE = 24717,
+ SPELL_HALLOWED_WAND_NINJA = 24718,
+ SPELL_HALLOWED_WAND_LEPER_GNOME = 24719,
+ SPELL_HALLOWED_WAND_RANDOM = 24720,
+ SPELL_HALLOWED_WAND_SKELETON = 24724,
+ SPELL_HALLOWED_WAND_WISP = 24733,
+ SPELL_HALLOWED_WAND_GHOST = 24737,
+ SPELL_HALLOWED_WAND_BAT = 24741
+};
+
+class spell_hallow_end_wand : public SpellScriptLoader
+{
+public:
+ spell_hallow_end_wand() : SpellScriptLoader("spell_hallow_end_wand") {}
+
+ class spell_hallow_end_wand_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_hallow_end_wand_SpellScript);
+
+ bool Validate(SpellInfo const* /*spellEntry*/) override
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_PIRATE_COSTUME_MALE) ||
+ !sSpellMgr->GetSpellInfo(SPELL_PIRATE_COSTUME_FEMALE) ||
+ !sSpellMgr->GetSpellInfo(SPELL_NINJA_COSTUME_MALE) ||
+ !sSpellMgr->GetSpellInfo(SPELL_NINJA_COSTUME_FEMALE) ||
+ !sSpellMgr->GetSpellInfo(SPELL_LEPER_GNOME_COSTUME_MALE) ||
+ !sSpellMgr->GetSpellInfo(SPELL_LEPER_GNOME_COSTUME_FEMALE) ||
+ !sSpellMgr->GetSpellInfo(SPELL_GHOST_COSTUME_MALE) ||
+ !sSpellMgr->GetSpellInfo(SPELL_GHOST_COSTUME_FEMALE))
+ return false;
+ return true;
+ }
+
+ void HandleScriptEffect()
+ {
+ Unit* caster = GetCaster();
+ Unit* target = GetHitUnit();
+
+ uint32 spellId = 0;
+ uint8 gender = target->getGender();
+
+ switch (GetSpellInfo()->Id)
+ {
+ case SPELL_HALLOWED_WAND_LEPER_GNOME:
+ spellId = gender ? SPELL_LEPER_GNOME_COSTUME_FEMALE : SPELL_LEPER_GNOME_COSTUME_MALE;
+ break;
+ case SPELL_HALLOWED_WAND_PIRATE:
+ spellId = gender ? SPELL_PIRATE_COSTUME_FEMALE : SPELL_PIRATE_COSTUME_MALE;
+ break;
+ case SPELL_HALLOWED_WAND_GHOST:
+ spellId = gender ? SPELL_GHOST_COSTUME_FEMALE : SPELL_GHOST_COSTUME_MALE;
+ break;
+ case SPELL_HALLOWED_WAND_NINJA:
+ spellId = gender ? SPELL_NINJA_COSTUME_FEMALE : SPELL_NINJA_COSTUME_MALE;
+ break;
+ case SPELL_HALLOWED_WAND_RANDOM:
+ spellId = RAND(SPELL_HALLOWED_WAND_PIRATE, SPELL_HALLOWED_WAND_NINJA, SPELL_HALLOWED_WAND_LEPER_GNOME, SPELL_HALLOWED_WAND_SKELETON, SPELL_HALLOWED_WAND_WISP, SPELL_HALLOWED_WAND_GHOST, SPELL_HALLOWED_WAND_BAT);
+ break;
+ default:
+ return;
+ }
+ caster->CastSpell(target, spellId, true);
+ }
+
+ void Register() override
+ {
+ AfterHit += SpellHitFn(spell_hallow_end_wand_SpellScript::HandleScriptEffect);
+ }
+ };
+
+ SpellScript* GetSpellScript() const override
+ {
+ return new spell_hallow_end_wand_SpellScript();
+ }
+};
+
enum PilgrimsBountyBuffFood
{
// Pilgrims Bounty Buff Food
@@ -886,6 +966,7 @@ void AddSC_holiday_spell_scripts()
new spell_hallow_end_trick();
new spell_hallow_end_trick_or_treat();
new spell_hallow_end_tricky_treat();
+ new spell_hallow_end_wand();
// Pilgrims Bounty
new spell_pilgrims_bounty_buff_food("spell_gen_slow_roasted_turkey", SPELL_WELL_FED_AP_TRIGGER);
new spell_pilgrims_bounty_buff_food("spell_gen_cranberry_chutney", SPELL_WELL_FED_ZM_TRIGGER);
diff --git a/src/server/scripts/Spells/spell_hunter.cpp b/src/server/scripts/Spells/spell_hunter.cpp
index f2871871c30..0ca4112b288 100644
--- a/src/server/scripts/Spells/spell_hunter.cpp
+++ b/src/server/scripts/Spells/spell_hunter.cpp
@@ -480,7 +480,7 @@ class spell_hun_misdirection : public SpellScriptLoader
void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
{
- if (GetTargetApplication()->GetRemoveMode() != AURA_REMOVE_BY_DEFAULT)
+ if (GetTargetApplication()->GetRemoveMode() != AURA_REMOVE_BY_DEFAULT || !GetTarget()->HasAura(SPELL_HUNTER_MISDIRECTION_PROC))
GetTarget()->ResetRedirectThreat();
}
diff --git a/src/server/scripts/Spells/spell_item.cpp b/src/server/scripts/Spells/spell_item.cpp
index 717382a0e36..f0b6c0945c2 100644
--- a/src/server/scripts/Spells/spell_item.cpp
+++ b/src/server/scripts/Spells/spell_item.cpp
@@ -2509,7 +2509,7 @@ class spell_item_chicken_cover : public SpellScriptLoader
if (!target->HasAura(SPELL_CHICKEN_NET) && (caster->GetQuestStatus(QUEST_CHICKEN_PARTY) == QUEST_STATUS_INCOMPLETE || caster->GetQuestStatus(QUEST_FLOWN_THE_COOP) == QUEST_STATUS_INCOMPLETE))
{
caster->CastSpell(caster, SPELL_CAPTURE_CHICKEN_ESCAPE, true);
- target->Kill(target);
+ target->KillSelf();
}
}
}
diff --git a/src/server/scripts/World/duel_reset.cpp b/src/server/scripts/World/duel_reset.cpp
index f08469d5bd5..9e720455692 100644
--- a/src/server/scripts/World/duel_reset.cpp
+++ b/src/server/scripts/World/duel_reset.cpp
@@ -17,6 +17,8 @@
#include "ScriptMgr.h"
#include "Player.h"
+#include "Pet.h"
+#include "SpellInfo.h"
class DuelResetScript : public PlayerScript
{
@@ -26,28 +28,89 @@ class DuelResetScript : public PlayerScript
// Called when a duel starts (after 3s countdown)
void OnDuelStart(Player* player1, Player* player2) override
{
+ // Cooldowns reset
if (sWorld->getBoolConfig(CONFIG_RESET_DUEL_COOLDOWNS))
{
player1->GetSpellHistory()->SaveCooldownStateBeforeDuel();
player2->GetSpellHistory()->SaveCooldownStateBeforeDuel();
- player1->RemoveArenaSpellCooldowns(true);
- player2->RemoveArenaSpellCooldowns(true);
+
+ ResetSpellCooldowns(player1);
+ ResetSpellCooldowns(player2);
+ }
+
+ // Health and mana reset
+ if (sWorld->getBoolConfig(CONFIG_RESET_DUEL_HEALTH_MANA))
+ {
+ player1->SaveHealthBeforeDuel();
+ player1->SetHealth(player1->GetMaxHealth());
+
+ player2->SaveHealthBeforeDuel();
+ player2->SetHealth(player2->GetMaxHealth());
+
+ // check if player1 class uses mana
+ if (player1->getPowerType() == POWER_MANA || player1->getClass() == CLASS_DRUID)
+ {
+ player1->SaveManaBeforeDuel();
+ player1->SetPower(POWER_MANA, player1->GetMaxPower(POWER_MANA));
+ }
+
+ // check if player2 class uses mana
+ if (player2->getPowerType() == POWER_MANA || player2->getClass() == CLASS_DRUID)
+ {
+ player2->SaveManaBeforeDuel();
+ player2->SetPower(POWER_MANA, player2->GetMaxPower(POWER_MANA));
+ }
}
}
// Called when a duel ends
- void OnDuelEnd(Player* winner, Player* loser, DuelCompleteType /*type*/) override
+ void OnDuelEnd(Player* winner, Player* loser, DuelCompleteType type) override
{
- if (sWorld->getBoolConfig(CONFIG_RESET_DUEL_COOLDOWNS))
+ // do not reset anything if DUEL_INTERRUPTED or DUEL_FLED
+ if (type == DUEL_WON)
{
- winner->RemoveArenaSpellCooldowns(true);
- loser->RemoveArenaSpellCooldowns(true);
+ // Cooldown restore
+ if (sWorld->getBoolConfig(CONFIG_RESET_DUEL_COOLDOWNS))
+ {
+
+ ResetSpellCooldowns(winner);
+ ResetSpellCooldowns(loser);
- winner->GetSpellHistory()->RestoreCooldownStateAfterDuel();
- loser->GetSpellHistory()->RestoreCooldownStateAfterDuel();
+ winner->GetSpellHistory()->RestoreCooldownStateAfterDuel();
+ loser->GetSpellHistory()->RestoreCooldownStateAfterDuel();
+ }
+
+ // Health and mana restore
+ if (sWorld->getBoolConfig(CONFIG_RESET_DUEL_HEALTH_MANA))
+ {
+ winner->RestoreHealthAfterDuel();
+ loser->RestoreHealthAfterDuel();
+
+ // check if player1 class uses mana
+ if (winner->getPowerType() == POWER_MANA || winner->getClass() == CLASS_DRUID)
+ winner->RestoreManaAfterDuel();
+
+ // check if player2 class uses mana
+ if (loser->getPowerType() == POWER_MANA || loser->getClass() == CLASS_DRUID)
+ loser->RestoreManaAfterDuel();
+ }
}
}
+
+ static void ResetSpellCooldowns(Player* player)
+ {
+ // remove cooldowns on spells that have < 10 min CD and has no onHold
+ player->GetSpellHistory()->ResetCooldowns([](SpellHistory::CooldownStorageType::iterator itr) -> bool
+ {
+ SpellInfo const* spellInfo = sSpellMgr->EnsureSpellInfo(itr->first);
+ return spellInfo->RecoveryTime < 10 * MINUTE * IN_MILLISECONDS && spellInfo->CategoryRecoveryTime < 10 * MINUTE * IN_MILLISECONDS && !itr->second.OnHold;
+ }, true);
+
+ // pet cooldowns
+ if (Pet* pet = player->GetPet())
+ pet->GetSpellHistory()->ResetAllCooldowns();
+ }
};
void AddSC_duel_reset()
diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist
index df2b2e73c1c..8619a864998 100644
--- a/src/server/worldserver/worldserver.conf.dist
+++ b/src/server/worldserver/worldserver.conf.dist
@@ -1142,11 +1142,11 @@ BirthdayTime = 1222964635
# DATABASE_CHARACTER = 2, // Character database
# DATABASE_WORLD = 4, // World database
#
-# Default: 0 - (All Disabled)
+# Default: 7 - (All enabled)
# 4 - (Enable world only)
-# 7 - (All enabled)
+# 0 - (All disabled)
-Updates.EnableDatabases = 0
+Updates.EnableDatabases = 7
#
# Updates.SourcePath
@@ -2640,6 +2640,13 @@ HonorPointsAfterDuel = 0
ResetDuelCooldowns = 0
+# ResetDuelHealthMana
+# Description: Reset health and mana before duel starts and restore them when duel ends.
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+
+ResetDuelHealthMana = 0
+
#
# AlwaysMaxWeaponSkill
# Description: Players will automatically gain max weapon/defense skill when logging in,
diff --git a/src/tools/map_extractor/CMakeLists.txt b/src/tools/map_extractor/CMakeLists.txt
index 87d68cc7a2f..b10956b3264 100644
--- a/src/tools/map_extractor/CMakeLists.txt
+++ b/src/tools/map_extractor/CMakeLists.txt
@@ -9,13 +9,16 @@
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-file(GLOB_RECURSE sources *.cpp *.h)
+file(GLOB_RECURSE mapextractor_SRCS *.cpp *.h)
set(include_Dirs
- ${CMAKE_SOURCE_DIR}/src/server/shared
- ${CMAKE_SOURCE_DIR}/dep/libmpq
- ${CMAKE_CURRENT_SOURCE_DIR}
- ${CMAKE_CURRENT_SOURCE_DIR}/loadlib
+ ${CMAKE_SOURCE_DIR}
+ ${CMAKE_SOURCE_DIR}/dep/cppformat
+ ${CMAKE_SOURCE_DIR}/dep/libmpq
+ ${CMAKE_SOURCE_DIR}/src/common
+ ${CMAKE_SOURCE_DIR}/src/common/Utilities
+ ${CMAKE_SOURCE_DIR}/src/server/shared
+ ${CMAKE_CURRENT_SOURCE_DIR}/loadlib
)
if( WIN32 )
@@ -28,13 +31,16 @@ endif()
include_directories(${include_Dirs})
add_executable(mapextractor
- ${sources}
+ ${mapextractor_SRCS}
)
target_link_libraries(mapextractor
+ common
+ format
mpq
${BZIP2_LIBRARIES}
${ZLIB_LIBRARIES}
+ ${Boost_LIBRARIES}
)
if( UNIX )
diff --git a/src/tools/map_extractor/System.cpp b/src/tools/map_extractor/System.cpp
index dcaa3ba0a76..c0497a1cefb 100644
--- a/src/tools/map_extractor/System.cpp
+++ b/src/tools/map_extractor/System.cpp
@@ -22,36 +22,17 @@
#include <deque>
#include <set>
#include <cstdlib>
-
-#ifdef _WIN32
-#include "direct.h"
-#else
-#include <sys/stat.h>
-#include <unistd.h>
-#endif
+#include <fstream>
#include "dbcfile.h"
#include "mpq_libmpq04.h"
+#include "StringFormat.h"
#include "adt.h"
#include "wdt.h"
-#include <fcntl.h>
-
-#if defined( __GNUC__ )
- #define _open open
- #define _close close
- #ifndef O_BINARY
- #define O_BINARY 0
- #endif
-#else
- #include <io.h>
-#endif
-
-#ifdef O_LARGEFILE
- #define OPEN_FLAGS (O_RDONLY | O_BINARY | O_LARGEFILE)
-#else
- #define OPEN_FLAGS (O_RDONLY | O_BINARY)
-#endif
+
+#include <boost/filesystem.hpp>
+
extern ArchiveSet gOpenArchives;
typedef struct
@@ -106,37 +87,14 @@ const char *CONF_mpq_list[]={
static const char* const langs[] = {"enGB", "enUS", "deDE", "esES", "frFR", "koKR", "zhCN", "zhTW", "enCN", "enTW", "esMX", "ruRU" };
#define LANG_COUNT 12
-void CreateDir( const std::string& Path )
-{
- if(chdir(Path.c_str()) == 0)
- {
- chdir("../");
- return;
- }
-
- int ret;
- #ifdef _WIN32
- ret = _mkdir( Path.c_str());
- #else
- ret = mkdir( Path.c_str(), 0777 );
- #endif
- if (ret != 0)
- {
- printf("Fatal Error: Could not create directory %s check your permissions", Path.c_str());
- exit(1);
- }
-}
-
-bool FileExists( const char* FileName )
+void CreateDir(boost::filesystem::path const& path)
{
- int fp = _open(FileName, OPEN_FLAGS);
- if(fp != -1)
- {
- _close(fp);
- return true;
- }
+ namespace fs = boost::filesystem;
+ if (fs::exists(path))
+ return;
- return false;
+ if (!fs::create_directory(path))
+ throw new std::runtime_error("Unable to create directory" + path.string());
}
void Usage(char* prg)
@@ -207,7 +165,7 @@ void HandleArgs(int argc, char * arg[])
uint32 ReadBuild(int locale)
{
// include build info file also
- std::string filename = std::string("component.wow-")+langs[locale]+".txt";
+ std::string filename = Trinity::StringFormat("component.wow-%s.txt", langs[locale]);
//printf("Read %s file... ", filename.c_str());
MPQFile m(filename.c_str());
@@ -269,7 +227,7 @@ uint32 ReadMapDBC()
strncpy(map_ids[x].name, map_name, max_map_name_length);
map_ids[x].name[max_map_name_length - 1] = '\0';
}
- printf("Done! (%u maps loaded)\n", (uint32)map_count);
+ printf("Done! (" SZFMTD "maps loaded)\n", map_count);
return map_count;
}
@@ -294,7 +252,7 @@ void ReadAreaTableDBC()
maxAreaId = dbc.getMaxId();
- printf("Done! (%u areas loaded)\n", (uint32)area_count);
+ printf("Done! (" SZFMTD " areas loaded)\n", area_count);
}
void ReadLiquidTypeTableDBC()
@@ -315,7 +273,7 @@ void ReadLiquidTypeTableDBC()
for(uint32 x = 0; x < liqTypeCount; ++x)
LiqType[dbc.getRecord(x).getUInt(0)] = dbc.getRecord(x).getUInt(3);
- printf("Done! (%u LiqTypes loaded)\n", (uint32)liqTypeCount);
+ printf("Done! (" SZFMTD " LiqTypes loaded)\n", liqTypeCount);
}
//
@@ -414,17 +372,17 @@ uint8 liquid_flags[ADT_CELLS_PER_GRID][ADT_CELLS_PER_GRID];
bool liquid_show[ADT_GRID_SIZE][ADT_GRID_SIZE];
float liquid_height[ADT_GRID_SIZE+1][ADT_GRID_SIZE+1];
-bool ConvertADT(char *filename, char *filename2, int /*cell_y*/, int /*cell_x*/, uint32 build)
+bool ConvertADT(std::string const& inputPath, std::string const& outputPath, int /*cell_y*/, int /*cell_x*/, uint32 build)
{
ADT_file adt;
- if (!adt.loadFile(filename))
+ if (!adt.loadFile(inputPath))
return false;
adt_MCIN *cells = adt.a_grid->getMCIN();
if (!cells)
{
- printf("Can't find cells in '%s'\n", filename);
+ printf("Can't find cells in '%s'\n", inputPath.c_str());
return false;
}
@@ -434,8 +392,8 @@ bool ConvertADT(char *filename, char *filename2, int /*cell_y*/, int /*cell_x*/,
// Prepare map header
map_fileheader map;
- map.mapMagic = *(uint32 const*)MAP_MAGIC;
- map.versionMagic = *(uint32 const*)MAP_VERSION_MAGIC;
+ map.mapMagic = *reinterpret_cast<uint32 const*>(MAP_MAGIC);
+ map.versionMagic = *reinterpret_cast<uint32 const*>(MAP_VERSION_MAGIC);
map.buildMagic = build;
// Get area flags data
@@ -452,7 +410,7 @@ bool ConvertADT(char *filename, char *filename2, int /*cell_y*/, int /*cell_x*/,
area_flags[i][j] = areas[areaid];
continue;
}
- printf("File: %s\nCan't find area flag for areaid %u [%d, %d].\n", filename, areaid, cell->ix, cell->iy);
+ printf("File: %s\nCan't find area flag for areaid %u [%d, %d].\n", inputPath.c_str(), areaid, cell->ix, cell->iy);
}
area_flags[i][j] = 0xffff;
}
@@ -478,7 +436,7 @@ bool ConvertADT(char *filename, char *filename2, int /*cell_y*/, int /*cell_x*/,
map.areaMapSize = sizeof(map_areaHeader);
map_areaHeader areaHeader;
- areaHeader.fourcc = *(uint32 const*)MAP_AREA_MAGIC;
+ areaHeader.fourcc = *reinterpret_cast<uint32 const*>(MAP_AREA_MAGIC);
areaHeader.flags = 0;
if (fullAreaData)
{
@@ -488,7 +446,7 @@ bool ConvertADT(char *filename, char *filename2, int /*cell_y*/, int /*cell_x*/,
else
{
areaHeader.flags |= MAP_AREA_NO_AREA;
- areaHeader.gridArea = (uint16)areaflag;
+ areaHeader.gridArea = static_cast<uint16>(areaflag);
}
//
@@ -607,7 +565,7 @@ bool ConvertADT(char *filename, char *filename2, int /*cell_y*/, int /*cell_x*/,
map.heightMapSize = sizeof(map_heightHeader);
map_heightHeader heightHeader;
- heightHeader.fourcc = *(uint32 const*)MAP_HEIGHT_MAGIC;
+ heightHeader.fourcc = *reinterpret_cast<uint32 const*>(MAP_HEIGHT_MAGIC);
heightHeader.flags = 0;
heightHeader.gridHeight = minHeight;
heightHeader.gridMaxHeight = maxHeight;
@@ -763,7 +721,7 @@ bool ConvertADT(char *filename, char *filename2, int /*cell_y*/, int /*cell_x*/,
case LIQUID_TYPE_MAGMA: liquid_flags[i][j] |= MAP_LIQUID_TYPE_MAGMA; break;
case LIQUID_TYPE_SLIME: liquid_flags[i][j] |= MAP_LIQUID_TYPE_SLIME; break;
default:
- printf("\nCan't find Liquid type %u for map %s\nchunk %d,%d\n", h->liquidType, filename, i, j);
+ printf("\nCan't find Liquid type %u for map %s\nchunk %d,%d\n", h->liquidType, inputPath.c_str(), i, j);
break;
}
// Dark water detect
@@ -848,7 +806,7 @@ bool ConvertADT(char *filename, char *filename2, int /*cell_y*/, int /*cell_x*/,
}
map.liquidMapOffset = map.heightMapOffset + map.heightMapSize;
map.liquidMapSize = sizeof(map_liquidHeader);
- liquidHeader.fourcc = *(uint32 const*)MAP_LIQUID_MAGIC;
+ liquidHeader.fourcc = *reinterpret_cast<uint32 const*>(MAP_LIQUID_MAGIC);
liquidHeader.flags = 0;
liquidHeader.liquidType = 0;
liquidHeader.offsetX = minX;
@@ -906,69 +864,72 @@ bool ConvertADT(char *filename, char *filename2, int /*cell_y*/, int /*cell_x*/,
map.holesSize = 0;
// Ok all data prepared - store it
- FILE* output = fopen(filename2, "wb");
- if (!output)
+
+ std::ofstream outFile(outputPath, std::ofstream::out | std::ofstream::binary);
+ if (!outFile)
{
- printf("Can't create the output file '%s'\n", filename2);
+ printf("Can't create the output file '%s'\n", outputPath.c_str());
return false;
}
- fwrite(&map, sizeof(map), 1, output);
+
+ outFile.write(reinterpret_cast<const char*>(&map), sizeof(map));
// Store area data
- fwrite(&areaHeader, sizeof(areaHeader), 1, output);
+ outFile.write(reinterpret_cast<const char*>(&areaHeader), sizeof(areaHeader));
if (!(areaHeader.flags&MAP_AREA_NO_AREA))
- fwrite(area_flags, sizeof(area_flags), 1, output);
+ outFile.write(reinterpret_cast<const char*>(area_flags), sizeof(area_flags));
// Store height data
- fwrite(&heightHeader, sizeof(heightHeader), 1, output);
+ outFile.write(reinterpret_cast<const char*>(&heightHeader), sizeof(heightHeader));
if (!(heightHeader.flags & MAP_HEIGHT_NO_HEIGHT))
{
if (heightHeader.flags & MAP_HEIGHT_AS_INT16)
{
- fwrite(uint16_V9, sizeof(uint16_V9), 1, output);
- fwrite(uint16_V8, sizeof(uint16_V8), 1, output);
+ outFile.write(reinterpret_cast<const char*>(uint16_V9), sizeof(uint16_V9));
+ outFile.write(reinterpret_cast<const char*>(uint16_V8), sizeof(uint16_V8));
}
else if (heightHeader.flags & MAP_HEIGHT_AS_INT8)
{
- fwrite(uint8_V9, sizeof(uint8_V9), 1, output);
- fwrite(uint8_V8, sizeof(uint8_V8), 1, output);
+ outFile.write(reinterpret_cast<const char*>(uint8_V9), sizeof(uint8_V9));
+ outFile.write(reinterpret_cast<const char*>(uint8_V8), sizeof(uint8_V8));
}
else
{
- fwrite(V9, sizeof(V9), 1, output);
- fwrite(V8, sizeof(V8), 1, output);
+ outFile.write(reinterpret_cast<const char*>(V9), sizeof(V9));
+ outFile.write(reinterpret_cast<const char*>(V8), sizeof(V8));
}
}
// Store liquid data if need
if (map.liquidMapOffset)
{
- fwrite(&liquidHeader, sizeof(liquidHeader), 1, output);
+ outFile.write(reinterpret_cast<const char*>(&liquidHeader), sizeof(liquidHeader));
+
if (!(liquidHeader.flags&MAP_LIQUID_NO_TYPE))
{
- fwrite(liquid_entry, sizeof(liquid_entry), 1, output);
- fwrite(liquid_flags, sizeof(liquid_flags), 1, output);
+ outFile.write(reinterpret_cast<const char*>(liquid_entry), sizeof(liquid_entry));
+ outFile.write(reinterpret_cast<const char*>(liquid_flags), sizeof(liquid_flags));
}
+
if (!(liquidHeader.flags&MAP_LIQUID_NO_HEIGHT))
{
- for (int y=0; y<liquidHeader.height;y++)
- fwrite(&liquid_height[y+liquidHeader.offsetY][liquidHeader.offsetX], sizeof(float), liquidHeader.width, output);
+ for (int y = 0; y < liquidHeader.height; y++)
+ outFile.write(reinterpret_cast<const char*>(&liquid_height[y + liquidHeader.offsetY][liquidHeader.offsetX]), sizeof(float) * liquidHeader.width);
}
}
// store hole data
if (hasHoles)
- fwrite(holes, map.holesSize, 1, output);
-
- fclose(output);
+ outFile.write(reinterpret_cast<const char*>(holes), map.holesSize);
+ outFile.close();
return true;
}
void ExtractMapsFromMpq(uint32 build)
{
- char mpq_filename[1024];
- char output_filename[1024];
- char mpq_map_name[1024];
+ std::string mpqFileName;
+ std::string outputFileName;
+ std::string mpqMapName;
printf("Extracting maps...\n");
@@ -986,9 +947,10 @@ void ExtractMapsFromMpq(uint32 build)
{
printf("Extract %s (%d/%u) \n", map_ids[z].name, z+1, map_count);
// Loadup map grid data
- sprintf(mpq_map_name, "World\\Maps\\%s\\%s.wdt", map_ids[z].name, map_ids[z].name);
+
+ mpqMapName = Trinity::StringFormat("World\\Maps\\%s\\%s.wdt", map_ids[z].name, map_ids[z].name);
WDT_file wdt;
- if (!wdt.loadFile(mpq_map_name, false))
+ if (!wdt.loadFile(mpqMapName, false))
{
// printf("Error loading %s map wdt data\n", map_ids[z].name);
continue;
@@ -1000,9 +962,10 @@ void ExtractMapsFromMpq(uint32 build)
{
if (!wdt.main->adt_list[y][x].exist)
continue;
- sprintf(mpq_filename, "World\\Maps\\%s\\%s_%u_%u.adt", map_ids[z].name, map_ids[z].name, x, y);
- sprintf(output_filename, "%s/maps/%03u%02u%02u.map", output_path, map_ids[z].id, y, x);
- ConvertADT(mpq_filename, output_filename, y, x, build);
+
+ mpqFileName = Trinity::StringFormat("World\\Maps\\%s\\%s_%u_%u.adt", map_ids[z].name, map_ids[z].name, x, y);
+ outputFileName = Trinity::StringFormat("%s/maps/%03u%02u%02u.map", output_path, map_ids[z].id, y, x);
+ ConvertADT(mpqFileName, outputFileName, y, x, build);
}
// draw progress bar
printf("Processing........................%d%%\r", (100 * (y+1)) / WDT_MAP_SIZE);
@@ -1038,9 +1001,9 @@ void ExtractDBCFiles(int locale, bool basicLocale)
// get DBC file list
for(ArchiveSet::iterator i = gOpenArchives.begin(); i != gOpenArchives.end();++i)
{
- vector<string> files;
+ std::vector<std::string> files;
(*i)->GetFileListTo(files);
- for (vector<string>::iterator iter = files.begin(); iter != files.end(); ++iter)
+ for (std::vector<std::string>::iterator iter = files.begin(); iter != files.end(); ++iter)
if (iter->rfind(".dbc") == iter->length() - strlen(".dbc"))
dbcfiles.insert(*iter);
}
@@ -1057,20 +1020,20 @@ void ExtractDBCFiles(int locale, bool basicLocale)
// extract Build info file
{
- string mpq_name = std::string("component.wow-") + langs[locale] + ".txt";
- string filename = path + mpq_name;
+ std::string mpq_name = std::string("component.wow-") + langs[locale] + ".txt";
+ std::string filename = path + mpq_name;
ExtractFile(mpq_name.c_str(), filename);
}
// extract DBCs
uint32 count = 0;
- for (set<string>::iterator iter = dbcfiles.begin(); iter != dbcfiles.end(); ++iter)
+ for (std::set<std::string>::iterator iter = dbcfiles.begin(); iter != dbcfiles.end(); ++iter)
{
- string filename = path;
+ std::string filename = path;
filename += (iter->c_str() + strlen("DBFilesClient\\"));
- if(FileExists(filename.c_str()))
+ if (boost::filesystem::exists(filename))
continue;
if (ExtractFile(iter->c_str(), filename))
@@ -1081,32 +1044,31 @@ void ExtractDBCFiles(int locale, bool basicLocale)
void LoadLocaleMPQFiles(int const locale)
{
- char filename[512];
+ std::string fileName = Trinity::StringFormat("%s/Data/%s/locale-%s.MPQ", input_path, langs[locale], langs[locale]);
- sprintf(filename,"%s/Data/%s/locale-%s.MPQ", input_path, langs[locale], langs[locale]);
- new MPQArchive(filename);
+ new MPQArchive(fileName.c_str());
for(int i = 1; i < 5; ++i)
{
- char ext[3] = "";
- if(i > 1)
- sprintf(ext, "-%i", i);
+ std::string ext;
+ if (i > 1)
+ ext = Trinity::StringFormat("-%i", i);
- sprintf(filename,"%s/Data/%s/patch-%s%s.MPQ", input_path, langs[locale], langs[locale], ext);
- if(FileExists(filename))
- new MPQArchive(filename);
+ fileName = Trinity::StringFormat("%s/Data/%s/patch-%s%s.MPQ", input_path, langs[locale], langs[locale], ext.c_str());
+ if (boost::filesystem::exists(fileName))
+ new MPQArchive(fileName.c_str());
}
}
void LoadCommonMPQFiles()
{
- char filename[512];
+ std::string fileName;
int count = sizeof(CONF_mpq_list)/sizeof(char*);
for(int i = 0; i < count; ++i)
{
- sprintf(filename, "%s/Data/%s", input_path, CONF_mpq_list[i]);
- if(FileExists(filename))
- new MPQArchive(filename);
+ fileName = Trinity::StringFormat("%s/Data/%s", input_path, CONF_mpq_list[i]);
+ if (boost::filesystem::exists(fileName))
+ new MPQArchive(fileName.c_str());
}
}
@@ -1128,9 +1090,8 @@ int main(int argc, char * arg[])
for (int i = 0; i < LANG_COUNT; i++)
{
- char tmp1[512];
- sprintf(tmp1, "%s/Data/%s/locale-%s.MPQ", input_path, langs[i], langs[i]);
- if (FileExists(tmp1))
+ std::string filename = Trinity::StringFormat("%s/Data/%s/locale-%s.MPQ", input_path, langs[i], langs[i]);
+ if (boost::filesystem::exists(filename))
{
printf("Detected locale: %s\n", langs[i]);
diff --git a/src/tools/map_extractor/loadlib.cpp b/src/tools/map_extractor/loadlib.cpp
index 936cdb72cfd..2f31b472b56 100644
--- a/src/tools/map_extractor/loadlib.cpp
+++ b/src/tools/map_extractor/loadlib.cpp
@@ -38,14 +38,14 @@ FileLoader::~FileLoader()
free();
}
-bool FileLoader::loadFile(char *filename, bool log)
+bool FileLoader::loadFile(std::string const& fileName, bool log)
{
free();
- MPQFile mf(filename);
+ MPQFile mf(fileName.c_str());
if(mf.isEof())
{
if (log)
- printf("No such file %s\n", filename);
+ printf("No such file %s\n", fileName.c_str());
return false;
}
@@ -57,7 +57,7 @@ bool FileLoader::loadFile(char *filename, bool log)
if (prepareLoadedData())
return true;
- printf("Error loading %s", filename);
+ printf("Error loading %s", fileName.c_str());
mf.close();
free();
return false;
diff --git a/src/tools/map_extractor/loadlib/loadlib.h b/src/tools/map_extractor/loadlib/loadlib.h
index a0b62a85983..6547704d885 100644
--- a/src/tools/map_extractor/loadlib/loadlib.h
+++ b/src/tools/map_extractor/loadlib/loadlib.h
@@ -19,31 +19,9 @@
#ifndef LOAD_LIB_H
#define LOAD_LIB_H
-#ifdef _WIN32
-typedef __int64 int64;
-typedef __int32 int32;
-typedef __int16 int16;
-typedef __int8 int8;
-typedef unsigned __int64 uint64;
-typedef unsigned __int32 uint32;
-typedef unsigned __int16 uint16;
-typedef unsigned __int8 uint8;
-#else
-#include <stdint.h>
-#ifndef uint64_t
-#ifdef __linux__
-#include <linux/types.h>
-#endif
-#endif
-typedef int64_t int64;
-typedef int32_t int32;
-typedef int16_t int16;
-typedef int8_t int8;
-typedef uint64_t uint64;
-typedef uint32_t uint32;
-typedef uint16_t uint16;
-typedef uint8_t uint8;
-#endif
+#include "Define.h"
+
+#include <string>
#define FILE_FORMAT_VERSION 18
@@ -79,7 +57,7 @@ public:
file_MVER *version;
FileLoader();
~FileLoader();
- bool loadFile(char *filename, bool log = true);
+ bool loadFile(std::string const& fileName, bool log = true);
virtual void free();
};
diff --git a/src/tools/map_extractor/mpq_libmpq04.h b/src/tools/map_extractor/mpq_libmpq04.h
index c6fe36a8221..b9b332f95b0 100644
--- a/src/tools/map_extractor/mpq_libmpq04.h
+++ b/src/tools/map_extractor/mpq_libmpq04.h
@@ -27,8 +27,6 @@
#include <iostream>
#include <deque>
-using namespace std;
-
class MPQArchive
{
@@ -39,7 +37,7 @@ public:
~MPQArchive() { close(); }
void close();
- void GetFileListTo(vector<string>& filelist) {
+ void GetFileListTo(std::vector<std::string>& filelist) {
uint32_t filenum;
if(libmpq__file_number(mpq_a, "(listfile)", &filenum)) return;
libmpq__off_t size, transferred;
@@ -58,7 +56,7 @@ public:
while ((token != NULL) && (counter < size)) {
//cout << token << endl;
token[strlen(token) - 1] = 0;
- string s = token;
+ std::string s = token;
filelist.push_back(s);
counter += strlen(token) + 2;
token = strtok(NULL, seps);
diff --git a/src/tools/mmaps_generator/IntermediateValues.h b/src/tools/mmaps_generator/IntermediateValues.h
index 95a651a2df8..580e9e43139 100644
--- a/src/tools/mmaps_generator/IntermediateValues.h
+++ b/src/tools/mmaps_generator/IntermediateValues.h
@@ -22,7 +22,6 @@
#include "PathCommon.h"
#include "TerrainBuilder.h"
#include "Recast.h"
-#include "DetourNavMesh.h"
namespace MMAP
{
diff --git a/src/tools/mmaps_generator/MapBuilder.cpp b/src/tools/mmaps_generator/MapBuilder.cpp
index 69ca5297024..0c41be65cf7 100644
--- a/src/tools/mmaps_generator/MapBuilder.cpp
+++ b/src/tools/mmaps_generator/MapBuilder.cpp
@@ -15,17 +15,15 @@
* 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 <limits.h>
#include "PathCommon.h"
#include "MapBuilder.h"
-
#include "MapTree.h"
-#include "ModelInstance.h"
-
#include "DetourNavMeshBuilder.h"
#include "DetourNavMesh.h"
-#include "DetourCommon.h"
+#include "IntermediateValues.h"
+
+#include <limits.h>
#define MMAP_MAGIC 0x4d4d4150 // 'MMAP'
#define MMAP_VERSION 5
@@ -164,7 +162,7 @@ namespace MMAP
{
while (1)
{
- uint32 mapId;
+ uint32 mapId = 0;
_queue.WaitAndPop(mapId);
@@ -215,12 +213,14 @@ namespace MMAP
}
/**************************************************************************/
- void MapBuilder::getGridBounds(uint32 mapID, uint32 &minX, uint32 &minY, uint32 &maxX, uint32 &maxY)
+ void MapBuilder::getGridBounds(uint32 mapID, uint32 &minX, uint32 &minY, uint32 &maxX, uint32 &maxY) const
{
- maxX = INT_MAX;
- maxY = INT_MAX;
- minX = INT_MIN;
- minY = INT_MIN;
+ // min and max are initialized to invalid values so the caller iterating the [min, max] range
+ // will never enter the loop unless valid min/max values are found
+ maxX = 0;
+ maxY = 0;
+ minX = std::numeric_limits<uint32>::max();
+ minY = std::numeric_limits<uint32>::max();
float bmin[3] = { 0, 0, 0 };
float bmax[3] = { 0, 0, 0 };
diff --git a/src/tools/mmaps_generator/MapBuilder.h b/src/tools/mmaps_generator/MapBuilder.h
index ced03d1dde8..432a0ee7489 100644
--- a/src/tools/mmaps_generator/MapBuilder.h
+++ b/src/tools/mmaps_generator/MapBuilder.h
@@ -19,20 +19,18 @@
#ifndef _MAP_BUILDER_H
#define _MAP_BUILDER_H
-#include <vector>
-#include <set>
-#include <map>
-#include <list>
-#include <atomic>
-#include <thread>
-
#include "TerrainBuilder.h"
-#include "IntermediateValues.h"
#include "Recast.h"
#include "DetourNavMesh.h"
#include "ProducerConsumerQueue.h"
+#include <vector>
+#include <set>
+#include <list>
+#include <atomic>
+#include <thread>
+
using namespace VMAP;
namespace MMAP
@@ -120,7 +118,7 @@ namespace MMAP
void getTileBounds(uint32 tileX, uint32 tileY,
float* verts, int vertCount,
float* bmin, float* bmax);
- void getGridBounds(uint32 mapID, uint32 &minX, uint32 &minY, uint32 &maxX, uint32 &maxY);
+ void getGridBounds(uint32 mapID, uint32 &minX, uint32 &minY, uint32 &maxX, uint32 &maxY) const;
bool shouldSkipMap(uint32 mapID);
bool isTransportMap(uint32 mapID);
diff --git a/src/tools/mmaps_generator/PathCommon.h b/src/tools/mmaps_generator/PathCommon.h
index 9451e9d03ba..aa9591fba22 100644
--- a/src/tools/mmaps_generator/PathCommon.h
+++ b/src/tools/mmaps_generator/PathCommon.h
@@ -19,11 +19,10 @@
#ifndef _MMAP_COMMON_H
#define _MMAP_COMMON_H
-#include <string>
-#include <vector>
-
#include "Common.h"
+#include <vector>
+
#ifndef _WIN32
#include <stddef.h>
#include <dirent.h>
diff --git a/src/tools/mmaps_generator/TerrainBuilder.cpp b/src/tools/mmaps_generator/TerrainBuilder.cpp
index 9043627994b..e525f24fced 100644
--- a/src/tools/mmaps_generator/TerrainBuilder.cpp
+++ b/src/tools/mmaps_generator/TerrainBuilder.cpp
@@ -764,12 +764,12 @@ namespace MMAP
}
uint32 liqOffset = meshData.liquidVerts.size() / 3;
- for (uint32 i = 0; i < liqVerts.size(); ++i)
- meshData.liquidVerts.append(liqVerts[i].y, liqVerts[i].z, liqVerts[i].x);
+ for (uint32 j = 0; j < liqVerts.size(); ++j)
+ meshData.liquidVerts.append(liqVerts[j].y, liqVerts[j].z, liqVerts[j].x);
- for (uint32 i = 0; i < liqTris.size() / 3; ++i)
+ for (uint32 j = 0; j < liqTris.size() / 3; ++j)
{
- meshData.liquidTris.append(liqTris[i*3+1] + liqOffset, liqTris[i*3+2] + liqOffset, liqTris[i*3] + liqOffset);
+ meshData.liquidTris.append(liqTris[j*3+1] + liqOffset, liqTris[j*3+2] + liqOffset, liqTris[j*3] + liqOffset);
meshData.liquidType.append(type);
}
}
diff --git a/src/tools/mmaps_generator/TerrainBuilder.h b/src/tools/mmaps_generator/TerrainBuilder.h
index 6c66ae45f46..f305bf0bd48 100644
--- a/src/tools/mmaps_generator/TerrainBuilder.h
+++ b/src/tools/mmaps_generator/TerrainBuilder.h
@@ -81,11 +81,13 @@ namespace MMAP
TerrainBuilder(bool skipLiquid);
~TerrainBuilder();
+ TerrainBuilder(const TerrainBuilder &tb) = delete;
+
void loadMap(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData);
bool loadVMap(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData);
void loadOffMeshConnections(uint32 mapID, uint32 tileX, uint32 tileY, MeshData &meshData, const char* offMeshFilePath);
- bool usesLiquids() { return !m_skipLiquid; }
+ bool usesLiquids() const { return !m_skipLiquid; }
// vert and triangle methods
static void transform(std::vector<G3D::Vector3> &original, std::vector<G3D::Vector3> &transformed,
@@ -104,9 +106,6 @@ namespace MMAP
/// Controls whether liquids are loaded
bool m_skipLiquid;
- /// Load the map terrain from file
- bool loadHeightMap(uint32 mapID, uint32 tileX, uint32 tileY, G3D::Array<float> &vertices, G3D::Array<int> &triangles, Spot portion);
-
/// Get the vector coordinate for a specific position
void getHeightCoord(int index, Grid grid, float xOffset, float yOffset, float* coord, float* v);
@@ -121,10 +120,6 @@ namespace MMAP
/// Get the liquid type for a specific position
uint8 getLiquidType(int square, const uint8 liquid_type[16][16]);
-
- // hide parameterless and copy constructor
- TerrainBuilder();
- TerrainBuilder(const TerrainBuilder &tb);
};
}
diff --git a/src/tools/vmap4_extractor/adtfile.cpp b/src/tools/vmap4_extractor/adtfile.cpp
index 557511f6d1e..3a8b3495655 100644
--- a/src/tools/vmap4_extractor/adtfile.cpp
+++ b/src/tools/vmap4_extractor/adtfile.cpp
@@ -86,11 +86,11 @@ bool ADTFile::init(uint32 map_num, uint32 tileX, uint32 tileY)
uint32 size;
- string xMap;
- string yMap;
+ std::string xMap;
+ std::string yMap;
Adtfilename.erase(Adtfilename.find(".adt"),4);
- string TempMapNumber;
+ std::string TempMapNumber;
TempMapNumber = Adtfilename.substr(Adtfilename.length()-6,6);
xMap = TempMapNumber.substr(TempMapNumber.find("_")+1,(TempMapNumber.find_last_of("_")-1) - (TempMapNumber.find("_")));
yMap = TempMapNumber.substr(TempMapNumber.find_last_of("_")+1,(TempMapNumber.length()) - (TempMapNumber.find_last_of("_")));
@@ -134,7 +134,7 @@ bool ADTFile::init(uint32 map_num, uint32 tileX, uint32 tileY)
ADT.read(buf, size);
char *p=buf;
int t=0;
- ModelInstansName = new string[size];
+ ModelInstansName = new std::string[size];
while (p<buf+size)
{
fixnamen(p,strlen(p));
@@ -143,7 +143,7 @@ bool ADTFile::init(uint32 map_num, uint32 tileX, uint32 tileY)
ModelInstansName[t++] = s;
- string path(p);
+ std::string path(p);
ExtractSingleModel(path);
p = p+strlen(p)+1;
diff --git a/src/tools/vmap4_extractor/mpq_libmpq04.h b/src/tools/vmap4_extractor/mpq_libmpq04.h
index 6196285627d..4c8b4d376ca 100644
--- a/src/tools/vmap4_extractor/mpq_libmpq04.h
+++ b/src/tools/vmap4_extractor/mpq_libmpq04.h
@@ -21,14 +21,12 @@
#include "loadlib/loadlib.h"
#include "libmpq/mpq.h"
+
#include <string.h>
-#include <ctype.h>
+#include <string>
#include <vector>
-#include <iostream>
#include <deque>
-using namespace std;
-
class MPQArchive
{
@@ -38,7 +36,7 @@ public:
MPQArchive(const char* filename);
~MPQArchive() { close(); }
- void GetFileListTo(vector<string>& filelist) {
+ void GetFileListTo(std::vector<std::string>& filelist) {
uint32_t filenum;
if(libmpq__file_number(mpq_a, "(listfile)", &filenum)) return;
libmpq__off_t size, transferred;
@@ -57,7 +55,7 @@ public:
while ((token != NULL) && (counter < size)) {
//cout << token << endl;
token[strlen(token) - 1] = 0;
- string s = token;
+ std::string s = token;
filelist.push_back(s);
counter += strlen(token) + 2;
token = strtok(NULL, seps);
@@ -78,9 +76,8 @@ class MPQFile
char *buffer;
libmpq__off_t pointer,size;
- // disable copying
- MPQFile(const MPQFile& /*f*/) {}
- void operator=(const MPQFile& /*f*/) {}
+ MPQFile(const MPQFile& /*f*/) = delete;
+ void operator=(const MPQFile& /*f*/) = delete;
public:
MPQFile(const char* filename); // filenames are not case sensitive
diff --git a/src/tools/vmap4_extractor/vmapexport.cpp b/src/tools/vmap4_extractor/vmapexport.cpp
index 7a12897563e..78d6e86358f 100644
--- a/src/tools/vmap4_extractor/vmapexport.cpp
+++ b/src/tools/vmap4_extractor/vmapexport.cpp
@@ -17,14 +17,15 @@
*/
#define _CRT_SECURE_NO_DEPRECATE
-#include <cstdio>
-#include <iostream>
-#include <vector>
-#include <list>
-#include <errno.h>
+
+#include "adtfile.h"
+#include "wdtfile.h"
+#include "dbcfile.h"
+#include "wmo.h"
+#include "mpq_libmpq04.h"
+#include "vmapexport.h"
#ifdef WIN32
- #include <Windows.h>
#include <sys/stat.h>
#include <direct.h>
#define mkdir _mkdir
@@ -32,23 +33,14 @@
#include <sys/stat.h>
#endif
+#include <cstdio>
+#include <iostream>
+#include <vector>
+#include <errno.h>
+
#undef min
#undef max
-//#pragma warning(disable : 4505)
-//#pragma comment(lib, "Winmm.lib")
-
-#include <map>
-
-//From Extractor
-#include "adtfile.h"
-#include "wdtfile.h"
-#include "dbcfile.h"
-#include "wmo.h"
-#include "mpq_libmpq04.h"
-
-#include "vmapexport.h"
-
//------------------------------------------------------------------------------
// Defines
@@ -129,12 +121,12 @@ bool ExtractWmo()
for (ArchiveSet::const_iterator ar_itr = gOpenArchives.begin(); ar_itr != gOpenArchives.end() && success; ++ar_itr)
{
- vector<string> filelist;
+ std::vector<std::string> filelist;
(*ar_itr)->GetFileListTo(filelist);
- for (vector<string>::iterator fname = filelist.begin(); fname != filelist.end() && success; ++fname)
+ for (std::vector<std::string>::iterator fname = filelist.begin(); fname != filelist.end() && success; ++fname)
{
- if (fname->find(".wmo") != string::npos)
+ if (fname->find(".wmo") != std::string::npos)
success = ExtractSingleWmo(*fname);
}
}
@@ -203,7 +195,7 @@ bool ExtractSingleWmo(std::string& fname)
sprintf(groupFileName, "%s_%03u.wmo", temp, i);
//printf("Trying to open groupfile %s\n",groupFileName);
- string s = groupFileName;
+ std::string s = groupFileName;
WMOGroup fgroup(s);
if(!fgroup.open())
{
@@ -305,7 +297,7 @@ bool fillArchiveNameVector(std::vector<std::string>& pArchiveNames)
printf("\nGame path: %s\n", input_path);
char path[512];
- string in_path(input_path);
+ std::string in_path(input_path);
std::vector<std::string> locales, searchLocales;
searchLocales.push_back("enGB");
@@ -345,10 +337,10 @@ bool fillArchiveNameVector(std::vector<std::string>& pArchiveNames)
}
// open expansion and common files
- pArchiveNames.push_back(input_path + string("common.MPQ"));
- pArchiveNames.push_back(input_path + string("common-2.MPQ"));
- pArchiveNames.push_back(input_path + string("expansion.MPQ"));
- pArchiveNames.push_back(input_path + string("lichking.MPQ"));
+ pArchiveNames.push_back(input_path + std::string("common.MPQ"));
+ pArchiveNames.push_back(input_path + std::string("common-2.MPQ"));
+ pArchiveNames.push_back(input_path + std::string("expansion.MPQ"));
+ pArchiveNames.push_back(input_path + std::string("lichking.MPQ"));
// now, scan for the patch levels in the core dir
printf("Scanning patch levels from data directory.\n");
diff --git a/src/tools/vmap4_extractor/wdtfile.cpp b/src/tools/vmap4_extractor/wdtfile.cpp
index 877f49ce371..4e0b7b97705 100644
--- a/src/tools/vmap4_extractor/wdtfile.cpp
+++ b/src/tools/vmap4_extractor/wdtfile.cpp
@@ -19,6 +19,7 @@
#include "vmapexport.h"
#include "wdtfile.h"
#include "adtfile.h"
+
#include <cstdio>
char * wdtGetPlainName(char * FileName)
@@ -30,7 +31,7 @@ char * wdtGetPlainName(char * FileName)
return FileName;
}
-WDTFile::WDTFile(char* file_name, char* file_name1) : WDT(file_name), gWmoInstansName(NULL), gnWMO(0)
+WDTFile::WDTFile(char* file_name, char* file_name1) : gWmoInstansName(NULL), gnWMO(0), WDT(file_name)
{
filename.append(file_name1,strlen(file_name1));
}
@@ -77,7 +78,7 @@ bool WDTFile::init(char* /*map_id*/, unsigned int mapID)
WDT.read(buf, size);
char *p=buf;
int q = 0;
- gWmoInstansName = new string[size];
+ gWmoInstansName = new std::string[size];
while (p < buf + size)
{
char* s=wdtGetPlainName(p);
diff --git a/src/tools/vmap4_extractor/wdtfile.h b/src/tools/vmap4_extractor/wdtfile.h
index 22702133b95..81e84b36d1e 100644
--- a/src/tools/vmap4_extractor/wdtfile.h
+++ b/src/tools/vmap4_extractor/wdtfile.h
@@ -20,26 +20,25 @@
#define WDTFILE_H
#include "mpq_libmpq04.h"
-#include "wmo.h"
#include <string>
-#include "stdlib.h"
class ADTFile;
class WDTFile
{
-private:
- MPQFile WDT;
- string filename;
public:
WDTFile(char* file_name, char* file_name1);
~WDTFile(void);
+
bool init(char* map_id, unsigned int mapID);
+ ADTFile* GetMap(int x, int z);
- string* gWmoInstansName;
+ std::string* gWmoInstansName;
int gnWMO;
- ADTFile* GetMap(int x, int z);
+private:
+ MPQFile WDT;
+ std::string filename;
};
#endif
diff --git a/src/tools/vmap4_extractor/wmo.cpp b/src/tools/vmap4_extractor/wmo.cpp
index f6f59bbc475..80dfbb97c5a 100644
--- a/src/tools/vmap4_extractor/wmo.cpp
+++ b/src/tools/vmap4_extractor/wmo.cpp
@@ -19,16 +19,14 @@
#include "vmapexport.h"
#include "wmo.h"
#include "vec3d.h"
+#include "mpq_libmpq04.h"
+
#include <cstdio>
#include <cstdlib>
#include <cassert>
-#include <map>
-#include <fstream>
#undef min
#undef max
-#include "mpq_libmpq04.h"
-using namespace std;
extern uint16 *LiqType;
WMORoot::WMORoot(std::string &filename)