From a93afab540d5e981fc47af16f8e988b83c3710bd Mon Sep 17 00:00:00 2001 From: Antz Date: Thu, 10 Jan 2019 00:11:41 +0000 Subject: [PATCH] Add Disables table based on the work of @Olion on Zero Added `disables` table Mostly backported from TC. Changed table format (mapID in lower 16 bit, areaID in higer 16 bit of data field). Added CREATURE_SPAWN and GAMEOBJECT_SPAWN disable types. --- src/game/BattleGround/BattleGroundHandler.cpp | 9 + src/game/BattleGround/BattleGroundMgr.cpp | 4 + src/game/ChatCommands/Level3.cpp | 10 + src/game/Object/ObjectMgr.cpp | 24 ++ src/game/Object/Player.cpp | 23 +- src/game/OutdoorPvP/OutdoorPvPMgr.cpp | 3 +- src/game/Tools/Language.h | 20 +- src/game/WorldHandlers/Chat.cpp | 1 + src/game/WorldHandlers/Chat.h | 1 + src/game/WorldHandlers/DisableMgr.cpp | 390 ++++++++++++++++++ src/game/WorldHandlers/DisableMgr.h | 71 ++++ src/game/WorldHandlers/Spell.cpp | 37 +- src/game/WorldHandlers/World.cpp | 14 + src/game/vmap/VMapManager2.cpp | 48 ++- src/game/vmap/VMapManager2.h | 11 + src/shared/revision.h | 4 +- 16 files changed, 621 insertions(+), 49 deletions(-) create mode 100644 src/game/WorldHandlers/DisableMgr.cpp create mode 100644 src/game/WorldHandlers/DisableMgr.h diff --git a/src/game/BattleGround/BattleGroundHandler.cpp b/src/game/BattleGround/BattleGroundHandler.cpp index d0d7a580f..c26f0217a 100644 --- a/src/game/BattleGround/BattleGroundHandler.cpp +++ b/src/game/BattleGround/BattleGroundHandler.cpp @@ -39,6 +39,7 @@ #include "Language.h" #include "ScriptMgr.h" #include "World.h" +#include "DisableMgr.h" void WorldSession::HandleBattlemasterHelloOpcode(WorldPacket& recv_data) { @@ -107,6 +108,14 @@ void WorldSession::HandleBattlemasterJoinOpcode(WorldPacket& recv_data) BattleGroundTypeId bgTypeId = BattleGroundTypeId(bgTypeId_); + DEBUG_LOG("WORLD: Received opcode CMSG_BATTLEMASTER_JOIN from %s", guid.GetString().c_str()); + + if (DisableMgr::IsDisabledFor(DISABLE_TYPE_BATTLEGROUND, bgTypeId)) + { + ChatHandler(this).SendSysMessage(LANG_BG_IS_DISABLED); + return; + } + // can do this, since it's battleground, not arena BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(bgTypeId, ARENA_TYPE_NONE); diff --git a/src/game/BattleGround/BattleGroundMgr.cpp b/src/game/BattleGround/BattleGroundMgr.cpp index 0d4f92cc3..a0ae6a531 100644 --- a/src/game/BattleGround/BattleGroundMgr.cpp +++ b/src/game/BattleGround/BattleGroundMgr.cpp @@ -48,6 +48,7 @@ #include "World.h" #include "WorldPacket.h" #include "GameEventMgr.h" +#include "DisableMgr.h" #ifdef ENABLE_ELUNA #include "LuaEngine.h" #endif /* ENABLE_ELUNA */ @@ -1902,6 +1903,9 @@ void BattleGroundMgr::CreateInitialBattleGrounds() continue; } + if (DisableMgr::IsDisabledFor(DISABLE_TYPE_BATTLEGROUND, bgTypeID_)) + continue; + BattleGroundTypeId bgTypeID = BattleGroundTypeId(bgTypeID_); bool IsArena = (bl->type == TYPE_ARENA); diff --git a/src/game/ChatCommands/Level3.cpp b/src/game/ChatCommands/Level3.cpp index 873a45bb0..fa94444d4 100644 --- a/src/game/ChatCommands/Level3.cpp +++ b/src/game/ChatCommands/Level3.cpp @@ -63,6 +63,7 @@ #include "DBCEnums.h" #include "AuctionHouseBot/AuctionHouseBot.h" #include "SQLStorages.h" +#include "DisableMgr.h" static uint32 ahbotQualityIds[MAX_AUCTION_QUALITY] = { @@ -1144,6 +1145,15 @@ bool ChatHandler::HandleReloadCreaturesStatsCommand(char* /*args*/) return true; } +bool ChatHandler::HandleReloadDisablesCommand(char * /*args*/) +{ + sLog.outString("Re-loading Disables..."); + DisableMgr::LoadDisables(); + DisableMgr::CheckQuestDisables(); + SendGlobalSysMessage("DB table `disables` reloaded."); + return true; +} + bool ChatHandler::HandleLoadScriptsCommand(char* args) { if (!*args) diff --git a/src/game/Object/ObjectMgr.cpp b/src/game/Object/ObjectMgr.cpp index 385ac1ae9..4417cc0a8 100644 --- a/src/game/Object/ObjectMgr.cpp +++ b/src/game/Object/ObjectMgr.cpp @@ -56,6 +56,7 @@ #include "GridNotifiers.h" #include "GridNotifiersImpl.h" #include "CellImpl.h" +#include "DisableMgr.h" #include @@ -1369,6 +1370,12 @@ void ObjectMgr::LoadCreatures() uint32 guid = fields[ 0].GetUInt32(); uint32 entry = fields[ 1].GetUInt32(); + if (DisableMgr::IsDisabledFor(DISABLE_TYPE_CREATURE_SPAWN, guid)) + { + sLog.outDebug("Creature guid %u (entry %u) spawning is disabled.", guid, entry); + continue; + } + CreatureInfo const* cInfo = GetCreatureTemplate(entry); if (!cInfo) { @@ -1587,6 +1594,12 @@ void ObjectMgr::LoadGameObjects() uint32 guid = fields[ 0].GetUInt32(); uint32 entry = fields[ 1].GetUInt32(); + if (DisableMgr::IsDisabledFor(DISABLE_TYPE_GAMEOBJECT_SPAWN, guid)) + { + sLog.outDebug("Gameobject guid %u (entry %u) spawning is disabled.", guid, entry); + continue; + } + GameObjectInfo const* gInfo = GetGameObjectInfo(entry); if (!gInfo) { @@ -2296,6 +2309,13 @@ void ObjectMgr::LoadItemPrototypes() { for (int j = 0; j < MAX_ITEM_PROTO_SPELLS; ++j) { + if (DisableMgr::IsDisabledFor(DISABLE_TYPE_SPELL, proto->Spells[j].SpellId)) + { + DEBUG_LOG("Spell %u on item %u (%s) is disabled.", proto->Spells[j].SpellId, proto->ItemId, proto->Name1); + const_cast(proto)->Spells[j].SpellId = 0; + continue; + } + if (proto->Spells[j].SpellTrigger >= MAX_ITEM_SPELLTRIGGER || proto->Spells[j].SpellTrigger == ITEM_SPELLTRIGGER_LEARN_SPELL_ID) { sLog.outErrorDb("Item (Entry: %u) has wrong item spell trigger value in spelltrigger_%d (%u)", i, j + 1, proto->Spells[j].SpellTrigger); @@ -3828,6 +3848,10 @@ void ObjectMgr::LoadQuests() for (QuestMap::iterator iter = mQuestTemplates.begin(); iter != mQuestTemplates.end(); ++iter) { + // skip post-loading checks for disabled quests + if (DisableMgr::IsDisabledFor(DISABLE_TYPE_QUEST, iter->first)) + continue; + Quest* qinfo = iter->second; // additional quest integrity checks (GO, creature_template and item_template must be loaded already) diff --git a/src/game/Object/Player.cpp b/src/game/Object/Player.cpp index f0b06b890..997c79983 100644 --- a/src/game/Object/Player.cpp +++ b/src/game/Object/Player.cpp @@ -73,6 +73,7 @@ #include "SQLStorages.h" #include "Vehicle.h" #include "Calendar.h" +#include "DisableMgr.h" #ifdef ENABLE_ELUNA #include "LuaEngine.h" #endif /* ENABLE_ELUNA */ @@ -1713,6 +1714,13 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati MapEntry const* mEntry = sMapStore.LookupEntry(mapid); // Validity checked in IsValidMapCoord + if (!isGameMaster() && DisableMgr::IsDisabledFor(DISABLE_TYPE_MAP, mapid, this)) + { + sLog.outDebug("Player (GUID: %u, name: %s) tried to enter a forbidden map %u", GetGUIDLow(), GetName(), mapid); + SendTransferAbortedByLockStatus(mEntry, AREA_LOCKSTATUS_NOT_ALLOWED); + return false; + } + // preparing unsummon pet if lost (we must get pet before teleportation or will not find it later) Pet* pet = GetPet(); @@ -13371,12 +13379,12 @@ Quest const* Player::GetNextQuest(ObjectGuid guid, Quest const* pQuest) */ bool Player::CanSeeStartQuest(Quest const* pQuest) const { - if (SatisfyQuestClass(pQuest, false) && SatisfyQuestRace(pQuest, false) && SatisfyQuestSkill(pQuest, false) && - SatisfyQuestExclusiveGroup(pQuest, false) && SatisfyQuestReputation(pQuest, false) && - SatisfyQuestPreviousQuest(pQuest, false) && SatisfyQuestNextChain(pQuest, false) && - SatisfyQuestPrevChain(pQuest, false) && SatisfyQuestDay(pQuest, false) && SatisfyQuestWeek(pQuest, false) && - SatisfyQuestMonth(pQuest, false) && - pQuest->IsActive()) + if (!DisableMgr::IsDisabledFor(DISABLE_TYPE_QUEST, pQuest->GetQuestId(), this) && + SatisfyQuestClass(pQuest, false) && SatisfyQuestRace(pQuest, false) && SatisfyQuestSkill(pQuest, false) && + SatisfyQuestExclusiveGroup(pQuest, false) && SatisfyQuestReputation(pQuest, false) && + SatisfyQuestPreviousQuest(pQuest, false) && SatisfyQuestNextChain(pQuest, false) && + SatisfyQuestPrevChain(pQuest, false) && SatisfyQuestDay(pQuest, false) && + pQuest->IsActive()) { int32 highLevelDiff = sWorld.getConfig(CONFIG_INT32_QUEST_HIGH_LEVEL_HIDE_DIFF); if (highLevelDiff < 0) @@ -13389,7 +13397,8 @@ bool Player::CanSeeStartQuest(Quest const* pQuest) const bool Player::CanTakeQuest(Quest const* pQuest, bool msg) const { - return SatisfyQuestStatus(pQuest, msg) && SatisfyQuestExclusiveGroup(pQuest, msg) && + return !DisableMgr::IsDisabledFor(DISABLE_TYPE_QUEST, pQuest->GetQuestId(), this) && + SatisfyQuestStatus(pQuest, msg) && SatisfyQuestExclusiveGroup(pQuest, msg) && SatisfyQuestClass(pQuest, msg) && SatisfyQuestRace(pQuest, msg) && SatisfyQuestLevel(pQuest, msg) && SatisfyQuestSkill(pQuest, msg) && SatisfyQuestReputation(pQuest, msg) && SatisfyQuestPreviousQuest(pQuest, msg) && SatisfyQuestTimed(pQuest, msg) && diff --git a/src/game/OutdoorPvP/OutdoorPvPMgr.cpp b/src/game/OutdoorPvP/OutdoorPvPMgr.cpp index 8c671fdd9..e13040371 100644 --- a/src/game/OutdoorPvP/OutdoorPvPMgr.cpp +++ b/src/game/OutdoorPvP/OutdoorPvPMgr.cpp @@ -34,6 +34,7 @@ #include "OutdoorPvPSI.h" #include "OutdoorPvPTF.h" #include "OutdoorPvPZM.h" +#include "DisableMgr.h" INSTANTIATE_SINGLETON_1(OutdoorPvPMgr); @@ -50,7 +51,7 @@ OutdoorPvPMgr::~OutdoorPvPMgr() } #define LOAD_OPVP_ZONE(a) \ - if (sWorld.getConfig(CONFIG_BOOL_OUTDOORPVP_##a##_ENABLED)) \ +if (sWorld.getConfig(CONFIG_BOOL_OUTDOORPVP_##a##_ENABLED) && !DisableMgr::IsDisabledFor(DISABLE_TYPE_OUTDOORPVP, OPVP_ID_##a)) \ { \ m_scripts[OPVP_ID_##a] = new OutdoorPvP##a(); \ ++counter; \ diff --git a/src/game/Tools/Language.h b/src/game/Tools/Language.h index 744c61184..d3fda9a8c 100644 --- a/src/game/Tools/Language.h +++ b/src/game/Tools/Language.h @@ -713,7 +713,7 @@ enum MangosStrings LANG_BG_QUEUE_ANNOUNCE_SELF = 711, LANG_BG_QUEUE_ANNOUNCE_WORLD = 712, LANG_YOUR_ARENA_LEVEL_REQ_ERROR = 713, -// = 714, not used + LANG_BG_IS_DISABLED = 714, LANG_YOUR_BG_LEVEL_REQ_ERROR = 715, // = 716, not used LANG_BG_STARTED_ANNOUNCE_WORLD = 717, @@ -742,15 +742,15 @@ enum MangosStrings LANG_DEBUG_ARENA_OFF = 738, LANG_DEBUG_BG_ON = 739, LANG_DEBUG_BG_OFF = 740, -// = 741, not used -// = 742, not used -// = 743, not used -// = 744, not used -// = 745, not used -// = 746, not used -// = 747, not used -// = 748, not used -// = 749, not used + LANG_DIST_ARENA_POINTS_START = 741, + LANG_DIST_ARENA_POINTS_ONLINE_START = 742, + LANG_DIST_ARENA_POINTS_ONLINE_END = 743, + LANG_DIST_ARENA_POINTS_TEAM_START = 744, + LANG_DIST_ARENA_POINTS_TEAM_END = 745, + LANG_DIST_ARENA_POINTS_END = 746, + LANG_MAP_IS_DISABLED = 747, + // = 748, not used + // = 749, not used LANG_BATTLEGROUND_PREMATURE_FINISH_WARNING = 750, // "Not enough players. This game will close in %u mins." LANG_BATTLEGROUND_PREMATURE_FINISH_WARNING_SECS = 751, // "Not enough players. This game will close in %u seconds." LANG_BATTLEGROUND_ONLY_ALLIANCE_USE = 752, // "Only The Alliance can use that portal" diff --git a/src/game/WorldHandlers/Chat.cpp b/src/game/WorldHandlers/Chat.cpp index 5b65f0e0a..ed347ad9d 100644 --- a/src/game/WorldHandlers/Chat.cpp +++ b/src/game/WorldHandlers/Chat.cpp @@ -552,6 +552,7 @@ ChatCommand* ChatHandler::getCommandTable() { "dbscripts_on_quest_end", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadDBScriptsOnQuestEndCommand, "", NULL }, { "dbscripts_on_quest_start", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadDBScriptsOnQuestStartCommand, "", NULL }, { "dbscripts_on_spell", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadDBScriptsOnSpellCommand, "", NULL }, + { "disables", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadDisablesCommand, "", NULL }, { "disenchant_loot_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLootTemplatesDisenchantCommand, "", NULL }, { "fishing_loot_template", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadLootTemplatesFishingCommand, "", NULL }, { "game_graveyard_zone", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadGameGraveyardZoneCommand, "", NULL }, diff --git a/src/game/WorldHandlers/Chat.h b/src/game/WorldHandlers/Chat.h index 3192eea5a..19f8596d4 100644 --- a/src/game/WorldHandlers/Chat.h +++ b/src/game/WorldHandlers/Chat.h @@ -510,6 +510,7 @@ class ChatHandler bool HandleReloadSpellTargetPositionCommand(char* args); bool HandleReloadSpellThreatsCommand(char* args); bool HandleReloadSpellPetAurasCommand(char* args); + bool HandleReloadDisablesCommand(char* args); bool HandleResetAchievementsCommand(char* args); bool HandleResetAllCommand(char* args); diff --git a/src/game/WorldHandlers/DisableMgr.cpp b/src/game/WorldHandlers/DisableMgr.cpp new file mode 100644 index 000000000..52f9c4aa4 --- /dev/null +++ b/src/game/WorldHandlers/DisableMgr.cpp @@ -0,0 +1,390 @@ +/* + * Copyright (C) 2015-2016 MaNGOS project + * Copyright (C) 2008-2015 TrinityCore + * Copyright (C) 2005-2009 MaNGOS + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include "DisableMgr.h" +#include "ObjectMgr.h" +#include "OutdoorPvPMgr.h" +#include "SpellMgr.h" +#include "Player.h" +#include "World.h" +#include "BattleGroundMgr.h" + +namespace DisableMgr +{ + +namespace +{ + struct DisableData + { + uint8 flags; + std::set params[2]; // data + }; + + // single disables here with optional data + typedef std::map DisableTypeMap; + // global disable map by source + typedef std::map DisableMap; + + DisableMap m_DisableMap; +} + +void LoadDisables() +{ + // reload case + for (DisableMap::iterator itr = m_DisableMap.begin(); itr != m_DisableMap.end(); ++itr) + itr->second.clear(); + + m_DisableMap.clear(); + + QueryResult* result = WorldDatabase.Query("SELECT sourceType, entry, flags, data FROM disables"); + + uint32 total_count = 0; + + if (!result) + { + sLog.outString(">> Loaded 0 disables. DB table `disables` is empty!"); + return; + } + + BarGoLink bar(result->GetRowCount()); + + do + { + Field* fields = result->Fetch(); + DisableType type = DisableType(fields[0].GetUInt32()); + if (type >= MAX_DISABLE_TYPES) + { + sLog.outErrorDb("Invalid type %u specified in `disables` table, skipped.", type); + continue; + } + + uint32 entry = fields[1].GetUInt32(); + uint8 flags = fields[2].GetUInt8(); + uint32 data0 = fields[3].GetUInt32(); + + DisableData* data; + if (m_DisableMap[type].find(entry) != m_DisableMap[type].end()) + data = &m_DisableMap[type][entry]; + else + data = new DisableData(); + + data->flags = flags; + + switch (type) + { + case DISABLE_TYPE_SPELL: + if (!(sSpellStore.LookupEntry(entry) || flags & SPELL_DISABLE_DEPRECATED_SPELL)) + { + ERROR_DB_STRICT_LOG("Spell entry %u from `disables` doesn't exist in dbc, skipped.", entry); + continue; + } + + if (!flags || flags > MAX_SPELL_DISABLE_TYPE) + { + ERROR_DB_STRICT_LOG("Disable flags for spell %u are invalid, skipped.", entry); + continue; + } + + if (flags & SPELL_DISABLE_MAP) + data->params[0].insert(data0 & 0xFFFF); + + if (flags & SPELL_DISABLE_AREA) + data->params[1].insert(data0 >> 16); + + break; + // checked later + case DISABLE_TYPE_QUEST: + break; + case DISABLE_TYPE_MAP: + { + MapEntry const* mapEntry = sMapStore.LookupEntry(entry); + if (!mapEntry) + { + ERROR_DB_STRICT_LOG("Map entry %u from `disables` doesn't exist in dbc, skipped.", entry); + continue; + } + bool isFlagInvalid = false; + switch (mapEntry->map_type) + { + case MAP_COMMON: + case MAP_INSTANCE: + case MAP_RAID: + if (flags) + isFlagInvalid = true; + break; + case MAP_BATTLEGROUND: + //case MAP_ARENA: [-ZERO] + ERROR_DB_STRICT_LOG("Battleground map %u specified to be disabled in map case, skipped.", entry); + continue; + } + if (isFlagInvalid) + { + ERROR_DB_STRICT_LOG("Disable flags for map %u are invalid, skipped.", entry); + continue; + } + break; + } + case DISABLE_TYPE_BATTLEGROUND: + if (!sBattleGroundMgr.GetBattleGroundTemplate(BattleGroundTypeId(entry))) + { + ERROR_DB_STRICT_LOG("Battleground entry %u from `disables` doesn't exist in dbc, skipped.", entry); + continue; + } + if (flags) + ERROR_DB_STRICT_LOG("Disable flags specified for battleground %u, useless data.", entry); + break; + case DISABLE_TYPE_OUTDOORPVP: + if (entry > MAX_OPVP_ID) + { + ERROR_DB_STRICT_LOG("OutdoorPvPTypes value %u from `disables` is invalid, skipped.", entry); + continue; + } + if (flags) + ERROR_DB_STRICT_LOG("Disable flags specified for outdoor PvP %u, useless data.", entry); + break; + //case DISABLE_TYPE_ACHIEVEMENT_CRITERIA: [-ZERO] + // if (!sAchievementMgr->GetAchievementCriteria(entry)) + // { + // ERROR_DB_STRICT_LOG("sql.sql", "Achievement Criteria entry %u from `disables` doesn't exist in dbc, skipped.", entry); + // continue; + // } + // if (flags) + // ERROR_DB_STRICT_LOG("sql.sql", "Disable flags specified for Achievement Criteria %u, useless data.", entry); + // break; + case DISABLE_TYPE_VMAP: + { + MapEntry const* mapEntry = sMapStore.LookupEntry(entry); + if (!mapEntry) + { + ERROR_DB_STRICT_LOG("Map entry %u from `disables` doesn't exist in dbc, skipped.", entry); + continue; + } + switch (mapEntry->map_type) + { + case MAP_COMMON: + if (flags & VMAP::VMAP_DISABLE_AREAFLAG) + sLog.outDebug("Areaflag disabled for world map %u.", entry); + if (flags & VMAP::VMAP_DISABLE_LIQUIDSTATUS) + sLog.outDebug("Liquid status disabled for world map %u.", entry); + break; + case MAP_INSTANCE: + case MAP_RAID: + if (flags & VMAP::VMAP_DISABLE_HEIGHT) + sLog.outDebug("Height disabled for instance map %u.", entry); + if (flags & VMAP::VMAP_DISABLE_LOS) + sLog.outDebug("LoS disabled for instance map %u.", entry); + break; + case MAP_BATTLEGROUND: + if (flags & VMAP::VMAP_DISABLE_HEIGHT) + sLog.outDebug("Height disabled for battleground map %u.", entry); + if (flags & VMAP::VMAP_DISABLE_LOS) + sLog.outDebug("LoS disabled for battleground map %u.", entry); + break; + //case MAP_ARENA: [-ZERO] + // if (flags & VMAP::VMAP_DISABLE_HEIGHT) + // TC_LOG_INFO("misc", "Height disabled for arena map %u.", entry); + // if (flags & VMAP::VMAP_DISABLE_LOS) + // TC_LOG_INFO("misc", "LoS disabled for arena map %u.", entry); + // break; + default: + break; + } + break; + } + case DISABLE_TYPE_MMAP: + { + MapEntry const* mapEntry = sMapStore.LookupEntry(entry); + if (!mapEntry) + { + ERROR_DB_STRICT_LOG("Map entry %u from `disables` doesn't exist in dbc, skipped.", entry); + continue; + } + switch (mapEntry->map_type) + { + case MAP_COMMON: + sLog.outDebug("Pathfinding disabled for world map %u.", entry); + break; + case MAP_INSTANCE: + case MAP_RAID: + sLog.outDebug("Pathfinding disabled for instance map %u.", entry); + break; + case MAP_BATTLEGROUND: + sLog.outDebug("Pathfinding disabled for battleground map %u.", entry); + break; + //case MAP_ARENA: [-ZERO] + // TC_LOG_INFO("misc", "Pathfinding disabled for arena map %u.", entry); + // break; + default: + break; + } + break; + } + case DISABLE_TYPE_CREATURE_SPAWN: + case DISABLE_TYPE_GAMEOBJECT_SPAWN: + break; + default: + break; + } + + m_DisableMap[type].insert(DisableTypeMap::value_type(entry, *data)); + ++total_count; + } + while (result->NextRow()); + delete result; + + sLog.outString(">> Loaded %u disables", total_count); +} + +void CheckQuestDisables() +{ + uint32 count = m_DisableMap[DISABLE_TYPE_QUEST].size(); + if (!count) + { + sLog.outString(">> Checked 0 quest disables."); + return; + } + + // check only quests, rest already done at startup + for (DisableTypeMap::iterator itr = m_DisableMap[DISABLE_TYPE_QUEST].begin(); itr != m_DisableMap[DISABLE_TYPE_QUEST].end();) + { + const uint32 entry = itr->first; + if (!sObjectMgr.GetQuestTemplate(entry)) + { + ERROR_DB_STRICT_LOG("Quest entry %u from `disables` doesn't exist, skipped.", entry); + m_DisableMap[DISABLE_TYPE_QUEST].erase(itr++); + continue; + } + if (itr->second.flags) + ERROR_DB_STRICT_LOG("Disable flags specified for quest %u, useless data.", entry); + ++itr; + } + + sLog.outString(">> Checked %u quest disables", count); +} + +bool IsDisabledFor(DisableType type, uint32 entry, Unit const* unit, uint8 flags) +{ + MANGOS_ASSERT(type < MAX_DISABLE_TYPES); + if (m_DisableMap[type].empty()) + return false; + + DisableTypeMap::iterator itr = m_DisableMap[type].find(entry); + if (itr == m_DisableMap[type].end()) // not disabled + return false; + + switch (type) + { + case DISABLE_TYPE_SPELL: + { + uint8 spellFlags = itr->second.flags; + if (unit) + { + if ((spellFlags & SPELL_DISABLE_PLAYER && unit->GetTypeId() == TYPEID_PLAYER) || + (unit->GetTypeId() == TYPEID_UNIT && ((unit->ToCreature()->IsPet() && spellFlags & SPELL_DISABLE_PET) || spellFlags & SPELL_DISABLE_CREATURE))) + { + if (spellFlags & SPELL_DISABLE_MAP) + { + std::set const& mapIds = itr->second.params[0]; + if (mapIds.find(unit->GetMapId()) != mapIds.end()) + return true; // Spell is disabled on current map + + if (!(spellFlags & SPELL_DISABLE_AREA)) + return false; // Spell is disabled on another map, but not this one, return false + + // Spell is disabled in an area, but not explicitly our current mapId. Continue processing. + } + + if (spellFlags & SPELL_DISABLE_AREA) + { + std::set const& areaIds = itr->second.params[1]; + if (areaIds.find(unit->GetAreaId()) != areaIds.end()) + return true; // Spell is disabled in this area + return false; // Spell is disabled in another area, but not this one, return false + } + else + return true; // Spell disabled for all maps + } + + return false; + } + else if (spellFlags & SPELL_DISABLE_DEPRECATED_SPELL) // call not from spellcast + return true; + else if (flags & SPELL_DISABLE_LOS) + return (spellFlags & SPELL_DISABLE_LOS) != 0; + + break; + } + case DISABLE_TYPE_MAP: + if (Player const* player = unit->ToPlayer()) + { + // [-ZERO] + //MapEntry const* mapEntry = sMapStore.LookupEntry(entry); + //if (mapEntry->IsDungeon()) + //{ + // uint8 disabledModes = itr->second.flags; + // Difficulty targetDifficulty = player->GetDifficulty(mapEntry->IsRaid()); + // GetDownscaledMapDifficultyData(entry, targetDifficulty); + // switch (targetDifficulty) + // { + // case DUNGEON_DIFFICULTY_NORMAL: + // return (disabledModes & DUNGEON_STATUSFLAG_NORMAL) != 0; + // case DUNGEON_DIFFICULTY_HEROIC: + // return (disabledModes & DUNGEON_STATUSFLAG_HEROIC) != 0; + // case RAID_DIFFICULTY_10MAN_HEROIC: + // return (disabledModes & RAID_STATUSFLAG_10MAN_HEROIC) != 0; + // case RAID_DIFFICULTY_25MAN_HEROIC: + // return (disabledModes & RAID_STATUSFLAG_25MAN_HEROIC) != 0; + // } + //} + //else if (mapEntry->map_type == MAP_COMMON) + return true; + } + return false; + case DISABLE_TYPE_QUEST: + if (!unit) + return true; + if (Player const* player = unit->ToPlayer()) + return !player->isGameMaster(); + return true; + case DISABLE_TYPE_BATTLEGROUND: + case DISABLE_TYPE_OUTDOORPVP: + case DISABLE_TYPE_ACHIEVEMENT_CRITERIA: + case DISABLE_TYPE_MMAP: + case DISABLE_TYPE_CREATURE_SPAWN: + case DISABLE_TYPE_GAMEOBJECT_SPAWN: + return true; + case DISABLE_TYPE_VMAP: + return (flags & itr->second.flags) != 0; + } + + return false; +} + +bool IsVMAPDisabledFor(uint32 entry, uint8 flags) +{ + return IsDisabledFor(DISABLE_TYPE_VMAP, entry, NULL, flags); +} + +bool IsPathfindingEnabled(uint32 mapId) +{ + return sWorld.getConfig(CONFIG_BOOL_MMAP_ENABLED) + && !IsDisabledFor(DISABLE_TYPE_MMAP, mapId); +} + +} // Namespace diff --git a/src/game/WorldHandlers/DisableMgr.h b/src/game/WorldHandlers/DisableMgr.h new file mode 100644 index 000000000..ad2a98e42 --- /dev/null +++ b/src/game/WorldHandlers/DisableMgr.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2015-2016 MaNGOS project + * Copyright (C) 2008-2015 TrinityCore + * Copyright (C) 2005-2009 MaNGOS + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef TRINITY_DISABLEMGR_H +#define TRINITY_DISABLEMGR_H + +#include "VMapManager2.h" + +class Unit; + +enum DisableType +{ + DISABLE_TYPE_SPELL = 0, + DISABLE_TYPE_QUEST = 1, + DISABLE_TYPE_MAP = 2, + DISABLE_TYPE_BATTLEGROUND = 3, + DISABLE_TYPE_ACHIEVEMENT_CRITERIA = 4, + DISABLE_TYPE_OUTDOORPVP = 5, + DISABLE_TYPE_VMAP = 6, + DISABLE_TYPE_MMAP = 7, + DISABLE_TYPE_CREATURE_SPAWN = 8, + DISABLE_TYPE_GAMEOBJECT_SPAWN = 9, + MAX_DISABLE_TYPES = 10 +}; + +enum SpellDisableTypes +{ + SPELL_DISABLE_PLAYER = 0x1, + SPELL_DISABLE_CREATURE = 0x2, + SPELL_DISABLE_PET = 0x4, + SPELL_DISABLE_DEPRECATED_SPELL = 0x8, + SPELL_DISABLE_MAP = 0x10, + SPELL_DISABLE_AREA = 0x20, + SPELL_DISABLE_LOS = 0x40, + MAX_SPELL_DISABLE_TYPE = ( SPELL_DISABLE_PLAYER | SPELL_DISABLE_CREATURE | SPELL_DISABLE_PET | + SPELL_DISABLE_DEPRECATED_SPELL | SPELL_DISABLE_MAP | SPELL_DISABLE_AREA | + SPELL_DISABLE_LOS) +}; + +enum SpawnDisableTypes +{ + SPAWN_DISABLE_FOR_ENTRY = 0, + SPAWN_DISABLE_FOR_GUID = 1 +}; + +namespace DisableMgr +{ + void LoadDisables(); + bool IsDisabledFor(DisableType type, uint32 entry, Unit const* unit = NULL, uint8 flags = 0); + void CheckQuestDisables(); + bool IsVMAPDisabledFor(uint32 entry, uint8 flags); + bool IsPathfindingEnabled(uint32 mapId); +} + +#endif //TRINITY_DISABLEMGR_H diff --git a/src/game/WorldHandlers/Spell.cpp b/src/game/WorldHandlers/Spell.cpp index 7d4d4f85e..3b0efa1bd 100644 --- a/src/game/WorldHandlers/Spell.cpp +++ b/src/game/WorldHandlers/Spell.cpp @@ -55,6 +55,7 @@ #include "Vehicle.h" #include "TemporarySummon.h" #include "SQLStorages.h" +#include "DisableMgr.h" #ifdef ENABLE_ELUNA #include "LuaEngine.h" #endif /* ENABLE_ELUNA */ @@ -1856,7 +1857,7 @@ void Spell::SetTargetMap(SpellEffectIndex effIndex, uint32 targetMode, UnitList& if (!prev->IsWithinDist(*next, CHAIN_SPELL_JUMP_RADIUS)) break; - if (!m_spellInfo->HasAttribute(SPELL_ATTR_EX2_IGNORE_LOS) && !prev->IsWithinLOSInMap(*next)) + if (!DisableMgr::IsDisabledFor(DISABLE_TYPE_SPELL, m_spellInfo->Id, NULL, SPELL_ATTR_EX2_IGNORE_LOS) && !prev->IsWithinLOSInMap(*next)) { ++next; continue; @@ -1938,7 +1939,7 @@ void Spell::SetTargetMap(SpellEffectIndex effIndex, uint32 targetMode, UnitList& if (!prev->IsWithinDist(*next, CHAIN_SPELL_JUMP_RADIUS)) break; - if (!m_spellInfo->HasAttribute(SPELL_ATTR_EX2_IGNORE_LOS) && !prev->IsWithinLOSInMap(*next)) + if (!DisableMgr::IsDisabledFor(DISABLE_TYPE_SPELL, m_spellInfo->Id, NULL, SPELL_ATTR_EX2_IGNORE_LOS) && !prev->IsWithinLOSInMap(*next)) { ++next; continue; @@ -2642,7 +2643,7 @@ void Spell::SetTargetMap(SpellEffectIndex effIndex, uint32 targetMode, UnitList& if (!prev->IsWithinDist(*next, CHAIN_SPELL_JUMP_RADIUS)) break; - if (!m_spellInfo->HasAttribute(SPELL_ATTR_EX2_IGNORE_LOS) && !prev->IsWithinLOSInMap(*next)) + if (!DisableMgr::IsDisabledFor(DISABLE_TYPE_SPELL, m_spellInfo->Id, NULL, SPELL_ATTR_EX2_IGNORE_LOS) && !prev->IsWithinLOSInMap(*next)) { ++next; continue; @@ -3128,6 +3129,21 @@ void Spell::SpellStart(SpellCastTargets const* targets, Aura* triggeredByAura) SpellEvent* Event = new SpellEvent(this); m_caster->m_Events.AddEvent(Event, m_caster->m_Events.CalculateTime(1)); + // Prevent casting at cast another spell (ServerSide check) + if (m_caster->IsNonMeleeSpellCasted(false, true, true) && m_cast_count) + { + SendCastResult(SPELL_FAILED_SPELL_IN_PROGRESS); + finish(false); + return; + } + + if (DisableMgr::IsDisabledFor(DISABLE_TYPE_SPELL, m_spellInfo->Id, m_caster)) + { + SendCastResult(SPELL_FAILED_SPELL_UNAVAILABLE); + finish(false); + return; + } + // Fill cost data m_powerCost = m_IsTriggeredSpell ? 0 : CalculatePowerCost(m_spellInfo, m_caster, this, m_CastItem); @@ -5314,8 +5330,8 @@ SpellCastResult Spell::CheckCast(bool strict) } } - if (!m_IsTriggeredSpell && !m_spellInfo->HasAttribute(SPELL_ATTR_EX2_IGNORE_LOS) && VMAP::VMapFactory::checkSpellForLoS(m_spellInfo->Id) && !m_caster->IsWithinLOSInMap(target)) - return SPELL_FAILED_LINE_OF_SIGHT; + if (!m_IsTriggeredSpell && !DisableMgr::IsDisabledFor(DISABLE_TYPE_SPELL, m_spellInfo->Id, NULL, SPELL_ATTR_EX2_IGNORE_LOS) && VMAP::VMapFactory::checkSpellForLoS(m_spellInfo->Id) && !m_caster->IsWithinLOSInMap(target)) + { return SPELL_FAILED_LINE_OF_SIGHT; } // auto selection spell rank implemented in WorldSession::HandleCastSpellOpcode // this case can be triggered if rank not found (too low-level target for first rank) @@ -7617,8 +7633,10 @@ bool Spell::CheckTarget(Unit* target, SpellEffectIndex eff) } // Check targets for LOS visibility (except spells without range limitations ) - switch(spellEffect->Effect) + if (!DisableMgr::IsDisabledFor(DISABLE_TYPE_SPELL, m_spellInfo->Id, NULL, SPELL_ATTR_EX2_IGNORE_LOS)) { + switch(spellEffect->Effect) + { case SPELL_EFFECT_SUMMON_PLAYER: // from anywhere break; case SPELL_EFFECT_DUMMY: @@ -7640,7 +7658,7 @@ bool Spell::CheckTarget(Unit* target, SpellEffectIndex eff) return false; if (!m_spellInfo->HasAttribute(SPELL_ATTR_EX2_IGNORE_LOS) && !corpse->IsWithinLOSInMap(m_caster)) - return false; + { return false; } } // all ok by some way or another, skip normal check @@ -7648,10 +7666,11 @@ bool Spell::CheckTarget(Unit* target, SpellEffectIndex eff) default: // normal case // Get GO cast coordinates if original caster -> GO if (target != m_caster) - if (WorldObject* caster = GetCastingObject()) + if (WorldObject* caster = GetCastingObject()) if (!m_spellInfo->HasAttribute(SPELL_ATTR_EX2_IGNORE_LOS) && !target->IsWithinLOSInMap(caster)) - return false; + { return false; } break; + } } if (target->GetTypeId() != TYPEID_PLAYER && m_spellInfo->HasAttribute(SPELL_ATTR_EX3_TARGET_ONLY_PLAYER) diff --git a/src/game/WorldHandlers/World.cpp b/src/game/WorldHandlers/World.cpp index c548c00a7..d4eb26387 100644 --- a/src/game/WorldHandlers/World.cpp +++ b/src/game/WorldHandlers/World.cpp @@ -78,6 +78,7 @@ #include "LFGMgr.h" #include "revision.h" #include "Language.h" +#include "DisableMgr.h" #ifdef ENABLE_ELUNA #include "LuaEngine.h" @@ -1032,6 +1033,13 @@ void World::SetInitialWorldSettings() ///- Initialize config settings LoadConfigSettings(); + ///- Initialize VMapManager function pointers (to untangle game/collision circular deps) + if (VMAP::VMapManager2* vmmgr2 = dynamic_cast(VMAP::VMapFactory::createOrGetVMapManager())) + { + //vmmgr2->GetLiquidFlagsPtr = &GetLiquidFlags; + vmmgr2->IsVMAPDisabledForPtr = &DisableMgr::IsVMAPDisabledFor; + } + ///- Check the existence of the map files for all races start areas. if (!MapManager::ExistMapAndVMap(0, -6240.32f, 331.033f) || // Dwarf/ Gnome !MapManager::ExistMapAndVMap(0, -8949.95f, -132.493f) || // Human @@ -1152,6 +1160,9 @@ void World::SetInitialWorldSettings() sLog.outString("Loading Item Random Enchantments Table..."); LoadRandomEnchantmentsTable(); + sLog.outString("Loading Disables..."); // must be before loading quests and items + DisableMgr::LoadDisables(); + sLog.outString("Loading Items..."); // must be after LoadRandomEnchantmentsTable and LoadPageTexts sObjectMgr.LoadItemPrototypes(); @@ -1242,6 +1253,9 @@ void World::SetInitialWorldSettings() sLog.outString(">>> Quests Relations loaded"); sLog.outString(); + sLog.outString("Checking Quest Disables..."); + DisableMgr::CheckQuestDisables(); // must be after loading quests + sLog.outString("Loading Game Event Data..."); // must be after sPoolMgr.LoadFromDB and quests to properly load pool events and quests for events sLog.outString(); sGameEventMgr.LoadFromDB(); diff --git a/src/game/vmap/VMapManager2.cpp b/src/game/vmap/VMapManager2.cpp index 052613cb3..c857c5346 100644 --- a/src/game/vmap/VMapManager2.cpp +++ b/src/game/vmap/VMapManager2.cpp @@ -146,7 +146,9 @@ namespace VMAP bool VMapManager2::isInLineOfSight(unsigned int pMapId, float x1, float y1, float z1, float x2, float y2, float z2) { - if (!isLineOfSightCalcEnabled()) { return true; } + if (!isLineOfSightCalcEnabled() || IsVMAPDisabledForPtr(pMapId, VMAP_DISABLE_LOS)) + return true; + bool result = true; InstanceTreeMap::iterator instanceTree = iInstanceMapTrees.find(pMapId); if (instanceTree != iInstanceMapTrees.end()) @@ -171,7 +173,7 @@ namespace VMAP rx = x2; ry = y2; rz = z2; - if (isLineOfSightCalcEnabled()) + if (isLineOfSightCalcEnabled() && !IsVMAPDisabledForPtr(pMapId, VMAP_DISABLE_LOS)) { InstanceTreeMap::iterator instanceTree = iInstanceMapTrees.find(pMapId); if (instanceTree != iInstanceMapTrees.end()) @@ -197,7 +199,7 @@ namespace VMAP float VMapManager2::getHeight(unsigned int pMapId, float x, float y, float z, float maxSearchDist) { float height = VMAP_INVALID_HEIGHT_VALUE; // no height - if (isHeightCalcEnabled()) + if (isHeightCalcEnabled() && !IsVMAPDisabledForPtr(pMapId, VMAP_DISABLE_HEIGHT)) { InstanceTreeMap::iterator instanceTree = iInstanceMapTrees.find(pMapId); if (instanceTree != iInstanceMapTrees.end()) @@ -218,32 +220,38 @@ namespace VMAP bool VMapManager2::getAreaInfo(unsigned int pMapId, float x, float y, float& z, uint32& flags, int32& adtId, int32& rootId, int32& groupId) const { bool result = false; - InstanceTreeMap::const_iterator instanceTree = iInstanceMapTrees.find(pMapId); - if (instanceTree != iInstanceMapTrees.end()) + if (!IsVMAPDisabledForPtr(pMapId, VMAP_DISABLE_AREAFLAG)) { - Vector3 pos = convertPositionToInternalRep(x, y, z); - result = instanceTree->second->getAreaInfo(pos, flags, adtId, rootId, groupId); - // z is not touched by convertPositionToMangosRep(), so just copy - z = pos.z; + InstanceTreeMap::const_iterator instanceTree = iInstanceMapTrees.find(pMapId); + if (instanceTree != iInstanceMapTrees.end()) + { + Vector3 pos = convertPositionToInternalRep(x, y, z); + result = instanceTree->second->getAreaInfo(pos, flags, adtId, rootId, groupId); + // z is not touched by convertPositionToMangosRep(), so just copy + z = pos.z; + } } return result; } bool VMapManager2::GetLiquidLevel(uint32 pMapId, float x, float y, float z, uint8 ReqLiquidType, float& level, float& floor, uint32& type) const { - InstanceTreeMap::const_iterator instanceTree = iInstanceMapTrees.find(pMapId); - if (instanceTree != iInstanceMapTrees.end()) + if (!IsVMAPDisabledForPtr(pMapId, VMAP_DISABLE_LIQUIDSTATUS)) { - LocationInfo info; - Vector3 pos = convertPositionToInternalRep(x, y, z); - if (instanceTree->second->GetLocationInfo(pos, info)) + InstanceTreeMap::const_iterator instanceTree = iInstanceMapTrees.find(pMapId); + if (instanceTree != iInstanceMapTrees.end()) { - floor = info.ground_Z; - type = info.hitModel->GetLiquidType(); - if (ReqLiquidType && !(type & ReqLiquidType)) - { return false; } - if (info.hitInstance->GetLiquidLevel(pos, info, level)) - { return true; } + LocationInfo info; + Vector3 pos = convertPositionToInternalRep(x, y, z); + if (instanceTree->second->GetLocationInfo(pos, info)) + { + floor = info.ground_Z; + type = info.hitModel->GetLiquidType(); + if (ReqLiquidType && !(type & ReqLiquidType)) + { return false; } + if (info.hitInstance->GetLiquidLevel(pos, info, level)) + { return true; } + } } } return false; diff --git a/src/game/vmap/VMapManager2.h b/src/game/vmap/VMapManager2.h index 98339ade0..a645e1a82 100644 --- a/src/game/vmap/VMapManager2.h +++ b/src/game/vmap/VMapManager2.h @@ -102,6 +102,14 @@ namespace VMAP */ typedef UNORDERED_MAP ModelFileMap; + enum DisableTypes + { + VMAP_DISABLE_AREAFLAG = 0x1, + VMAP_DISABLE_HEIGHT = 0x2, + VMAP_DISABLE_LOS = 0x4, + VMAP_DISABLE_LIQUIDSTATUS = 0x8 + }; + /** * @brief * @@ -302,6 +310,9 @@ namespace VMAP */ bool existsMap(const char* pBasePath, unsigned int pMapId, int x, int y) override; + typedef bool(*IsVMAPDisabledForFn)(uint32 entry, uint8 flags); + IsVMAPDisabledForFn IsVMAPDisabledForPtr; + #ifdef MMAP_GENERATOR public: void getInstanceMapTree(InstanceTreeMap& instanceMapTree); diff --git a/src/shared/revision.h b/src/shared/revision.h index 0050fcd2e..b92ad56f0 100644 --- a/src/shared/revision.h +++ b/src/shared/revision.h @@ -37,7 +37,7 @@ #define CHAR_DB_UPDATE_DESCRIPTION "Remove DbDocs" #define WORLD_DB_VERSION_NR 21 - #define WORLD_DB_STRUCTURE_NR 9 + #define WORLD_DB_STRUCTURE_NR 10 #define WORLD_DB_CONTENT_NR 1 - #define WORLD_DB_UPDATE_DESCRIPTION "quest_relations" + #define WORLD_DB_UPDATE_DESCRIPTION "Add_Disables_table" #endif // __REVISION_H__