diff options
Diffstat (limited to 'src')
79 files changed, 1053 insertions, 929 deletions
diff --git a/src/server/authserver/authserver.conf.dist b/src/server/authserver/authserver.conf.dist index 437ec221e94..1b10fa4e31d 100644 --- a/src/server/authserver/authserver.conf.dist +++ b/src/server/authserver/authserver.conf.dist @@ -155,6 +155,7 @@ LoginDatabase.WorkerThreads = 1 # 1 - (Enabled) Wrong.Password.Login.Logging = 0 + # ################################################################################################### diff --git a/src/server/database/CMakeLists.txt b/src/server/database/CMakeLists.txt index 2375f18d7b5..19fa0ee0acf 100644 --- a/src/server/database/CMakeLists.txt +++ b/src/server/database/CMakeLists.txt @@ -8,7 +8,9 @@ # WITHOUT ANY WARRANTY, to the extent permitted by law; without even the # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -find_package(MySQL REQUIRED) +if (NOT MYSQL_FOUND) + message(SEND_ERROR "MySQL wasn't found on your system but it's required to build the servers!") +endif() if( USE_COREPCH ) include_directories(${CMAKE_CURRENT_BINARY_DIR}) diff --git a/src/server/database/Database/Implementation/CharacterDatabase.cpp b/src/server/database/Database/Implementation/CharacterDatabase.cpp index 6494f92b522..14e7571dd42 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/database/Database/Implementation/CharacterDatabase.cpp @@ -127,8 +127,8 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_SEL_CHARACTER_ACTIONS_SPEC, "SELECT button, action, type FROM character_action WHERE guid = ? AND spec = ? ORDER BY button", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_MAILITEMS, "SELECT creatorGuid, giftCreatorGuid, count, duration, charges, flags, enchantments, randomPropertyId, durability, playedTime, text, item_guid, itemEntry, owner_guid FROM mail_items mi JOIN item_instance ii ON mi.item_guid = ii.guid WHERE mail_id = ?", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_AUCTION_ITEMS, "SELECT creatorGuid, giftCreatorGuid, count, duration, charges, flags, enchantments, randomPropertyId, durability, playedTime, text, itemguid, itemEntry FROM auctionhouse ah JOIN item_instance ii ON ah.itemguid = ii.guid", CONNECTION_SYNCH); - PrepareStatement(CHAR_SEL_AUCTIONS, "SELECT id, auctioneerguid, itemguid, itemEntry, count, itemowner, buyoutprice, time, buyguid, lastbid, startbid, deposit FROM auctionhouse ah INNER JOIN item_instance ii ON ii.guid = ah.itemguid", CONNECTION_SYNCH); - PrepareStatement(CHAR_INS_AUCTION, "INSERT INTO auctionhouse (id, auctioneerguid, itemguid, itemowner, buyoutprice, time, buyguid, lastbid, startbid, deposit) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); + PrepareStatement(CHAR_SEL_AUCTIONS, "SELECT id, houseid, itemguid, itemEntry, count, itemowner, buyoutprice, time, buyguid, lastbid, startbid, deposit FROM auctionhouse ah INNER JOIN item_instance ii ON ii.guid = ah.itemguid", CONNECTION_SYNCH); + PrepareStatement(CHAR_INS_AUCTION, "INSERT INTO auctionhouse (id, houseid, itemguid, itemowner, buyoutprice, time, buyguid, lastbid, startbid, deposit) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_AUCTION, "DELETE FROM auctionhouse WHERE id = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_UPD_AUCTION_BID, "UPDATE auctionhouse SET buyguid = ?, lastbid = ? WHERE id = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_INS_MAIL, "INSERT INTO mail(id, messageType, stationery, mailTemplateId, sender, receiver, subject, body, has_items, expire_time, deliver_time, money, cod, checked) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); @@ -320,6 +320,7 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_INS_CORPSE, "INSERT INTO corpse (guid, posX, posY, posZ, orientation, mapId, displayId, itemCache, bytes1, bytes2, guildId, flags, dynFlags, time, corpseType, instanceId, phaseMask) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_CORPSE, "DELETE FROM corpse WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_CORPSES_FROM_MAP, "DELETE FROM corpse WHERE mapId = ? AND instanceId = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_SEL_CORPSE_LOCATION, "SELECT mapId, posX, posY, posZ, orientation FROM corpse WHERE guid = ?", CONNECTION_ASYNC); // Creature respawn PrepareStatement(CHAR_SEL_CREATURE_RESPAWNS, "SELECT guid, respawnTime FROM creature_respawn WHERE mapId = ? AND instanceId = ?", CONNECTION_SYNCH); @@ -336,7 +337,7 @@ void CharacterDatabaseConnection::DoPrepareStatements() // GM Tickets PrepareStatement(CHAR_SEL_GM_TICKETS, "SELECT id, playerGuid, name, description, createTime, mapId, posX, posY, posZ, lastModifiedTime, closedBy, assignedTo, comment, response, completed, escalated, viewed, needMoreHelp FROM gm_ticket", CONNECTION_SYNCH); - PrepareStatement(CHAR_REP_GM_TICKET, "REPLACE INTO gm_ticket (id, playerGuid, name, description, createTime, mapId, posX, posY, posZ, lastModifiedTime, closedBy, assignedTo, comment, response, completed, escalated, viewed, needMoreHelp) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); + PrepareStatement(CHAR_REP_GM_TICKET, "REPLACE INTO gm_ticket (id, playerGuid, name, description, createTime, mapId, posX, posY, posZ, lastModifiedTime, closedBy, assignedTo, comment, response, completed, escalated, viewed, needMoreHelp, resolvedBy) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_GM_TICKET, "DELETE FROM gm_ticket WHERE id = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_PLAYER_GM_TICKETS, "DELETE FROM gm_ticket WHERE playerGuid = ?", CONNECTION_ASYNC); diff --git a/src/server/database/Database/Implementation/CharacterDatabase.h b/src/server/database/Database/Implementation/CharacterDatabase.h index 5ebafe0d0f8..c76c584a0a8 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.h +++ b/src/server/database/Database/Implementation/CharacterDatabase.h @@ -269,6 +269,7 @@ enum CharacterDatabaseStatements CHAR_INS_CORPSE, CHAR_DEL_CORPSE, CHAR_DEL_CORPSES_FROM_MAP, + CHAR_SEL_CORPSE_LOCATION, CHAR_SEL_CREATURE_RESPAWNS, CHAR_REP_CREATURE_RESPAWN, diff --git a/src/server/database/Updater/DBUpdater.cpp b/src/server/database/Updater/DBUpdater.cpp index ebdd6604fef..d90d71c5594 100644 --- a/src/server/database/Updater/DBUpdater.cpp +++ b/src/server/database/Updater/DBUpdater.cpp @@ -26,6 +26,8 @@ #include <iostream> #include <unordered_map> #include <boost/process.hpp> +#include <boost/iostreams/stream.hpp> +#include <boost/iostreams/copy.hpp> #include <boost/iostreams/device/file_descriptor.hpp> #include <boost/system/system_error.hpp> @@ -33,24 +35,64 @@ using namespace boost::process; using namespace boost::process::initializers; using namespace boost::iostreams; -template<class T> -std::string DBUpdater<T>::GetSourceDirectory() +std::string DBUpdaterUtil::GetMySqlCli() { - std::string const entry = sConfigMgr->GetStringDefault("Updates.SourcePath", ""); - if (!entry.empty()) - return entry; + if (!corrected_path().empty()) + return corrected_path(); else - return GitRevision::GetSourceDirectory(); + { + std::string const entry = sConfigMgr->GetStringDefault("Updates.MySqlCLIPath", ""); + if (!entry.empty()) + return entry; + else + return GitRevision::GetMySQLExecutable(); + } +} + +bool DBUpdaterUtil::CheckExecutable() +{ + boost::filesystem::path exe(GetMySqlCli()); + if (!exists(exe)) + { + exe.clear(); + + try + { + exe = search_path("mysql"); + } + catch (std::runtime_error&) + { + } + + if (!exe.empty() && exists(exe)) + { + // Correct the path to the cli + corrected_path() = absolute(exe).generic_string(); + return true; + } + + TC_LOG_FATAL("sql.updates", "Didn't find executeable mysql binary at \'%s\' or in path, correct the path in the *.conf (\"Updates.MySqlCLIPath\").", + absolute(exe).generic_string().c_str()); + + return false; + } + return true; +} + +std::string& DBUpdaterUtil::corrected_path() +{ + static std::string path; + return path; } template<class T> -std::string DBUpdater<T>::GetMySqlCli() +std::string DBUpdater<T>::GetSourceDirectory() { - std::string const entry = sConfigMgr->GetStringDefault("Updates.MySqlCLIPath", ""); + std::string const entry = sConfigMgr->GetStringDefault("Updates.SourcePath", ""); if (!entry.empty()) return entry; else - return GitRevision::GetMySQLExecutable(); + return GitRevision::GetSourceDirectory(); } // Auth Database @@ -145,36 +187,6 @@ BaseLocation DBUpdater<T>::GetBaseLocationType() } template<class T> -bool DBUpdater<T>::CheckExecutable() -{ - DBUpdater<T>::Path const exe(DBUpdater<T>::GetMySqlCli()); - if (!exists(exe)) - { - // Check for mysql in path - std::vector<std::string> args = {"--version"}; - uint32 ret; - try - { - child c = execute(run_exe("mysql"), set_args(args), throw_on_error(), close_stdout()); - ret = wait_for_exit(c); - } - catch (boost::system::system_error&) - { - ret = EXIT_FAILURE; - } - - if (ret == EXIT_FAILURE) - { - TC_LOG_FATAL("sql.updates", "Didn't find executeable mysql binary at \'%s\', correct the path in the *.conf (\"Updates.MySqlCLIPath\").", - absolute(exe).generic_string().c_str()); - - return false; - } - } - return true; -} - -template<class T> bool DBUpdater<T>::Create(DatabaseWorkerPool<T>& pool) { TC_LOG_INFO("sql.updates", "Database \"%s\" does not exist, do you want to create it? [yes (default) / no]: ", @@ -222,7 +234,7 @@ bool DBUpdater<T>::Create(DatabaseWorkerPool<T>& pool) template<class T> bool DBUpdater<T>::Update(DatabaseWorkerPool<T>& pool) { - if (!DBUpdater<T>::CheckExecutable()) + if (!DBUpdaterUtil::CheckExecutable()) return false; TC_LOG_INFO("sql.updates", "Updating %s database...", DBUpdater<T>::GetTableName().c_str()); @@ -273,7 +285,7 @@ bool DBUpdater<T>::Populate(DatabaseWorkerPool<T>& pool) return true; } - if (!DBUpdater<T>::CheckExecutable()) + if (!DBUpdaterUtil::CheckExecutable()) return false; TC_LOG_INFO("sql.updates", "Database %s is empty, auto populating it...", DBUpdater<T>::GetTableName().c_str()); @@ -346,7 +358,10 @@ void DBUpdater<T>::ApplyFile(DatabaseWorkerPool<T>& pool, std::string const& hos std::string const& password, std::string const& port_or_socket, std::string const& database, Path const& path) { std::vector<std::string> args; - args.reserve(7); + args.reserve(8); + + // args[0] represents the program name + args.push_back("mysql"); // CLI Client connection info args.push_back("-h" + host); @@ -391,9 +406,29 @@ void DBUpdater<T>::ApplyFile(DatabaseWorkerPool<T>& pool, std::string const& hos uint32 ret; try { - child c = execute(run_exe(DBUpdater<T>::GetMySqlCli().empty() ? "mysql" : - boost::filesystem::absolute(DBUpdater<T>::GetMySqlCli()).generic_string()), - set_args(args), bind_stdin(source), throw_on_error()); + boost::process::pipe outPipe = create_pipe(); + boost::process::pipe errPipe = create_pipe(); + + child c = execute(run_exe( + boost::filesystem::absolute(DBUpdaterUtil::GetMySqlCli()).generic_string()), + set_args(args), bind_stdin(source), throw_on_error(), + bind_stdout(file_descriptor_sink(outPipe.sink, close_handle)), + bind_stderr(file_descriptor_sink(errPipe.sink, close_handle))); + + file_descriptor_source mysqlOutfd(outPipe.source, close_handle); + file_descriptor_source mysqlErrfd(errPipe.source, close_handle); + + stream<file_descriptor_source> mysqlOutStream(mysqlOutfd); + stream<file_descriptor_source> mysqlErrStream(mysqlErrfd); + + std::stringstream out; + std::stringstream err; + + copy(mysqlOutStream, out); + copy(mysqlErrStream, err); + + TC_LOG_INFO("sql.updates", "%s", out.str().c_str()); + TC_LOG_ERROR("sql.updates", "%s", err.str().c_str()); ret = wait_for_exit(c); } diff --git a/src/server/database/Updater/DBUpdater.h b/src/server/database/Updater/DBUpdater.h index a2b12bed235..c8aa5d69fbc 100644 --- a/src/server/database/Updater/DBUpdater.h +++ b/src/server/database/Updater/DBUpdater.h @@ -54,6 +54,17 @@ struct UpdateResult size_t archived; }; +class DBUpdaterUtil +{ +public: + static std::string GetMySqlCli(); + + static bool CheckExecutable(); + +private: + static std::string& corrected_path(); +}; + template <class T> class DBUpdater { @@ -79,9 +90,6 @@ public: static bool Populate(DatabaseWorkerPool<T>& pool); private: - static std::string GetMySqlCli(); - static bool CheckExecutable(); - static QueryResult Retrieve(DatabaseWorkerPool<T>& pool, std::string const& query); static void Apply(DatabaseWorkerPool<T>& pool, std::string const& query); static void ApplyFile(DatabaseWorkerPool<T>& pool, Path const& path); diff --git a/src/server/game/AI/CoreAI/UnitAI.cpp b/src/server/game/AI/CoreAI/UnitAI.cpp index 5aa6ea8ea7a..f0d9d34db69 100644 --- a/src/server/game/AI/CoreAI/UnitAI.cpp +++ b/src/server/game/AI/CoreAI/UnitAI.cpp @@ -29,7 +29,15 @@ void UnitAI::AttackStart(Unit* victim) { if (victim && me->Attack(victim, true)) + { + // Clear distracted state on attacking + if (me->HasUnitState(UNIT_STATE_DISTRACTED)) + { + me->ClearUnitState(UNIT_STATE_DISTRACTED); + me->GetMotionMaster()->Clear(); + } me->GetMotionMaster()->MoveChase(victim); + } } void UnitAI::AttackStartCaster(Unit* victim, float dist) diff --git a/src/server/game/AI/CoreAI/UnitAI.h b/src/server/game/AI/CoreAI/UnitAI.h index 1939c96dac9..c93ed0db008 100644 --- a/src/server/game/AI/CoreAI/UnitAI.h +++ b/src/server/game/AI/CoreAI/UnitAI.h @@ -255,8 +255,8 @@ class UnitAI static void FillAISpellInfo(); virtual void sGossipHello(Player* /*player*/) { } - virtual void sGossipSelect(Player* /*player*/, uint32 /*sender*/, uint32 /*action*/) { } - virtual void sGossipSelectCode(Player* /*player*/, uint32 /*sender*/, uint32 /*action*/, char const* /*code*/) { } + virtual void sGossipSelect(Player* /*player*/, uint32 /*menuId*/, uint32 /*gossipListId*/) { } + virtual void sGossipSelectCode(Player* /*player*/, uint32 /*menuId*/, uint32 /*gossipListId*/, char const* /*code*/) { } virtual void sQuestAccept(Player* /*player*/, Quest const* /*quest*/) { } virtual void sQuestSelect(Player* /*player*/, Quest const* /*quest*/) { } virtual void sQuestReward(Player* /*player*/, Quest const* /*quest*/, uint32 /*opt*/) { } diff --git a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp index 8e2a82dbfd3..7b145268d22 100644 --- a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp +++ b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp @@ -470,6 +470,7 @@ void BossAI::_Reset() if (!me->IsAlive()) return; + me->SetCombatPulseDelay(0); me->ResetLootMode(); events.Reset(); summons.DespawnAll(); @@ -500,6 +501,7 @@ void BossAI::_EnterCombat() instance->SetBossState(_bossId, IN_PROGRESS); } + me->SetCombatPulseDelay(5); me->setActive(true); DoZoneInCombat(); ScheduleTasks(); diff --git a/src/server/game/AI/SmartScripts/SmartAI.cpp b/src/server/game/AI/SmartScripts/SmartAI.cpp index 1a51bb2d897..46cf934356d 100644 --- a/src/server/game/AI/SmartScripts/SmartAI.cpp +++ b/src/server/game/AI/SmartScripts/SmartAI.cpp @@ -208,7 +208,7 @@ void SmartAI::EndPath(bool fail) if (targets->size() == 1 && GetScript()->IsPlayer((*targets->begin()))) { Player* player = (*targets->begin())->ToPlayer(); - if (!fail && player->IsAtGroupRewardDistance(me) && !player->GetCorpse()) + if (!fail && player->IsAtGroupRewardDistance(me) && !player->HasCorpse()) player->GroupEventHappens(mEscortQuestID, me); if (fail && player->GetQuestStatus(mEscortQuestID) == QUEST_STATUS_INCOMPLETE) @@ -220,7 +220,7 @@ void SmartAI::EndPath(bool fail) { Player* groupGuy = groupRef->GetSource(); - if (!fail && groupGuy->IsAtGroupRewardDistance(me) && !groupGuy->GetCorpse()) + if (!fail && groupGuy->IsAtGroupRewardDistance(me) && !groupGuy->HasCorpse()) groupGuy->AreaExploredOrEventHappens(mEscortQuestID); if (fail && groupGuy->GetQuestStatus(mEscortQuestID) == QUEST_STATUS_INCOMPLETE) groupGuy->FailQuest(mEscortQuestID); @@ -233,7 +233,7 @@ void SmartAI::EndPath(bool fail) if (GetScript()->IsPlayer((*iter))) { Player* player = (*iter)->ToPlayer(); - if (!fail && player->IsAtGroupRewardDistance(me) && !player->GetCorpse()) + if (!fail && player->IsAtGroupRewardDistance(me) && !player->HasCorpse()) player->AreaExploredOrEventHappens(mEscortQuestID); if (fail && player->GetQuestStatus(mEscortQuestID) == QUEST_STATUS_INCOMPLETE) player->FailQuest(mEscortQuestID); @@ -453,45 +453,15 @@ void SmartAI::MoveInLineOfSight(Unit* who) GetScript()->OnMoveInLineOfSight(who); - if (me->HasReactState(REACT_PASSIVE) || AssistPlayerInCombat(who)) + if (AssistPlayerInCombat(who)) return; - if (!CanAIAttack(who)) - return; - - if (!me->CanStartAttack(who, false)) - return; - - if (me->IsHostileTo(who)) - { - float fAttackRadius = me->GetAttackDistance(who); - if (me->IsWithinDistInMap(who, fAttackRadius) && me->IsWithinLOSInMap(who)) - { - if (!me->GetVictim()) - { - // Clear distracted state on combat - if (me->HasUnitState(UNIT_STATE_DISTRACTED)) - { - me->ClearUnitState(UNIT_STATE_DISTRACTED); - me->GetMotionMaster()->Clear(); - } - - AttackStart(who); - } - else/* if (me->GetMap()->IsDungeon())*/ - { - who->SetInCombatWith(me); - me->AddThreat(who, 0.0f); - } - } - } + CreatureAI::MoveInLineOfSight(who); } bool SmartAI::CanAIAttack(const Unit* /*who*/) const { - if (me->GetReactState() == REACT_PASSIVE) - return false; - return true; + return !(me->HasReactState(REACT_PASSIVE)); } bool SmartAI::AssistPlayerInCombat(Unit* who) @@ -728,12 +698,12 @@ void SmartAI::sGossipHello(Player* player) GetScript()->ProcessEventsFor(SMART_EVENT_GOSSIP_HELLO, player); } -void SmartAI::sGossipSelect(Player* player, uint32 sender, uint32 action) +void SmartAI::sGossipSelect(Player* player, uint32 menuId, uint32 gossipListId) { - GetScript()->ProcessEventsFor(SMART_EVENT_GOSSIP_SELECT, player, sender, action); + GetScript()->ProcessEventsFor(SMART_EVENT_GOSSIP_SELECT, player, menuId, gossipListId); } -void SmartAI::sGossipSelectCode(Player* /*player*/, uint32 /*sender*/, uint32 /*action*/, const char* /*code*/) { } +void SmartAI::sGossipSelectCode(Player* /*player*/, uint32 /*menuId*/, uint32 /*gossipListId*/, const char* /*code*/) { } void SmartAI::sQuestAccept(Player* player, Quest const* quest) { diff --git a/src/server/game/AI/SmartScripts/SmartAI.h b/src/server/game/AI/SmartScripts/SmartAI.h index 1e287cd5b9e..ea03a821846 100644 --- a/src/server/game/AI/SmartScripts/SmartAI.h +++ b/src/server/game/AI/SmartScripts/SmartAI.h @@ -175,8 +175,8 @@ class SmartAI : public CreatureAI void SetInvincibilityHpLevel(uint32 level) { mInvincibilityHpLevel = level; } void sGossipHello(Player* player) override; - void sGossipSelect(Player* player, uint32 sender, uint32 action) override; - void sGossipSelectCode(Player* player, uint32 sender, uint32 action, const char* code) override; + void sGossipSelect(Player* player, uint32 menuId, uint32 gossipListId) override; + void sGossipSelectCode(Player* player, uint32 menuId, uint32 gossipListId, const char* code) override; void sQuestAccept(Player* player, Quest const* quest) override; //void sQuestSelect(Player* player, Quest const* quest) override; void sQuestReward(Player* player, Quest const* quest, uint32 opt) override; diff --git a/src/server/game/AuctionHouse/AuctionHouseMgr.cpp b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp index 265eb6f38ba..1809e306a45 100644 --- a/src/server/game/AuctionHouse/AuctionHouseMgr.cpp +++ b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp @@ -61,12 +61,25 @@ AuctionHouseObject* AuctionHouseMgr::GetAuctionsMap(uint32 factionTemplateId) return &mNeutralAuctions; } +AuctionHouseObject* AuctionHouseMgr::GetAuctionsMapByHouseId(uint8 auctionHouseId) +{ + if (sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION)) + return &mNeutralAuctions; + + switch(auctionHouseId) + { + case AUCTIONHOUSE_ALLIANCE : return &mAllianceAuctions; + case AUCTIONHOUSE_HORDE : return &mHordeAuctions; + default : return &mNeutralAuctions; + } +} + uint32 AuctionHouseMgr::GetAuctionDeposit(AuctionHouseEntry const* entry, uint32 time, Item* pItem, uint32 count) { uint32 MSV = pItem->GetTemplate()->SellPrice; if (MSV <= 0) - return AH_MINIMUM_DEPOSIT; + return AH_MINIMUM_DEPOSIT * sWorld->getRate(RATE_AUCTION_DEPOSIT); float multiplier = CalculatePct(float(entry->depositPercent), 3); uint32 timeHr = (((time / 60) / 60) / 12); @@ -77,8 +90,8 @@ uint32 AuctionHouseMgr::GetAuctionDeposit(AuctionHouseEntry const* entry, uint32 TC_LOG_DEBUG("auctionHouse", "Multiplier: %f", multiplier); TC_LOG_DEBUG("auctionHouse", "Deposit: %u", deposit); - if (deposit < AH_MINIMUM_DEPOSIT) - return AH_MINIMUM_DEPOSIT; + if (deposit < AH_MINIMUM_DEPOSIT * sWorld->getRate(RATE_AUCTION_DEPOSIT)) + return AH_MINIMUM_DEPOSIT * sWorld->getRate(RATE_AUCTION_DEPOSIT); else return deposit; } @@ -342,7 +355,7 @@ void AuctionHouseMgr::LoadAuctions() continue; } - GetAuctionsMap(aItem->factionTemplateId)->AddAuction(aItem); + GetAuctionsMapByHouseId(aItem->houseId)->AddAuction(aItem); ++count; } while (result->NextRow()); @@ -385,44 +398,32 @@ void AuctionHouseMgr::Update() AuctionHouseEntry const* AuctionHouseMgr::GetAuctionHouseEntry(uint32 factionTemplateId) { - uint32 houseid = 7; // goblin auction house + uint32 houseid = AUCTIONHOUSE_NEUTRAL; // goblin auction house if (!sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION)) { // FIXME: found way for proper auctionhouse selection by another way // AuctionHouse.dbc have faction field with _player_ factions associated with auction house races. // but no easy way convert creature faction to player race faction for specific city - switch (factionTemplateId) - { - case 12: houseid = 1; break; // human - case 29: houseid = 6; break; // orc, and generic for horde - case 55: houseid = 2; break; // dwarf, and generic for alliance - case 68: houseid = 4; break; // undead - case 80: houseid = 3; break; // n-elf - case 104: houseid = 5; break; // trolls - case 120: houseid = 7; break; // booty bay, neutral - case 474: houseid = 7; break; // gadgetzan, neutral - case 855: houseid = 7; break; // everlook, neutral - case 1604: houseid = 6; break; // b-elfs, - default: // for unknown case - { - FactionTemplateEntry const* u_entry = sFactionTemplateStore.LookupEntry(factionTemplateId); - if (!u_entry) - houseid = 7; // goblin auction house - else if (u_entry->ourMask & FACTION_MASK_ALLIANCE) - houseid = 1; // human auction house - else if (u_entry->ourMask & FACTION_MASK_HORDE) - houseid = 6; // orc auction house - else - houseid = 7; // goblin auction house - break; - } - } + FactionTemplateEntry const* u_entry = sFactionTemplateStore.LookupEntry(factionTemplateId); + if (!u_entry) + houseid = AUCTIONHOUSE_NEUTRAL; // goblin auction house + else if (u_entry->ourMask & FACTION_MASK_ALLIANCE) + houseid = AUCTIONHOUSE_ALLIANCE; // human auction house + else if (u_entry->ourMask & FACTION_MASK_HORDE) + houseid = AUCTIONHOUSE_HORDE; // orc auction house + else + houseid = AUCTIONHOUSE_NEUTRAL; // goblin auction house } return sAuctionHouseStore.LookupEntry(houseid); } +AuctionHouseEntry const* AuctionHouseMgr::GetAuctionHouseEntryFromHouse(uint8 houseId) +{ + return (sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION)) ? sAuctionHouseStore.LookupEntry(AUCTIONHOUSE_NEUTRAL) : sAuctionHouseStore.LookupEntry(houseId); +} + void AuctionHouseObject::AddAuction(AuctionEntry* auction) { ASSERT(auction); @@ -687,7 +688,7 @@ void AuctionEntry::SaveToDB(SQLTransaction& trans) const { PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_AUCTION); stmt->setUInt32(0, Id); - stmt->setUInt32(1, auctioneer); + stmt->setUInt8(1, houseId); stmt->setUInt32(2, itemGUIDLow); stmt->setUInt32(3, owner); stmt->setUInt32(4, buyout); @@ -702,7 +703,7 @@ void AuctionEntry::SaveToDB(SQLTransaction& trans) const bool AuctionEntry::LoadFromDB(Field* fields) { Id = fields[0].GetUInt32(); - auctioneer = fields[1].GetUInt32(); + houseId = fields[1].GetUInt8(); itemGUIDLow = fields[2].GetUInt32(); itemEntry = fields[3].GetUInt32(); itemCount = fields[4].GetUInt32(); @@ -714,25 +715,10 @@ bool AuctionEntry::LoadFromDB(Field* fields) startbid = fields[10].GetUInt32(); deposit = fields[11].GetUInt32(); - CreatureData const* auctioneerData = sObjectMgr->GetCreatureData(auctioneer); - if (!auctioneerData) - { - TC_LOG_ERROR("misc", "Auction %u has not a existing auctioneer (GUID : %u)", Id, auctioneer); - return false; - } - - CreatureTemplate const* auctioneerInfo = sObjectMgr->GetCreatureTemplate(auctioneerData->id); - if (!auctioneerInfo) - { - TC_LOG_ERROR("misc", "Auction %u has not a existing auctioneer (GUID : %u Entry: %u)", Id, auctioneer, auctioneerData->id); - return false; - } - - factionTemplateId = auctioneerInfo->faction; - auctionHouseEntry = AuctionHouseMgr::GetAuctionHouseEntry(factionTemplateId); + auctionHouseEntry = AuctionHouseMgr::GetAuctionHouseEntryFromHouse(houseId); if (!auctionHouseEntry) { - TC_LOG_ERROR("misc", "Auction %u has auctioneer (GUID : %u Entry: %u) with wrong faction %u", Id, auctioneer, auctioneerData->id, factionTemplateId); + TC_LOG_ERROR("misc", "Auction %u has invalid house id %u", Id, houseId); return false; } diff --git a/src/server/game/AuctionHouse/AuctionHouseMgr.h b/src/server/game/AuctionHouse/AuctionHouseMgr.h index d67c8143996..f05d8583329 100644 --- a/src/server/game/AuctionHouse/AuctionHouseMgr.h +++ b/src/server/game/AuctionHouse/AuctionHouseMgr.h @@ -61,10 +61,17 @@ enum MailAuctionAnswers AUCTION_SALE_PENDING = 6 }; +enum AuctionHouses +{ + AUCTIONHOUSE_ALLIANCE = 2, + AUCTIONHOUSE_HORDE = 6, + AUCTIONHOUSE_NEUTRAL = 7 +}; + struct AuctionEntry { uint32 Id; - uint32 auctioneer; // creature low guid + uint8 houseId; uint32 itemGUIDLow; uint32 itemEntry; uint32 itemCount; @@ -76,11 +83,9 @@ struct AuctionEntry uint32 bidder; uint32 deposit; //deposit can be calculated only when creating auction AuctionHouseEntry const* auctionHouseEntry; // in AuctionHouse.dbc - uint32 factionTemplateId; // helpers - uint32 GetHouseId() const { return auctionHouseEntry->houseId; } - uint32 GetHouseFaction() const { return auctionHouseEntry->faction; } + uint8 GetHouseId() const { return houseId; } uint32 GetAuctionCut() const; uint32 GetAuctionOutBid() const; bool BuildAuctionInfo(WorldPacket & data) const; @@ -148,6 +153,7 @@ class AuctionHouseMgr typedef std::unordered_map<uint32, Item*> ItemMap; AuctionHouseObject* GetAuctionsMap(uint32 factionTemplateId); + AuctionHouseObject* GetAuctionsMapByHouseId(uint8 auctionHouseId); AuctionHouseObject* GetBidsMap(uint32 factionTemplateId); Item* GetAItem(uint32 id) @@ -169,7 +175,7 @@ class AuctionHouseMgr static uint32 GetAuctionDeposit(AuctionHouseEntry const* entry, uint32 time, Item* pItem, uint32 count); static AuctionHouseEntry const* GetAuctionHouseEntry(uint32 factionTemplateId); - + static AuctionHouseEntry const* GetAuctionHouseEntryFromHouse(uint8 houseId); public: //load first auction items, because of check if item exists, when loading diff --git a/src/server/game/AuctionHouseBot/AuctionHouseBotSeller.cpp b/src/server/game/AuctionHouseBot/AuctionHouseBotSeller.cpp index e6164b77005..22dc5bb0bd0 100644 --- a/src/server/game/AuctionHouseBot/AuctionHouseBotSeller.cpp +++ b/src/server/game/AuctionHouseBot/AuctionHouseBotSeller.cpp @@ -906,15 +906,17 @@ void AuctionBotSeller::AddNewAuctions(SellerConfiguration& config) items = sAuctionBotConfig->GetItemPerCycleNormal(); uint32 houseid = 0; - uint32 auctioneer = 0; switch (config.GetHouseType()) { case AUCTION_HOUSE_ALLIANCE: - houseid = 1; auctioneer = 79707; break; + houseid = AUCTIONHOUSE_ALLIANCE; + break; case AUCTION_HOUSE_HORDE: - houseid = 6; auctioneer = 4656; break; + houseid = AUCTIONHOUSE_HORDE; + break; default: - houseid = 7; auctioneer = 23442; break; + houseid = AUCTIONHOUSE_NEUTRAL; + break; } AuctionHouseEntry const* ahEntry = sAuctionHouseStore.LookupEntry(houseid); @@ -995,7 +997,7 @@ void AuctionBotSeller::AddNewAuctions(SellerConfiguration& config) auctionEntry->itemEntry = item->GetEntry(); auctionEntry->startbid = bidPrice; auctionEntry->buyout = buyoutPrice; - auctionEntry->auctioneer = auctioneer; + auctionEntry->houseId = houseid; auctionEntry->bidder = 0; auctionEntry->bid = 0; auctionEntry->deposit = sAuctionMgr->GetAuctionDeposit(ahEntry, etime, item, stackCount); diff --git a/src/server/game/Battlefield/Battlefield.cpp b/src/server/game/Battlefield/Battlefield.cpp index 46181011719..d22ac0ea5d5 100644 --- a/src/server/game/Battlefield/Battlefield.cpp +++ b/src/server/game/Battlefield/Battlefield.cpp @@ -695,7 +695,7 @@ void BfGraveyard::Resurrect() player->CastSpell(player, 6962, true); player->CastSpell(player, SPELL_SPIRIT_HEAL_MANA, true); - sObjectAccessor->ConvertCorpseForPlayer(player->GetGUID()); + player->SpawnCorpseBones(false); } m_ResurrectQueue.clear(); diff --git a/src/server/game/Battlegrounds/Battleground.cpp b/src/server/game/Battlegrounds/Battleground.cpp index 2b4d5a4df5e..ad8a7b9e729 100644 --- a/src/server/game/Battlegrounds/Battleground.cpp +++ b/src/server/game/Battlegrounds/Battleground.cpp @@ -367,7 +367,7 @@ inline void Battleground::_ProcessResurrect(uint32 diff) player->ResurrectPlayer(1.0f); player->CastSpell(player, 6962, true); player->CastSpell(player, SPELL_SPIRIT_HEAL_MANA, true); - sObjectAccessor->ConvertCorpseForPlayer(*itr); + player->SpawnCorpseBones(false); } m_ResurrectQueue.clear(); } @@ -889,8 +889,11 @@ void Battleground::RemovePlayerAtLeave(ObjectGuid guid, bool Transport, bool Sen player->SpawnCorpseBones(); } } - else // try to resurrect the offline player. If he is alive nothing will happen - sObjectAccessor->ConvertCorpseForPlayer(guid); + else + { + SQLTransaction trans(nullptr); + Player::OfflineResurrect(guid, trans); + } RemovePlayer(player, guid, team); // BG subclass specific code diff --git a/src/server/game/Entities/Corpse/Corpse.cpp b/src/server/game/Entities/Corpse/Corpse.cpp index 11c0885beef..360cfdf1ae3 100644 --- a/src/server/game/Entities/Corpse/Corpse.cpp +++ b/src/server/game/Entities/Corpse/Corpse.cpp @@ -22,6 +22,7 @@ #include "UpdateMask.h" #include "ObjectAccessor.h" #include "DatabaseEnv.h" +#include "World.h" Corpse::Corpse(CorpseType type) : WorldObject(type != CORPSE_BONES), m_type(type) { @@ -138,10 +139,14 @@ void Corpse::DeleteBonesFromWorld() void Corpse::DeleteFromDB(SQLTransaction& trans) { - PreparedStatement* stmt = NULL; - stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CORPSE); - stmt->setUInt32(0, GetOwnerGUID().GetCounter()); - trans->Append(stmt); + DeleteFromDB(GetOwnerGUID(), trans); +} + +void Corpse::DeleteFromDB(ObjectGuid const& ownerGuid, SQLTransaction& trans) +{ + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CORPSE); + stmt->setUInt32(0, ownerGuid.GetCounter()); + CharacterDatabase.ExecuteOrAppend(trans, stmt); } bool Corpse::LoadCorpseFromDB(uint32 guid, Field* fields) @@ -193,6 +198,10 @@ bool Corpse::LoadCorpseFromDB(uint32 guid, Field* fields) bool Corpse::IsExpired(time_t t) const { + // Deleted character + if (!sWorld->GetCharacterNameData(GetOwnerGUID())) + return true; + if (m_type == CORPSE_BONES) return m_time < t - 60 * MINUTE; else diff --git a/src/server/game/Entities/Corpse/Corpse.h b/src/server/game/Entities/Corpse/Corpse.h index c3a693d30d3..eeb22a064ba 100644 --- a/src/server/game/Entities/Corpse/Corpse.h +++ b/src/server/game/Entities/Corpse/Corpse.h @@ -63,6 +63,7 @@ class Corpse : public WorldObject, public GridObject<Corpse> void DeleteBonesFromWorld(); void DeleteFromDB(SQLTransaction& trans); + static void DeleteFromDB(ObjectGuid const& ownerGuid, SQLTransaction& trans); ObjectGuid GetOwnerGUID() const { return GetGuidValue(CORPSE_FIELD_OWNER); } diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 7fe3628f893..c8a258ad386 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -138,7 +138,7 @@ bool ForcedDespawnDelayEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/) Creature::Creature(bool isWorldObject): Unit(isWorldObject), MapObject(), m_groupLootTimer(0), lootingGroupLowGUID(0), m_PlayerDamageReq(0), m_lootRecipient(), m_lootRecipientGroup(0), _skinner(), _pickpocketLootRestore(0), m_corpseRemoveTime(0), m_respawnTime(0), -m_respawnDelay(300), m_corpseDelay(60), m_respawnradius(0.0f), m_reactState(REACT_AGGRESSIVE), +m_respawnDelay(300), m_corpseDelay(60), m_respawnradius(0.0f), m_combatPulseTime(0), m_combatPulseDelay(0), m_reactState(REACT_AGGRESSIVE), m_defaultMovementType(IDLE_MOTION_TYPE), m_spawnId(0), m_equipmentId(0), m_originalEquipmentId(0), m_AlreadyCallAssistance(false), m_AlreadySearchedAssistance(false), m_regenHealth(true), m_AI_locked(false), m_meleeDamageSchoolMask(SPELL_SCHOOL_MASK_NORMAL), m_originalEntry(0), m_homePosition(), m_transportHomePosition(), m_creatureInfo(NULL), m_creatureData(NULL), m_waypointID(0), m_path_id(0), m_formation(NULL) @@ -539,10 +539,44 @@ void Creature::Update(uint32 diff) LastCharmerGUID.Clear(); } + // if periodic combat pulse is enabled and we are both in combat and in a dungeon, do this now + if (m_combatPulseDelay > 0 && IsInCombat() && GetMap()->IsDungeon()) + { + if (diff > m_combatPulseTime) + m_combatPulseTime = 0; + else + m_combatPulseTime -= diff; + + if (m_combatPulseTime == 0) + { + Map::PlayerList const &players = GetMap()->GetPlayers(); + if (!players.isEmpty()) + for (Map::PlayerList::const_iterator it = players.begin(); it != players.end(); ++it) + { + if (Player* player = it->GetSource()) + { + if (player->IsGameMaster()) + continue; + + if (player->IsAlive() && this->IsHostileTo(player)) + { + if (CanHaveThreatList()) + AddThreat(player, 0.0f); + this->SetInCombatWith(player); + player->SetInCombatWith(this); + } + } + } + + m_combatPulseTime = m_combatPulseDelay * IN_MILLISECONDS; + } + } + if (!IsInEvadeMode() && IsAIEnabled) { // do not allow the AI to be changed during update m_AI_locked = true; + i_AI->UpdateAI(diff); m_AI_locked = false; } diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index 3e381063772..79832b70f52 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -601,6 +601,14 @@ class Creature : public Unit, public GridObject<Creature>, public MapObject float GetRespawnRadius() const { return m_respawnradius; } void SetRespawnRadius(float dist) { m_respawnradius = dist; } + uint32 GetCombatPulseDelay() const { return m_combatPulseDelay; } + void SetCombatPulseDelay(uint32 delay) // (secs) interval at which the creature pulses the entire zone into combat (only works in dungeons) + { + m_combatPulseDelay = delay; + if (m_combatPulseTime == 0 || m_combatPulseTime > delay) + m_combatPulseTime = delay; + } + uint32 m_groupLootTimer; // (msecs)timer used for group loot uint32 lootingGroupLowGUID; // used to find group which is looting corpse @@ -686,6 +694,8 @@ class Creature : public Unit, public GridObject<Creature>, public MapObject uint32 m_respawnDelay; // (secs) delay between corpse disappearance and respawning uint32 m_corpseDelay; // (secs) delay between death and corpse disappearance float m_respawnradius; + uint32 m_combatPulseTime; // (msecs) remaining time for next zone-in-combat pulse + uint32 m_combatPulseDelay; // (secs) how often the creature puts the entire zone in combat (only works in dungeons) ReactStates m_reactState; // for AI, not charmInfo void RegenerateMana(); diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index f5af3556332..21a65440a55 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -1743,7 +1743,7 @@ void WorldObject::SendObjectDeSpawnAnim(ObjectGuid guid) void WorldObject::SetMap(Map* map) { ASSERT(map); - ASSERT(!IsInWorld() || GetTypeId() == TYPEID_CORPSE); + ASSERT(!IsInWorld()); if (m_currMap == map) // command add npc: first create, than loadfromdb return; if (m_currMap) diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index b5d3ebaa562..1be3c2454b2 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -2022,6 +2022,8 @@ bool Player::BuildEnumData(PreparedQueryResult result, WorldPacket* data) uint32 charFlags = 0; uint32 playerFlags = fields[14].GetUInt32(); + if (atLoginFlags & AT_LOGIN_RESURRECT) + playerFlags &= ~PLAYER_FLAGS_GHOST; if (playerFlags & PLAYER_FLAGS_HIDE_HELM) charFlags |= CHARACTER_FLAG_HIDE_HELM; if (playerFlags & PLAYER_FLAGS_HIDE_CLOAK) @@ -4537,10 +4539,6 @@ void Player::DeleteFromDB(ObjectGuid playerguid, uint32 accountId, bool updateRe charDelete_method = CHAR_DELETE_REMOVE; } - // convert corpse to bones if exist (to prevent exiting Corpse in World without DB entry) - // bones will be deleted by corpse/bones deleting thread shortly - sObjectAccessor->ConvertCorpseForPlayer(playerguid); - if (uint32 guildId = GetGuildIdFromDB(playerguid)) if (Guild* guild = sGuildMgr->GetGuildById(guildId)) guild->DeleteMember(playerguid, false, false, true); @@ -4841,6 +4839,8 @@ void Player::DeleteFromDB(ObjectGuid playerguid, uint32 accountId, bool updateRe stmt->setUInt32(0, guid); trans->Append(stmt); + Corpse::DeleteFromDB(playerguid, trans); + CharacterDatabase.CommitTransaction(trans); break; } @@ -4940,18 +4940,17 @@ void Player::BuildPlayerRepop() // there must be SMSG.FORCE_RUN_SPEED_CHANGE, SMSG.FORCE_SWIM_SPEED_CHANGE, SMSG.MOVE_WATER_WALK // there must be SMSG.STOP_MIRROR_TIMER - // there we must send 888 opcode - // the player cannot have a corpse already, only bones which are not returned by GetCorpse - if (GetCorpse()) + // the player cannot have a corpse already on current map, only bones which are not returned by GetCorpse + WorldLocation corpseLocation = GetCorpseLocation(); + if (corpseLocation.GetMapId() == GetMapId()) { TC_LOG_ERROR("entities.player", "BuildPlayerRepop: player %s(%d) already has a corpse", GetName().c_str(), GetGUID().GetCounter()); return; } // create a corpse and place it at the player's location - CreateCorpse(); - Corpse* corpse = GetCorpse(); + Corpse* corpse = CreateCorpse(); if (!corpse) { TC_LOG_ERROR("entities.player", "Error creating corpse for Player %s [%u]", GetName().c_str(), GetGUID().GetCounter()); @@ -5093,7 +5092,16 @@ void Player::KillPlayer() UpdateObjectVisibility(); } -void Player::CreateCorpse() +void Player::OfflineResurrect(ObjectGuid const& guid, SQLTransaction& trans) +{ + Corpse::DeleteFromDB(guid, trans); + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ADD_AT_LOGIN_FLAG); + stmt->setUInt16(0, uint16(AT_LOGIN_RESURRECT)); + stmt->setUInt64(1, guid.GetCounter()); + CharacterDatabase.ExecuteOrAppend(trans, stmt); +} + +Corpse* Player::CreateCorpse() { // prevent existence 2 corpse for player SpawnCorpseBones(); @@ -5106,9 +5114,11 @@ void Player::CreateCorpse() if (!corpse->Create(GetMap()->GenerateLowGuid<HighGuid::Corpse>(), this)) { delete corpse; - return; + return nullptr; } + _corpseLocation.WorldRelocate(*this); + _uf = GetUInt32Value(UNIT_FIELD_BYTES_0); _pb = GetUInt32Value(PLAYER_BYTES); _pb2 = GetUInt32Value(PLAYER_BYTES_2); @@ -5159,19 +5169,21 @@ void Player::CreateCorpse() corpse->SaveToDB(); // register for player, but not show - sObjectAccessor->AddCorpse(corpse); + GetMap()->AddCorpse(corpse); + return corpse; } -void Player::SpawnCorpseBones() +void Player::SpawnCorpseBones(bool triggerSave /*= true*/) { - if (sObjectAccessor->ConvertCorpseForPlayer(GetGUID())) - if (!GetSession()->PlayerLogoutWithSave()) // at logout we will already store the player - SaveToDB(); // prevent loading as ghost without corpse + _corpseLocation.WorldRelocate(); + if (GetMap()->ConvertCorpseToBones(GetGUID())) + if (triggerSave && !GetSession()->PlayerLogoutWithSave()) // at logout we will already store the player + SaveToDB(); // prevent loading as ghost without corpse } Corpse* Player::GetCorpse() const { - return sObjectAccessor->GetCorpseForPlayerGUID(GetGUID()); + return GetMap()->GetCorpseByPlayer(GetGUID()); } void Player::DurabilityLossAll(double percent, bool inventory) @@ -7598,6 +7610,20 @@ void Player::DuelComplete(DuelCompleteType type) duel->opponent->SetGuidValue(PLAYER_DUEL_ARBITER, ObjectGuid::Empty); duel->opponent->SetUInt32Value(PLAYER_DUEL_TEAM, 0); + if (sWorld->getBoolConfig(CONFIG_RESET_COOLDOWN_AFTER_DUEL) && + type != DUEL_INTERRUPTED) + { + if (!HasCoolDownBeforeDuel()) + RemoveArenaSpellCooldowns(true); + else + ChatHandler(GetSession()).PSendSysMessage(LANG_COOLDOWN_NOT_RESET_AFTER_DUEL); + + if (!duel->opponent->HasCoolDownBeforeDuel()) + duel->opponent->RemoveArenaSpellCooldowns(true); + else + ChatHandler(duel->opponent->GetSession()).PSendSysMessage(LANG_COOLDOWN_NOT_RESET_AFTER_DUEL); + } + delete duel->opponent->duel; duel->opponent->duel = NULL; delete duel; @@ -8580,9 +8606,11 @@ void Player::RemovedInsignia(Player* looterPlr) RepopAtGraveyard(); } + _corpseLocation.WorldRelocate(); + // We have to convert player corpse to bones, not to be able to resurrect there // SpawnCorpseBones isn't handy, 'cos it saves player while he in BG - Corpse* bones = sObjectAccessor->ConvertCorpseForPlayer(GetGUID(), true); + Corpse* bones = GetMap()->ConvertCorpseToBones(GetGUID(), true); if (!bones) return; @@ -17910,18 +17938,24 @@ void Player::_LoadGlyphAuras() } } -void Player::LoadCorpse() +void Player::LoadCorpse(PreparedQueryResult result) { - if (IsAlive()) - sObjectAccessor->ConvertCorpseForPlayer(GetGUID()); - else + if (IsAlive() || HasAtLoginFlag(AT_LOGIN_RESURRECT)) + SpawnCorpseBones(false); + + if (!IsAlive()) { - if (Corpse* corpse = GetCorpse()) - ApplyModFlag(PLAYER_FIELD_BYTES, PLAYER_FIELD_BYTE_RELEASE_TIMER, corpse && !sMapStore.LookupEntry(corpse->GetMapId())->Instanceable()); + if (result && !HasAtLoginFlag(AT_LOGIN_RESURRECT)) + { + Field* fields = result->Fetch(); + _corpseLocation.WorldRelocate(fields[0].GetUInt16(), fields[1].GetFloat(), fields[2].GetFloat(), fields[3].GetFloat(), fields[4].GetFloat()); + ApplyModFlag(PLAYER_FIELD_BYTES, PLAYER_FIELD_BYTE_RELEASE_TIMER, !sMapStore.LookupEntry(_corpseLocation.GetMapId())->Instanceable()); + } else - //Prevent Dead Player login without corpse ResurrectPlayer(0.5f); } + + RemoveAtLoginFlag(AT_LOGIN_RESURRECT); } void Player::_LoadInventory(PreparedQueryResult result, uint32 timeDiff) diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 94d0fa5697c..0c1fdd8f03d 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -27,6 +27,7 @@ #include "PetDefines.h" #include "QuestDef.h" #include "SpellMgr.h" +#include "SpellHistory.h" #include "Unit.h" #include <limits> @@ -284,7 +285,7 @@ struct PvPInfo struct DuelInfo { - DuelInfo() : initiator(NULL), opponent(NULL), startTimer(0), startTime(0), outOfBound(0), isMounted(false) { } + DuelInfo() : initiator(NULL), opponent(NULL), startTimer(0), startTime(0), outOfBound(0), isMounted(false), hasCoolDownBeforeDuel(false) { } Player* initiator; Player* opponent; @@ -292,6 +293,7 @@ struct DuelInfo time_t startTime; time_t outOfBound; bool isMounted; + bool hasCoolDownBeforeDuel; }; struct Areas @@ -503,15 +505,16 @@ enum PlayerExtraFlags // 2^n values enum AtLoginFlags { - AT_LOGIN_NONE = 0x00, - AT_LOGIN_RENAME = 0x01, - AT_LOGIN_RESET_SPELLS = 0x02, - AT_LOGIN_RESET_TALENTS = 0x04, - AT_LOGIN_CUSTOMIZE = 0x08, - AT_LOGIN_RESET_PET_TALENTS = 0x10, - AT_LOGIN_FIRST = 0x20, - AT_LOGIN_CHANGE_FACTION = 0x40, - AT_LOGIN_CHANGE_RACE = 0x80 + AT_LOGIN_NONE = 0x000, + AT_LOGIN_RENAME = 0x001, + AT_LOGIN_RESET_SPELLS = 0x002, + AT_LOGIN_RESET_TALENTS = 0x004, + AT_LOGIN_CUSTOMIZE = 0x008, + AT_LOGIN_RESET_PET_TALENTS = 0x010, + AT_LOGIN_FIRST = 0x020, + AT_LOGIN_CHANGE_FACTION = 0x040, + AT_LOGIN_CHANGE_RACE = 0x080, + AT_LOGIN_RESURRECT = 0x100, }; typedef std::map<uint32, QuestStatusData> QuestStatusMap; @@ -819,6 +822,7 @@ enum PlayerLoginQueryIndex PLAYER_LOGIN_QUERY_LOAD_INSTANCE_LOCK_TIMES = 30, PLAYER_LOGIN_QUERY_LOAD_SEASONAL_QUEST_STATUS = 31, PLAYER_LOGIN_QUERY_LOAD_MONTHLY_QUEST_STATUS = 32, + PLAYER_LOGIN_QUERY_LOAD_CORPSE_LOCATION = 33, MAX_PLAYER_LOGIN_QUERY }; @@ -1343,7 +1347,7 @@ class Player : public Unit, public GridObject<Player> void AddItemDurations(Item* item); void RemoveItemDurations(Item* item); void SendItemDurations(); - void LoadCorpse(); + void LoadCorpse(PreparedQueryResult result); void LoadPet(); bool AddItem(uint32 itemId, uint32 count); @@ -1835,17 +1839,20 @@ class Player : public Unit, public GridObject<Player> bool UpdatePosition(const Position &pos, bool teleport = false) { return UpdatePosition(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation(), teleport); } void UpdateUnderwaterState(Map* m, float x, float y, float z) override; - void SendMessageToSet(WorldPacket* data, bool self) override {SendMessageToSetInRange(data, GetVisibilityRange(), self); }// overwrite Object::SendMessageToSet - void SendMessageToSetInRange(WorldPacket* data, float fist, bool self) override;// overwrite Object::SendMessageToSetInRange + void SendMessageToSet(WorldPacket* data, bool self) override { SendMessageToSetInRange(data, GetVisibilityRange(), self); } + void SendMessageToSetInRange(WorldPacket* data, float dist, bool self) override; void SendMessageToSetInRange(WorldPacket* data, float dist, bool self, bool own_team_only); void SendMessageToSet(WorldPacket* data, Player const* skipped_rcvr) override; void SendTeleportAckPacket(); Corpse* GetCorpse() const; - void SpawnCorpseBones(); - void CreateCorpse(); + void SpawnCorpseBones(bool triggerSave = true); + Corpse* CreateCorpse(); void KillPlayer(); + static void OfflineResurrect(ObjectGuid const& guid, SQLTransaction& trans); + bool HasCorpse() const { return _corpseLocation.GetMapId() != MAPID_INVALID; } + WorldLocation GetCorpseLocation() const { return _corpseLocation; } uint32 GetResurrectionSpellId(); void ResurrectPlayer(float restore_percent, bool applySickness = false); void BuildPlayerRepop(); @@ -1877,7 +1884,7 @@ class Player : public Unit, public GridObject<Player> void UpdateWeaponSkill (WeaponAttackType attType); void UpdateCombatSkills(Unit* victim, WeaponAttackType attType, bool defence); - void SetSkill(uint16 id, uint16 step, uint16 currVal, uint16 maxVal); + void SetSkill(uint16 id, uint16 step, uint16 newVal, uint16 maxVal); uint16 GetMaxSkillValue(uint32 skill) const; // max + perm. bonus + temp bonus uint16 GetPureMaxSkillValue(uint32 skill) const; // max uint16 GetSkillValue(uint32 skill) const; // skill value + perm. bonus + temp bonus @@ -1941,6 +1948,9 @@ class Player : public Unit, public GridObject<Player> void SetHonorPoints(uint32 value); void SetArenaPoints(uint32 value); + bool HasCoolDownBeforeDuel() const { return duel->hasCoolDownBeforeDuel; } + void UpdateHasCoolDownBeforeDuel() { duel->hasCoolDownBeforeDuel = GetSpellHistory()->GetArenaCooldownsSize() > 0; } + //End of PvP System void SetDrunkValue(uint8 newDrunkValue, uint32 itemId = 0); @@ -2624,6 +2634,8 @@ class Player : public Unit, public GridObject<Player> uint32 _pendingBindTimer; uint32 _activeCheats; + + WorldLocation _corpseLocation; }; void AddItemsSetItem(Player* player, Item* item); diff --git a/src/server/game/Entities/Transport/Transport.cpp b/src/server/game/Entities/Transport/Transport.cpp index 236675a7d29..144805b88b5 100644 --- a/src/server/game/Entities/Transport/Transport.cpp +++ b/src/server/game/Entities/Transport/Transport.cpp @@ -100,7 +100,6 @@ bool Transport::Create(uint32 guidlow, uint32 entry, uint32 mapid, float x, floa void Transport::CleanupsBeforeDelete(bool finalCleanup /*= true*/) { - HashMapHolder<Transport>::Remove(this); UnloadStaticPassengers(); while (!_passengers.empty()) { diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index f5bcd8f23e8..f085bab165a 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -16975,6 +16975,12 @@ void Unit::_EnterVehicle(Vehicle* vehicle, int8 seatId, AuraApplication const* a } } + // If vehicle flag for fixed position set (cannons), or if the following hardcoded units, then set state rooted + // 30236 | Argent Cannon + // 39759 | Tankbuster Cannon + if ((vehicle->GetVehicleInfo()->m_flags & VEHICLE_FLAG_FIXED_POSITION) || vehicle->GetBase()->GetEntry() == 30236 || vehicle->GetBase()->GetEntry() == 39759) + SetControlled(true, UNIT_STATE_ROOT); + ASSERT(!m_vehicle); (void)vehicle->AddPassenger(this, seatId); } diff --git a/src/server/game/Entities/Vehicle/Vehicle.cpp b/src/server/game/Entities/Vehicle/Vehicle.cpp index fcb74424eb1..982b931fd4f 100755 --- a/src/server/game/Entities/Vehicle/Vehicle.cpp +++ b/src/server/game/Entities/Vehicle/Vehicle.cpp @@ -192,6 +192,12 @@ void Vehicle::ApplyAllImmunities() _me->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN, true); } + // If vehicle flag for fixed position set (cannons), or if the following hardcoded units, then set state rooted + // 30236 | Argent Cannon + // 39759 | Tankbuster Cannon + if ((GetVehicleInfo()->m_flags & VEHICLE_FLAG_FIXED_POSITION) || GetBase()->GetEntry() == 30236 || GetBase()->GetEntry() == 39759) + _me->SetControlled(true, UNIT_STATE_ROOT); + // Different immunities for vehicles goes below switch (GetVehicleInfo()->m_ID) { diff --git a/src/server/game/Entities/Vehicle/VehicleDefines.h b/src/server/game/Entities/Vehicle/VehicleDefines.h index c00bc429f38..3726e9ddf8f 100644 --- a/src/server/game/Entities/Vehicle/VehicleDefines.h +++ b/src/server/game/Entities/Vehicle/VehicleDefines.h @@ -44,7 +44,8 @@ enum VehicleFlags VEHICLE_FLAG_FULLSPEEDPITCHING = 0x00000020, // Sets MOVEFLAG2_FULLSPEEDPITCHING VEHICLE_FLAG_CUSTOM_PITCH = 0x00000040, // If set use pitchMin and pitchMax from DBC, otherwise pitchMin = -pi/2, pitchMax = pi/2 VEHICLE_FLAG_ADJUST_AIM_ANGLE = 0x00000400, // Lua_IsVehicleAimAngleAdjustable - VEHICLE_FLAG_ADJUST_AIM_POWER = 0x00000800 // Lua_IsVehicleAimPowerAdjustable + VEHICLE_FLAG_ADJUST_AIM_POWER = 0x00000800, // Lua_IsVehicleAimPowerAdjustable + VEHICLE_FLAG_FIXED_POSITION = 0x00200000 // Used for cannons, when they should be rooted }; enum VehicleSpells diff --git a/src/server/game/Globals/ObjectAccessor.cpp b/src/server/game/Globals/ObjectAccessor.cpp index f5e9b6bb5b7..ca974ba9a37 100644 --- a/src/server/game/Globals/ObjectAccessor.cpp +++ b/src/server/game/Globals/ObjectAccessor.cpp @@ -34,10 +34,6 @@ #include <boost/thread/shared_mutex.hpp> #include <boost/thread/locks.hpp> -ObjectAccessor::ObjectAccessor() { } - -ObjectAccessor::~ObjectAccessor() { } - WorldObject* ObjectAccessor::GetWorldObject(WorldObject const& p, ObjectGuid const& guid) { switch (guid.GetHigh()) @@ -210,6 +206,11 @@ Player* ObjectAccessor::FindConnectedPlayerByName(std::string const& name) return NULL; } +HashMapHolder<Player>::MapType const& ObjectAccessor::GetPlayers() +{ + return HashMapHolder<Player>::GetContainer(); +} + void ObjectAccessor::SaveAllPlayers() { boost::shared_lock<boost::shared_mutex> lock(*HashMapHolder<Player>::GetLock()); @@ -219,185 +220,6 @@ void ObjectAccessor::SaveAllPlayers() itr->second->SaveToDB(); } -Corpse* ObjectAccessor::GetCorpseForPlayerGUID(ObjectGuid const& guid) -{ - boost::shared_lock<boost::shared_mutex> lock(_corpseLock); - - Player2CorpsesMapType::iterator iter = i_player2corpse.find(guid); - if (iter == i_player2corpse.end()) - return NULL; - - ASSERT(iter->second->GetType() != CORPSE_BONES); - - return iter->second; -} - -void ObjectAccessor::RemoveCorpse(Corpse* corpse) -{ - ASSERT(corpse && corpse->GetType() != CORPSE_BONES); - - boost::upgrade_lock<boost::shared_mutex> lock(_corpseLock); - - /// @todo more works need to be done for corpse and other world object - if (Map* map = corpse->FindMap()) - { - corpse->DestroyForNearbyPlayers(); - if (corpse->IsInGrid()) - map->RemoveFromMap(corpse, false); - else - { - corpse->RemoveFromWorld(); - corpse->ResetMap(); - } - } - else - corpse->RemoveFromWorld(); - - // Critical section - { - boost::upgrade_to_unique_lock<boost::shared_mutex> uniqueLock(lock); - - Player2CorpsesMapType::iterator iter = i_player2corpse.find(corpse->GetOwnerGUID()); - if (iter == i_player2corpse.end()) /// @todo Fix this - return; - - // build mapid*cellid -> guid_set map - CellCoord cellCoord = Trinity::ComputeCellCoord(corpse->GetPositionX(), corpse->GetPositionY()); - sObjectMgr->DeleteCorpseCellData(corpse->GetMapId(), cellCoord.GetId(), corpse->GetOwnerGUID().GetCounter()); - - i_player2corpse.erase(iter); - } -} - -void ObjectAccessor::AddCorpse(Corpse* corpse) -{ - ASSERT(corpse && corpse->GetType() != CORPSE_BONES); - - // Critical section - { - boost::unique_lock<boost::shared_mutex> lock(_corpseLock); - - ASSERT(i_player2corpse.find(corpse->GetOwnerGUID()) == i_player2corpse.end()); - i_player2corpse[corpse->GetOwnerGUID()] = corpse; - - // build mapid*cellid -> guid_set map - CellCoord cellCoord = Trinity::ComputeCellCoord(corpse->GetPositionX(), corpse->GetPositionY()); - sObjectMgr->AddCorpseCellData(corpse->GetMapId(), cellCoord.GetId(), corpse->GetOwnerGUID().GetCounter(), corpse->GetInstanceId()); - } -} - -void ObjectAccessor::AddCorpsesToGrid(GridCoord const& gridpair, GridType& grid, Map* map) -{ - boost::shared_lock<boost::shared_mutex> lock(_corpseLock); - - for (Player2CorpsesMapType::iterator iter = i_player2corpse.begin(); iter != i_player2corpse.end(); ++iter) - { - // We need this check otherwise a corpose may be added to a grid twice - if (iter->second->IsInGrid()) - continue; - - if (iter->second->GetGridCoord() == gridpair) - { - // verify, if the corpse in our instance (add only corpses which are) - if (map->Instanceable()) - { - if (iter->second->GetInstanceId() == map->GetInstanceId()) - grid.AddWorldObject(iter->second); - } - else - grid.AddWorldObject(iter->second); - } - } -} - -Corpse* ObjectAccessor::ConvertCorpseForPlayer(ObjectGuid const& player_guid, bool insignia /*=false*/) -{ - Corpse* corpse = GetCorpseForPlayerGUID(player_guid); - if (!corpse) - { - //in fact this function is called from several places - //even when player doesn't have a corpse, not an error - return NULL; - } - - TC_LOG_DEBUG("misc", "Deleting Corpse and spawned bones."); - - // Map can be NULL - Map* map = corpse->FindMap(); - - // remove corpse from player_guid -> corpse map and from current map - RemoveCorpse(corpse); - - // remove corpse from DB - SQLTransaction trans = CharacterDatabase.BeginTransaction(); - corpse->DeleteFromDB(trans); - CharacterDatabase.CommitTransaction(trans); - - Corpse* bones = NULL; - // create the bones only if the map and the grid is loaded at the corpse's location - // ignore bones creating option in case insignia - - if (map && (insignia || - (map->IsBattlegroundOrArena() ? sWorld->getBoolConfig(CONFIG_DEATH_BONES_BG_OR_ARENA) : sWorld->getBoolConfig(CONFIG_DEATH_BONES_WORLD))) && - !map->IsRemovalGrid(corpse->GetPositionX(), corpse->GetPositionY())) - { - // Create bones, don't change Corpse - bones = new Corpse; - bones->Create(corpse->GetGUID().GetCounter(), map); - - for (uint8 i = OBJECT_FIELD_TYPE + 1; i < CORPSE_END; ++i) // don't overwrite guid and object type - bones->SetUInt32Value(i, corpse->GetUInt32Value(i)); - - bones->SetGridCoord(corpse->GetGridCoord()); - // bones->m_time = m_time; // don't overwrite time - // bones->m_type = m_type; // don't overwrite type - bones->Relocate(corpse->GetPositionX(), corpse->GetPositionY(), corpse->GetPositionZ(), corpse->GetOrientation()); - bones->SetPhaseMask(corpse->GetPhaseMask(), false); - - bones->SetUInt32Value(CORPSE_FIELD_FLAGS, CORPSE_FLAG_UNK2 | CORPSE_FLAG_BONES); - bones->SetGuidValue(CORPSE_FIELD_OWNER, ObjectGuid::Empty); - - for (uint8 i = 0; i < EQUIPMENT_SLOT_END; ++i) - { - if (corpse->GetUInt32Value(CORPSE_FIELD_ITEM + i)) - bones->SetUInt32Value(CORPSE_FIELD_ITEM + i, 0); - } - - // add bones in grid store if grid loaded where corpse placed - map->AddToMap(bones); - } - - // all references to the corpse should be removed at this point - delete corpse; - - return bones; -} - -void ObjectAccessor::RemoveOldCorpses() -{ - time_t now = time(NULL); - Player2CorpsesMapType::iterator next; - for (Player2CorpsesMapType::iterator itr = i_player2corpse.begin(); itr != i_player2corpse.end(); itr = next) - { - next = itr; - ++next; - - if (!itr->second->IsExpired(now)) - continue; - - ConvertCorpseForPlayer(itr->first); - } -} - -void ObjectAccessor::UnloadAll() -{ - for (Player2CorpsesMapType::const_iterator itr = i_player2corpse.begin(); itr != i_player2corpse.end(); ++itr) - { - itr->second->RemoveFromWorld(); - delete itr->second; - } -} - /// Define the static members of HashMapHolder template <class T> typename HashMapHolder<T>::MapType HashMapHolder<T>::_objectMap; diff --git a/src/server/game/Globals/ObjectAccessor.h b/src/server/game/Globals/ObjectAccessor.h index 54e2a7298ef..d5fb8564b96 100644 --- a/src/server/game/Globals/ObjectAccessor.h +++ b/src/server/game/Globals/ObjectAccessor.h @@ -85,85 +85,47 @@ class HashMapHolder static MapType _objectMap; }; -class ObjectAccessor +namespace ObjectAccessor { - private: - ObjectAccessor(); - ~ObjectAccessor(); - ObjectAccessor(const ObjectAccessor&); - ObjectAccessor& operator=(const ObjectAccessor&); - - public: - /// @todo: Override these template functions for each holder type and add assertions - - static ObjectAccessor* instance() - { - static ObjectAccessor instance; - return &instance; - } - - // these functions return objects only if in map of specified object - static WorldObject* GetWorldObject(WorldObject const&, ObjectGuid const&); - static Object* GetObjectByTypeMask(WorldObject const&, ObjectGuid const&, uint32 typemask); - static Corpse* GetCorpse(WorldObject const& u, ObjectGuid const& guid); - static GameObject* GetGameObject(WorldObject const& u, ObjectGuid const& guid); - static Transport* GetTransport(WorldObject const& u, ObjectGuid const& guid); - static DynamicObject* GetDynamicObject(WorldObject const& u, ObjectGuid const& guid); - static Unit* GetUnit(WorldObject const&, ObjectGuid const& guid); - static Creature* GetCreature(WorldObject const& u, ObjectGuid const& guid); - static Pet* GetPet(WorldObject const&, ObjectGuid const& guid); - static Player* GetPlayer(Map const*, ObjectGuid const& guid); - static Player* GetPlayer(WorldObject const&, ObjectGuid const& guid); - static Creature* GetCreatureOrPetOrVehicle(WorldObject const&, ObjectGuid const&); - - // these functions return objects if found in whole world - // ACCESS LIKE THAT IS NOT THREAD SAFE - static Player* FindPlayer(ObjectGuid const&); - static Player* FindPlayerByName(std::string const& name); - - // this returns Player even if he is not in world, for example teleporting - static Player* FindConnectedPlayer(ObjectGuid const&); - static Player* FindConnectedPlayerByName(std::string const& name); - - // when using this, you must use the hashmapholder's lock - static HashMapHolder<Player>::MapType const& GetPlayers() - { - return HashMapHolder<Player>::GetContainer(); - } - - template<class T> static void AddObject(T* object) - { - HashMapHolder<T>::Insert(object); - } - - template<class T> static void RemoveObject(T* object) - { - HashMapHolder<T>::Remove(object); - } - - static void SaveAllPlayers(); - - //non-static functions - - //Thread safe - Corpse* GetCorpseForPlayerGUID(ObjectGuid const& guid); - void RemoveCorpse(Corpse* corpse); - void AddCorpse(Corpse* corpse); - void AddCorpsesToGrid(GridCoord const& gridpair, GridType& grid, Map* map); - Corpse* ConvertCorpseForPlayer(ObjectGuid const& player_guid, bool insignia = false); - - //Thread unsafe - void RemoveOldCorpses(); - void UnloadAll(); - - private: - typedef std::unordered_map<ObjectGuid, Corpse*> Player2CorpsesMapType; - typedef std::unordered_map<Player*, UpdateData>::value_type UpdateDataValueType; - - Player2CorpsesMapType i_player2corpse; - - boost::shared_mutex _corpseLock; + // these functions return objects only if in map of specified object + WorldObject* GetWorldObject(WorldObject const&, ObjectGuid const&); + Object* GetObjectByTypeMask(WorldObject const&, ObjectGuid const&, uint32 typemask); + Corpse* GetCorpse(WorldObject const& u, ObjectGuid const& guid); + GameObject* GetGameObject(WorldObject const& u, ObjectGuid const& guid); + Transport* GetTransport(WorldObject const& u, ObjectGuid const& guid); + DynamicObject* GetDynamicObject(WorldObject const& u, ObjectGuid const& guid); + Unit* GetUnit(WorldObject const&, ObjectGuid const& guid); + Creature* GetCreature(WorldObject const& u, ObjectGuid const& guid); + Pet* GetPet(WorldObject const&, ObjectGuid const& guid); + Player* GetPlayer(Map const*, ObjectGuid const& guid); + Player* GetPlayer(WorldObject const&, ObjectGuid const& guid); + Creature* GetCreatureOrPetOrVehicle(WorldObject const&, ObjectGuid const&); + + // these functions return objects if found in whole world + // ACCESS LIKE THAT IS NOT THREAD SAFE + Player* FindPlayer(ObjectGuid const&); + Player* FindPlayerByName(std::string const& name); + + // this returns Player even if he is not in world, for example teleporting + Player* FindConnectedPlayer(ObjectGuid const&); + Player* FindConnectedPlayerByName(std::string const& name); + + // when using this, you must use the hashmapholder's lock + HashMapHolder<Player>::MapType const& GetPlayers(); + + template<class T> + void AddObject(T* object) + { + HashMapHolder<T>::Insert(object); + } + + template<class T> + void RemoveObject(T* object) + { + HashMapHolder<T>::Remove(object); + } + + void SaveAllPlayers(); }; -#define sObjectAccessor ObjectAccessor::instance() #endif diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index 4bf86facbef..b5d8d08a6a4 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -7358,20 +7358,6 @@ void ObjectMgr::DeleteGOData(uint32 guid) _gameObjectDataStore.erase(guid); } -void ObjectMgr::AddCorpseCellData(uint32 mapid, uint32 cellid, uint32 player_guid, uint32 instance) -{ - // corpses are always added to spawn mode 0 and they are spawned by their instance id - CellObjectGuids& cell_guids = _mapObjectGuidsStore[MAKE_PAIR32(mapid, 0)][cellid]; - cell_guids.corpses[player_guid] = instance; -} - -void ObjectMgr::DeleteCorpseCellData(uint32 mapid, uint32 cellid, uint32 player_guid) -{ - // corpses are always added to spawn mode 0 and they are spawned by their instance id - CellObjectGuids& cell_guids = _mapObjectGuidsStore[MAKE_PAIR32(mapid, 0)][cellid]; - cell_guids.corpses.erase(player_guid); -} - void ObjectMgr::LoadQuestRelationsHelper(QuestRelations& map, QuestRelationsReverse* reverseMap, std::string const& table, bool starter, bool go) { uint32 oldMSTime = getMSTime(); @@ -8590,7 +8576,7 @@ void ObjectMgr::LoadScriptNames() do { - _scriptNamesStore.emplace_back((*result)[0].GetCString()); + _scriptNamesStore.push_back((*result)[0].GetString()); } while (result->NextRow()); diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h index b9d755636cd..462370001dd 100644 --- a/src/server/game/Globals/ObjectMgr.h +++ b/src/server/game/Globals/ObjectMgr.h @@ -445,13 +445,11 @@ struct BroadcastText typedef std::unordered_map<uint32, BroadcastText> BroadcastTextContainer; -typedef std::set<uint32> CellGuidSet; -typedef std::map<uint32/*player guid*/, uint32/*instance*/> CellCorpseSet; +typedef std::set<ObjectGuid::LowType> CellGuidSet; struct CellObjectGuids { CellGuidSet creatures; CellGuidSet gameobjects; - CellCorpseSet corpses; }; typedef std::unordered_map<uint32/*cell_id*/, CellObjectGuids> CellObjectGuidsMap; typedef std::unordered_map<uint32/*(mapid, spawnMode) pair*/, CellObjectGuidsMap> MapObjectGuids; @@ -1219,9 +1217,6 @@ class ObjectMgr LocaleConstant GetDBCLocaleIndex() const { return DBCLocaleIndex; } void SetDBCLocaleIndex(LocaleConstant locale) { DBCLocaleIndex = locale; } - void AddCorpseCellData(uint32 mapid, uint32 cellid, uint32 player_guid, uint32 instance); - void DeleteCorpseCellData(uint32 mapid, uint32 cellid, uint32 player_guid); - // grid objects void AddCreatureToGrid(uint32 guid, CreatureData const* data); void RemoveCreatureFromGrid(uint32 guid, CreatureData const* data); diff --git a/src/server/game/Grids/ObjectGridLoader.cpp b/src/server/game/Grids/ObjectGridLoader.cpp index d0ae90d002f..de0ab490e9c 100644 --- a/src/server/game/Grids/ObjectGridLoader.cpp +++ b/src/server/game/Grids/ObjectGridLoader.cpp @@ -62,7 +62,7 @@ class ObjectWorldLoader { public: explicit ObjectWorldLoader(ObjectGridLoader& gloader) - : i_cell(gloader.i_cell), i_map(gloader.i_map), i_corpses (0) + : i_cell(gloader.i_cell), i_map(gloader.i_map), i_grid(gloader.i_grid), i_corpses(gloader.i_corpses) { } void Visit(CorpseMapType &m); @@ -72,8 +72,9 @@ class ObjectWorldLoader private: Cell i_cell; Map* i_map; + NGridType& i_grid; public: - uint32 i_corpses; + uint32& i_corpses; }; template<class T> void ObjectGridLoader::SetObjectCell(T* /*obj*/, CellCoord const& /*cellCoord*/) { } @@ -129,38 +130,6 @@ void LoadHelper(CellGuidSet const& guid_set, CellCoord &cell, GridRefManager<T> } } -void LoadHelper(CellCorpseSet const& cell_corpses, CellCoord &cell, CorpseMapType &m, uint32 &count, Map* map) -{ - if (cell_corpses.empty()) - return; - - for (CellCorpseSet::const_iterator itr = cell_corpses.begin(); itr != cell_corpses.end(); ++itr) - { - if (itr->second != map->GetInstanceId()) - continue; - - ObjectGuid player_guid(HighGuid::Player, itr->first); - - Corpse* obj = sObjectAccessor->GetCorpseForPlayerGUID(player_guid); - if (!obj) - continue; - - /// @todo this is a hack - // corpse's map should be reset when the map is unloaded - // but it may still exist when the grid is unloaded but map is not - // in that case map == currMap - obj->SetMap(map); - - if (obj->IsInGrid()) - { - obj->AddToWorld(); - continue; - } - - AddObjectHelper(cell, m, count, map, obj); - } -} - void ObjectGridLoader::Visit(GameObjectMapType &m) { CellCoord cellCoord = i_cell.GetCellCoord(); @@ -175,22 +144,28 @@ void ObjectGridLoader::Visit(CreatureMapType &m) LoadHelper(cell_guids.creatures, cellCoord, m, i_creatures, i_map); } -void ObjectWorldLoader::Visit(CorpseMapType &m) +void ObjectWorldLoader::Visit(CorpseMapType& /*m*/) { CellCoord cellCoord = i_cell.GetCellCoord(); - // corpses are always added to spawn mode 0 and they are spawned by their instance id - CellObjectGuids const& cell_guids = sObjectMgr->GetCellObjectGuids(i_map->GetId(), 0, cellCoord.GetId()); - LoadHelper(cell_guids.corpses, cellCoord, m, i_corpses, i_map); + if (std::unordered_set<Corpse*> const* corpses = i_map->GetCorpsesInCell(cellCoord.GetId())) + { + for (Corpse* corpse : *corpses) + { + corpse->AddToWorld(); + i_grid.GetGridType(i_cell.CellX(), i_cell.CellY()).AddWorldObject(corpse); + ++i_corpses; + } + } } void ObjectGridLoader::LoadN(void) { i_gameObjects = 0; i_creatures = 0; i_corpses = 0; i_cell.data.Part.cell_y = 0; - for (unsigned int x=0; x < MAX_NUMBER_OF_CELLS; ++x) + for (uint32 x = 0; x < MAX_NUMBER_OF_CELLS; ++x) { i_cell.data.Part.cell_x = x; - for (unsigned int y=0; y < MAX_NUMBER_OF_CELLS; ++y) + for (uint32 y = 0; y < MAX_NUMBER_OF_CELLS; ++y) { i_cell.data.Part.cell_y = y; @@ -205,7 +180,6 @@ void ObjectGridLoader::LoadN(void) ObjectWorldLoader worker(*this); TypeContainerVisitor<ObjectWorldLoader, WorldTypeMapContainer> visitor(worker); i_grid.VisitGrid(x, y, visitor); - i_corpses += worker.i_corpses; } } } diff --git a/src/server/game/Handlers/AuctionHouseHandler.cpp b/src/server/game/Handlers/AuctionHouseHandler.cpp index aa51b19a659..0293901c9e3 100644 --- a/src/server/game/Handlers/AuctionHouseHandler.cpp +++ b/src/server/game/Handlers/AuctionHouseHandler.cpp @@ -154,7 +154,6 @@ void WorldSession::HandleAuctionSellItem(WorldPacket& recvData) return; } - Creature* creature = GetPlayer()->GetNPCIfCanInteractWith(auctioneer, UNIT_NPC_FLAG_AUCTIONEER); if (!creature) { @@ -259,9 +258,26 @@ void WorldSession::HandleAuctionSellItem(WorldPacket& recvData) AuctionEntry* AH = new AuctionEntry(); if (sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION)) - AH->auctioneer = 23442; ///@TODO - HARDCODED DB GUID, BAD BAD BAD + AH->houseId = AUCTIONHOUSE_NEUTRAL; else - AH->auctioneer = auctioneer.GetCounter(); + { + CreatureData const* auctioneerData = sObjectMgr->GetCreatureData(creature->GetSpawnId()); + if (!auctioneerData) + { + TC_LOG_ERROR("misc", "Data for auctioneer not found (%s)", auctioneer.ToString().c_str()); + return; + } + + CreatureTemplate const* auctioneerInfo = sObjectMgr->GetCreatureTemplate(auctioneerData->id); + if (!auctioneerInfo) + { + TC_LOG_ERROR("misc", "Non existing auctioneer (%s)", auctioneer.ToString().c_str()); + return; + } + + const AuctionHouseEntry* AHEntry = sAuctionMgr->GetAuctionHouseEntry(auctioneerInfo->faction); + AH->houseId = AHEntry->houseId; + } // Required stack size of auction matches to current item stack size, just move item to auctionhouse if (itemsCount == 1 && item->GetCount() == count[0]) @@ -285,8 +301,8 @@ void WorldSession::HandleAuctionSellItem(WorldPacket& recvData) AH->deposit = deposit; AH->auctionHouseEntry = auctionHouseEntry; - TC_LOG_INFO("network", "CMSG_AUCTION_SELL_ITEM: Player %s (guid %d) is selling item %s entry %u (guid %d) to auctioneer %u 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(), AH->auctioneer, item->GetCount(), bid, buyout, auctionTime, AH->GetHouseId()); + 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); @@ -333,8 +349,8 @@ void WorldSession::HandleAuctionSellItem(WorldPacket& recvData) AH->deposit = deposit; AH->auctionHouseEntry = auctionHouseEntry; - TC_LOG_INFO("network", "CMSG_AUCTION_SELL_ITEM: Player %s (guid %d) is selling item %s entry %u (guid %d) to auctioneer %u 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(), AH->auctioneer, newItem->GetCount(), bid, buyout, auctionTime, AH->GetHouseId()); + 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); diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp index 61b2837dc10..ba22ab6aa07 100644 --- a/src/server/game/Handlers/CharacterHandler.cpp +++ b/src/server/game/Handlers/CharacterHandler.cpp @@ -203,6 +203,10 @@ bool LoginQueryHolder::Initialize() stmt->setUInt32(0, m_accountId); res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_INSTANCE_LOCK_TIMES, stmt); + stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CORPSE_LOCATION); + stmt->setUInt64(0, lowGuid); + res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_CORPSE_LOCATION, stmt); + return res; } @@ -892,7 +896,7 @@ void WorldSession::HandlePlayerLogin(LoginQueryHolder* holder) pCurrChar->TeleportTo(pCurrChar->m_homebindMapId, pCurrChar->m_homebindX, pCurrChar->m_homebindY, pCurrChar->m_homebindZ, pCurrChar->GetOrientation()); } - sObjectAccessor->AddObject(pCurrChar); + ObjectAccessor::AddObject(pCurrChar); //TC_LOG_DEBUG("Player %s added to Map.", pCurrChar->GetName().c_str()); pCurrChar->SendInitialPacketsAfterAddToMap(); @@ -923,7 +927,7 @@ void WorldSession::HandlePlayerLogin(LoginQueryHolder* holder) sSocialMgr->SendFriendStatus(pCurrChar, FRIEND_ONLINE, pCurrChar->GetGUID().GetCounter(), true); // Place character in world (and load zone) before some object loading - pCurrChar->LoadCorpse(); + pCurrChar->LoadCorpse(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_CORPSE_LOCATION)); // setting Ghost+speed if dead if (pCurrChar->m_deathState != ALIVE) @@ -1672,11 +1676,11 @@ void WorldSession::HandleCharFactionOrRaceChange(WorldPacket& recvData) } } - // resurrect the character in case he's dead - sObjectAccessor->ConvertCorpseForPlayer(factionChangeInfo.Guid); - SQLTransaction trans = CharacterDatabase.BeginTransaction(); + // resurrect the character in case he's dead + Player::OfflineResurrect(factionChangeInfo.Guid, trans); + CharacterDatabase.EscapeString(factionChangeInfo.Name); Player::Customize(&factionChangeInfo, trans); @@ -1820,6 +1824,7 @@ void WorldSession::HandleCharFactionOrRaceChange(WorldPacket& recvData) trans->Append(stmt); } + /// @todo: make this part async if (!sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD)) { // Reset guild diff --git a/src/server/game/Handlers/DuelHandler.cpp b/src/server/game/Handlers/DuelHandler.cpp index 2f39a91afbe..6e8ff5547d7 100644 --- a/src/server/game/Handlers/DuelHandler.cpp +++ b/src/server/game/Handlers/DuelHandler.cpp @@ -43,6 +43,9 @@ void WorldSession::HandleDuelAcceptedOpcode(WorldPacket& recvPacket) TC_LOG_DEBUG("network", "Player 1 is: %u (%s)", player->GetGUID().GetCounter(), player->GetName().c_str()); TC_LOG_DEBUG("network", "Player 2 is: %u (%s)", plTarget->GetGUID().GetCounter(), plTarget->GetName().c_str()); + player->UpdateHasCoolDownBeforeDuel(); + plTarget->UpdateHasCoolDownBeforeDuel(); + time_t now = time(NULL); player->duel->startTimer = now; plTarget->duel->startTimer = now; diff --git a/src/server/game/Handlers/MiscHandler.cpp b/src/server/game/Handlers/MiscHandler.cpp index 51f7e3f840a..2eebbfc1004 100644 --- a/src/server/game/Handlers/MiscHandler.cpp +++ b/src/server/game/Handlers/MiscHandler.cpp @@ -741,7 +741,6 @@ void WorldSession::HandleReclaimCorpseOpcode(WorldPacket& recvData) return; Corpse* corpse = _player->GetCorpse(); - if (!corpse) return; diff --git a/src/server/game/Handlers/MovementHandler.cpp b/src/server/game/Handlers/MovementHandler.cpp index 518baa42e97..84816bf3f33 100644 --- a/src/server/game/Handlers/MovementHandler.cpp +++ b/src/server/game/Handlers/MovementHandler.cpp @@ -142,8 +142,8 @@ void WorldSession::HandleMoveWorldportAckOpcode() } // resurrect character at enter into instance where his corpse exist after add to map - Corpse* corpse = GetPlayer()->GetCorpse(); - if (corpse && corpse->GetType() != CORPSE_BONES && corpse->GetMapId() == GetPlayer()->GetMapId()) + Corpse* corpse = GetPlayer()->GetMap()->GetCorpseByPlayer(GetPlayer()->GetGUID()); + if (corpse && corpse->GetType() != CORPSE_BONES) { if (mEntry->IsDungeon()) { diff --git a/src/server/game/Handlers/NPCHandler.cpp b/src/server/game/Handlers/NPCHandler.cpp index 14436a864f9..f8d7f73e077 100644 --- a/src/server/game/Handlers/NPCHandler.cpp +++ b/src/server/game/Handlers/NPCHandler.cpp @@ -403,10 +403,12 @@ void WorldSession::SendSpiritResurrect() // get corpse nearest graveyard WorldSafeLocsEntry const* corpseGrave = NULL; - Corpse* corpse = _player->GetCorpse(); - if (corpse) - corpseGrave = sObjectMgr->GetClosestGraveYard( - corpse->GetPositionX(), corpse->GetPositionY(), corpse->GetPositionZ(), corpse->GetMapId(), _player->GetTeam()); + WorldLocation corpseLocation = _player->GetCorpseLocation(); + if (_player->HasCorpse()) + { + corpseGrave = sObjectMgr->GetClosestGraveYard(corpseLocation.GetPositionX(), corpseLocation.GetPositionY(), + corpseLocation.GetPositionZ(), corpseLocation.GetMapId(), _player->GetTeam()); + } // now can spawn bones _player->SpawnCorpseBones(); diff --git a/src/server/game/Handlers/QueryHandler.cpp b/src/server/game/Handlers/QueryHandler.cpp index 906c3017100..6d896bee26c 100644 --- a/src/server/game/Handlers/QueryHandler.cpp +++ b/src/server/game/Handlers/QueryHandler.cpp @@ -218,11 +218,7 @@ void WorldSession::HandleGameObjectQueryOpcode(WorldPacket& recvData) void WorldSession::HandleCorpseQueryOpcode(WorldPacket & /*recvData*/) { - TC_LOG_DEBUG("network", "WORLD: Received MSG_CORPSE_QUERY"); - - Corpse* corpse = GetPlayer()->GetCorpse(); - - if (!corpse) + if (!_player->HasCorpse()) { WorldPacket data(MSG_CORPSE_QUERY, 1); data << uint8(0); // corpse not found @@ -230,24 +226,25 @@ void WorldSession::HandleCorpseQueryOpcode(WorldPacket & /*recvData*/) return; } - uint32 mapid = corpse->GetMapId(); - float x = corpse->GetPositionX(); - float y = corpse->GetPositionY(); - float z = corpse->GetPositionZ(); - uint32 corpsemapid = mapid; + WorldLocation corpseLocation = _player->GetCorpseLocation(); + uint32 corpseMapID = corpseLocation.GetMapId(); + uint32 mapID = corpseLocation.GetMapId(); + float x = corpseLocation.GetPositionX(); + float y = corpseLocation.GetPositionY(); + float z = corpseLocation.GetPositionZ(); // if corpse at different map - if (mapid != _player->GetMapId()) + if (mapID != _player->GetMapId()) { // search entrance map for proper show entrance - if (MapEntry const* corpseMapEntry = sMapStore.LookupEntry(mapid)) + if (MapEntry const* corpseMapEntry = sMapStore.LookupEntry(mapID)) { if (corpseMapEntry->IsDungeon() && corpseMapEntry->entrance_map >= 0) { // if corpse map have entrance if (Map const* entranceMap = sMapMgr->CreateBaseMap(corpseMapEntry->entrance_map)) { - mapid = corpseMapEntry->entrance_map; + mapID = corpseMapEntry->entrance_map; x = corpseMapEntry->entrance_x; y = corpseMapEntry->entrance_y; z = entranceMap->GetHeight(GetPlayer()->GetPhaseMask(), x, y, MAX_HEIGHT); @@ -258,11 +255,11 @@ void WorldSession::HandleCorpseQueryOpcode(WorldPacket & /*recvData*/) WorldPacket data(MSG_CORPSE_QUERY, 1+(6*4)); data << uint8(1); // corpse found - data << int32(mapid); + data << int32(mapID); data << float(x); data << float(y); data << float(z); - data << int32(corpsemapid); + data << int32(corpseMapID); data << uint32(0); // unknown SendPacket(&data); } diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index b50099b9787..2af77ffdec3 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -50,9 +50,9 @@ GridState* si_GridStates[MAX_GRID_STATE]; Map::~Map() { - sScriptMgr->OnDestroyMap(this); + // UnloadAll must be called before deleting the map - UnloadAll(); + sScriptMgr->OnDestroyMap(this); while (!i_worldObjects.empty()) { @@ -400,11 +400,18 @@ void Map::DeleteFromWorld(T* obj) template<> void Map::DeleteFromWorld(Player* player) { - sObjectAccessor->RemoveObject(player); + ObjectAccessor::RemoveObject(player); RemoveUpdateObject(player); /// @todo I do not know why we need this, it should be removed in ~Object anyway delete player; } +template<> +void Map::DeleteFromWorld(Transport* transport) +{ + ObjectAccessor::RemoveObject(transport); + delete transport; +} + void Map::EnsureGridCreated(const GridCoord &p) { std::lock_guard<std::mutex> lock(_gridLock); @@ -468,8 +475,6 @@ bool Map::EnsureGridLoaded(const Cell &cell) ObjectGridLoader loader(*grid, this, cell); loader.LoadN(); - // Add resurrectable corpses to world object list in grid - sObjectAccessor->AddCorpsesToGrid(GridCoord(cell.GridX(), cell.GridY()), grid->GetGridType(cell.CellX(), cell.CellY()), this); Balance(); return true; } @@ -507,6 +512,9 @@ bool Map::AddPlayerToMap(Player* player) player->m_clientGUIDs.clear(); player->UpdateObjectVisibility(false); + if (player->IsAlive()) + ConvertCorpseToBones(player->GetGUID()); + sScriptMgr->OnPlayerEnterMap(this, player); return true; } @@ -2610,7 +2618,7 @@ void Map::SendObjectUpdates() _updateObjects.erase(_updateObjects.begin()); obj->BuildUpdate(update_players); } - + WorldPacket packet; // here we allocate a std::vector with a size of 0x10000 for (UpdateDataMapType::iterator iter = update_players.begin(); iter != update_players.end(); ++iter) { @@ -3530,7 +3538,6 @@ void Map::LoadCorpseData() if (!result) return; - uint32 count = 0; do { Field* fields = result->Fetch(); @@ -3550,8 +3557,8 @@ void Map::LoadCorpseData() continue; } - sObjectAccessor->AddCorpse(corpse); - ++count; + AddCorpse(corpse); + } while (result->NextRow()); } @@ -3564,6 +3571,97 @@ void Map::DeleteCorpseData() CharacterDatabase.Execute(stmt); } +void Map::AddCorpse(Corpse* corpse) +{ + corpse->SetMap(this); + + CellCoord cellCoord = Trinity::ComputeCellCoord(corpse->GetPositionX(), corpse->GetPositionY()); + _corpsesByCell[cellCoord.GetId()].insert(corpse); + _corpsesByPlayer[corpse->GetOwnerGUID()] = corpse; +} + +void Map::RemoveCorpse(Corpse* corpse) +{ + ASSERT(corpse && corpse->GetType() != CORPSE_BONES); + + corpse->DestroyForNearbyPlayers(); + if (corpse->IsInGrid()) + RemoveFromMap(corpse, false); + else + { + corpse->RemoveFromWorld(); + corpse->ResetMap(); + } + + CellCoord cellCoord = Trinity::ComputeCellCoord(corpse->GetPositionX(), corpse->GetPositionY()); + _corpsesByCell[cellCoord.GetId()].erase(corpse); + _corpsesByPlayer.erase(corpse->GetOwnerGUID()); +} + +Corpse* Map::ConvertCorpseToBones(ObjectGuid const& ownerGuid, bool insignia /*= false*/) +{ + Corpse* corpse = GetCorpseByPlayer(ownerGuid); + if (!corpse) + return nullptr; + + RemoveCorpse(corpse); + + // remove corpse from DB + SQLTransaction trans = CharacterDatabase.BeginTransaction(); + corpse->DeleteFromDB(trans); + CharacterDatabase.CommitTransaction(trans); + + Corpse* bones = NULL; + + // create the bones only if the map and the grid is loaded at the corpse's location + // ignore bones creating option in case insignia + if ((insignia || + (IsBattlegroundOrArena() ? sWorld->getBoolConfig(CONFIG_DEATH_BONES_BG_OR_ARENA) : sWorld->getBoolConfig(CONFIG_DEATH_BONES_WORLD))) && + !IsRemovalGrid(corpse->GetPositionX(), corpse->GetPositionY())) + { + // Create bones, don't change Corpse + bones = new Corpse(); + bones->Create(corpse->GetGUID().GetCounter(), this); + + for (uint8 i = OBJECT_FIELD_TYPE + 1; i < CORPSE_END; ++i) // don't overwrite guid and object type + bones->SetUInt32Value(i, corpse->GetUInt32Value(i)); + + bones->SetGridCoord(corpse->GetGridCoord()); + bones->Relocate(corpse->GetPositionX(), corpse->GetPositionY(), corpse->GetPositionZ(), corpse->GetOrientation()); + bones->SetPhaseMask(corpse->GetPhaseMask(), false); + + bones->SetUInt32Value(CORPSE_FIELD_FLAGS, CORPSE_FLAG_UNK2 | CORPSE_FLAG_BONES); + bones->SetGuidValue(CORPSE_FIELD_OWNER, ObjectGuid::Empty); + + for (uint8 i = 0; i < EQUIPMENT_SLOT_END; ++i) + if (corpse->GetUInt32Value(CORPSE_FIELD_ITEM + i)) + bones->SetUInt32Value(CORPSE_FIELD_ITEM + i, 0); + + // add bones in grid store if grid loaded where corpse placed + AddToMap(bones); + } + + // all references to the corpse should be removed at this point + delete corpse; + + return bones; +} + +void Map::RemoveOldCorpses() +{ + time_t now = time(nullptr); + + std::vector<ObjectGuid> corpses; + corpses.reserve(_corpsesByPlayer.size()); + + for (auto const& p : _corpsesByPlayer) + if (p.second->IsExpired(now)) + corpses.push_back(p.first); + + for (ObjectGuid const& ownerGuid : corpses) + ConvertCorpseToBones(ownerGuid); +} + void Map::SendZoneDynamicInfo(Player* player) { uint32 zoneId = GetZoneId(player->GetPositionX(), player->GetPositionY(), player->GetPositionZ()); diff --git a/src/server/game/Maps/Map.h b/src/server/game/Maps/Map.h index 9b5e67cdfaf..2ba5e10c82c 100644 --- a/src/server/game/Maps/Map.h +++ b/src/server/game/Maps/Map.h @@ -459,6 +459,24 @@ class Map : public GridRefManager<NGridType> typedef std::unordered_multimap<ObjectGuid::LowType, GameObject*> GameObjectBySpawnIdContainer; GameObjectBySpawnIdContainer& GetGameObjectBySpawnIdStore() { return _gameobjectBySpawnIdStore; } + std::unordered_set<Corpse*> const* GetCorpsesInCell(uint32 cellId) const + { + auto itr = _corpsesByCell.find(cellId); + if (itr != _corpsesByCell.end()) + return &itr->second; + + return nullptr; + } + + Corpse* GetCorpseByPlayer(ObjectGuid const& ownerGuid) const + { + auto itr = _corpsesByPlayer.find(ownerGuid); + if (itr != _corpsesByPlayer.end()) + return itr->second; + + return nullptr; + } + MapInstanced* ToMapInstanced() { if (Instanceable()) return reinterpret_cast<MapInstanced*>(this); return NULL; } MapInstanced const* ToMapInstanced() const { if (Instanceable()) return reinterpret_cast<MapInstanced const*>(this); return NULL; } @@ -508,6 +526,10 @@ class Map : public GridRefManager<NGridType> void LoadCorpseData(); void DeleteCorpseData(); + void AddCorpse(Corpse* corpse); + void RemoveCorpse(Corpse* corpse); + Corpse* ConvertCorpseToBones(ObjectGuid const& ownerGuid, bool insignia = false); + void RemoveOldCorpses(); static void DeleteRespawnTimesInDB(uint16 mapId, uint32 instanceId); @@ -709,6 +731,8 @@ class Map : public GridRefManager<NGridType> MapStoredObjectTypesContainer _objectsStore; CreatureBySpawnIdContainer _creatureBySpawnIdStore; GameObjectBySpawnIdContainer _gameobjectBySpawnIdStore; + std::unordered_map<uint32/*cellId*/, std::unordered_set<Corpse*>> _corpsesByCell; + std::unordered_map<ObjectGuid, Corpse*> _corpsesByPlayer; std::unordered_set<Object*> _updateObjects; }; diff --git a/src/server/game/Maps/MapManager.cpp b/src/server/game/Maps/MapManager.cpp index 82be6336dc6..0e7f9dbb611 100644 --- a/src/server/game/Maps/MapManager.cpp +++ b/src/server/game/Maps/MapManager.cpp @@ -164,10 +164,10 @@ bool MapManager::CanPlayerEnter(uint32 mapid, Player* player, bool loginCheck) if (!player->IsAlive()) { - if (Corpse* corpse = player->GetCorpse()) + if (player->HasCorpse()) { // let enter in ghost mode in instance that connected to inner instance with corpse - uint32 corpseMap = corpse->GetMapId(); + uint32 corpseMap = player->GetCorpseLocation().GetMapId(); do { if (corpseMap == mapid) @@ -184,9 +184,8 @@ bool MapManager::CanPlayerEnter(uint32 mapid, Player* player, bool loginCheck) TC_LOG_DEBUG("maps", "MAP: Player '%s' does not have a corpse in instance '%s' and cannot enter.", player->GetName().c_str(), mapName); return false; } + TC_LOG_DEBUG("maps", "MAP: Player '%s' has corpse in instance '%s' and can enter.", player->GetName().c_str(), mapName); - player->ResurrectPlayer(0.5f, false); - player->SpawnCorpseBones(); } else TC_LOG_DEBUG("maps", "Map::CanPlayerEnter - player '%s' is dead but does not have a corpse!", player->GetName().c_str()); diff --git a/src/server/game/Miscellaneous/Language.h b/src/server/game/Miscellaneous/Language.h index a4ba9866064..d374c8c509a 100644 --- a/src/server/game/Miscellaneous/Language.h +++ b/src/server/game/Miscellaneous/Language.h @@ -1202,6 +1202,8 @@ enum TrinityStrings LANG_BAN_ACCOUNT_YOUPERMBANNEDMESSAGE_WORLD = 11007, LANG_NPCINFO_INHABIT_TYPE = 11008, - LANG_NPCINFO_FLAGS_EXTRA = 11009 + LANG_NPCINFO_FLAGS_EXTRA = 11009, + + LANG_COOLDOWN_NOT_RESET_AFTER_DUEL = 11010 }; #endif diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp index 53da37bc730..45a816eda9b 100644 --- a/src/server/game/Server/WorldSession.cpp +++ b/src/server/game/Server/WorldSession.cpp @@ -840,13 +840,33 @@ void WorldSession::ReadMovementInfo(WorldPacket &data, MovementInfo* mi) mi->RemoveMovementFlag((maskToRemove)); #endif - /*! This must be a packet spoofing attempt. MOVEMENTFLAG_ROOT sent from the client is not valid in conjunction with any of the moving movement flags such as MOVEMENTFLAG_FORWARD. It will freeze clients that receive this player's movement info. */ - REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_ROOT), - MOVEMENTFLAG_ROOT); + // Only adjust movement flag removal for vehicles with the VEHICLE_FLAG_FIXED_POSITION flag, or the hard coded exceptions below: + // 30236 | Argent Cannon + // 39759 | Tankbuster Cannon + if (GetPlayer()->GetVehicleBase() && ((GetPlayer()->GetVehicle()->GetVehicleInfo()->m_flags & VEHICLE_FLAG_FIXED_POSITION) || GetPlayer()->GetVehicleBase()->GetEntry() == 30236 || GetPlayer()->GetVehicleBase()->GetEntry() == 39759)) + { + // Actually players in rooted vehicles still send commands, don't clear root for these! + // Check specifically for the following conditions: + // MOVEMENTFLAG_ROOT + no other flags (0x800) + // MOVEMENTFLAG_ROOT + MOVEMENTFLAG_LEFT (0x810) + // MOVEMENTFLAG_ROOT + MOVEMENTFLAG_RIGHT (0x820) + // MOVEMENTFLAG_ROOT + MOVEMENTFLAG_PITCH_UP (0x840) + // MOVEMENTFLAG_ROOT + MOVEMENTFLAG_PITCH_DOWN (0x880) + // If none of these are true, clear the root + if (mi->HasMovementFlag(MOVEMENTFLAG_ROOT) && mi->HasMovementFlag(MOVEMENTFLAG_LEFT | MOVEMENTFLAG_RIGHT | MOVEMENTFLAG_PITCH_UP | MOVEMENTFLAG_PITCH_DOWN)) + REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_ROOT), + MOVEMENTFLAG_MASK_MOVING); + } + else + { + // Only remove here for non vehicles + REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_ROOT), + MOVEMENTFLAG_ROOT); + } //! Cannot hover without SPELL_AURA_HOVER REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_HOVER) && !GetPlayer()->HasAuraType(SPELL_AURA_HOVER), diff --git a/src/server/game/Spells/SpellHistory.cpp b/src/server/game/Spells/SpellHistory.cpp index 09e3be690b1..208b4cf7ed9 100644 --- a/src/server/game/Spells/SpellHistory.cpp +++ b/src/server/game/Spells/SpellHistory.cpp @@ -637,6 +637,23 @@ void SpellHistory::BuildCooldownPacket(WorldPacket& data, uint8 flags, PacketCoo } } +uint16 SpellHistory::GetArenaCooldownsSize() +{ + uint16 count = 0; + + for (auto itr = _spellCooldowns.begin(); itr != _spellCooldowns.end();) + { + SpellInfo const* spellInfo = sSpellMgr->EnsureSpellInfo(itr->first); + + if (spellInfo->RecoveryTime < 10 * MINUTE * IN_MILLISECONDS && + spellInfo->CategoryRecoveryTime < 10 * MINUTE * IN_MILLISECONDS) + ++count; + ++itr; + } + + return count; +} + template void SpellHistory::LoadFromDB<Player>(PreparedQueryResult cooldownsResult); template void SpellHistory::LoadFromDB<Pet>(PreparedQueryResult cooldownsResult); template void SpellHistory::SaveToDB<Player>(SQLTransaction& trans); diff --git a/src/server/game/Spells/SpellHistory.h b/src/server/game/Spells/SpellHistory.h index f1533d57aef..4d0642d644e 100644 --- a/src/server/game/Spells/SpellHistory.h +++ b/src/server/game/Spells/SpellHistory.h @@ -117,6 +117,7 @@ public: void BuildCooldownPacket(WorldPacket& data, uint8 flags, uint32 spellId, uint32 cooldown) const; CooldownStorageType::size_type GetCooldownsSizeForPacket() const { return _spellCooldowns.size(); } + uint16 GetArenaCooldownsSize(); private: Player* GetPlayerOwner() const; diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp index f27f9220aaa..55d0ad0b4dc 100644 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -3105,11 +3105,13 @@ void SpellMgr::LoadSpellInfoCorrections() case 28796: // Poison Bolt Volly - Faerlina spellInfo->MaxAffectedTargets = 5; break; + case 54835: // Curse of the Plaguebringer - Noth (H) + spellInfo->MaxAffectedTargets = 8; + break; case 40827: // Sinful Beam case 40859: // Sinister Beam case 40860: // Vile Beam case 40861: // Wicked Beam - case 54835: // Curse of the Plaguebringer - Noth (H) case 54098: // Poison Bolt Volly - Faerlina (H) spellInfo->MaxAffectedTargets = 10; break; diff --git a/src/server/game/Tickets/TicketMgr.cpp b/src/server/game/Tickets/TicketMgr.cpp index a51c9fb16d2..dbf66c6bd71 100644 --- a/src/server/game/Tickets/TicketMgr.cpp +++ b/src/server/game/Tickets/TicketMgr.cpp @@ -75,8 +75,8 @@ bool GmTicket::LoadFromDB(Field* fields) void GmTicket::SaveToDB(SQLTransaction& trans) const { - // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 - // ticketId, guid, name, message, createTime, mapId, posX, posY, posZ, lastModifiedTime, closedBy, assignedTo, comment, completed, escalated, viewed + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 + // id, playerGuid, name, description, createTime, mapId, posX, posY, posZ, lastModifiedTime, closedBy, assignedTo, comment, response, completed, escalated, viewed, needMoreHelp, resolvedBy uint8 index = 0; PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_GM_TICKET); stmt->setUInt32( index, _id); @@ -97,6 +97,7 @@ void GmTicket::SaveToDB(SQLTransaction& trans) const stmt->setUInt8 (++index, uint8(_escalatedStatus)); stmt->setBool (++index, _viewed); stmt->setBool (++index, _needMoreHelp); + stmt->setInt32 (++index, int32(_resolvedBy.GetCounter())); CharacterDatabase.ExecuteOrAppend(trans, stmt); } @@ -361,6 +362,19 @@ void TicketMgr::CloseTicket(uint32 ticketId, ObjectGuid source) } } +void TicketMgr::ResolveAndCloseTicket(uint32 ticketId, ObjectGuid source) +{ + if (GmTicket* ticket = GetTicket(ticketId)) + { + SQLTransaction trans = SQLTransaction(nullptr); + ticket->SetClosedBy(source); + ticket->SetResolvedBy(source); + if (source) + --_openTicketCount; + ticket->SaveToDB(trans); + } +} + void TicketMgr::RemoveTicket(uint32 ticketId) { if (GmTicket* ticket = GetTicket(ticketId)) diff --git a/src/server/game/Tickets/TicketMgr.h b/src/server/game/Tickets/TicketMgr.h index e9892c22edc..f7d3178978f 100644 --- a/src/server/game/Tickets/TicketMgr.h +++ b/src/server/game/Tickets/TicketMgr.h @@ -119,6 +119,7 @@ public: _escalatedStatus = TICKET_ASSIGNED; } void SetClosedBy(ObjectGuid value) { _closedBy = value; } + void SetResolvedBy(ObjectGuid value) { _resolvedBy = value; } void SetCompleted() { _completed = true; } void SetMessage(std::string const& message) { @@ -158,7 +159,8 @@ private: std::string _message; uint64 _createTime; uint64 _lastModifiedTime; - ObjectGuid _closedBy; // 0 = Open, -1 = Console, playerGuid = player abandoned ticket, other = GM who closed it. + ObjectGuid _closedBy; // 0 = Open, -1 = Console, playerGuid = player abandoned ticket or read the GM response message, other = GM who closed it. + ObjectGuid _resolvedBy; // 0 = Open, -1 = Resolved by Console, GM who resolved it by closing or completing the ticket. ObjectGuid _assignedTo; std::string _comment; bool _completed; @@ -216,6 +218,7 @@ public: void AddTicket(GmTicket* ticket); void CloseTicket(uint32 ticketId, ObjectGuid source); + void ResolveAndCloseTicket(uint32 ticketId, ObjectGuid source); // used when GM resolves a ticket by simply closing it void RemoveTicket(uint32 ticketId); bool GetStatus() const { return _status; } diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index ce1837fbc86..85e79168b71 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -1190,6 +1190,7 @@ void World::LoadConfigSettings(bool reload) m_int_configs[CONFIG_MAX_WHO] = sConfigMgr->GetIntDefault("MaxWhoListReturns", 49); m_bool_configs[CONFIG_START_ALL_SPELLS] = sConfigMgr->GetBoolDefault("PlayerStart.AllSpells", false); m_int_configs[CONFIG_HONOR_AFTER_DUEL] = sConfigMgr->GetIntDefault("HonorPointsAfterDuel", 0); + m_bool_configs[CONFIG_RESET_COOLDOWN_AFTER_DUEL] = sConfigMgr->GetBoolDefault("ResetCoolDownAfterDuel", 0); m_bool_configs[CONFIG_START_ALL_EXPLORED] = sConfigMgr->GetBoolDefault("PlayerStart.MapsExplored", false); m_bool_configs[CONFIG_START_ALL_REP] = sConfigMgr->GetBoolDefault("PlayerStart.AllReputation", false); m_bool_configs[CONFIG_ALWAYS_MAXSKILL] = sConfigMgr->GetBoolDefault("AlwaysMaxWeaponSkill", false); @@ -2150,7 +2151,10 @@ void World::Update(uint32 diff) if (m_timers[WUPDATE_CORPSES].Passed()) { m_timers[WUPDATE_CORPSES].Reset(); - sObjectAccessor->RemoveOldCorpses(); + sMapMgr->DoForAllMaps([](Map* map) + { + map->RemoveOldCorpses(); + }); } ///- Process Game events when necessary @@ -3264,3 +3268,9 @@ void World::ReloadRBAC() if (WorldSession* session = itr->second) session->InvalidateRBACData(); } + +void World::RemoveOldCorpses() +{ + m_timers[WUPDATE_CORPSES].SetCurrent(m_timers[WUPDATE_CORPSES].GetInterval()); +} + diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h index af89adcb04e..1f71975cde2 100644 --- a/src/server/game/World/World.h +++ b/src/server/game/World/World.h @@ -161,6 +161,7 @@ enum WorldBoolConfigs CONFIG_ALLOW_TRACK_BOTH_RESOURCES, CONFIG_CALCULATE_CREATURE_ZONE_AREA_DATA, CONFIG_CALCULATE_GAMEOBJECT_ZONE_AREA_DATA, + CONFIG_RESET_COOLDOWN_AFTER_DUEL, BOOL_CONFIG_VALUE_COUNT }; @@ -761,6 +762,8 @@ class World void ReloadRBAC(); + void RemoveOldCorpses(); + protected: void _UpdateGameTime(); // callback for UpdateRealmCharacters diff --git a/src/server/scripts/Commands/cs_instance.cpp b/src/server/scripts/Commands/cs_instance.cpp index 78c8f970f52..c960d3f3753 100644 --- a/src/server/scripts/Commands/cs_instance.cpp +++ b/src/server/scripts/Commands/cs_instance.cpp @@ -217,7 +217,7 @@ public: { playerName = param3; if (normalizePlayerName(playerName)) - player = sObjectAccessor->FindPlayerByName(playerName); + player = ObjectAccessor::FindPlayerByName(playerName); } if (!player) @@ -283,7 +283,7 @@ public: { playerName = param2; if (normalizePlayerName(playerName)) - player = sObjectAccessor->FindPlayerByName(playerName); + player = ObjectAccessor::FindPlayerByName(playerName); } if (!player) diff --git a/src/server/scripts/Commands/cs_misc.cpp b/src/server/scripts/Commands/cs_misc.cpp index dfb1d84c6cc..21a55b1558a 100644 --- a/src/server/scripts/Commands/cs_misc.cpp +++ b/src/server/scripts/Commands/cs_misc.cpp @@ -600,8 +600,10 @@ public: target->SaveToDB(); } else - // will resurrected at login without corpse - sObjectAccessor->ConvertCorpseForPlayer(targetGuid); + { + SQLTransaction trans(nullptr); + Player::OfflineResurrect(targetGuid, trans); + } return true; } @@ -820,7 +822,7 @@ public: // Save all players in the world static bool HandleSaveAllCommand(ChatHandler* handler, char const* /*args*/) { - sObjectAccessor->SaveAllPlayers(); + ObjectAccessor::SaveAllPlayers(); handler->SendSysMessage(LANG_PLAYERS_SAVED); return true; } diff --git a/src/server/scripts/Commands/cs_server.cpp b/src/server/scripts/Commands/cs_server.cpp index 6fa84d1bc75..fe5ae940f88 100644 --- a/src/server/scripts/Commands/cs_server.cpp +++ b/src/server/scripts/Commands/cs_server.cpp @@ -100,7 +100,7 @@ public: // Triggering corpses expire check in world static bool HandleServerCorpsesCommand(ChatHandler* /*handler*/, char const* /*args*/) { - sObjectAccessor->RemoveOldCorpses(); + sWorld->RemoveOldCorpses(); return true; } diff --git a/src/server/scripts/Commands/cs_ticket.cpp b/src/server/scripts/Commands/cs_ticket.cpp index b609dfe18dd..f7634381fc9 100644 --- a/src/server/scripts/Commands/cs_ticket.cpp +++ b/src/server/scripts/Commands/cs_ticket.cpp @@ -153,7 +153,7 @@ public: return true; } - sTicketMgr->CloseTicket(ticket->GetId(), player ? player->GetGUID() : ObjectGuid(uint64(-1))); + sTicketMgr->ResolveAndCloseTicket(ticket->GetId(), player ? player->GetGUID() : ObjectGuid(uint64(-1))); sTicketMgr->UpdateLastChange(); std::string msg = ticket->FormatMessageString(*handler, player ? player->GetName().c_str() : "Console", NULL, NULL, NULL, NULL); @@ -248,7 +248,10 @@ public: if (Player* player = ticket->GetPlayer()) ticket->SendResponse(player->GetSession()); + Player* gm = handler->GetSession() ? handler->GetSession()->GetPlayer() : nullptr; + SQLTransaction trans = SQLTransaction(NULL); + ticket->SetResolvedBy(gm ? gm->GetGUID() : ObjectGuid(uint64(-1))); ticket->SetCompleted(); ticket->SaveToDB(trans); diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_nefarian.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_nefarian.cpp index 51e1bf67f16..a89571e8197 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_nefarian.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_nefarian.cpp @@ -367,9 +367,9 @@ public: } } - void sGossipSelect(Player* player, uint32 sender, uint32 action) override + void sGossipSelect(Player* player, uint32 menuId, uint32 gossipListId) override { - if (sender == GOSSIP_ID && action == GOSSIP_OPTION_ID) + if (menuId == GOSSIP_ID && gossipListId == GOSSIP_OPTION_ID) { player->CLOSE_GOSSIP_MENU(); Talk(SAY_GAMESBEGIN_1); diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_vaelastrasz.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_vaelastrasz.cpp index 636554a0ae4..76ab4736275 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_vaelastrasz.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_vaelastrasz.cpp @@ -221,9 +221,9 @@ public: DoMeleeAttackIfReady(); } - void sGossipSelect(Player* player, uint32 sender, uint32 action) override + void sGossipSelect(Player* player, uint32 menuId, uint32 gossipListId) override { - if (sender == GOSSIP_ID && action == 0) + if (menuId == GOSSIP_ID && gossipListId == 0) { player->CLOSE_GOSSIP_MENU(); BeginSpeech(player); diff --git a/src/server/scripts/EasternKingdoms/Karazhan/karazhan.cpp b/src/server/scripts/EasternKingdoms/Karazhan/karazhan.cpp index f3c59654295..87945ccf916 100644 --- a/src/server/scripts/EasternKingdoms/Karazhan/karazhan.cpp +++ b/src/server/scripts/EasternKingdoms/Karazhan/karazhan.cpp @@ -25,7 +25,6 @@ EndScriptData */ /* ContentData npc_barnes -npc_berthold npc_image_of_medivh EndContentData */ @@ -416,41 +415,6 @@ public: }; /*### -# npc_berthold -####*/ - -#define GOSSIP_ITEM_TELEPORT "Teleport me to the Guardian's Library" - -class npc_berthold : public CreatureScript -{ -public: - npc_berthold() : CreatureScript("npc_berthold") { } - - bool OnGossipSelect(Player* player, Creature* /*creature*/, uint32 /*sender*/, uint32 action) override - { - player->PlayerTalkClass->ClearMenus(); - if (action == GOSSIP_ACTION_INFO_DEF + 1) - player->CastSpell(player, SPELL_TELEPORT, true); - - player->CLOSE_GOSSIP_MENU(); - return true; - } - - bool OnGossipHello(Player* player, Creature* creature) override - { - if (InstanceScript* instance = creature->GetInstanceScript()) - { - // Check if Shade of Aran event is done - if (instance->GetData(TYPE_ARAN) == DONE) - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_TELEPORT, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); - } - - player->SEND_GOSSIP_MENU(player->GetGossipTextId(creature), creature->GetGUID()); - return true; - } -}; - -/*### # npc_image_of_medivh ####*/ @@ -668,6 +632,5 @@ public: void AddSC_karazhan() { new npc_barnes(); - new npc_berthold(); new npc_image_of_medivh(); } diff --git a/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter1.cpp b/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter1.cpp index b37d505d766..9747d31952b 100644 --- a/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter1.cpp +++ b/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter1.cpp @@ -729,13 +729,19 @@ class npc_dark_rider_of_acherus : public CreatureScript ## npc_salanar_the_horseman ######*/ -enum Spells_Salanar +enum SalanarTheHorseman { - SPELL_REALM_OF_SHADOWS = 52693, + GOSSIP_SALANAR_MENU = 9739, + GOSSIP_SALANAR_OPTION = 0, + SALANAR_SAY = 0, + QUEST_INTO_REALM_OF_SHADOWS = 12687, + NPC_DARK_RIDER_OF_ACHERUS = 28654, + NPC_SALANAR_IN_REALM_OF_SHADOWS = 28788, SPELL_EFFECT_STOLEN_HORSE = 52263, SPELL_DELIVER_STOLEN_HORSE = 52264, SPELL_CALL_DARK_RIDER = 52266, - SPELL_EFFECT_OVERTAKE = 52349 + SPELL_EFFECT_OVERTAKE = 52349, + SPELL_REALM_OF_SHADOWS = 52693 }; class npc_salanar_the_horseman : public CreatureScript @@ -743,15 +749,19 @@ class npc_salanar_the_horseman : public CreatureScript public: npc_salanar_the_horseman() : CreatureScript("npc_salanar_the_horseman") { } - CreatureAI* GetAI(Creature* creature) const override - { - return new npc_salanar_the_horsemanAI(creature); - } - struct npc_salanar_the_horsemanAI : public ScriptedAI { npc_salanar_the_horsemanAI(Creature* creature) : ScriptedAI(creature) { } + void sGossipSelect(Player* player, uint32 menuId, uint32 gossipListId) override + { + if (menuId == GOSSIP_SALANAR_MENU && gossipListId == GOSSIP_SALANAR_OPTION) + { + player->CastSpell(player, SPELL_REALM_OF_SHADOWS, true); + player->PlayerTalkClass->SendCloseGossip(); + } + } + void SpellHit(Unit* caster, const SpellInfo* spell) override { if (spell->Id == SPELL_DELIVER_STOLEN_HORSE) @@ -766,7 +776,7 @@ public: caster->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPELLCLICK); caster->setFaction(35); DoCast(caster, SPELL_CALL_DARK_RIDER, true); - if (Creature* Dark_Rider = me->FindNearestCreature(28654, 15)) + if (Creature* Dark_Rider = me->FindNearestCreature(NPC_DARK_RIDER_OF_ACHERUS, 15)) ENSURE_AI(npc_dark_rider_of_acherus::npc_dark_rider_of_acherusAI, Dark_Rider->AI())->InitDespawnHorse(caster); } } @@ -784,10 +794,11 @@ public: { if (Player* player = charmer->ToPlayer()) { - // for quest Into the Realm of Shadows(12687) - if (me->GetEntry() == 28788 && player->GetQuestStatus(12687) == QUEST_STATUS_INCOMPLETE) + // for quest Into the Realm of Shadows(QUEST_INTO_REALM_OF_SHADOWS) + if (me->GetEntry() == NPC_SALANAR_IN_REALM_OF_SHADOWS && player->GetQuestStatus(QUEST_INTO_REALM_OF_SHADOWS) == QUEST_STATUS_INCOMPLETE) { - player->GroupEventHappens(12687, me); + player->GroupEventHappens(QUEST_INTO_REALM_OF_SHADOWS, me); + Talk(SALANAR_SAY); charmer->RemoveAurasDueToSpell(SPELL_EFFECT_OVERTAKE); if (Creature* creature = who->ToCreature()) { @@ -796,14 +807,17 @@ public: } } - if (player->HasAura(SPELL_REALM_OF_SHADOWS)) - player->RemoveAurasDueToSpell(SPELL_REALM_OF_SHADOWS); + player->RemoveAurasDueToSpell(SPELL_REALM_OF_SHADOWS); } } } } }; + CreatureAI* GetAI(Creature* creature) const override + { + return new npc_salanar_the_horsemanAI(creature); + } }; /*###### diff --git a/src/server/scripts/EasternKingdoms/ZulAman/zulaman.cpp b/src/server/scripts/EasternKingdoms/ZulAman/zulaman.cpp index 3b52e6775b2..1927b0f9829 100644 --- a/src/server/scripts/EasternKingdoms/ZulAman/zulaman.cpp +++ b/src/server/scripts/EasternKingdoms/ZulAman/zulaman.cpp @@ -268,9 +268,9 @@ class npc_harrison_jones : public CreatureScript void EnterCombat(Unit* /*who*/) override { } - void sGossipSelect(Player* player, uint32 sender, uint32 action) override + void sGossipSelect(Player* player, uint32 menuId, uint32 gossipListId) override { - if (me->GetCreatureTemplate()->GossipMenuId == sender && !action) + if (me->GetCreatureTemplate()->GossipMenuId == menuId && !gossipListId) { player->CLOSE_GOSSIP_MENU(); me->SetFacingToObject(player); diff --git a/src/server/scripts/Kalimdor/BlackfathomDeeps/blackfathom_deeps.cpp b/src/server/scripts/Kalimdor/BlackfathomDeeps/blackfathom_deeps.cpp index 8d9406adee1..ab7509c7a2b 100644 --- a/src/server/scripts/Kalimdor/BlackfathomDeeps/blackfathom_deeps.cpp +++ b/src/server/scripts/Kalimdor/BlackfathomDeeps/blackfathom_deeps.cpp @@ -226,7 +226,7 @@ public: } } - void sGossipSelect(Player* player, uint32 /*sender*/, uint32 /*action*/) override + void sGossipSelect(Player* player, uint32 /*menuId*/, uint32 /*gossipListId*/) override { DoCast(player, SPELL_TELEPORT_DARNASSUS); } diff --git a/src/server/scripts/Kalimdor/zone_azshara.cpp b/src/server/scripts/Kalimdor/zone_azshara.cpp index 6727aa69552..d6aa943123b 100644 --- a/src/server/scripts/Kalimdor/zone_azshara.cpp +++ b/src/server/scripts/Kalimdor/zone_azshara.cpp @@ -345,7 +345,7 @@ public: } } - void sGossipSelect(Player* player, uint32 /*sender*/, uint32 /*action*/) override + void sGossipSelect(Player* player, uint32 /*menuId*/, uint32 /*gossipListId*/) override { player->CLOSE_GOSSIP_MENU(); me->CastSpell(player, SPELL_GIVE_SOUTHFURY_MOONSTONE, true); diff --git a/src/server/scripts/Kalimdor/zone_azuremyst_isle.cpp b/src/server/scripts/Kalimdor/zone_azuremyst_isle.cpp index 388834d1eff..52e32d3d37c 100644 --- a/src/server/scripts/Kalimdor/zone_azuremyst_isle.cpp +++ b/src/server/scripts/Kalimdor/zone_azuremyst_isle.cpp @@ -230,7 +230,7 @@ public: Talk(ATTACK_YELL, who); } - void sGossipSelect(Player* player, uint32 /*sender*/, uint32 /*action*/) override + void sGossipSelect(Player* player, uint32 /*menuId*/, uint32 /*gossipListId*/) override { player->CLOSE_GOSSIP_MENU(); me->setFaction(FACTION_HOSTILE); diff --git a/src/server/scripts/Kalimdor/zone_bloodmyst_isle.cpp b/src/server/scripts/Kalimdor/zone_bloodmyst_isle.cpp index 427d4bae217..c4ab8f76487 100644 --- a/src/server/scripts/Kalimdor/zone_bloodmyst_isle.cpp +++ b/src/server/scripts/Kalimdor/zone_bloodmyst_isle.cpp @@ -541,7 +541,7 @@ public: case PHASE_PLANT_FIRST_DETONATE: // first explosives detonate finish for (GuidList::iterator itr = _explosivesGuids.begin(); itr != _explosivesGuids.end(); ++itr) { - if (GameObject* explosive = sObjectAccessor->GetGameObject(*me, *itr)) + if (GameObject* explosive = ObjectAccessor::GetGameObject(*me, *itr)) me->RemoveGameObject(explosive, true); } _explosivesGuids.clear(); @@ -638,7 +638,7 @@ public: case PHASE_PLANT_SECOND_DETONATE: // second explosives detonate finish for (GuidList::iterator itr = _explosivesGuids.begin(); itr != _explosivesGuids.end(); ++itr) { - if (GameObject* explosive = sObjectAccessor->GetGameObject(*me, *itr)) + if (GameObject* explosive = ObjectAccessor::GetGameObject(*me, *itr)) me->RemoveGameObject(explosive, true); } _explosivesGuids.clear(); diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_deathbringer_saurfang.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_deathbringer_saurfang.cpp index 943be27943d..365e0a1588d 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_deathbringer_saurfang.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_deathbringer_saurfang.cpp @@ -203,6 +203,9 @@ enum Misc { DATA_MADE_A_MESS = 45374613, // 4537, 4613 are achievement IDs FACTION_SCOURGE = 974, + + GOSSIP_MENU_MURADIN_BRONZEBEARD = 10934, + GOSSIP_MENU_HIGH_OVERLORD_SAURFANG = 10952 }; enum MovePoints @@ -634,6 +637,15 @@ class npc_high_overlord_saurfang_icc : public CreatureScript _events.Reset(); } + void sGossipSelect(Player* player, uint32 menuId, uint32 /*gossipListId*/) override + { + if (menuId == GOSSIP_MENU_HIGH_OVERLORD_SAURFANG) + { + player->PlayerTalkClass->SendCloseGossip(); + DoAction(ACTION_START_EVENT); + } + } + void DoAction(int32 action) override { switch (action) @@ -798,28 +810,6 @@ class npc_high_overlord_saurfang_icc : public CreatureScript std::list<Creature*> _guardList; }; - bool OnGossipHello(Player* player, Creature* creature) override - { - InstanceScript* instance = creature->GetInstanceScript(); - if (instance && instance->GetBossState(DATA_DEATHBRINGER_SAURFANG) != DONE) - { - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, "We are ready to go, High Overlord. The Lich King must fall!", 631, -ACTION_START_EVENT); - player->SEND_GOSSIP_MENU(DEFAULT_GOSSIP_MESSAGE, creature->GetGUID()); - } - - return true; - } - - bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override - { - player->PlayerTalkClass->ClearMenus(); - player->CLOSE_GOSSIP_MENU(); - if (action == -ACTION_START_EVENT) - creature->AI()->DoAction(ACTION_START_EVENT); - - return true; - } - CreatureAI* GetAI(Creature* creature) const override { return GetIcecrownCitadelAI<npc_high_overlord_saurfangAI>(creature); @@ -843,6 +833,15 @@ class npc_muradin_bronzebeard_icc : public CreatureScript _events.Reset(); } + void sGossipSelect(Player* player, uint32 menuId, uint32 /*gossipListId*/) override + { + if (menuId == GOSSIP_MENU_MURADIN_BRONZEBEARD) + { + player->PlayerTalkClass->SendCloseGossip(); + DoAction(ACTION_START_EVENT); + } + } + void DoAction(int32 action) override { switch (action) @@ -946,28 +945,6 @@ class npc_muradin_bronzebeard_icc : public CreatureScript std::list<Creature*> _guardList; }; - bool OnGossipHello(Player* player, Creature* creature) override - { - InstanceScript* instance = creature->GetInstanceScript(); - if (instance && instance->GetBossState(DATA_DEATHBRINGER_SAURFANG) != DONE) - { - player->ADD_GOSSIP_ITEM(0, "Let it begin...", 631, -ACTION_START_EVENT + 1); - player->SEND_GOSSIP_MENU(DEFAULT_GOSSIP_MESSAGE, creature->GetGUID()); - } - - return true; - } - - bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override - { - player->PlayerTalkClass->ClearMenus(); - player->CLOSE_GOSSIP_MENU(); - if (action == -ACTION_START_EVENT + 1) - creature->AI()->DoAction(ACTION_START_EVENT); - - return true; - } - CreatureAI* GetAI(Creature* creature) const override { return GetIcecrownCitadelAI<npc_muradin_bronzebeard_iccAI>(creature); diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp index 78730b535aa..babd444288f 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp @@ -961,7 +961,7 @@ class npc_high_overlord_saurfang_igb : public CreatureScript } } - void sGossipSelect(Player* /*player*/, uint32 /*sender*/, uint32 /*action*/) override + void sGossipSelect(Player* /*player*/, uint32 /*menuId*/, uint32 /*gossipListId*/) override { me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); me->GetTransport()->EnableMovement(true); @@ -1229,7 +1229,7 @@ class npc_muradin_bronzebeard_igb : public CreatureScript } } - void sGossipSelect(Player* /*player*/, uint32 /*sender*/, uint32 /*action*/) override + void sGossipSelect(Player* /*player*/, uint32 /*menuId*/, uint32 /*gossipListId*/) override { me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); me->GetTransport()->EnableMovement(true); @@ -1394,7 +1394,7 @@ class npc_zafod_boombox : public CreatureScript me->SetReactState(REACT_PASSIVE); } - void sGossipSelect(Player* player, uint32 /*sender*/, uint32 /*action*/) override + void sGossipSelect(Player* player, uint32 /*menuId*/, uint32 /*gossipListId*/) override { player->AddItem(ITEM_GOBLIN_ROCKET_PACK, 1); player->PlayerTalkClass->SendCloseGossip(); diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp index 6c512546a24..4fadc0bf8fa 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp @@ -1199,9 +1199,9 @@ class npc_tirion_fordring_tft : public CreatureScript SetEquipmentSlots(true); // remove glow on ashbringer } - void sGossipSelect(Player* /*player*/, uint32 sender, uint32 action) override + void sGossipSelect(Player* /*player*/, uint32 menuId, uint32 gossipListId) override { - if (me->GetCreatureTemplate()->GossipMenuId == sender && !action) + if (me->GetCreatureTemplate()->GossipMenuId == menuId && !gossipListId) { _events.SetPhase(PHASE_INTRO); me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); diff --git a/src/server/scripts/Northrend/Naxxramas/boss_heigan.cpp b/src/server/scripts/Northrend/Naxxramas/boss_heigan.cpp index 3d7c128c8dd..5248c48029c 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_heigan.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_heigan.cpp @@ -21,36 +21,42 @@ #include "naxxramas.h" #include "Player.h" -enum Heigan +enum Spells { - SPELL_DECREPIT_FEVER = 29998, // 25-man: 55011 - SPELL_SPELL_DISRUPTION = 29310, - SPELL_PLAGUE_CLOUD = 29350, - - SAY_AGGRO = 0, - SAY_SLAY = 1, - SAY_TAUNT = 2, - SAY_DEATH = 3 + SPELL_DECREPIT_FEVER = 29998, // 25-man: 55011 + SPELL_SPELL_DISRUPTION = 29310, + SPELL_PLAGUE_CLOUD = 29350, + SPELL_TELEPORT_SELF = 30211, +}; + +enum Yells +{ + SAY_AGGRO = 0, + SAY_SLAY = 1, + SAY_TAUNT = 2, + SAY_DEATH = 3, + + EMOTE_DANCE = 4, + EMOTE_DANCE_END = 5, }; enum Events { - EVENT_NONE, - EVENT_DISRUPT, + EVENT_DISRUPT = 1, EVENT_FEVER, EVENT_ERUPT, - EVENT_PHASE, + EVENT_DANCE, + EVENT_DANCE_END }; enum Phases { PHASE_FIGHT = 1, - PHASE_DANCE, + PHASE_DANCE }; enum Misc { - ACTION_SAFETY_DANCE_FAIL = 1, DATA_SAFETY_DANCE = 19962139 }; @@ -66,39 +72,25 @@ public: struct boss_heiganAI : public BossAI { - boss_heiganAI(Creature* creature) : BossAI(creature, BOSS_HEIGAN) + boss_heiganAI(Creature* creature) : BossAI(creature, BOSS_HEIGAN), eruptSection(0), eruptDirection(false), safetyDance(false) { } + + void Reset() override { - eruptSection = 0; - eruptDirection = false; - safetyDance = false; - phase = PHASE_FIGHT; + me->SetReactState(REACT_AGGRESSIVE); + _Reset(); } - uint32 eruptSection; - bool eruptDirection; - bool safetyDance; - Phases phase; - void KilledUnit(Unit* who) override { - if (!(rand32() % 5)) - Talk(SAY_SLAY); + Talk(SAY_SLAY); + if (who->GetTypeId() == TYPEID_PLAYER) safetyDance = false; } - void SetData(uint32 id, uint32 data) override - { - if (id == DATA_SAFETY_DANCE) - safetyDance = data ? true : false; - } - uint32 GetData(uint32 type) const override { - if (type == DATA_SAFETY_DANCE) - return safetyDance ? 1 : 0; - - return 0; + return (type == DATA_SAFETY_DANCE && safetyDance) ? 1u : 0u; } void JustDied(Unit* /*killer*/) override @@ -111,35 +103,14 @@ public: { _EnterCombat(); Talk(SAY_AGGRO); - EnterPhase(PHASE_FIGHT); - safetyDance = true; - } - - void EnterPhase(Phases newPhase) - { - phase = newPhase; - events.Reset(); + eruptSection = 3; - if (phase == PHASE_FIGHT) - { - events.ScheduleEvent(EVENT_DISRUPT, urand(10000, 25000)); - events.ScheduleEvent(EVENT_FEVER, urand(15000, 20000)); - events.ScheduleEvent(EVENT_PHASE, 90000); - events.ScheduleEvent(EVENT_ERUPT, 15000); - me->GetMotionMaster()->MoveChase(me->GetVictim()); - } - else - { - float x, y, z, o; - me->GetHomePosition(x, y, z, o); - me->NearTeleportTo(x, y, z, o - (float(M_PI) / 2)); - me->GetMotionMaster()->Clear(); - me->GetMotionMaster()->MoveIdle(); - me->SetTarget(ObjectGuid::Empty); - DoCastAOE(SPELL_PLAGUE_CLOUD); - events.ScheduleEvent(EVENT_PHASE, 45000); - events.ScheduleEvent(EVENT_ERUPT, 8000); - } + events.ScheduleEvent(EVENT_DISRUPT, urand(15 * IN_MILLISECONDS, 20 * IN_MILLISECONDS), 0, PHASE_FIGHT); + events.ScheduleEvent(EVENT_FEVER, urand(10 * IN_MILLISECONDS, 20 * IN_MILLISECONDS), 0, PHASE_FIGHT); + events.ScheduleEvent(EVENT_DANCE, 90 * IN_MILLISECONDS, 0, PHASE_FIGHT); + events.ScheduleEvent(EVENT_ERUPT, 15 * IN_MILLISECONDS, 0, PHASE_FIGHT); + + safetyDance = true; } void UpdateAI(uint32 diff) override @@ -155,15 +126,36 @@ public: { case EVENT_DISRUPT: DoCastAOE(SPELL_SPELL_DISRUPTION); - events.ScheduleEvent(EVENT_DISRUPT, urand(5000, 10000)); + events.ScheduleEvent(EVENT_DISRUPT, 11 * IN_MILLISECONDS); break; case EVENT_FEVER: DoCastAOE(SPELL_DECREPIT_FEVER); - events.ScheduleEvent(EVENT_FEVER, urand(20000, 25000)); + events.ScheduleEvent(EVENT_FEVER, urand(20 * IN_MILLISECONDS, 25 * IN_MILLISECONDS)); + break; + case EVENT_DANCE: + events.SetPhase(PHASE_DANCE); + Talk(SAY_TAUNT); + Talk(EMOTE_DANCE); + eruptSection = 3; + me->SetReactState(REACT_PASSIVE); + me->AttackStop(); + me->StopMoving(); + DoCast(SPELL_TELEPORT_SELF); + DoCastAOE(SPELL_PLAGUE_CLOUD); + events.ScheduleEvent(EVENT_DANCE_END, 45 * IN_MILLISECONDS, 0, PHASE_DANCE); + events.ScheduleEvent(EVENT_ERUPT, 10 * IN_MILLISECONDS); break; - case EVENT_PHASE: - /// @todo Add missing texts for both phase switches - EnterPhase(phase == PHASE_FIGHT ? PHASE_DANCE : PHASE_FIGHT); + case EVENT_DANCE_END: + events.SetPhase(PHASE_FIGHT); + Talk(EMOTE_DANCE_END); + eruptSection = 3; + events.ScheduleEvent(EVENT_DISRUPT, urand(10, 25) * IN_MILLISECONDS, 0, PHASE_FIGHT); + events.ScheduleEvent(EVENT_FEVER, urand(15, 20) * IN_MILLISECONDS, 0, PHASE_FIGHT); + events.ScheduleEvent(EVENT_DANCE, 90 * IN_MILLISECONDS, 0, PHASE_FIGHT); + events.ScheduleEvent(EVENT_ERUPT, 15 * IN_MILLISECONDS, 0, PHASE_FIGHT); + me->CastStop(); + me->SetReactState(REACT_AGGRESSIVE); + DoZoneInCombat(); break; case EVENT_ERUPT: instance->SetData(DATA_HEIGAN_ERUPT, eruptSection); @@ -176,13 +168,22 @@ public: eruptDirection ? ++eruptSection : --eruptSection; - events.ScheduleEvent(EVENT_ERUPT, phase == PHASE_FIGHT ? 10000 : 3000); + if (events.IsInPhase(PHASE_DANCE)) + events.ScheduleEvent(EVENT_ERUPT, 3 * IN_MILLISECONDS, 0, PHASE_DANCE); + else + events.ScheduleEvent(EVENT_ERUPT, 10 * IN_MILLISECONDS, 0, PHASE_FIGHT); break; } } DoMeleeAttackIfReady(); } + + private: + uint32 eruptSection; + bool eruptDirection; + + bool safetyDance; // is achievement still possible? (= no player deaths yet) }; }; @@ -205,7 +206,7 @@ class spell_heigan_eruption : public SpellScriptLoader if (GetHitDamage() >= int32(GetHitPlayer()->GetHealth())) if (InstanceScript* instance = caster->GetInstanceScript()) if (Creature* Heigan = ObjectAccessor::GetCreature(*caster, instance->GetGuidData(DATA_HEIGAN))) - Heigan->AI()->SetData(DATA_SAFETY_DANCE, 0); + Heigan->AI()->KilledUnit(GetHitPlayer()); } void Register() override @@ -223,9 +224,7 @@ class spell_heigan_eruption : public SpellScriptLoader class achievement_safety_dance : public AchievementCriteriaScript { public: - achievement_safety_dance() : AchievementCriteriaScript("achievement_safety_dance") - { - } + achievement_safety_dance() : AchievementCriteriaScript("achievement_safety_dance") { } bool OnCheck(Player* /*player*/, Unit* target) override { diff --git a/src/server/scripts/Northrend/Naxxramas/boss_noth.cpp b/src/server/scripts/Northrend/Naxxramas/boss_noth.cpp index f5e5b287571..810d446111a 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_noth.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_noth.cpp @@ -19,51 +19,58 @@ #include "ScriptedCreature.h" #include "naxxramas.h" -enum Noth +enum Phases { - SAY_AGGRO = 0, - SAY_SUMMON = 1, - SAY_SLAY = 2, - SAY_DEATH = 3, - - SOUND_DEATH = 8848, - - SPELL_CURSE_PLAGUEBRINGER = 29213, // 25-man: 54835 - SPELL_CRIPPLE = 29212, // 25-man: 54814 - SPELL_TELEPORT = 29216, - - NPC_WARRIOR = 16984, - NPC_CHAMPION = 16983, - NPC_GUARDIAN = 16981 + PHASE_NONE, + PHASE_GROUND, + PHASE_BALCONY }; -#define SPELL_BLINK RAND(29208, 29209, 29210, 29211) +enum Events +{ + EVENT_CURSE = 1, // curse of the plaguebringer + EVENT_BLINK, // blink (25m only) + EVENT_WARRIOR, // summon warriors during ground phase + EVENT_BALCONY, // become untargetable and begin balcony phase + EVENT_BALCONY_TELEPORT, // actually teleport to balcony, this is slightly delayed + EVENT_WAVE, // spawn wave during balcony phase + EVENT_GROUND, // end balcony phase and teleport to ground + EVENT_GROUND_ATTACKABLE // become attackable and aggressive again at start of ground phase, once again slightly delayed to prevent motionmaster weirdness +}; -// Teleport position of Noth on his balcony -Position const Teleport = { 2631.370f, -3529.680f, 274.040f, 6.277f }; +enum Talk +{ + SAY_AGGRO = 0, + SAY_SUMMON = 1, + SAY_SLAY = 2, + SAY_DEATH = 3, -#define MAX_SUMMON_POS 5 + EMOTE_SUMMON = 4, // ground phase + EMOTE_SUMMON_WAVE = 5, // balcony phase + EMOTE_TELEPORT_1 = 6, // ground to balcony + EMOTE_TELEPORT_2 = 7 // balcony to ground +}; -Position const SummonPos[MAX_SUMMON_POS] = +enum Spells { - { 2728.12f, -3544.43f, 261.91f, 6.04f }, - { 2729.05f, -3544.47f, 261.91f, 5.58f }, - { 2728.24f, -3465.08f, 264.20f, 3.56f }, - { 2704.11f, -3456.81f, 265.53f, 4.51f }, - { 2663.56f, -3464.43f, 262.66f, 5.20f } + SPELL_CURSE = 29213, // 25-man: 54835 + SPELL_CRIPPLE = 29212, // 25-man: 54814 + + SPELL_TELEPORT = 29216, // ground to balcony + SPELL_TELEPORT_BACK = 29231 // balcony to ground }; -enum Events +enum Adds { - EVENT_NONE, - EVENT_BERSERK, - EVENT_CURSE, - EVENT_BLINK, - EVENT_WARRIOR, - EVENT_BALCONY, - EVENT_WAVE, - EVENT_GROUND + N_WARRIOR_SPELLS = 3, + N_CHAMPION_SPELLS = 6, + N_GUARDIAN_SPELLS = 3 }; +const uint32 SummonWarriorSpells[N_WARRIOR_SPELLS] = { 29247, 29248, 29249 }; +const uint32 SummonChampionSpells[N_CHAMPION_SPELLS] = { 29238, 29255, 29257, 29258, 29262, 29267 }; +const uint32 SummonGuardianSpells[N_GUARDIAN_SPELLS] = { 29239, 29256, 29268 }; + +#define SPELL_BLINK RAND(29208, 29209, 29210, 29211) class boss_noth : public CreatureScript { @@ -72,16 +79,38 @@ public: struct boss_nothAI : public BossAI { - boss_nothAI(Creature* creature) : BossAI(creature, BOSS_NOTH) + boss_nothAI(Creature* creature) : BossAI(creature, BOSS_NOTH), balconyCount(0), justBlinked(false) { - balconyCount = 0; - waveCount = 0; + std::copy(SummonWarriorSpells, SummonWarriorSpells + N_WARRIOR_SPELLS, _SummonWarriorSpells); + std::copy(SummonChampionSpells, SummonChampionSpells + N_CHAMPION_SPELLS, _SummonChampionSpells); + std::copy(SummonGuardianSpells, SummonGuardianSpells + N_GUARDIAN_SPELLS, _SummonGuardianSpells); + + events.SetPhase(PHASE_NONE); + } + + void EnterEvadeMode() override + { + Reset(); // teleport back first + _EnterEvadeMode(); } void Reset() override { - me->SetReactState(REACT_AGGRESSIVE); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + if (!me->IsAlive()) + return; + + // in case we reset during balcony phase + if (events.IsInPhase(PHASE_BALCONY)) + { + DoCastAOE(SPELL_TELEPORT_BACK); + me->SetReactState(REACT_AGGRESSIVE); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_NOT_SELECTABLE); + } + + balconyCount = 0; + events.SetPhase(PHASE_NONE); + justBlinked = false; + _Reset(); } @@ -89,31 +118,44 @@ public: { _EnterCombat(); Talk(SAY_AGGRO); - balconyCount = 0; EnterPhaseGround(); } void EnterPhaseGround() { - me->SetReactState(REACT_AGGRESSIVE); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + events.SetPhase(PHASE_GROUND); + DoZoneInCombat(); if (me->getThreatManager().isThreatListEmpty()) - EnterEvadeMode(); + Reset(); else { - events.ScheduleEvent(EVENT_BALCONY, 110000); - events.ScheduleEvent(EVENT_CURSE, 10000 + rand32() % 15000); - events.ScheduleEvent(EVENT_WARRIOR, 30000); + uint8 secondsGround; + switch (balconyCount) + { + case 0: + secondsGround = 90; + break; + case 1: + secondsGround = 110; + break; + case 2: + default: + secondsGround = 180; + } + events.ScheduleEvent(EVENT_GROUND_ATTACKABLE, 2 * IN_MILLISECONDS, 0, PHASE_GROUND); + events.ScheduleEvent(EVENT_BALCONY, secondsGround * IN_MILLISECONDS, 0, PHASE_GROUND); + events.ScheduleEvent(EVENT_CURSE, urand(10,25) * IN_MILLISECONDS, 0, PHASE_GROUND); + events.ScheduleEvent(EVENT_WARRIOR, urand(20,30) * IN_MILLISECONDS, 0, PHASE_GROUND); if (GetDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL) - events.ScheduleEvent(EVENT_BLINK, urand(20000, 40000)); + events.ScheduleEvent(EVENT_BLINK, urand(20,30) * IN_MILLISECONDS, 0, PHASE_GROUND); } } - void KilledUnit(Unit* /*victim*/) override + void KilledUnit(Unit* victim) override { - if (!(rand32() % 5)) + if(victim->GetTypeId() == TYPEID_PLAYER) Talk(SAY_SLAY); } @@ -121,7 +163,7 @@ public: { summons.Summon(summon); summon->setActive(true); - summon->AI()->DoZoneInCombat(); + summon->AI()->DoZoneInCombat(nullptr, 250.0f); // specify range to cover entire room - default 50yd is not enough } void JustDied(Unit* /*killer*/) override @@ -130,10 +172,35 @@ public: Talk(SAY_DEATH); } - void SummonUndead(uint32 entry, uint32 num) + void DamageTaken(Unit* /*who*/, uint32& damage) // prevent noth from somehow dying in the balcony phase { - for (uint32 i = 0; i < num; ++i) - me->SummonCreature(entry, SummonPos[rand32() % MAX_SUMMON_POS], TEMPSUMMON_CORPSE_DESPAWN, 60000); + if (!events.IsInPhase(PHASE_BALCONY)) + return; + if (damage < me->GetHealth()) + return; + + me->SetHealth(1u); + damage = 0u; + } + + void HandleSummon(uint32* spellsList, const uint8 nSpells, uint8 num) + { // this ensures we do not spawn two mobs using the same spell (<=> in the same position) if we can help it + while (num) + for (uint8 it = 0; it < nSpells && num; ++it) + { + num--; + uint8 selected = urand(it, nSpells - 1); + DoCastAOE(spellsList[selected]); + if (selected != it) // shuffle the selected into the part of the array that is no longer being searched + std::swap(spellsList[selected], spellsList[it]); + } + } + + void CastSummon(uint8 nWarrior, uint8 nChampion, uint8 nGuardian) + { + HandleSummon(_SummonWarriorSpells, N_WARRIOR_SPELLS, nWarrior); + HandleSummon(_SummonChampionSpells, N_CHAMPION_SPELLS, nChampion); + HandleSummon(_SummonGuardianSpells, N_GUARDIAN_SPELLS, nGuardian); } void UpdateAI(uint32 diff) override @@ -151,72 +218,115 @@ public: switch (eventId) { case EVENT_CURSE: - DoCastAOE(SPELL_CURSE_PLAGUEBRINGER); - events.ScheduleEvent(EVENT_CURSE, urand(50000, 60000)); - return; + { + DoCastAOE(SPELL_CURSE); + events.ScheduleEvent(EVENT_CURSE, urand(50, 70) * IN_MILLISECONDS, 0, PHASE_GROUND); + break; + } case EVENT_WARRIOR: Talk(SAY_SUMMON); - SummonUndead(NPC_WARRIOR, RAID_MODE(2, 3)); - events.ScheduleEvent(EVENT_WARRIOR, 30000); - return; + Talk(EMOTE_SUMMON); + + CastSummon(RAID_MODE(2, 3), 0, 0); + + events.ScheduleEvent(EVENT_WARRIOR, 40 * IN_MILLISECONDS, 0, PHASE_GROUND); + break; case EVENT_BLINK: DoCastAOE(SPELL_CRIPPLE, true); DoCastAOE(SPELL_BLINK); DoResetThreat(); - events.ScheduleEvent(EVENT_BLINK, 40000); - return; + justBlinked = true; + + events.ScheduleEvent(EVENT_BLINK, 40000, 0, PHASE_GROUND); + break; case EVENT_BALCONY: + events.SetPhase(PHASE_BALCONY); me->SetReactState(REACT_PASSIVE); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_NOT_SELECTABLE); me->AttackStop(); + me->StopMoving(); me->RemoveAllAuras(); - me->NearTeleportTo(Teleport.GetPositionX(), Teleport.GetPositionY(), Teleport.GetPositionZ(), Teleport.GetOrientation()); - events.Reset(); - events.ScheduleEvent(EVENT_WAVE, urand(2000, 5000)); - waveCount = 0; - return; + + events.ScheduleEvent(EVENT_BALCONY_TELEPORT, 3 * IN_MILLISECONDS, 0, PHASE_BALCONY); + events.ScheduleEvent(EVENT_WAVE, urand(5 * IN_MILLISECONDS, 8 * IN_MILLISECONDS), 0, PHASE_BALCONY); + + uint8 secondsBalcony; + switch (balconyCount) + { + case 0: + secondsBalcony = 70; + break; + case 1: + secondsBalcony = 97; + break; + case 2: + default: + secondsBalcony = 120; + break; + } + events.ScheduleEvent(EVENT_GROUND, secondsBalcony * IN_MILLISECONDS, 0, PHASE_BALCONY); + break; + case EVENT_BALCONY_TELEPORT: + Talk(EMOTE_TELEPORT_1); + DoCastAOE(SPELL_TELEPORT); + break; case EVENT_WAVE: - Talk(SAY_SUMMON); + Talk(EMOTE_SUMMON_WAVE); switch (balconyCount) { case 0: - SummonUndead(NPC_CHAMPION, RAID_MODE(2, 4)); + CastSummon(0, RAID_MODE(2, 4), 0); break; case 1: - SummonUndead(NPC_CHAMPION, RAID_MODE(1, 2)); - SummonUndead(NPC_GUARDIAN, RAID_MODE(1, 2)); + CastSummon(0, RAID_MODE(1, 2), RAID_MODE(1, 2)); break; case 2: - SummonUndead(NPC_GUARDIAN, RAID_MODE(2, 4)); + CastSummon(0, 0, RAID_MODE(2, 4)); break; default: - SummonUndead(NPC_CHAMPION, RAID_MODE(5, 10)); - SummonUndead(NPC_GUARDIAN, RAID_MODE(5, 10)); + CastSummon(0, RAID_MODE(5, 10), RAID_MODE(5, 10)); break; } - ++waveCount; - events.ScheduleEvent(waveCount < 2 ? EVENT_WAVE : EVENT_GROUND, urand(30000, 45000)); - return; + events.ScheduleEvent(EVENT_WAVE, urand(30, 45) * IN_MILLISECONDS, 0, PHASE_BALCONY); + break; case EVENT_GROUND: - { ++balconyCount; - float x, y, z, o; - me->GetHomePosition(x, y, z, o); - me->NearTeleportTo(x, y, z, o); - events.ScheduleEvent(EVENT_BALCONY, 110000); + + DoCastAOE(SPELL_TELEPORT_BACK); + Talk(EMOTE_TELEPORT_2); + EnterPhaseGround(); - return; - } + break; + case EVENT_GROUND_ATTACKABLE: + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_NOT_SELECTABLE); + me->SetReactState(REACT_AGGRESSIVE); + break; } } - if (me->HasReactState(REACT_AGGRESSIVE)) - DoMeleeAttackIfReady(); + if (events.IsInPhase(PHASE_GROUND)) + { + /* workaround for movechase breaking after blinking + without this noth would just stand there unless his current target moves */ + if (justBlinked && me->GetVictim() && !me->IsWithinMeleeRange(me->EnsureVictim())) + { + me->GetMotionMaster()->Clear(); + me->GetMotionMaster()->MoveChase(me->EnsureVictim()); + justBlinked = false; + } + else + DoMeleeAttackIfReady(); + } } private: - uint32 waveCount; uint32 balconyCount; + + bool justBlinked; + + uint32 _SummonWarriorSpells[N_WARRIOR_SPELLS]; + uint32 _SummonChampionSpells[N_CHAMPION_SPELLS]; + uint32 _SummonGuardianSpells[N_GUARDIAN_SPELLS]; }; CreatureAI* GetAI(Creature* creature) const override diff --git a/src/server/scripts/Northrend/Naxxramas/instance_naxxramas.cpp b/src/server/scripts/Northrend/Naxxramas/instance_naxxramas.cpp index 3a431ab3ac0..692e6d8b589 100644 --- a/src/server/scripts/Northrend/Naxxramas/instance_naxxramas.cpp +++ b/src/server/scripts/Northrend/Naxxramas/instance_naxxramas.cpp @@ -78,12 +78,13 @@ ObjectData const objectData[] = { 0, 0, } }; -float const HeiganPos[2] = { 2796.0f, -3707.0f }; +// from P2 teleport spell stored target +float const HeiganPos[2] = { 2793.86f, -3707.38f }; float const HeiganEruptionSlope[3] = { - (-3685.0f - HeiganPos[1]) / (2724.0f - HeiganPos[0]), - (-3647.0f - HeiganPos[1]) / (2749.0f - HeiganPos[0]), - (-3637.0f - HeiganPos[1]) / (2771.0f - HeiganPos[0]) + (-3703.303223f - HeiganPos[1]) / (2777.494141f - HeiganPos[0]), // between right center and far right + (-3696.948242f - HeiganPos[1]) / (2785.624268f - HeiganPos[0]), // between left and right halves + (-3691.880615f - HeiganPos[1]) / (2790.280029f - HeiganPos[0]) // between far left and left center }; // 0 H x @@ -246,7 +247,6 @@ class instance_naxxramas : public InstanceMapScript if (go->GetGOInfo()->displayId == 6785 || go->GetGOInfo()->displayId == 1287) { uint32 section = GetEruptionSection(go->GetPositionX(), go->GetPositionY()); - HeiganEruptionGUID[section].erase(go->GetGUID()); return; } @@ -552,7 +552,7 @@ class instance_naxxramas : public InstanceMapScript // This Function is called in CheckAchievementCriteriaMeet and CheckAchievementCriteriaMeet is called before SetBossState(bossId, DONE), // so to check if all bosses are done the checker must exclude 1 boss, the last done, if there is at most 1 encouter in progress when is // called this function then all bosses are done. The one boss that check is the boss that calls this function, so it is dead. - bool AreAllEncoutersDone() + bool AreAllEncountersDone() { uint32 numBossAlive = 0; for (uint32 i = 0; i < EncounterCount; ++i) @@ -589,7 +589,7 @@ class instance_naxxramas : public InstanceMapScript case 13239: // Loatheb case 13240: // Thaddius case 7617: // Kel'Thuzad - if (AreAllEncoutersDone() && !playerDied) + if (AreAllEncountersDone() && !playerDied) return true; return false; } diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_yogg_saron.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_yogg_saron.cpp index 59c28cd3a5a..6f07a88536d 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_yogg_saron.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_yogg_saron.cpp @@ -1508,9 +1508,9 @@ class npc_observation_ring_keeper : public CreatureScript DoCast(SPELL_KEEPER_ACTIVE); } - void sGossipSelect(Player* player, uint32 sender, uint32 /*action*/) override + void sGossipSelect(Player* player, uint32 menuId, uint32 /*gossipListId*/) override { - if (sender != 10333) + if (menuId != 10333) return; me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); diff --git a/src/server/scripts/Northrend/zone_grizzly_hills.cpp b/src/server/scripts/Northrend/zone_grizzly_hills.cpp index 83fd3859716..54b06260e78 100644 --- a/src/server/scripts/Northrend/zone_grizzly_hills.cpp +++ b/src/server/scripts/Northrend/zone_grizzly_hills.cpp @@ -762,7 +762,7 @@ public: } } - void sGossipSelect(Player* player, uint32 /*sender*/, uint32 /*action*/) override + void sGossipSelect(Player* player, uint32 /*menuId*/, uint32 /*gossipListId*/) override { DoCast(player, SPELL_SUMMON_ASHWOOD_BRAND); } diff --git a/src/server/scripts/Northrend/zone_storm_peaks.cpp b/src/server/scripts/Northrend/zone_storm_peaks.cpp index 25bf4826000..e86e907b550 100644 --- a/src/server/scripts/Northrend/zone_storm_peaks.cpp +++ b/src/server/scripts/Northrend/zone_storm_peaks.cpp @@ -85,13 +85,13 @@ public: DoMeleeAttackIfReady(); } - void sGossipSelect(Player* player, uint32 sender, uint32 action) override + void sGossipSelect(Player* player, uint32 menuId, uint32 gossipListId) override { - if (sender == GOSSIP_ID && action == GOSSIP_OPTION_ID) + if (menuId == GOSSIP_ID && gossipListId == GOSSIP_OPTION_ID) { player->CLOSE_GOSSIP_MENU(); me->setFaction(113); - npc_escortAI::Start(true, true, player->GetGUID()); + Start(true, true, player->GetGUID()); } } }; @@ -476,7 +476,7 @@ public: objectCounter = 0; } - void sGossipSelect(Player* player, uint32 /*sender*/, uint32 /*action*/) override + void sGossipSelect(Player* player, uint32 /*menuId*/, uint32 /*gossipListId*/) override { player->CLOSE_GOSSIP_MENU(); playerGUID = player->GetGUID(); diff --git a/src/server/scripts/Northrend/zone_zuldrak.cpp b/src/server/scripts/Northrend/zone_zuldrak.cpp index 91c796a6e69..e36930f5745 100644 --- a/src/server/scripts/Northrend/zone_zuldrak.cpp +++ b/src/server/scripts/Northrend/zone_zuldrak.cpp @@ -267,7 +267,7 @@ public: return; } - void sGossipSelect(Player* player, uint32 /*sender*/, uint32 /*action*/) override + void sGossipSelect(Player* player, uint32 /*menuId*/, uint32 /*gossipListId*/) override { _events.ScheduleEvent(EVENT_RECRUIT_1, 100); player->CLOSE_GOSSIP_MENU(); @@ -552,7 +552,7 @@ public: } } - void sGossipSelect(Player* player, uint32 /*sender*/, uint32 /*action*/) override + void sGossipSelect(Player* player, uint32 /*menuId*/, uint32 /*gossipListId*/) override { player->CLOSE_GOSSIP_MENU(); DoCast(player, SPELL_ALCHEMIST_APPRENTICE_INVISBUFF); diff --git a/src/server/scripts/Outland/BlackTemple/black_temple.cpp b/src/server/scripts/Outland/BlackTemple/black_temple.cpp index 764cd9594ea..f044fcef5c7 100644 --- a/src/server/scripts/Outland/BlackTemple/black_temple.cpp +++ b/src/server/scripts/Outland/BlackTemple/black_temple.cpp @@ -15,22 +15,12 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -/* -Name: Black_Temple -Complete: 100% -Comment: Spirit of Olum: Player Teleporter to Seer Kanai Teleport after defeating Naj'entus and Supremus. -*/ - #include "ScriptMgr.h" #include "ScriptedCreature.h" -#include "ScriptedGossip.h" #include "black_temple.h" -#include "Player.h" enum Spells { - // Spirit of Olum - SPELL_TELEPORT = 41566, // Wrathbone Flayer SPELL_CLEAVE = 15496, SPELL_IGNORED = 39544, @@ -53,36 +43,6 @@ enum Events }; // ######################################################## -// Spirit of Olum -// ######################################################## - -class npc_spirit_of_olum : public CreatureScript -{ -public: - npc_spirit_of_olum() : CreatureScript("npc_spirit_of_olum") { } - - struct npc_spirit_of_olumAI : public ScriptedAI - { - npc_spirit_of_olumAI(Creature* creature) : ScriptedAI(creature) { } - - void sGossipSelect(Player* player, uint32 /*sender*/, uint32 action) override - { - if (action == 1) - { - player->CLOSE_GOSSIP_MENU(); - player->InterruptNonMeleeSpells(false); - player->CastSpell(player, SPELL_TELEPORT, false); - } - } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return new npc_spirit_of_olumAI(creature); - } -}; - -// ######################################################## // Wrathbone Flayer // ######################################################## @@ -123,7 +83,6 @@ public: void UpdateAI(uint32 diff) override { - if (!_enteredCombat) { _events.Update(diff); @@ -215,12 +174,11 @@ public: CreatureAI* GetAI(Creature* creature) const override { - return GetInstanceAI<npc_wrathbone_flayerAI>(creature); + return GetBlackTempleAI<npc_wrathbone_flayerAI>(creature); } }; void AddSC_black_temple() { - new npc_spirit_of_olum(); new npc_wrathbone_flayer(); } diff --git a/src/server/scripts/Outland/BlackTemple/boss_illidan.cpp b/src/server/scripts/Outland/BlackTemple/boss_illidan.cpp index 7cc460d4548..f8c031f69e7 100644 --- a/src/server/scripts/Outland/BlackTemple/boss_illidan.cpp +++ b/src/server/scripts/Outland/BlackTemple/boss_illidan.cpp @@ -1779,7 +1779,7 @@ public: DoMeleeAttackIfReady(); } - void sGossipSelect(Player* player, uint32 /*sender*/, uint32 /*action*/) override + void sGossipSelect(Player* player, uint32 /*menuId*/, uint32 /*gossipListId*/) override { player->CLOSE_GOSSIP_MENU(); EnterPhase(PHASE_CHANNEL); diff --git a/src/server/scripts/Outland/BlackTemple/boss_shade_of_akama.cpp b/src/server/scripts/Outland/BlackTemple/boss_shade_of_akama.cpp index e4369f0348d..25ff1bf9536 100644 --- a/src/server/scripts/Outland/BlackTemple/boss_shade_of_akama.cpp +++ b/src/server/scripts/Outland/BlackTemple/boss_shade_of_akama.cpp @@ -519,9 +519,9 @@ public: DoMeleeAttackIfReady(); } - void sGossipSelect(Player* player, uint32 /*sender*/, uint32 action) override + void sGossipSelect(Player* player, uint32 /*menuId*/, uint32 gossipListId) override { - if (action == 0) + if (gossipListId == 0) { player->CLOSE_GOSSIP_MENU(); StartChannel = true; diff --git a/src/server/worldserver/Main.cpp b/src/server/worldserver/Main.cpp index cf33ba300f7..ab132621437 100644 --- a/src/server/worldserver/Main.cpp +++ b/src/server/worldserver/Main.cpp @@ -256,7 +256,6 @@ extern int main(int argc, char** argv) sInstanceSaveMgr->Unload(); sOutdoorPvPMgr->Die(); sMapMgr->UnloadAll(); // unload all grids (including locked in memory) - sObjectAccessor->UnloadAll(); // unload 'i_player2corpse' storage and remove from world sScriptMgr->Unload(); // set server offline diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist index b4a7e7a4864..1689f05966a 100644 --- a/src/server/worldserver/worldserver.conf.dist +++ b/src/server/worldserver/worldserver.conf.dist @@ -2605,6 +2605,14 @@ PlayerStart.MapsExplored = 0 HonorPointsAfterDuel = 0 # +# ResetCoolDownAfterDuel +# Description: Reset all cooldowns after duel, but only if player has no cooldowns before the duel. +# Default: 0 - (Disabled) +# 1 - (Enabled) + +ResetCoolDownAfterDuel = 0 + +# # AlwaysMaxWeaponSkill # Description: Players will automatically gain max weapon/defense skill when logging in, # or leveling. |