diff options
Diffstat (limited to 'src')
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) |