diff options
-rw-r--r-- | sql/updates/hotfixes/master/2021_06_06_00_hotfixes.sql | 15 | ||||
-rw-r--r-- | src/server/database/Database/Implementation/HotfixDatabase.cpp | 5 | ||||
-rw-r--r-- | src/server/database/Database/Implementation/HotfixDatabase.h | 3 | ||||
-rw-r--r-- | src/server/game/DataStores/DB2LoadInfo.h | 16 | ||||
-rw-r--r-- | src/server/game/DataStores/DB2Stores.cpp | 12 | ||||
-rw-r--r-- | src/server/game/DataStores/DB2Stores.h | 2 | ||||
-rw-r--r-- | src/server/game/DataStores/DB2Structure.h | 8 | ||||
-rw-r--r-- | src/server/game/Globals/ObjectMgr.cpp | 5 | ||||
-rw-r--r-- | src/server/game/Reputation/ReputationMgr.cpp | 108 | ||||
-rw-r--r-- | src/server/game/Reputation/ReputationMgr.h | 4 |
10 files changed, 167 insertions, 11 deletions
diff --git a/sql/updates/hotfixes/master/2021_06_06_00_hotfixes.sql b/sql/updates/hotfixes/master/2021_06_06_00_hotfixes.sql new file mode 100644 index 00000000000..4f98b077074 --- /dev/null +++ b/sql/updates/hotfixes/master/2021_06_06_00_hotfixes.sql @@ -0,0 +1,15 @@ +-- +-- Table structure for table `paragon_reputation` +-- +DROP TABLE IF EXISTS `paragon_reputation`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `paragon_reputation` ( + `ID` int(10) unsigned NOT NULL DEFAULT '0', + `FactionID` int(10) unsigned NOT NULL DEFAULT '0', + `LevelThreshold` int(11) NOT NULL DEFAULT '0', + `QuestID` int(11) NOT NULL DEFAULT '0', + `VerifiedBuild` int(11) NOT NULL DEFAULT '0', + PRIMARY KEY (`ID`,`VerifiedBuild`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; diff --git a/src/server/database/Database/Implementation/HotfixDatabase.cpp b/src/server/database/Database/Implementation/HotfixDatabase.cpp index 208d9ca486b..57c1d975348 100644 --- a/src/server/database/Database/Implementation/HotfixDatabase.cpp +++ b/src/server/database/Database/Implementation/HotfixDatabase.cpp @@ -1054,6 +1054,11 @@ void HotfixDatabaseConnection::DoPrepareStatements() "Spells10, PlayerActionBarFileDataID, Flags FROM override_spell_data WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH); PREPARE_MAX_ID_STMT(HOTFIX_SEL_OVERRIDE_SPELL_DATA, "SELECT MAX(ID) + 1 FROM override_spell_data", CONNECTION_SYNCH); + // ParagonReputation.db2 + PrepareStatement(HOTFIX_SEL_PARAGON_REPUTATION, "SELECT ID, FactionID, LevelThreshold, QuestID FROM paragon_reputation" + " WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH); + PREPARE_MAX_ID_STMT(HOTFIX_SEL_PARAGON_REPUTATION, "SELECT MAX(ID) + 1 FROM paragon_reputation", CONNECTION_SYNCH); + // Phase.db2 PrepareStatement(HOTFIX_SEL_PHASE, "SELECT ID, Flags FROM phase WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH); PREPARE_MAX_ID_STMT(HOTFIX_SEL_PHASE, "SELECT MAX(ID) + 1 FROM phase", CONNECTION_SYNCH); diff --git a/src/server/database/Database/Implementation/HotfixDatabase.h b/src/server/database/Database/Implementation/HotfixDatabase.h index e2d4a788865..77d62fae5db 100644 --- a/src/server/database/Database/Implementation/HotfixDatabase.h +++ b/src/server/database/Database/Implementation/HotfixDatabase.h @@ -613,6 +613,9 @@ enum HotfixDatabaseStatements : uint32 HOTFIX_SEL_OVERRIDE_SPELL_DATA, HOTFIX_SEL_OVERRIDE_SPELL_DATA_MAX_ID, + HOTFIX_SEL_PARAGON_REPUTATION, + HOTFIX_SEL_PARAGON_REPUTATION_MAX_ID, + HOTFIX_SEL_PHASE, HOTFIX_SEL_PHASE_MAX_ID, diff --git a/src/server/game/DataStores/DB2LoadInfo.h b/src/server/game/DataStores/DB2LoadInfo.h index 885d366c000..4763c7d13ed 100644 --- a/src/server/game/DataStores/DB2LoadInfo.h +++ b/src/server/game/DataStores/DB2LoadInfo.h @@ -3971,6 +3971,22 @@ struct OverrideSpellDataLoadInfo } }; +struct ParagonReputationLoadInfo +{ + static DB2LoadInfo const* Instance() + { + static DB2FieldMeta const fields[] = + { + { false, FT_INT, "ID" }, + { false, FT_INT, "FactionID" }, + { true, FT_INT, "LevelThreshold" }, + { true, FT_INT, "QuestID" }, + }; + static DB2LoadInfo const loadInfo(&fields[0], std::extent<decltype(fields)>::value, ParagonReputationMeta::Instance(), HOTFIX_SEL_PARAGON_REPUTATION); + return &loadInfo; + } +}; + struct PhaseLoadInfo { static DB2LoadInfo const* Instance() diff --git a/src/server/game/DataStores/DB2Stores.cpp b/src/server/game/DataStores/DB2Stores.cpp index 0e2ffc7222b..564967cfd0b 100644 --- a/src/server/game/DataStores/DB2Stores.cpp +++ b/src/server/game/DataStores/DB2Stores.cpp @@ -218,6 +218,7 @@ DB2Storage<NamesReservedEntry> sNamesReservedStore("NamesReserv DB2Storage<NamesReservedLocaleEntry> sNamesReservedLocaleStore("NamesReservedLocale.db2", NamesReservedLocaleLoadInfo::Instance()); DB2Storage<NumTalentsAtLevelEntry> sNumTalentsAtLevelStore("NumTalentsAtLevel.db2", NumTalentsAtLevelLoadInfo::Instance()); DB2Storage<OverrideSpellDataEntry> sOverrideSpellDataStore("OverrideSpellData.db2", OverrideSpellDataLoadInfo::Instance()); +DB2Storage<ParagonReputationEntry> sParagonReputationStore("ParagonReputation.db2", ParagonReputationLoadInfo::Instance()); DB2Storage<PhaseEntry> sPhaseStore("Phase.db2", PhaseLoadInfo::Instance()); DB2Storage<PhaseXPhaseGroupEntry> sPhaseXPhaseGroupStore("PhaseXPhaseGroup.db2", PhaseXPhaseGroupLoadInfo::Instance()); DB2Storage<PlayerConditionEntry> sPlayerConditionStore("PlayerCondition.db2", PlayerConditionLoadInfo::Instance()); @@ -437,6 +438,7 @@ namespace MountDisplaysCointainer _mountDisplays; NameGenContainer _nameGenData; NameValidationRegexContainer _nameValidators; + std::unordered_map<uint32, ParagonReputationEntry const*> _paragonReputations; PhaseGroupContainer _phasesByGroup; PowerTypesContainer _powerTypes; std::unordered_map<uint32, uint8> _pvpItemBonus; @@ -761,6 +763,7 @@ uint32 DB2Manager::LoadStores(std::string const& dataPath, LocaleConstant defaul LOAD_DB2(sNamesReservedLocaleStore); LOAD_DB2(sNumTalentsAtLevelStore); LOAD_DB2(sOverrideSpellDataStore); + LOAD_DB2(sParagonReputationStore); LOAD_DB2(sPhaseStore); LOAD_DB2(sPhaseXPhaseGroupStore); LOAD_DB2(sPlayerConditionStore); @@ -1226,6 +1229,10 @@ uint32 DB2Manager::LoadStores(std::string const& dataPath, LocaleConstant defaul } } + for (ParagonReputationEntry const* paragonReputation : sParagonReputationStore) + if (sFactionStore.HasRecord(paragonReputation->FactionID)) + _paragonReputations[paragonReputation->FactionID] = paragonReputation; + for (PhaseXPhaseGroupEntry const* group : sPhaseXPhaseGroupStore) if (PhaseEntry const* phase = sPhaseStore.LookupEntry(group->PhaseID)) _phasesByGroup[group->PhaseGroupID].push_back(phase->ID); @@ -2650,6 +2657,11 @@ int32 DB2Manager::GetNumTalentsAtLevel(uint32 level, Classes playerClass) return 0; } +ParagonReputationEntry const* DB2Manager::GetParagonReputation(uint32 factionId) const +{ + return Trinity::Containers::MapGetValuePtr(_paragonReputations, factionId); +} + PVPDifficultyEntry const* DB2Manager::GetBattlegroundBracketByLevel(uint32 mapid, uint32 level) { PVPDifficultyEntry const* maxEntry = nullptr; // used for level > max listed level case diff --git a/src/server/game/DataStores/DB2Stores.h b/src/server/game/DataStores/DB2Stores.h index 99b29d64be7..3025bb73ce4 100644 --- a/src/server/game/DataStores/DB2Stores.h +++ b/src/server/game/DataStores/DB2Stores.h @@ -162,6 +162,7 @@ TC_GAME_API extern DB2Storage<MountCapabilityEntry> sMountCapabi TC_GAME_API extern DB2Storage<MountEntry> sMountStore; TC_GAME_API extern DB2Storage<MovieEntry> sMovieStore; TC_GAME_API extern DB2Storage<OverrideSpellDataEntry> sOverrideSpellDataStore; +TC_GAME_API extern DB2Storage<ParagonReputationEntry> sParagonReputationStore; TC_GAME_API extern DB2Storage<PhaseEntry> sPhaseStore; TC_GAME_API extern DB2Storage<PlayerConditionEntry> sPlayerConditionStore; TC_GAME_API extern DB2Storage<PowerDisplayEntry> sPowerDisplayStore; @@ -393,6 +394,7 @@ public: std::string GetNameGenEntry(uint8 race, uint8 gender) const; ResponseCodes ValidateName(std::wstring const& name, LocaleConstant locale) const; static int32 GetNumTalentsAtLevel(uint32 level, Classes playerClass); + ParagonReputationEntry const* GetParagonReputation(uint32 factionId) const; std::vector<uint32> const* GetPhasesForGroup(uint32 group) const; PowerTypeEntry const* GetPowerTypeEntry(Powers power) const; PowerTypeEntry const* GetPowerTypeByName(std::string const& name) const; diff --git a/src/server/game/DataStores/DB2Structure.h b/src/server/game/DataStores/DB2Structure.h index 8823e2b01d9..d8f32bc1bf9 100644 --- a/src/server/game/DataStores/DB2Structure.h +++ b/src/server/game/DataStores/DB2Structure.h @@ -2453,6 +2453,14 @@ struct OverrideSpellDataEntry uint8 Flags; }; +struct ParagonReputationEntry +{ + uint32 ID; + uint32 FactionID; + int32 LevelThreshold; + int32 QuestID; +}; + struct PhaseEntry { uint32 ID; diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index 909660aa310..8bd7173f44f 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -4935,6 +4935,11 @@ void ObjectMgr::LoadQuests() } } + // Make all paragon reward quests repeatable + for (ParagonReputationEntry const* paragonReputation : sParagonReputationStore) + if (Quest const* quest = GetQuestTemplate(paragonReputation->QuestID)) + const_cast<Quest*>(quest)->SetSpecialFlag(QUEST_SPECIAL_FLAGS_REPEATABLE); + TC_LOG_INFO("server.loading", ">> Loaded " SZFMTD " quests definitions in %u ms", _questTemplates.size(), GetMSTimeDiffToNow(oldMSTime)); } diff --git a/src/server/game/Reputation/ReputationMgr.cpp b/src/server/game/Reputation/ReputationMgr.cpp index b7fa7ddc567..e662daf4a97 100644 --- a/src/server/game/Reputation/ReputationMgr.cpp +++ b/src/server/game/Reputation/ReputationMgr.cpp @@ -136,11 +136,30 @@ int32 ReputationMgr::GetMinReputation(FactionEntry const* factionEntry) const int32 ReputationMgr::GetMaxReputation(FactionEntry const* factionEntry) const { + if (ParagonReputationEntry const* paragonReputation = sDB2Manager.GetParagonReputation(factionEntry->ID)) + { + // has reward quest, cap is just before threshold for another quest reward + // for example: if current reputation is 12345 and questa are given every 10000 and player has unclaimed reward + // then cap will be 19999 + + // otherwise cap is one theshold level larger + // if current reputation is 12345 and questa are given every 10000 and player does NOT have unclaimed reward + // then cap will be 29999 + + int32 reputation = GetReputation(factionEntry); + int32 cap = reputation + paragonReputation->LevelThreshold - reputation % paragonReputation->LevelThreshold - 1; + + if (_player->GetQuestStatus(paragonReputation->QuestID) == QUEST_STATUS_NONE) + cap += paragonReputation->LevelThreshold; + + return cap; + } + if (std::set<FriendshipRepReactionEntry const*> const* friendshipReactions = sDB2Manager.GetFriendshipRepReactions(factionEntry->FriendshipRepID)) return (*friendshipReactions->rbegin())->ReactionThreshold; int32 dataIndex = GetFactionDataIndexForRaceAndClass(factionEntry); - if (dataIndex >= 0 && factionEntry->ReputationMax[dataIndex]) + if (dataIndex >= 0) return factionEntry->ReputationMax[dataIndex]; return *ReputationRankThresholds.rbegin(); @@ -191,6 +210,22 @@ ReputationRank const* ReputationMgr::GetForcedRankIfAny(FactionTemplateEntry con return GetForcedRankIfAny(factionTemplateEntry->Faction); } +int32 ReputationMgr::GetParagonLevel(uint32 paragonFactionId) const +{ + return GetParagonLevel(sFactionStore.LookupEntry(paragonFactionId)); +} + +int32 ReputationMgr::GetParagonLevel(FactionEntry const* paragonFactionEntry) const +{ + if (!paragonFactionEntry) + return 0; + + if (ParagonReputationEntry const* paragonReputation = sDB2Manager.GetParagonReputation(paragonFactionEntry->ID)) + return GetReputation(paragonFactionEntry) / paragonReputation->LevelThreshold; + + return 0; +} + void ReputationMgr::ApplyForceReaction(uint32 faction_id, ReputationRank rank, bool apply) { if (apply) @@ -201,11 +236,19 @@ void ReputationMgr::ApplyForceReaction(uint32 faction_id, ReputationRank rank, b ReputationFlags ReputationMgr::GetDefaultStateFlags(FactionEntry const* factionEntry) const { - int32 dataIndex = GetFactionDataIndexForRaceAndClass(factionEntry); - if (dataIndex < 0) - return ReputationFlags::None; + ReputationFlags flags = [&]() + { + int32 dataIndex = GetFactionDataIndexForRaceAndClass(factionEntry); + if (dataIndex < 0) + return ReputationFlags::None; + + return static_cast<ReputationFlags>(factionEntry->ReputationFlags[dataIndex]); + }(); + + if (sDB2Manager.GetParagonReputation(factionEntry->ID)) + flags |= ReputationFlags::ShowPropagated; - return static_cast<ReputationFlags>(factionEntry->ReputationFlags[dataIndex]); + return flags; } void ReputationMgr::SendForceReactions() @@ -374,12 +417,22 @@ bool ReputationMgr::SetReputation(FactionEntry const* factionEntry, int32 standi FactionStateList::iterator faction = _factions.find(factionEntry->ReputationIndex); if (faction != _factions.end()) { - // if we update spillover only, do not update main reputation (rank exceeds creature reward rate) - if (!spillOverOnly) - res = SetOneFactionReputation(factionEntry, standing, incremental); + FactionEntry const* primaryFactionToModify = factionEntry; + if (incremental && standing > 0 && CanGainParagonReputationForFaction(factionEntry)) + { + primaryFactionToModify = sFactionStore.AssertEntry(factionEntry->ParagonFactionID); + faction = _factions.find(primaryFactionToModify->ReputationIndex); + } + + if (faction != _factions.end()) + { + // if we update spillover only, do not update main reputation (rank exceeds creature reward rate) + if (!spillOverOnly) + res = SetOneFactionReputation(primaryFactionToModify, standing, incremental); - // only this faction gets reported to client, even if it has no own visible standing - SendState(&faction->second); + // only this faction gets reported to client, even if it has no own visible standing + SendState(&faction->second); + } } return res; } @@ -406,6 +459,7 @@ bool ReputationMgr::SetOneFactionReputation(FactionEntry const* factionEntry, in ReputationRank old_rank = ReputationToRank(factionEntry, itr->second.Standing + BaseRep); ReputationRank new_rank = ReputationToRank(factionEntry, standing); + int32 oldStanding = itr->second.Standing + BaseRep; int32 newStanding = standing - BaseRep; _player->ReputationChanged(factionEntry, newStanding - itr->second.Standing); @@ -422,7 +476,17 @@ bool ReputationMgr::SetOneFactionReputation(FactionEntry const* factionEntry, in if (new_rank > old_rank) _sendFactionIncreased = true; - if (!factionEntry->FriendshipRepID) + ParagonReputationEntry const* paragonReputation = sDB2Manager.GetParagonReputation(factionEntry->ID); + if (paragonReputation) + { + int32 oldParagonLevel = oldStanding / paragonReputation->LevelThreshold; + int32 newParagonLevel = standing / paragonReputation->LevelThreshold; + if (oldParagonLevel != newParagonLevel) + if (Quest const* paragonRewardQuest = sObjectMgr->GetQuestTemplate(paragonReputation->QuestID)) + _player->AddQuestAndCheckCompletion(paragonRewardQuest, nullptr); + } + + if (!factionEntry->FriendshipRepID && !paragonReputation) UpdateRankCounters(old_rank, new_rank); _player->UpdateCriteria(CRITERIA_TYPE_KNOWN_FACTIONS, factionEntry->ID); @@ -468,6 +532,9 @@ void ReputationMgr::SetVisible(FactionState* faction) if (faction->Flags.HasFlag(ReputationFlags::Header) && !faction->Flags.HasFlag(ReputationFlags::HeaderShowsBar)) return; + if (sDB2Manager.GetParagonReputation(faction->ID)) + return; + // already set if (faction->Flags.HasFlag(ReputationFlags::Visible)) return; @@ -661,3 +728,22 @@ int32 ReputationMgr::GetFactionDataIndexForRaceAndClass(FactionEntry const* fact return -1; } + +bool ReputationMgr::CanGainParagonReputationForFaction(FactionEntry const* factionEntry) const +{ + if (!sFactionStore.LookupEntry(factionEntry->ParagonFactionID)) + return false; + + if (GetRank(factionEntry) != REP_EXALTED) + return false; + + ParagonReputationEntry const* paragonReputation = sDB2Manager.GetParagonReputation(factionEntry->ParagonFactionID); + if (!paragonReputation) + return false; + + Quest const* quest = sObjectMgr->GetQuestTemplate(paragonReputation->QuestID); + if (!quest) + return false; + + return _player->getLevel() >= _player->GetQuestMinLevel(quest); +} diff --git a/src/server/game/Reputation/ReputationMgr.h b/src/server/game/Reputation/ReputationMgr.h index 1ca2c9afbaa..3d3d411fe1e 100644 --- a/src/server/game/Reputation/ReputationMgr.h +++ b/src/server/game/Reputation/ReputationMgr.h @@ -121,6 +121,9 @@ class TC_GAME_API ReputationMgr return forceItr != _forcedReactions.end() ? &forceItr->second : nullptr; } + int32 GetParagonLevel(uint32 paragonFactionId) const; + int32 GetParagonLevel(FactionEntry const* paragonFactionEntry) const; + public: // modifiers bool SetReputation(FactionEntry const* factionEntry, int32 standing) { @@ -156,6 +159,7 @@ class TC_GAME_API ReputationMgr void SendVisible(FactionState const* faction, bool visible = true) const; void UpdateRankCounters(ReputationRank old_rank, ReputationRank new_rank); int32 GetFactionDataIndexForRaceAndClass(FactionEntry const* factionEntry) const; + bool CanGainParagonReputationForFaction(FactionEntry const* factionEntry) const; private: Player* _player; |