diff options
-rw-r--r-- | sql/base/characters_database.sql | 2 | ||||
-rw-r--r-- | sql/base/world_database.sql | 14 | ||||
-rw-r--r-- | sql/updates/9081_access_requirement.sql | 12 | ||||
-rw-r--r-- | sql/updates/9081_areatrigger_teleport.sql | 1 | ||||
-rw-r--r-- | sql/updates/9081_characters_characters.sql | 1 | ||||
-rw-r--r-- | src/server/game/DataStores/DBCStores.cpp | 26 | ||||
-rw-r--r-- | src/server/game/DataStores/DBCStores.h | 1 | ||||
-rw-r--r-- | src/server/game/DataStores/DBCStructure.h | 23 | ||||
-rw-r--r-- | src/server/game/DataStores/DBCfmt.h | 2 | ||||
-rw-r--r-- | src/server/game/Entities/Player/Player.cpp | 93 | ||||
-rw-r--r-- | src/server/game/Entities/Player/Player.h | 16 | ||||
-rw-r--r-- | src/server/game/Globals/ObjectMgr.cpp | 86 | ||||
-rw-r--r-- | src/server/game/Globals/ObjectMgr.h | 5 | ||||
-rw-r--r-- | src/server/game/Groups/Group.cpp | 8 | ||||
-rw-r--r-- | src/server/game/Maps/MapInstanced.cpp | 4 | ||||
-rw-r--r-- | src/server/game/Maps/MapManager.cpp | 21 | ||||
-rw-r--r-- | src/server/game/Server/Protocol/Handlers/CharacterHandler.cpp | 2 | ||||
-rw-r--r-- | src/server/game/Server/Protocol/Handlers/MiscHandler.cpp | 6 | ||||
-rw-r--r-- | src/server/game/Spells/Spell.cpp | 5 |
19 files changed, 172 insertions, 156 deletions
diff --git a/sql/base/characters_database.sql b/sql/base/characters_database.sql index 16ed16de332..7bd03bffb07 100644 --- a/sql/base/characters_database.sql +++ b/sql/base/characters_database.sql @@ -342,7 +342,7 @@ CREATE TABLE `characters` ( `position_z` float NOT NULL default '0', `map` int(11) unsigned NOT NULL default '0' COMMENT 'Map Identifier', `instance_id` int(11) unsigned NOT NULL default '0', - `dungeon_difficulty` tinyint(1) unsigned NOT NULL default '0', + `instance_mode_mask` tinyint(2) unsigned NOT NULL default '0', `orientation` float NOT NULL default '0', `taximask` longtext, `online` tinyint(3) unsigned NOT NULL default '0', diff --git a/sql/base/world_database.sql b/sql/base/world_database.sql index 7720b40f901..efca1c78603 100644 --- a/sql/base/world_database.sql +++ b/sql/base/world_database.sql @@ -25,20 +25,18 @@ DROP TABLE IF EXISTS `access_requirement`; /*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET character_set_client = utf8 */; CREATE TABLE `access_requirement` ( - `id` bigint(20) unsigned NOT NULL COMMENT 'Identifier', + `mapId` mediumint(8) unsigned NOT NULL, + `difficulty` tinyint(3) unsigned NOT NULL DEFAULT '0', `level_min` tinyint(3) unsigned NOT NULL DEFAULT '0', - `heroic_level_min` tinyint(3) unsigned NOT NULL DEFAULT '0', `level_max` tinyint(3) unsigned NOT NULL DEFAULT '0', `item` mediumint(8) unsigned NOT NULL DEFAULT '0', `item2` mediumint(8) unsigned NOT NULL DEFAULT '0', - `heroic_key` mediumint(8) unsigned NOT NULL DEFAULT '0', - `heroic_key2` mediumint(8) unsigned NOT NULL DEFAULT '0', - `quest_done` mediumint(8) unsigned NOT NULL DEFAULT '0', + `quest_done_A` mediumint(8) unsigned NOT NULL DEFAULT '0', + `quest_done_H` mediumint(8) unsigned NOT NULL DEFAULT '0', + `completed_achievement` mediumint(8) unsigned NOT NULL DEFAULT '0', `quest_failed_text` text, - `heroic_quest_done` mediumint(8) unsigned NOT NULL DEFAULT '0', - `heroic_quest_failed_text` text, `comment` text, - PRIMARY KEY (`id`) + PRIMARY KEY (`mapId`,`difficulty`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='Access Requirements'; /*!40101 SET character_set_client = @saved_cs_client */; diff --git a/sql/updates/9081_access_requirement.sql b/sql/updates/9081_access_requirement.sql new file mode 100644 index 00000000000..da7e108636a --- /dev/null +++ b/sql/updates/9081_access_requirement.sql @@ -0,0 +1,12 @@ +ALTER TABLE `access_requirement` + DROP PRIMARY KEY, + CHANGE `id` `mapId` mediumint(8) unsigned NOT NULL FIRST, + ADD `difficulty` tinyint(3) unsigned NOT NULL DEFAULT '0' AFTER `mapId`, + DROP `heroic_level_min`, + DROP `heroic_key`, + DROP `heroic_key2`, + CHANGE `quest_done` `quest_done_A` mediumint(8) unsigned NOT NULL DEFAULT '0' AFTER `item2`, + CHANGE `heroic_quest_done` `quest_done_H` mediumint(8) unsigned NOT NULL DEFAULT '0' AFTER `quest_done_A`, + ADD `completed_achievement` mediumint(8) unsigned NOT NULL DEFAULT '0' AFTER `quest_done_H`, + DROP `heroic_quest_failed_text`, + ADD PRIMARY KEY(`mapId`,`difficulty`); diff --git a/sql/updates/9081_areatrigger_teleport.sql b/sql/updates/9081_areatrigger_teleport.sql new file mode 100644 index 00000000000..d8df8fafbf9 --- /dev/null +++ b/sql/updates/9081_areatrigger_teleport.sql @@ -0,0 +1 @@ +ALTER TABLE `areatrigger_teleport` DROP `access_id`; diff --git a/sql/updates/9081_characters_characters.sql b/sql/updates/9081_characters_characters.sql new file mode 100644 index 00000000000..1e7b32e47a5 --- /dev/null +++ b/sql/updates/9081_characters_characters.sql @@ -0,0 +1 @@ +ALTER TABLE `characters` CHANGE `dungeon_difficulty` `instance_mode_mask` tinyint(2) UNSIGNED NOT NULL DEFAULT 0 AFTER `instance_id`; diff --git a/src/server/game/DataStores/DBCStores.cpp b/src/server/game/DataStores/DBCStores.cpp index 7ec071a4b54..022f5b3d2f0 100644 --- a/src/server/game/DataStores/DBCStores.cpp +++ b/src/server/game/DataStores/DBCStores.cpp @@ -352,7 +352,7 @@ void LoadDBCStores(const std::string& dataPath) // fill data for (uint32 i = 1; i < sMapDifficultyStore.GetNumRows(); ++i) if (MapDifficultyEntry const* entry = sMapDifficultyStore.LookupEntry(i)) - sMapDifficultyMap[MAKE_PAIR32(entry->MapId,entry->Difficulty)] = MapDifficulty(entry->resetTime,entry->maxPlayers); + sMapDifficultyMap[MAKE_PAIR32(entry->MapId,entry->Difficulty)] = MapDifficulty(entry->resetTime,entry->maxPlayers,strlen(entry->areaTriggerText)>0); sMapDifficultyStore.Clear(); LoadDBC(availableDbcLocales,bar,bad_dbc_files,sMovieStore, dbcPath,"Movie.dbc"); @@ -782,6 +782,30 @@ MapDifficulty const* GetMapDifficultyData(uint32 mapId, Difficulty difficulty) return itr != sMapDifficultyMap.end() ? &itr->second : NULL; } +MapDifficulty const* GetDownscaledMapDifficultyData(uint32 mapId, Difficulty &difficulty) +{ + uint32 tmpDiff = difficulty; + MapDifficulty const* mapDiff = GetMapDifficultyData(mapId, Difficulty(tmpDiff)); + if (!mapDiff) + { + if (tmpDiff > RAID_DIFFICULTY_25MAN_NORMAL) // heroic, downscale to normal + tmpDiff -= 2; + else + tmpDiff -= 1; // any non-normal mode for raids like tbc (only one mode) + + // pull new data + mapDiff = GetMapDifficultyData(mapId, Difficulty(tmpDiff)); // we are 10 normal or 25 normal + if (!mapDiff) + { + tmpDiff -= 1; + mapDiff = GetMapDifficultyData(mapId, Difficulty(tmpDiff)); // 10 normal + } + } + + difficulty = Difficulty(tmpDiff); + return mapDiff; +} + PvPDifficultyEntry const* GetBattlegroundBracketByLevel(uint32 mapid, uint32 level) { // prevent out-of-range levels for dbc data diff --git a/src/server/game/DataStores/DBCStores.h b/src/server/game/DataStores/DBCStores.h index cd08f23b9d4..4b5e6724fe9 100644 --- a/src/server/game/DataStores/DBCStores.h +++ b/src/server/game/DataStores/DBCStores.h @@ -58,6 +58,7 @@ void Map2ZoneCoordinates(float &x, float &y, uint32 zone); typedef std::map<uint32/*pair32(map,diff)*/,MapDifficulty> MapDifficultyMap; MapDifficulty const* GetMapDifficultyData(uint32 mapId, Difficulty difficulty); +MapDifficulty const* GetDownscaledMapDifficultyData(uint32 mapId, Difficulty &difficulty); uint32 const* /*[3]*/ GetTalentTabPages(uint8 cls); diff --git a/src/server/game/DataStores/DBCStructure.h b/src/server/game/DataStores/DBCStructure.h index 7663aaf3dcb..c8eb3701c33 100644 --- a/src/server/game/DataStores/DBCStructure.h +++ b/src/server/game/DataStores/DBCStructure.h @@ -1099,22 +1099,22 @@ struct ItemSetEntry struct LFGDungeonEntry { uint32 ID; // 0 - //char* name[16]; // 1-17 Name lang + //char* name[16]; // 1-17 Name lang uint32 minlevel; // 18 uint32 maxlevel; // 19 uint32 reclevel; // 20 uint32 recminlevel; // 21 uint32 recmaxlevel; // 22 - uint32 map; // 23 - uint32 heroic; // 24 - //uint32 unk; // 25 + int32 map; // 23 + uint32 difficulty; // 24 + //uint32 unk; // 25 uint32 type; // 26 - //uint32 unk2; // 27 - //char* unk3; // 28 + //uint32 unk2; // 27 + //char* unk3; // 28 uint32 expansion; // 29 - //uint32 unk4; // 30 + //uint32 unk4; // 30 uint32 grouptype; // 31 - //char* desc[16]; // 32-47 Description + //char* desc[16]; // 32-47 Description // Helpers uint32 Entry() const { return ID + (type << 24); } }; @@ -1194,7 +1194,7 @@ struct MapDifficultyEntry //uint32 Id; // 0 uint32 MapId; // 1 uint32 Difficulty; // 2 (for arenas: arena slot) - //char* areaTriggerText[16]; // 3-18 text showed when transfer to map failed (missing requirements) + char* areaTriggerText; // 3-18 text showed when transfer to map failed (missing requirements) //uint32 textFlags; // 19 uint32 resetTime; // 20 uint32 maxPlayers; // 21 @@ -1881,11 +1881,12 @@ struct WorldSafeLocsEntry // Structures not used for casting to loaded DBC data and not required then packing struct MapDifficulty { - MapDifficulty() : resetTime(0), maxPlayers(0) {} - MapDifficulty(uint32 _resetTime, uint32 _maxPlayers) : resetTime(_resetTime), maxPlayers(_maxPlayers) {} + MapDifficulty() : resetTime(0), maxPlayers(0), hasErrorMessage(false) {} + MapDifficulty(uint32 _resetTime, uint32 _maxPlayers, bool _hasErrorMessage) : resetTime(_resetTime), maxPlayers(_maxPlayers), hasErrorMessage(_hasErrorMessage) {} uint32 resetTime; uint32 maxPlayers; + bool hasErrorMessage; }; struct TalentSpellPos diff --git a/src/server/game/DataStores/DBCfmt.h b/src/server/game/DataStores/DBCfmt.h index 80d8791bd01..b83ea207804 100644 --- a/src/server/game/DataStores/DBCfmt.h +++ b/src/server/game/DataStores/DBCfmt.h @@ -77,7 +77,7 @@ const char LFGDungeonEntryfmt[]="nxxxxxxxxxxxxxxxxxiiiiiiixixxixixxxxxxxxxxxxxxx const char LockEntryfmt[]="niiiiiiiiiiiiiiiiiiiiiiiixxxxxxxx"; const char MailTemplateEntryfmt[]="nxxxxxxxxxxxxxxxxxssssssssssssssssx"; const char MapEntryfmt[]="nxixxssssssssssssssssxixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxixiffxiix"; -const char MapDifficultyEntryfmt[]="diixxxxxxxxxxxxxxxxxiix"; +const char MapDifficultyEntryfmt[]="diisxxxxxxxxxxxxxxxxiix"; const char MovieEntryfmt[]="nxx"; const char QuestSortEntryfmt[]="nxxxxxxxxxxxxxxxxx"; const char QuestXPfmt[]="niiiiiiiiii"; diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 8b2b74b572d..44f39528146 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -6540,15 +6540,13 @@ void Player::RewardReputation(Unit *pVictim, float rate) Map const *pMap = GetMap(); if (pMap && pMap->IsDungeon()) { - bool Heroic = ((InstanceMap*)pMap)->GetDifficulty() == DUNGEON_DIFFICULTY_HEROIC; - InstanceTemplate const *pInstance = objmgr.GetInstanceTemplate(pMap->GetId()); if (pInstance) { - AccessRequirement const *pAccessRequirement = objmgr.GetAccessRequirement(pInstance->access_id); + AccessRequirement const *pAccessRequirement = objmgr.GetAccessRequirement(pMap->GetId(), ((InstanceMap*)pMap)->GetDifficulty()); if (pAccessRequirement) { - if (!pMap->IsRaid() && ((!Heroic && pAccessRequirement->levelMin == 80) || (Heroic && pAccessRequirement->heroicLevelMin == 80))) + if (!pMap->IsRaid() && pAccessRequirement->levelMin == 80) ChampioningFaction = GetChampioningFaction(); } } @@ -15891,7 +15889,7 @@ bool Player::LoadFromDB(uint32 guid, SqlQueryHolder *holder) // 12 13 14 15 16 17 18 19 20 21 22 23 24 //"position_x, position_y, position_z, map, orientation, taximask, cinematic, totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, resettalents_cost," // 25 26 27 28 29 30 31 32 33 34 35 36 37 38 - //"resettalents_time, trans_x, trans_y, trans_z, trans_o, transguid, extra_flags, stable_slots, at_login, zone, online, death_expire_time, taxi_path, dungeon_difficulty," + //"resettalents_time, trans_x, trans_y, trans_z, trans_o, transguid, extra_flags, stable_slots, at_login, zone, online, death_expire_time, taxi_path, instance_mode_mask," // 39 40 41 42 43 44 45 46 47 48 49 //"arenaPoints, totalHonorPoints, todayHonorPoints, yesterdayHonorPoints, totalKills, todayKills, yesterdayKills, chosenTitle, knownCurrencies, watchedFaction, drunk," // 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 @@ -15997,10 +15995,15 @@ bool Player::LoadFromDB(uint32 guid, SqlQueryHolder *holder) uint32 mapId = fields[15].GetUInt32(); uint32 instanceId = fields[58].GetFloat(); - uint32 difficulty = fields[38].GetUInt32(); - if (difficulty >= MAX_DUNGEON_DIFFICULTY) - difficulty = DUNGEON_DIFFICULTY_NORMAL; - SetDungeonDifficulty(Difficulty(difficulty)); // may be changed in _LoadGroup + uint32 dungeonDiff = fields[38].GetUInt32() & 0x0F; + if (dungeonDiff >= MAX_DUNGEON_DIFFICULTY) + dungeonDiff = DUNGEON_DIFFICULTY_NORMAL; + uint32 raidDiff = (fields[38].GetUInt32() >> 4) & 0x0F; + if (raidDiff >= MAX_RAID_DIFFICULTY) + raidDiff = RAID_DIFFICULTY_10MAN_NORMAL; + SetDungeonDifficulty(Difficulty(dungeonDiff)); // may be changed in _LoadGroup + SetRaidDifficulty(Difficulty(raidDiff)); // may be changed in _LoadGroup + std::string taxi_nodes = fields[37].GetCppString(); #define RelocateToHomebind(){ mapId = m_homebindMapId; instanceId = 0; Relocate(m_homebindX, m_homebindY, m_homebindZ); } @@ -16252,6 +16255,7 @@ bool Player::LoadFromDB(uint32 guid, SqlQueryHolder *holder) } SetMap(map); + StoreRaidMapDifficulty(); // randomize first save time in range [CONFIG_INTERVAL_SAVE] around [CONFIG_INTERVAL_SAVE] // this must help in case next save after mass player load after server startup @@ -17293,7 +17297,7 @@ void Player::_LoadBoundInstances(QueryResult_AutoPtr result) InstancePlayerBind* Player::GetBoundInstance(uint32 mapid, Difficulty difficulty) { // some instances only have one difficulty - MapDifficulty const* mapDiff = GetMapDifficultyData(mapid,difficulty); + MapDifficulty const* mapDiff = GetDownscaledMapDifficultyData(mapid,difficulty); if (!mapDiff) return NULL; @@ -17489,7 +17493,7 @@ void Player::ConvertInstancesToGroup(Player *player, Group *group, uint64 player CharacterDatabase.PExecute("DELETE FROM character_instance WHERE guid = '%d' AND permanent = 0", GUID_LOPART(player_guid)); } -bool Player::Satisfy(AccessRequirement const *ar, uint32 target_map, bool report) +bool Player::Satisfy(AccessRequirement const* ar, uint32 target_map, bool report) { if (!isGameMaster() && ar) { @@ -17504,8 +17508,6 @@ bool Player::Satisfy(AccessRequirement const *ar, uint32 target_map, bool report { if (ar->levelMin && getLevel() < ar->levelMin) LevelMin = ar->levelMin; - if (mapEntry->IsNonRaidDungeon() && ar->heroicLevelMin && GetDungeonDifficulty() == DUNGEON_DIFFICULTY_HEROIC && getLevel() < ar->heroicLevelMin) - LevelMin = ar->heroicLevelMin; if (ar->levelMax && getLevel() > ar->levelMax) LevelMax = ar->levelMax; } @@ -17530,39 +17532,28 @@ bool Player::Satisfy(AccessRequirement const *ar, uint32 target_map, bool report ? (GetRaidDifficulty() == RAID_DIFFICULTY_10MAN_NORMAL) : (GetDungeonDifficulty() == DUNGEON_DIFFICULTY_NORMAL); - uint32 missingKey = 0; - uint32 missingHeroicQuest = 0; - if (!isNormalTargetMap) - { - if (ar->heroicKey) - { - if (!HasItemCount(ar->heroicKey, 1) && - (!ar->heroicKey2 || !HasItemCount(ar->heroicKey2, 1))) - missingKey = ar->heroicKey; - } - else if (ar->heroicKey2 && !HasItemCount(ar->heroicKey2, 1)) - missingKey = ar->heroicKey2; - - if (ar->heroicQuest && !GetQuestRewardStatus(ar->heroicQuest)) - missingHeroicQuest = ar->heroicQuest; - } - uint32 missingQuest = 0; - if (ar->quest && !GetQuestRewardStatus(ar->quest)) - missingQuest = ar->quest; + if (GetTeam() == ALLIANCE && ar->quest_A && !GetQuestRewardStatus(ar->quest_A)) + missingQuest = ar->quest_A; + else if (GetTeam() == HORDE && ar->quest_H && !GetQuestRewardStatus(ar->quest_H)) + missingQuest = ar->quest_H; - if (LevelMin || LevelMax || missingItem || missingKey || missingQuest || missingHeroicQuest) + uint32 missingAchievement = 0; + if (ar->achievement && !GetAchievementMgr().HasAchieved(sAchievementStore.LookupEntry(ar->achievement))) + missingAchievement = ar->achievement; + + Difficulty target_difficulty = GetDifficulty(mapEntry->IsRaid()); + MapDifficulty const* mapDiff = GetDownscaledMapDifficultyData(target_map, target_difficulty); + if (LevelMin || LevelMax || missingItem || missingQuest || missingAchievement) { if (report) { - if (missingItem) + if (missingQuest && !ar->questFailedText.empty()) + ChatHandler(GetSession()).PSendSysMessage(ar->questFailedText.c_str()); + else if (mapDiff->hasErrorMessage) // if (missingAchievement) covered by this case + SendTransferAborted(target_map, TRANSFER_ABORT_DIFFICULTY, target_difficulty); + else if (missingItem) GetSession()->SendAreaTriggerMessage(GetSession()->GetTrinityString(LANG_LEVEL_MINREQUIRED_AND_ITEM), LevelMin, objmgr.GetItemPrototype(missingItem)->Name1); - else if (missingKey) - SendTransferAborted(target_map, TRANSFER_ABORT_DIFFICULTY, isNormalTargetMap ? DUNGEON_DIFFICULTY_NORMAL : DUNGEON_DIFFICULTY_HEROIC); - else if (missingHeroicQuest) - GetSession()->SendAreaTriggerMessage(ar->heroicQuestFailedText.c_str()); - else if (missingQuest) - GetSession()->SendAreaTriggerMessage(ar->questFailedText.c_str()); else if (LevelMin) GetSession()->SendAreaTriggerMessage(GetSession()->GetTrinityString(LANG_LEVEL_MINREQUIRED), LevelMin); } @@ -17672,7 +17663,7 @@ void Player::SaveToDB() std::ostringstream ss; ss << "REPLACE INTO characters (guid,account,name,race,class,gender,level,xp,money,playerBytes,playerBytes2,playerFlags," - "map, instance_id, dungeon_difficulty, position_x, position_y, position_z, orientation, " + "map, instance_id, instance_mode_mask, position_x, position_y, position_z, orientation, " "taximask, online, cinematic, " "totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, resettalents_cost, resettalents_time, " "trans_x, trans_y, trans_z, trans_o, transguid, extra_flags, stable_slots, at_login, zone, " @@ -17696,7 +17687,7 @@ void Player::SaveToDB() { ss << GetMapId() << ", " << (uint32)GetInstanceId() << ", " - << (uint32)GetDungeonDifficulty() << ", " + << uint32(uint8(GetDungeonDifficulty()) | uint8(GetRaidDifficulty()) << 4) << ", " << finiteAlways(GetPositionX()) << ", " << finiteAlways(GetPositionY()) << ", " << finiteAlways(GetPositionZ()) << ", " @@ -17706,7 +17697,7 @@ void Player::SaveToDB() { ss << GetTeleportDest().GetMapId() << ", " << (uint32)0 << ", " - << (uint32)GetDungeonDifficulty() << ", " + << uint32(uint8(GetDungeonDifficulty()) | uint8(GetRaidDifficulty()) << 4) << ", " << finiteAlways(GetTeleportDest().GetPositionX()) << ", " << finiteAlways(GetTeleportDest().GetPositionY()) << ", " << finiteAlways(GetTeleportDest().GetPositionZ()) << ", " @@ -18387,11 +18378,11 @@ void Player::SendDungeonDifficulty(bool IsInGroup) GetSession()->SendPacket(&data); } -void Player::SendRaidDifficulty(bool IsInGroup) +void Player::SendRaidDifficulty(bool IsInGroup, int32 forcedDifficulty) { uint8 val = 0x00000001; WorldPacket data(MSG_SET_RAID_DIFFICULTY, 12); - data << uint32(GetRaidDifficulty()); + data << uint32(forcedDifficulty == -1 ? GetRaidDifficulty() : forcedDifficulty); data << uint32(val); data << uint32(IsInGroup); GetSession()->SendPacket(&data); @@ -21005,6 +20996,18 @@ void Player::SendInitialPacketsAfterAddToMap() SendAurasForTarget(this); SendEnchantmentDurations(); // must be after add to map SendItemDurations(); // must be after add to map + + // raid downscaling - send difficulty to player + if (GetMap()->IsRaid()) + { + if (GetMap()->GetDifficulty() != GetRaidDifficulty()) + { + StoreRaidMapDifficulty(); + SendRaidDifficulty(GetGroup() != NULL, GetStoredRaidDifficulty()); + } + } + else if (GetRaidDifficulty() != GetStoredRaidDifficulty()) + SendRaidDifficulty(GetGroup() != NULL); } void Player::SendUpdateToOutOfRangeGroupMembers() diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index cfe956320af..82efe19e3c7 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -829,15 +829,12 @@ struct AccessRequirement { uint8 levelMin; uint8 levelMax; - uint8 heroicLevelMin; uint32 item; uint32 item2; - uint32 heroicKey; - uint32 heroicKey2; - uint32 quest; + uint32 quest_A; + uint32 quest_H; + uint32 achievement; std::string questFailedText; - uint32 heroicQuest; - std::string heroicQuestFailedText; }; enum CharDeleteMethod @@ -1743,8 +1740,10 @@ class Player : public Unit, public GridObject<Player> Difficulty GetDifficulty(bool isRaid) const { return isRaid ? m_raidDifficulty : m_dungeonDifficulty; } Difficulty GetDungeonDifficulty() const { return m_dungeonDifficulty; } Difficulty GetRaidDifficulty() const { return m_raidDifficulty; } + Difficulty GetStoredRaidDifficulty() const { return m_raidMapDifficulty; } // only for use in difficulty packet after exiting to raid map void SetDungeonDifficulty(Difficulty dungeon_difficulty) { m_dungeonDifficulty = dungeon_difficulty; } void SetRaidDifficulty(Difficulty raid_difficulty) { m_raidDifficulty = raid_difficulty; } + void StoreRaidMapDifficulty() { m_raidMapDifficulty = GetMap()->GetDifficulty(); } bool UpdateSkill(uint32 skill_id, uint32 step); bool UpdateSkillPro(uint16 SkillId, int32 Chance, uint32 step); @@ -1830,7 +1829,7 @@ class Player : public Unit, public GridObject<Player> void SendExplorationExperience(uint32 Area, uint32 Experience); void SendDungeonDifficulty(bool IsInGroup); - void SendRaidDifficulty(bool IsInGroup); + void SendRaidDifficulty(bool IsInGroup, int32 forcedDifficulty = -1); void ResetInstances(uint8 method, bool isRaid); void SendResetInstanceSuccess(uint32 MapId); void SendResetInstanceFailed(uint32 reason, uint32 MapId); @@ -2268,7 +2267,7 @@ class Player : public Unit, public GridObject<Player> void SendRaidInfo(); void SendSavedInstances(); static void ConvertInstancesToGroup(Player *player, Group *group = NULL, uint64 player_guid = 0); - bool Satisfy(AccessRequirement const*, uint32 target_map, bool report = false); + bool Satisfy(AccessRequirement const* ar, uint32 target_map, bool report = false); bool CheckInstanceLoginValid(); // last used pet number (for BG's) @@ -2458,6 +2457,7 @@ class Player : public Unit, public GridObject<Player> uint32 m_speakCount; Difficulty m_dungeonDifficulty; Difficulty m_raidDifficulty; + Difficulty m_raidMapDifficulty; uint32 m_atLoginFlags; diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index 4890c67c6b4..d0af5081c89 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -5885,8 +5885,8 @@ void ObjectMgr::LoadAreaTriggerTeleports() uint32 count = 0; - // 0 1 2 3 4 5 6 - QueryResult_AutoPtr result = WorldDatabase.Query("SELECT id, access_id, target_map, target_position_x, target_position_y, target_position_z, target_orientation FROM areatrigger_teleport"); + // 0 1 2 3 4 5 + QueryResult_AutoPtr result = WorldDatabase.Query("SELECT id, target_map, target_position_x, target_position_y, target_position_z, target_orientation FROM areatrigger_teleport"); if (!result) { @@ -5913,12 +5913,11 @@ void ObjectMgr::LoadAreaTriggerTeleports() AreaTrigger at; - at.access_id = fields[1].GetUInt32(); - at.target_mapId = fields[2].GetUInt32(); - at.target_X = fields[3].GetFloat(); - at.target_Y = fields[4].GetFloat(); - at.target_Z = fields[5].GetFloat(); - at.target_Orientation = fields[6].GetFloat(); + at.target_mapId = fields[1].GetUInt32(); + at.target_X = fields[2].GetFloat(); + at.target_Y = fields[3].GetFloat(); + at.target_Z = fields[4].GetFloat(); + at.target_Orientation = fields[5].GetFloat(); AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(Trigger_ID); if (!atEntry) @@ -5954,8 +5953,8 @@ void ObjectMgr::LoadAccessRequirements() uint32 count = 0; - // 0 1 2 3 4 5 6 7 8 9 10 11 - QueryResult_AutoPtr result = WorldDatabase.Query("SELECT id, level_min, level_max, item, item2, heroic_key, heroic_key2, quest_done, quest_failed_text, heroic_quest_done, heroic_quest_failed_text, heroic_level_min FROM access_requirement"); + // 0 1 2 3 4 5 6 7 8 9 + QueryResult_AutoPtr result = WorldDatabase.Query("SELECT mapid, difficulty, level_min, level_max, item, item2, quest_done_A, quest_done_H, completed_achievement, quest_failed_text FROM access_requirement"); if (!result) { @@ -5978,28 +5977,27 @@ void ObjectMgr::LoadAccessRequirements() ++count; - uint32 requiremt_ID = fields[0].GetUInt32(); + uint32 mapid = fields[0].GetUInt32(); + uint8 difficulty = fields[1].GetUInt8(); + uint32 requirement_ID = MAKE_PAIR32(mapid,difficulty); AccessRequirement ar; - ar.levelMin = fields[1].GetUInt8(); - ar.levelMax = fields[2].GetUInt8(); - ar.heroicLevelMin = fields[11].GetUInt8(); - ar.item = fields[3].GetUInt32(); - ar.item2 = fields[4].GetUInt32(); - ar.heroicKey = fields[5].GetUInt32(); - ar.heroicKey2 = fields[6].GetUInt32(); - ar.quest = fields[7].GetUInt32(); - ar.questFailedText = fields[8].GetCppString(); - ar.heroicQuest = fields[9].GetUInt32(); - ar.heroicQuestFailedText = fields[10].GetCppString(); + ar.levelMin = fields[2].GetUInt8(); + ar.levelMax = fields[3].GetUInt8(); + ar.item = fields[4].GetUInt32(); + ar.item2 = fields[5].GetUInt32(); + ar.quest_A = fields[6].GetUInt32(); + ar.quest_H = fields[7].GetUInt32(); + ar.achievement = fields[8].GetUInt32(); + ar.questFailedText = fields[9].GetCppString(); if (ar.item) { ItemPrototype const *pProto = GetItemPrototype(ar.item); if (!pProto) { - sLog.outError("Key item %u does not exist for requirement %u, removing key requirement.", ar.item, requiremt_ID); + sLog.outError("Key item %u does not exist for map %u difficulty %u, removing key requirement.", ar.item, mapid, difficulty); ar.item = 0; } } @@ -6009,53 +6007,39 @@ void ObjectMgr::LoadAccessRequirements() ItemPrototype const *pProto = GetItemPrototype(ar.item2); if (!pProto) { - sLog.outError("Second item %u does not exist for requirement %u, removing key requirement.", ar.item2, requiremt_ID); + sLog.outError("Second item %u does not exist for map %u difficulty %u, removing key requirement.", ar.item2, mapid, difficulty); ar.item2 = 0; } } - if (ar.heroicKey) + if (ar.quest_A) { - ItemPrototype const *pProto = GetItemPrototype(ar.heroicKey); - if (!pProto) - { - sLog.outError("Heroic key %u not exist for trigger %u, remove key requirement.", ar.heroicKey, requiremt_ID); - ar.heroicKey = 0; - } - } - - if (ar.heroicKey2) - { - ItemPrototype const *pProto = GetItemPrototype(ar.heroicKey2); - if (!pProto) + if (!GetQuestTemplate(ar.quest_A)) { - sLog.outError("Second heroic key %u not exist for trigger %u, remove key requirement.", ar.heroicKey2, requiremt_ID); - ar.heroicKey2 = 0; + sLog.outErrorDb("Required Alliance Quest %u not exist for map %u difficulty %u, remove quest done requirement.", ar.quest_A, mapid, difficulty); + ar.quest_A = 0; } } - if (ar.heroicQuest) + if (ar.quest_H) { - QuestMap::iterator qReqItr = mQuestTemplates.find(ar.heroicQuest); - if (qReqItr == mQuestTemplates.end()) + if (!GetQuestTemplate(ar.quest_H)) { - sLog.outErrorDb("Required Heroic Quest %u not exist for trigger %u, remove heroic quest done requirement.",ar.heroicQuest,requiremt_ID); - ar.heroicQuest = 0; + sLog.outErrorDb("Required Horde Quest %u not exist for map %u difficulty %u, remove quest done requirement.", ar.quest_H, mapid, difficulty); + ar.quest_H = 0; } } - if (ar.quest) + if (ar.achievement) { - QuestMap::iterator qReqItr = mQuestTemplates.find(ar.quest); - if (qReqItr == mQuestTemplates.end()) + if (!sAchievementStore.LookupEntry(ar.achievement)) { - sLog.outErrorDb("Required Quest %u not exist for trigger %u, remove quest done requirement.",ar.quest,requiremt_ID); - ar.quest = 0; + sLog.outErrorDb("Required Achievement %u not exist for map %u difficulty %u, remove quest done requirement.", ar.achievement, mapid, difficulty); + ar.achievement = 0; } } - mAccessRequirements[requiremt_ID] = ar; - + mAccessRequirements[requirement_ID] = ar; } while (result->NextRow()); sLog.outString(); diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h index 53be3a0e1e0..f3f15faeeb4 100644 --- a/src/server/game/Globals/ObjectMgr.h +++ b/src/server/game/Globals/ObjectMgr.h @@ -120,7 +120,6 @@ typedef std::pair<SpellClickInfoMap::const_iterator,SpellClickInfoMap::const_ite struct AreaTrigger { - uint32 access_id; uint32 target_mapId; float target_X; float target_Y; @@ -529,9 +528,9 @@ class ObjectMgr return NULL; } - AccessRequirement const* GetAccessRequirement(uint32 requirement) const + AccessRequirement const* GetAccessRequirement(uint32 mapid, Difficulty difficulty) const { - AccessRequirementMap::const_iterator itr = mAccessRequirements.find(requirement); + AccessRequirementMap::const_iterator itr = mAccessRequirements.find(MAKE_PAIR32(mapid,difficulty)); if (itr != mAccessRequirements.end()) return &itr->second; return NULL; diff --git a/src/server/game/Groups/Group.cpp b/src/server/game/Groups/Group.cpp index 2679cbb412d..3f5d422e957 100644 --- a/src/server/game/Groups/Group.cpp +++ b/src/server/game/Groups/Group.cpp @@ -1769,9 +1769,7 @@ InstanceGroupBind* Group::GetBoundInstance(Map* aMap) Difficulty difficulty = GetDifficulty(aMap->IsRaid()); // some instances only have one difficulty - MapDifficulty const* mapDiff = GetMapDifficultyData(aMap->GetId(),difficulty); - if (!mapDiff) - return NULL; + MapDifficulty const* mapDiff = GetDownscaledMapDifficultyData(aMap->GetId(),difficulty); BoundInstancesMap::iterator itr = m_boundInstances[difficulty].find(aMap->GetId()); if (itr != m_boundInstances[difficulty].end()) @@ -1788,9 +1786,7 @@ InstanceGroupBind* Group::GetBoundInstance(MapEntry const* mapEntry) Difficulty difficulty = GetDifficulty(mapEntry->IsRaid()); // some instances only have one difficulty - MapDifficulty const* mapDiff = GetMapDifficultyData(mapEntry->MapID,difficulty); - if (!mapDiff) - difficulty = DUNGEON_DIFFICULTY_NORMAL; + MapDifficulty const* mapDiff = GetDownscaledMapDifficultyData(mapEntry->MapID,difficulty); BoundInstancesMap::iterator itr = m_boundInstances[difficulty].find(mapEntry->MapID); if (itr != m_boundInstances[difficulty].end()) diff --git a/src/server/game/Maps/MapInstanced.cpp b/src/server/game/Maps/MapInstanced.cpp index 008baeb79d9..3a7375f52f4 100644 --- a/src/server/game/Maps/MapInstanced.cpp +++ b/src/server/game/Maps/MapInstanced.cpp @@ -200,9 +200,7 @@ InstanceMap* MapInstanced::CreateInstance(uint32 InstanceId, InstanceSave *save, } // some instances only have one difficulty - MapDifficulty const* mapDiff = GetMapDifficultyData(GetId(),difficulty); - if (!mapDiff) - difficulty = DUNGEON_DIFFICULTY_NORMAL; + MapDifficulty const* mapDiff = GetDownscaledMapDifficultyData(GetId(),difficulty); sLog.outDebug("MapInstanced::CreateInstance: %s map instance %d for %d created with difficulty %s", save?"":"new ", InstanceId, GetId(), difficulty?"heroic":"normal"); diff --git a/src/server/game/Maps/MapManager.cpp b/src/server/game/Maps/MapManager.cpp index 5cdd2f90089..5e8a2803419 100644 --- a/src/server/game/Maps/MapManager.cpp +++ b/src/server/game/Maps/MapManager.cpp @@ -165,18 +165,19 @@ bool MapManager::CanPlayerEnter(uint32 mapid, Player* player, bool loginCheck) if (!instance) return false; + Difficulty targetDifficulty = player->GetDifficulty(entry->IsRaid()); //The player has a heroic mode and tries to enter into instance which has no a heroic mode - MapDifficulty const* mapDiff = GetMapDifficultyData(entry->MapID,player->GetDifficulty(entry->IsRaid())); + MapDifficulty const* mapDiff = GetMapDifficultyData(entry->MapID, targetDifficulty); if (!mapDiff) { - bool isNormalTargetMap = entry->IsRaid() - ? (player->GetRaidDifficulty() == RAID_DIFFICULTY_10MAN_NORMAL) - : (player->GetDungeonDifficulty() == DUNGEON_DIFFICULTY_NORMAL); - - // Send aborted message - // FIX ME: what about absent normal/heroic mode with specific players limit... - player->SendTransferAborted(mapid, TRANSFER_ABORT_DIFFICULTY, isNormalTargetMap ? DUNGEON_DIFFICULTY_NORMAL : DUNGEON_DIFFICULTY_HEROIC); - return false; + // Send aborted message for dungeons + if (entry->IsNonRaidDungeon()) + { + player->SendTransferAborted(mapid, TRANSFER_ABORT_DIFFICULTY, player->GetDungeonDifficulty()); + return false; + } + else // attempt to downscale + mapDiff = GetDownscaledMapDifficultyData(entry->MapID, targetDifficulty); } //Bypass checks for GMs @@ -249,7 +250,7 @@ bool MapManager::CanPlayerEnter(uint32 mapid, Player* player, bool loginCheck) } //Other requirements - return player->Satisfy(objmgr.GetAccessRequirement(instance->access_id), mapid, true); + return player->Satisfy(objmgr.GetAccessRequirement(mapid, targetDifficulty), mapid, true); } void MapManager::RemoveBonesFromMap(uint32 mapid, uint64 guid, float x, float y) diff --git a/src/server/game/Server/Protocol/Handlers/CharacterHandler.cpp b/src/server/game/Server/Protocol/Handlers/CharacterHandler.cpp index ca08d197c3f..9d9132ae3c3 100644 --- a/src/server/game/Server/Protocol/Handlers/CharacterHandler.cpp +++ b/src/server/game/Server/Protocol/Handlers/CharacterHandler.cpp @@ -67,7 +67,7 @@ bool LoginQueryHolder::Initialize() // !!! NOTE: including unused `zone`,`online` res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADFROM, "SELECT guid, account, name, race, class, gender, level, xp, money, playerBytes, playerBytes2, playerFlags," "position_x, position_y, position_z, map, orientation, taximask, cinematic, totaltime, leveltime, rest_bonus, logout_time, is_logout_resting, resettalents_cost," - "resettalents_time, trans_x, trans_y, trans_z, trans_o, transguid, extra_flags, stable_slots, at_login, zone, online, death_expire_time, taxi_path, dungeon_difficulty," + "resettalents_time, trans_x, trans_y, trans_z, trans_o, transguid, extra_flags, stable_slots, at_login, zone, online, death_expire_time, taxi_path, instance_mode_mask," "arenaPoints, totalHonorPoints, todayHonorPoints, yesterdayHonorPoints, totalKills, todayKills, yesterdayKills, chosenTitle, knownCurrencies, watchedFaction, drunk," "health, power1, power2, power3, power4, power5, power6, power7, instance_id, speccount, activespec, exploredZones, equipmentCache, ammoId, knownTitles, actionBars FROM characters WHERE guid = '%u'", GUID_LOPART(m_guid)); res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADGROUP, "SELECT guid FROM group_member WHERE memberGuid =%u", GUID_LOPART(m_guid)); diff --git a/src/server/game/Server/Protocol/Handlers/MiscHandler.cpp b/src/server/game/Server/Protocol/Handlers/MiscHandler.cpp index 4225a99c72b..88e4e70998d 100644 --- a/src/server/game/Server/Protocol/Handlers/MiscHandler.cpp +++ b/src/server/game/Server/Protocol/Handlers/MiscHandler.cpp @@ -931,10 +931,6 @@ void WorldSession::HandleAreaTriggerOpcode(WorldPacket & recv_data) if (!at) return; - // MapManager::CanPlayerEnter() calls players->Satisfy() so this is not needed here - // if (!GetPlayer()->Satisfy(objmgr.GetAccessRequirement(at->access_id), at->target_mapId, true)) - // return; - // Check only if target map != current player's map // check if player can enter instance : instance not full, and raid instance not in encounter fight if (GetPlayer()->GetMapId() != at->target_mapId && !sMapMgr.CanPlayerEnter(at->target_mapId, GetPlayer(), false)) @@ -1623,7 +1619,7 @@ void WorldSession::HandleSetRaidDifficultyOpcode(WorldPacket & recv_data) { _player->ResetInstances(INSTANCE_RESET_CHANGE_DIFFICULTY, true); _player->SetRaidDifficulty(Difficulty(mode)); - } + } } void WorldSession::HandleCancelMountAuraOpcode(WorldPacket & /*recv_data*/) diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 9582286ab23..f8cbfd76ad2 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -5312,10 +5312,11 @@ SpellCastResult Spell::CheckCast(bool strict) // check if our map is dungeon if (sMapStore.LookupEntry(m_caster->GetMapId())->IsDungeon()) { - InstanceTemplate const* instance = ObjectMgr::GetInstanceTemplate(m_caster->GetMapId()); + Map const* pMap = m_caster->GetMap(); + InstanceTemplate const* instance = ObjectMgr::GetInstanceTemplate(pMap->GetId()); if (!instance) return SPELL_FAILED_TARGET_NOT_IN_INSTANCE; - if (!target->Satisfy(objmgr.GetAccessRequirement(instance->access_id), m_caster->GetMapId())) + if (!target->Satisfy(objmgr.GetAccessRequirement(pMap->GetId(), pMap->GetDifficulty()), pMap->GetId())) return SPELL_FAILED_BAD_TARGETS; } break; |