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()
|
void ObjectMgr::LoadRaceAndClassExpansionRequirements()
|
||||||
{
|
{
|
||||||
uint32 oldMSTime = getMSTime();
|
uint32 oldMSTime = getMSTime();
|
||||||
_raceUnlockRequirementStore.clear();
|
_raceClassRequirementStore.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();
|
|
||||||
|
|
||||||
// 0 1 2 3
|
// 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)
|
if (result)
|
||||||
{
|
{
|
||||||
@@ -10633,7 +10588,7 @@ void ObjectMgr::LoadRaceAndClassExpansionRequirements()
|
|||||||
|
|
||||||
for (auto&& race : temp)
|
for (auto&& race : temp)
|
||||||
{
|
{
|
||||||
RaceClassAvailability& raceClassAvailability = _classExpansionRequirementStore.emplace_back();
|
RaceClassAvailability& raceClassAvailability = _raceClassRequirementStore.emplace_back();
|
||||||
|
|
||||||
raceClassAvailability.RaceID = race.first;
|
raceClassAvailability.RaceID = race.first;
|
||||||
|
|
||||||
@@ -10652,21 +10607,77 @@ void ObjectMgr::LoadRaceAndClassExpansionRequirements()
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
TC_LOG_INFO("server.loading", ">> Loaded 0 class expansion requirements. DB table `class_expansion_requirement` is empty.");
|
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
|
ClassAvailability const* ObjectMgr::GetClassExpansionRequirement(uint8 raceId, uint8 classId) const
|
||||||
{
|
{
|
||||||
auto raceItr = std::find_if(_classExpansionRequirementStore.begin(), _classExpansionRequirementStore.end(), [raceId](RaceClassAvailability const& raceClass)
|
auto raceItr = std::ranges::find(_raceClassRequirementStore, raceId, &RaceClassAvailability::RaceID);
|
||||||
{
|
if (raceItr == _raceClassRequirementStore.end())
|
||||||
return raceClass.RaceID == raceId;
|
|
||||||
});
|
|
||||||
if (raceItr == _classExpansionRequirementStore.end())
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
auto classItr = std::find_if(raceItr->Classes.begin(), raceItr->Classes.end(), [classId](ClassAvailability const& classAvailability)
|
auto classItr = std::ranges::find(raceItr->Classes, classId, &ClassAvailability::ClassID);
|
||||||
{
|
|
||||||
return classAvailability.ClassID == classId;
|
|
||||||
});
|
|
||||||
if (classItr == raceItr->Classes.end())
|
if (classItr == raceItr->Classes.end())
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
@@ -10675,7 +10686,7 @@ ClassAvailability const* ObjectMgr::GetClassExpansionRequirement(uint8 raceId, u
|
|||||||
|
|
||||||
ClassAvailability const* ObjectMgr::GetClassExpansionRequirementFallback(uint8 classId) const
|
ClassAvailability const* ObjectMgr::GetClassExpansionRequirementFallback(uint8 classId) const
|
||||||
{
|
{
|
||||||
for (RaceClassAvailability const& raceClassAvailability : _classExpansionRequirementStore)
|
for (RaceClassAvailability const& raceClassAvailability : _raceClassRequirementStore)
|
||||||
for (ClassAvailability const& classAvailability : raceClassAvailability.Classes)
|
for (ClassAvailability const& classAvailability : raceClassAvailability.Classes)
|
||||||
if (classAvailability.ClassID == classId)
|
if (classAvailability.ClassID == classId)
|
||||||
return &classAvailability;
|
return &classAvailability;
|
||||||
|
|||||||
@@ -932,18 +932,19 @@ struct ClassAvailability
|
|||||||
uint8 MinActiveExpansionLevel = 0;
|
uint8 MinActiveExpansionLevel = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct RaceClassAvailability
|
|
||||||
{
|
|
||||||
uint8 RaceID = 0;
|
|
||||||
std::vector<ClassAvailability> Classes;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct RaceUnlockRequirement
|
struct RaceUnlockRequirement
|
||||||
{
|
{
|
||||||
uint8 Expansion;
|
uint8 Expansion;
|
||||||
uint32 AchievementId;
|
uint32 AchievementId;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct RaceClassAvailability
|
||||||
|
{
|
||||||
|
uint8 RaceID = 0;
|
||||||
|
RaceUnlockRequirement UnlockRequirement;
|
||||||
|
std::vector<ClassAvailability> Classes;
|
||||||
|
};
|
||||||
|
|
||||||
enum QueryDataGroup
|
enum QueryDataGroup
|
||||||
{
|
{
|
||||||
QUERY_DATA_CREATURES = 0x01,
|
QUERY_DATA_CREATURES = 0x01,
|
||||||
@@ -1653,16 +1654,8 @@ class TC_GAME_API ObjectMgr
|
|||||||
|
|
||||||
std::string GetPhaseName(uint32 phaseId) const;
|
std::string GetPhaseName(uint32 phaseId) const;
|
||||||
|
|
||||||
std::unordered_map<uint8, RaceUnlockRequirement> const& GetRaceUnlockRequirements() const { return _raceUnlockRequirementStore; }
|
std::vector<RaceClassAvailability> const& GetRaceClassRequirements() const { return _raceClassRequirementStore; }
|
||||||
RaceUnlockRequirement const* GetRaceUnlockRequirement(uint8 race) const
|
RaceUnlockRequirement const* GetRaceUnlockRequirement(uint8 raceId) const;
|
||||||
{
|
|
||||||
auto itr = _raceUnlockRequirementStore.find(race);
|
|
||||||
if (itr != _raceUnlockRequirementStore.end())
|
|
||||||
return &itr->second;
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<RaceClassAvailability> const& GetClassExpansionRequirements() const { return _classExpansionRequirementStore; }
|
|
||||||
ClassAvailability const* GetClassExpansionRequirement(uint8 raceId, uint8 classId) const;
|
ClassAvailability const* GetClassExpansionRequirement(uint8 raceId, uint8 classId) const;
|
||||||
ClassAvailability const* GetClassExpansionRequirementFallback(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::unordered_map<uint32, Trainer::Trainer> _trainers;
|
||||||
std::map<std::tuple<uint32, uint32, uint32>, uint32> _creatureDefaultTrainers;
|
std::map<std::tuple<uint32, uint32, uint32>, uint32> _creatureDefaultTrainers;
|
||||||
|
|
||||||
std::unordered_map<uint8, RaceUnlockRequirement> _raceUnlockRequirementStore;
|
std::vector<RaceClassAvailability> _raceClassRequirementStore;
|
||||||
std::vector<RaceClassAvailability> _classExpansionRequirementStore;
|
|
||||||
RealmNameContainer _realmNameStore;
|
RealmNameContainer _realmNameStore;
|
||||||
|
|
||||||
SceneTemplateContainer _sceneTemplateStore;
|
SceneTemplateContainer _sceneTemplateStore;
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ void WorldSession::SendAuthResponse(uint32 code, bool queued, uint32 queuePos)
|
|||||||
for (auto&& templ : sCharacterTemplateDataStore->GetCharacterTemplates())
|
for (auto&& templ : sCharacterTemplateDataStore->GetCharacterTemplates())
|
||||||
response.SuccessInfo->Templates.push_back(&templ.second);
|
response.SuccessInfo->Templates.push_back(&templ.second);
|
||||||
|
|
||||||
response.SuccessInfo->AvailableClasses = &sObjectMgr->GetClassExpansionRequirements();
|
response.SuccessInfo->AvailableClasses = &sObjectMgr->GetRaceClassRequirements();
|
||||||
|
|
||||||
// TEMPORARY - prevent creating characters in uncompletable zone
|
// TEMPORARY - prevent creating characters in uncompletable zone
|
||||||
// This has the side effect of disabling Exile's Reach choice clientside without actually forcing character templates
|
// 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);
|
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;
|
WorldPackets::Character::EnumCharactersResult::RaceUnlock& raceUnlock = charEnum.RaceUnlockData.emplace_back();
|
||||||
raceUnlock.RaceID = requirement.first;
|
raceUnlock.RaceID = requirement.RaceID;
|
||||||
raceUnlock.HasUnlockedLicense = GetAccountExpansion() >= requirement.second.Expansion;
|
raceUnlock.HasUnlockedLicense = GetAccountExpansion() >= requirement.UnlockRequirement.Expansion;
|
||||||
raceUnlock.HasUnlockedAchievement = requirement.second.AchievementId != 0
|
raceUnlock.HasUnlockedAchievement = requirement.UnlockRequirement.AchievementId != 0
|
||||||
&& (sWorld->getBoolConfig(CONFIG_CHARACTER_CREATING_DISABLE_ALLIED_RACE_ACHIEVEMENT_REQUIREMENT)
|
&& (sWorld->getBoolConfig(CONFIG_CHARACTER_CREATING_DISABLE_ALLIED_RACE_ACHIEVEMENT_REQUIREMENT)
|
||||||
/* || HasAccountAchievement(requirement.second.AchievementId)*/);
|
/* || HasAccountAchievement(requirement.UnlockRequirement.AchievementId)*/);
|
||||||
charEnum.RaceUnlockData.push_back(raceUnlock);
|
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());
|
SendPacket(charEnum.Write());
|
||||||
|
|||||||
@@ -342,8 +342,10 @@ ByteBuffer& operator<<(ByteBuffer& data, EnumCharactersResult::RaceUnlock const&
|
|||||||
data << Bits<1>(raceUnlock.HasUnlockedLicense);
|
data << Bits<1>(raceUnlock.HasUnlockedLicense);
|
||||||
data << Bits<1>(raceUnlock.HasUnlockedAchievement);
|
data << Bits<1>(raceUnlock.HasUnlockedAchievement);
|
||||||
data << Bits<1>(raceUnlock.HasHeritageArmorUnlockAchievement);
|
data << Bits<1>(raceUnlock.HasHeritageArmorUnlockAchievement);
|
||||||
|
data << Bits<1>(raceUnlock.HasEntitlement);
|
||||||
data << Bits<1>(raceUnlock.HideRaceOnClient);
|
data << Bits<1>(raceUnlock.HideRaceOnClient);
|
||||||
data << Bits<1>(raceUnlock.FactionBalanceDisabled);
|
data << Bits<1>(raceUnlock.FactionBalanceDisabled);
|
||||||
|
data << Bits<1>(raceUnlock.DoesNotHaveAvailableClasses);
|
||||||
data.FlushBits();
|
data.FlushBits();
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
|
|||||||
@@ -248,8 +248,10 @@ namespace WorldPackets
|
|||||||
bool HasUnlockedLicense = false;
|
bool HasUnlockedLicense = false;
|
||||||
bool HasUnlockedAchievement = false;
|
bool HasUnlockedAchievement = false;
|
||||||
bool HasHeritageArmorUnlockAchievement = false;
|
bool HasHeritageArmorUnlockAchievement = false;
|
||||||
|
bool HasEntitlement = false;
|
||||||
bool HideRaceOnClient = false;
|
bool HideRaceOnClient = false;
|
||||||
bool FactionBalanceDisabled = false;
|
bool FactionBalanceDisabled = false;
|
||||||
|
bool DoesNotHaveAvailableClasses = false;
|
||||||
std::vector<ClassUnlock> ClassUnlocks;
|
std::vector<ClassUnlock> ClassUnlocks;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user