Core/Players: Fix character creation
This commit is contained in:
@@ -10529,55 +10529,10 @@ PlayerInfo const* ObjectMgr::GetPlayerInfo(uint32 race, uint32 class_) const
|
||||
void ObjectMgr::LoadRaceAndClassExpansionRequirements()
|
||||
{
|
||||
uint32 oldMSTime = getMSTime();
|
||||
_raceUnlockRequirementStore.clear();
|
||||
|
||||
// 0 1 2
|
||||
QueryResult result = WorldDatabase.Query("SELECT raceID, expansion, achievementId FROM `race_unlock_requirement`");
|
||||
|
||||
if (result)
|
||||
{
|
||||
do
|
||||
{
|
||||
Field* fields = result->Fetch();
|
||||
|
||||
uint8 raceID = fields[0].GetUInt8();
|
||||
uint8 expansion = fields[1].GetUInt8();
|
||||
uint32 achievementId = fields[2].GetUInt32();
|
||||
|
||||
ChrRacesEntry const* raceEntry = sChrRacesStore.LookupEntry(raceID);
|
||||
if (!raceEntry)
|
||||
{
|
||||
TC_LOG_ERROR("sql.sql", "Race {} defined in `race_unlock_requirement` does not exists, skipped.", raceID);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (expansion >= MAX_ACCOUNT_EXPANSIONS)
|
||||
{
|
||||
TC_LOG_ERROR("sql.sql", "Race {} defined in `race_unlock_requirement` has incorrect expansion {}, skipped.", raceID, expansion);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (achievementId && !sAchievementStore.LookupEntry(achievementId))
|
||||
{
|
||||
TC_LOG_ERROR("sql.sql", "Race {} defined in `race_unlock_requirement` has incorrect achievement {}, skipped.", raceID, achievementId);
|
||||
continue;
|
||||
}
|
||||
|
||||
RaceUnlockRequirement& raceUnlockRequirement = _raceUnlockRequirementStore[raceID];
|
||||
raceUnlockRequirement.Expansion = expansion;
|
||||
raceUnlockRequirement.AchievementId = achievementId;
|
||||
}
|
||||
while (result->NextRow());
|
||||
TC_LOG_INFO("server.loading", ">> Loaded {} race expansion requirements in {} ms.", _raceUnlockRequirementStore.size(), GetMSTimeDiffToNow(oldMSTime));
|
||||
}
|
||||
else
|
||||
TC_LOG_INFO("server.loading", ">> Loaded 0 race expansion requirements. DB table `race_expansion_requirement` is empty.");
|
||||
|
||||
oldMSTime = getMSTime();
|
||||
_classExpansionRequirementStore.clear();
|
||||
_raceClassRequirementStore.clear();
|
||||
|
||||
// 0 1 2 3
|
||||
result = WorldDatabase.Query("SELECT ClassID, RaceID, ActiveExpansionLevel, AccountExpansionLevel FROM `class_expansion_requirement`");
|
||||
QueryResult result = WorldDatabase.Query("SELECT ClassID, RaceID, ActiveExpansionLevel, AccountExpansionLevel FROM `class_expansion_requirement`");
|
||||
|
||||
if (result)
|
||||
{
|
||||
@@ -10633,7 +10588,7 @@ void ObjectMgr::LoadRaceAndClassExpansionRequirements()
|
||||
|
||||
for (auto&& race : temp)
|
||||
{
|
||||
RaceClassAvailability& raceClassAvailability = _classExpansionRequirementStore.emplace_back();
|
||||
RaceClassAvailability& raceClassAvailability = _raceClassRequirementStore.emplace_back();
|
||||
|
||||
raceClassAvailability.RaceID = race.first;
|
||||
|
||||
@@ -10652,21 +10607,77 @@ void ObjectMgr::LoadRaceAndClassExpansionRequirements()
|
||||
}
|
||||
else
|
||||
TC_LOG_INFO("server.loading", ">> Loaded 0 class expansion requirements. DB table `class_expansion_requirement` is empty.");
|
||||
|
||||
oldMSTime = getMSTime();
|
||||
|
||||
// 0 1 2
|
||||
result = WorldDatabase.Query("SELECT raceID, expansion, achievementId FROM `race_unlock_requirement`");
|
||||
|
||||
if (result)
|
||||
{
|
||||
uint32 loadedRows = 0;
|
||||
do
|
||||
{
|
||||
Field* fields = result->Fetch();
|
||||
|
||||
uint8 raceID = fields[0].GetUInt8();
|
||||
uint8 expansion = fields[1].GetUInt8();
|
||||
uint32 achievementId = fields[2].GetUInt32();
|
||||
|
||||
ChrRacesEntry const* raceEntry = sChrRacesStore.LookupEntry(raceID);
|
||||
if (!raceEntry)
|
||||
{
|
||||
TC_LOG_ERROR("sql.sql", "Race {} defined in `race_unlock_requirement` does not exists, skipped.", raceID);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (expansion >= MAX_ACCOUNT_EXPANSIONS)
|
||||
{
|
||||
TC_LOG_ERROR("sql.sql", "Race {} defined in `race_unlock_requirement` has incorrect expansion {}, skipped.", raceID, expansion);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (achievementId && !sAchievementStore.LookupEntry(achievementId))
|
||||
{
|
||||
TC_LOG_ERROR("sql.sql", "Race {} defined in `race_unlock_requirement` has incorrect achievement {}, skipped.", raceID, achievementId);
|
||||
continue;
|
||||
}
|
||||
|
||||
auto itr = std::ranges::find(_raceClassRequirementStore, raceID, &RaceClassAvailability::RaceID);
|
||||
if (itr == _raceClassRequirementStore.end())
|
||||
{
|
||||
itr = _raceClassRequirementStore.emplace(_raceClassRequirementStore.end());
|
||||
itr->RaceID = raceID;
|
||||
}
|
||||
|
||||
RaceUnlockRequirement& raceUnlockRequirement = itr->UnlockRequirement;
|
||||
raceUnlockRequirement.Expansion = expansion;
|
||||
raceUnlockRequirement.AchievementId = achievementId;
|
||||
++loadedRows;
|
||||
}
|
||||
while (result->NextRow());
|
||||
TC_LOG_INFO("server.loading", ">> Loaded {} race expansion requirements in {} ms.", loadedRows, GetMSTimeDiffToNow(oldMSTime));
|
||||
}
|
||||
else
|
||||
TC_LOG_INFO("server.loading", ">> Loaded 0 race expansion requirements. DB table `race_expansion_requirement` is empty.");
|
||||
}
|
||||
|
||||
RaceUnlockRequirement const* ObjectMgr::GetRaceUnlockRequirement(uint8 raceId) const
|
||||
{
|
||||
auto raceItr = std::ranges::find(_raceClassRequirementStore, raceId, &RaceClassAvailability::RaceID);
|
||||
if (raceItr == _raceClassRequirementStore.end())
|
||||
return nullptr;
|
||||
|
||||
return &raceItr->UnlockRequirement;
|
||||
}
|
||||
|
||||
ClassAvailability const* ObjectMgr::GetClassExpansionRequirement(uint8 raceId, uint8 classId) const
|
||||
{
|
||||
auto raceItr = std::find_if(_classExpansionRequirementStore.begin(), _classExpansionRequirementStore.end(), [raceId](RaceClassAvailability const& raceClass)
|
||||
{
|
||||
return raceClass.RaceID == raceId;
|
||||
});
|
||||
if (raceItr == _classExpansionRequirementStore.end())
|
||||
auto raceItr = std::ranges::find(_raceClassRequirementStore, raceId, &RaceClassAvailability::RaceID);
|
||||
if (raceItr == _raceClassRequirementStore.end())
|
||||
return nullptr;
|
||||
|
||||
auto classItr = std::find_if(raceItr->Classes.begin(), raceItr->Classes.end(), [classId](ClassAvailability const& classAvailability)
|
||||
{
|
||||
return classAvailability.ClassID == classId;
|
||||
});
|
||||
auto classItr = std::ranges::find(raceItr->Classes, classId, &ClassAvailability::ClassID);
|
||||
if (classItr == raceItr->Classes.end())
|
||||
return nullptr;
|
||||
|
||||
@@ -10675,7 +10686,7 @@ ClassAvailability const* ObjectMgr::GetClassExpansionRequirement(uint8 raceId, u
|
||||
|
||||
ClassAvailability const* ObjectMgr::GetClassExpansionRequirementFallback(uint8 classId) const
|
||||
{
|
||||
for (RaceClassAvailability const& raceClassAvailability : _classExpansionRequirementStore)
|
||||
for (RaceClassAvailability const& raceClassAvailability : _raceClassRequirementStore)
|
||||
for (ClassAvailability const& classAvailability : raceClassAvailability.Classes)
|
||||
if (classAvailability.ClassID == classId)
|
||||
return &classAvailability;
|
||||
|
||||
@@ -932,18 +932,19 @@ struct ClassAvailability
|
||||
uint8 MinActiveExpansionLevel = 0;
|
||||
};
|
||||
|
||||
struct RaceClassAvailability
|
||||
{
|
||||
uint8 RaceID = 0;
|
||||
std::vector<ClassAvailability> Classes;
|
||||
};
|
||||
|
||||
struct RaceUnlockRequirement
|
||||
{
|
||||
uint8 Expansion;
|
||||
uint32 AchievementId;
|
||||
};
|
||||
|
||||
struct RaceClassAvailability
|
||||
{
|
||||
uint8 RaceID = 0;
|
||||
RaceUnlockRequirement UnlockRequirement;
|
||||
std::vector<ClassAvailability> Classes;
|
||||
};
|
||||
|
||||
enum QueryDataGroup
|
||||
{
|
||||
QUERY_DATA_CREATURES = 0x01,
|
||||
@@ -1653,16 +1654,8 @@ class TC_GAME_API ObjectMgr
|
||||
|
||||
std::string GetPhaseName(uint32 phaseId) const;
|
||||
|
||||
std::unordered_map<uint8, RaceUnlockRequirement> const& GetRaceUnlockRequirements() const { return _raceUnlockRequirementStore; }
|
||||
RaceUnlockRequirement const* GetRaceUnlockRequirement(uint8 race) const
|
||||
{
|
||||
auto itr = _raceUnlockRequirementStore.find(race);
|
||||
if (itr != _raceUnlockRequirementStore.end())
|
||||
return &itr->second;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<RaceClassAvailability> const& GetClassExpansionRequirements() const { return _classExpansionRequirementStore; }
|
||||
std::vector<RaceClassAvailability> const& GetRaceClassRequirements() const { return _raceClassRequirementStore; }
|
||||
RaceUnlockRequirement const* GetRaceUnlockRequirement(uint8 raceId) const;
|
||||
ClassAvailability const* GetClassExpansionRequirement(uint8 raceId, uint8 classId) const;
|
||||
ClassAvailability const* GetClassExpansionRequirementFallback(uint8 classId) const;
|
||||
|
||||
@@ -1861,8 +1854,7 @@ class TC_GAME_API ObjectMgr
|
||||
std::unordered_map<uint32, Trainer::Trainer> _trainers;
|
||||
std::map<std::tuple<uint32, uint32, uint32>, uint32> _creatureDefaultTrainers;
|
||||
|
||||
std::unordered_map<uint8, RaceUnlockRequirement> _raceUnlockRequirementStore;
|
||||
std::vector<RaceClassAvailability> _classExpansionRequirementStore;
|
||||
std::vector<RaceClassAvailability> _raceClassRequirementStore;
|
||||
RealmNameContainer _realmNameStore;
|
||||
|
||||
SceneTemplateContainer _sceneTemplateStore;
|
||||
|
||||
@@ -54,7 +54,7 @@ void WorldSession::SendAuthResponse(uint32 code, bool queued, uint32 queuePos)
|
||||
for (auto&& templ : sCharacterTemplateDataStore->GetCharacterTemplates())
|
||||
response.SuccessInfo->Templates.push_back(&templ.second);
|
||||
|
||||
response.SuccessInfo->AvailableClasses = &sObjectMgr->GetClassExpansionRequirements();
|
||||
response.SuccessInfo->AvailableClasses = &sObjectMgr->GetRaceClassRequirements();
|
||||
|
||||
// TEMPORARY - prevent creating characters in uncompletable zone
|
||||
// This has the side effect of disabling Exile's Reach choice clientside without actually forcing character templates
|
||||
|
||||
@@ -476,15 +476,30 @@ void WorldSession::HandleCharEnum(CharacterDatabaseQueryHolder const& holder)
|
||||
while (result->NextRow() && charEnum.Characters.size() < MAX_CHARACTERS_PER_REALM);
|
||||
}
|
||||
|
||||
for (std::pair<uint8 const, RaceUnlockRequirement> const& requirement : sObjectMgr->GetRaceUnlockRequirements())
|
||||
for (RaceClassAvailability const& requirement : sObjectMgr->GetRaceClassRequirements())
|
||||
{
|
||||
WorldPackets::Character::EnumCharactersResult::RaceUnlock raceUnlock;
|
||||
raceUnlock.RaceID = requirement.first;
|
||||
raceUnlock.HasUnlockedLicense = GetAccountExpansion() >= requirement.second.Expansion;
|
||||
raceUnlock.HasUnlockedAchievement = requirement.second.AchievementId != 0
|
||||
WorldPackets::Character::EnumCharactersResult::RaceUnlock& raceUnlock = charEnum.RaceUnlockData.emplace_back();
|
||||
raceUnlock.RaceID = requirement.RaceID;
|
||||
raceUnlock.HasUnlockedLicense = GetAccountExpansion() >= requirement.UnlockRequirement.Expansion;
|
||||
raceUnlock.HasUnlockedAchievement = requirement.UnlockRequirement.AchievementId != 0
|
||||
&& (sWorld->getBoolConfig(CONFIG_CHARACTER_CREATING_DISABLE_ALLIED_RACE_ACHIEVEMENT_REQUIREMENT)
|
||||
/* || HasAccountAchievement(requirement.second.AchievementId)*/);
|
||||
charEnum.RaceUnlockData.push_back(raceUnlock);
|
||||
/* || HasAccountAchievement(requirement.UnlockRequirement.AchievementId)*/);
|
||||
raceUnlock.HasEntitlement = true;
|
||||
|
||||
for (ClassAvailability const& classRequirement : requirement.Classes)
|
||||
{
|
||||
WorldPackets::Character::EnumCharactersResult::ClassUnlock& classUnlock = raceUnlock.ClassUnlocks.emplace_back();
|
||||
classUnlock.ClassID = classRequirement.ClassID;
|
||||
//classUnlock.AchievementID = classRequirement.AchievementId;
|
||||
classUnlock.HasExpansion = GetAccountExpansion() >= classRequirement.AccountExpansionLevel && GetExpansion() >= classRequirement.ActiveExpansionLevel;
|
||||
classUnlock.HasUnlockedAchievement = true/*classRequirement.AchievementId == 0 || HasAccountAchievement(classRequirement.AchievementId)*/;
|
||||
classUnlock.HasEntitlement = true;
|
||||
}
|
||||
|
||||
raceUnlock.DoesNotHaveAvailableClasses = std::ranges::none_of(raceUnlock.ClassUnlocks, [](WorldPackets::Character::EnumCharactersResult::ClassUnlock const& classUnlock)
|
||||
{
|
||||
return classUnlock.HasExpansion && classUnlock.HasUnlockedAchievement && classUnlock.HasEntitlement;
|
||||
});
|
||||
}
|
||||
|
||||
SendPacket(charEnum.Write());
|
||||
|
||||
@@ -342,8 +342,10 @@ ByteBuffer& operator<<(ByteBuffer& data, EnumCharactersResult::RaceUnlock const&
|
||||
data << Bits<1>(raceUnlock.HasUnlockedLicense);
|
||||
data << Bits<1>(raceUnlock.HasUnlockedAchievement);
|
||||
data << Bits<1>(raceUnlock.HasHeritageArmorUnlockAchievement);
|
||||
data << Bits<1>(raceUnlock.HasEntitlement);
|
||||
data << Bits<1>(raceUnlock.HideRaceOnClient);
|
||||
data << Bits<1>(raceUnlock.FactionBalanceDisabled);
|
||||
data << Bits<1>(raceUnlock.DoesNotHaveAvailableClasses);
|
||||
data.FlushBits();
|
||||
|
||||
return data;
|
||||
|
||||
@@ -248,8 +248,10 @@ namespace WorldPackets
|
||||
bool HasUnlockedLicense = false;
|
||||
bool HasUnlockedAchievement = false;
|
||||
bool HasHeritageArmorUnlockAchievement = false;
|
||||
bool HasEntitlement = false;
|
||||
bool HideRaceOnClient = false;
|
||||
bool FactionBalanceDisabled = false;
|
||||
bool DoesNotHaveAvailableClasses = false;
|
||||
std::vector<ClassUnlock> ClassUnlocks;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user