This commit is contained in:
2026-04-02 21:51:05 +04:00
parent b0d62064ba
commit 6516b32fc6
21 changed files with 636 additions and 19 deletions
@@ -41,14 +41,18 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PrepareStatement(CHAR_SEL_BANINFO_LIST, "SELECT bandate, unbandate, bannedby, banreason FROM character_banned WHERE guid = ? ORDER BY unbandate", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_BANNED_NAME, "SELECT characters.name FROM characters, character_banned WHERE character_banned.guid = ? AND character_banned.guid = characters.guid", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_ENUM, "SELECT c.guid, c.name, c.race, c.class, c.gender, c.skin, c.face, c.hairStyle, c.hairColor, c.facialStyle, c.level, c.zone, c.map, c.position_x, c.position_y, c.position_z, "
"gm.guildid, c.playerFlags, c.at_login, cp.entry, cp.modelid, cp.level, c.equipmentCache, cb.guid, c.extra_flags "
"gm.guildid, c.playerFlags, c.at_login, cp.entry, cp.modelid, cp.level, c.equipmentCache, cb.guid, c.extra_flags, COALESCE(mgc.game_mode, 0) "
"FROM characters AS c LEFT JOIN character_pet AS cp ON c.guid = cp.owner AND cp.slot = ? LEFT JOIN guild_member AS gm ON c.guid = gm.guid "
"LEFT JOIN character_banned AS cb ON c.guid = cb.guid AND cb.active = 1 WHERE c.account = ? AND c.deleteInfos_Name IS NULL ORDER BY COALESCE(c.order, c.guid)", CONNECTION_ASYNC);
"LEFT JOIN character_banned AS cb ON c.guid = cb.guid AND cb.active = 1 "
"LEFT JOIN mod_gamemodes_characters AS mgc ON c.guid = mgc.guid "
"WHERE c.account = ? AND c.deleteInfos_Name IS NULL ORDER BY COALESCE(c.order, c.guid)", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_ENUM_DECLINED_NAME, "SELECT c.guid, c.name, c.race, c.class, c.gender, c.skin, c.face, c.hairStyle, c.hairColor, c.facialStyle, c.level, c.zone, c.map, "
"c.position_x, c.position_y, c.position_z, gm.guildid, c.playerFlags, c.at_login, cp.entry, cp.modelid, cp.level, c.equipmentCache, "
"cb.guid, c.extra_flags, cd.genitive FROM characters AS c LEFT JOIN character_pet AS cp ON c.guid = cp.owner AND cp.slot = ? "
"cb.guid, c.extra_flags, COALESCE(mgc.game_mode, 0), cd.genitive FROM characters AS c LEFT JOIN character_pet AS cp ON c.guid = cp.owner AND cp.slot = ? "
"LEFT JOIN character_declinedname AS cd ON c.guid = cd.guid LEFT JOIN guild_member AS gm ON c.guid = gm.guid "
"LEFT JOIN character_banned AS cb ON c.guid = cb.guid AND cb.active = 1 WHERE c.account = ? AND c.deleteInfos_Name IS NULL ORDER BY COALESCE(c.order, c.guid)", CONNECTION_ASYNC);
"LEFT JOIN character_banned AS cb ON c.guid = cb.guid AND cb.active = 1 "
"LEFT JOIN mod_gamemodes_characters AS mgc ON c.guid = mgc.guid "
"WHERE c.account = ? AND c.deleteInfos_Name IS NULL ORDER BY COALESCE(c.order, c.guid)", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_FREE_NAME, "SELECT guid, name, at_login FROM characters WHERE guid = ? AND account = ? AND NOT EXISTS (SELECT NULL FROM characters WHERE name = ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_CHAR_ZONE, "SELECT zone FROM characters WHERE guid = ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_CHARACTER_NAME_DATA, "SELECT race, class, gender, level FROM characters WHERE guid = ?", CONNECTION_SYNCH);
+8 -5
View File
@@ -486,7 +486,7 @@ void Player::CleanupsBeforeDelete(bool finalCleanup)
bool Player::Create(ObjectGuid::LowType guidlow, CharacterCreateInfo* createInfo)
{
// FIXME: outfitId not used in player creating
m_outfitId = createInfo->OutfitId;
/// @todo: need more checks against packet modifications
// should check that skin, face, hair* are valid via DBC per race/class
// also do it in Player::BuildEnumData, Player::LoadFromDB
@@ -1111,8 +1111,8 @@ bool Player::BuildEnumData(PreparedQueryResult result, WorldPacket* data)
// characters.hairColor, characters.facialStyle, character.level, characters.zone, characters.map, characters.position_x, characters.position_y, characters.position_z,
// 16 17 18 19 20 21 22 23
// guild_member.guildid, characters.playerFlags, characters.at_login, character_pet.entry, character_pet.modelid, character_pet.level, characters.equipmentCache, character_banned.guid,
// 24 25
// characters.extra_flags, character_declinedname.genitive
// 24 25 26
// characters.extra_flags, mod_gamemodes_characters.game_mode, character_declinedname.genitive
Field* fields = result->Fetch();
@@ -1184,12 +1184,15 @@ bool Player::BuildEnumData(PreparedQueryResult result, WorldPacket* data)
charFlags |= CHARACTER_FLAG_LOCKED_BY_BILLING;
if (sWorld->getBoolConfig(CONFIG_DECLINED_NAMES_USED))
{
if (!fields[25].Get<std::string>().empty())
if (!fields[26].Get<std::string>().empty())
charFlags |= CHARACTER_FLAG_DECLINED;
}
else
charFlags |= CHARACTER_FLAG_DECLINED;
if (fields[25].Get<uint8>() == 1 /* GAMEMODE_TRAITOR */)
charFlags |= CHARACTER_FLAG_UNK31;
*data << uint32(charFlags); // character flags
// character customize flags
@@ -5972,7 +5975,7 @@ void Player::RewardReputation(Unit* victim)
ChampioningFaction = GetChampioningFaction();
}
TeamId teamId = GetTeamId(true); // Always check player original reputation when rewarding
TeamId teamId = GetTeamId();
if (Rep->RepFaction1 && (!Rep->TeamDependent || teamId == TEAM_ALLIANCE))
{
+4 -1
View File
@@ -486,7 +486,7 @@ enum PlayerFlags : uint32
PLAYER_FLAGS_UNK27 = 0x08000000,
PLAYER_FLAGS_UNK28 = 0x10000000,
PLAYER_FLAGS_UNK29 = 0x20000000,
PLAYER_FLAGS_UNK30 = 0x40000000,
PLAYER_FLAGS_TRAITOR = 0x40000000, // mod-gamemodes: player is playing for the opposite faction
PLAYER_FLAGS_UNK31 = 0x80000000,
};
@@ -2130,6 +2130,8 @@ public:
void SetFactionForRace(uint8 race);
void setTeamId(TeamId teamid) { m_team = teamid; };
[[nodiscard]] uint8 GetOutfitId() const { return m_outfitId; }
void InitDisplayIds();
bool IsAtGroupRewardDistance(WorldObject const* pRewardSource) const;
@@ -2823,6 +2825,7 @@ protected:
ObjectGuid m_lootGuid;
TeamId m_team;
uint8 m_outfitId{0};
uint32 m_nextSave; // pussywizard
uint16 m_additionalSaveTimer; // pussywizard
uint8 m_additionalSaveMask; // pussywizard
@@ -1106,8 +1106,18 @@ bool Player::SatisfyQuestRace(Quest const* qInfo, bool msg) const
uint32 reqraces = qInfo->GetAllowableRaces();
if (reqraces == 0)
return true;
if ((reqraces & getRaceMask()) == 0)
{
// Allow faction-swapped characters to take quests restricted to their active faction,
// while still respecting truly race-specific quests on the opposite side.
if (GetTeamId() != GetTeamId(true))
{
uint32 currentFactionRaceMask = (GetTeamId() == TEAM_ALLIANCE) ? RACEMASK_ALLIANCE : RACEMASK_HORDE;
if ((reqraces & currentFactionRaceMask) == reqraces)
return true;
}
if (msg)
SendCanTakeQuestResponse(INVALIDREASON_QUEST_FAILED_WRONG_RACE);
return false;
@@ -5543,6 +5543,7 @@ bool Player::LoadFromDB(ObjectGuid playerGuid, CharacterDatabaseQueryHolder cons
// must be before inventory (some items required reputation check)
m_reputationMgr->LoadFromDB(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_REPUTATION));
sScriptMgr->OnPlayerReputationLoadFromDB(this);
// xinef: load mails before inventory, so problematic items can be added to already loaded mails
_LoadMail(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_MAILS), holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_MAIL_ITEMS));
@@ -1244,7 +1244,7 @@ void Player::UpdateArea(uint32 newArea)
else
RemoveByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SANCTUARY);
uint32 const areaRestFlag = (GetTeamId(true) == TEAM_ALLIANCE)
uint32 const areaRestFlag = (GetTeamId() == TEAM_ALLIANCE)
? AREA_FLAG_REST_ZONE_ALLIANCE
: AREA_FLAG_REST_ZONE_HORDE;
if (area && area->flags & areaRestFlag)
@@ -1305,12 +1305,12 @@ void Player::UpdateZone(uint32 newZone, uint32 newArea, bool force)
{
case AREATEAM_ALLY:
pvpInfo.IsInHostileArea =
GetTeamId(true) != TEAM_ALLIANCE &&
GetTeamId() != TEAM_ALLIANCE &&
(sWorld->IsPvPRealm() || zone->flags & AREA_FLAG_CAPITAL);
break;
case AREATEAM_HORDE:
pvpInfo.IsInHostileArea =
GetTeamId(true) != TEAM_HORDE &&
GetTeamId() != TEAM_HORDE &&
(sWorld->IsPvPRealm() || zone->flags & AREA_FLAG_CAPITAL);
break;
case AREATEAM_NONE:
+24 -2
View File
@@ -29,6 +29,20 @@ const int32 ReputationMgr::PointsInRank[MAX_REPUTATION_RANK] = {36000, 3000, 300
const int32 ReputationMgr::Reputation_Cap = 42999;
const int32 ReputationMgr::Reputation_Bottom = -42000;
namespace
{
uint32 GetEffectiveReputationRaceMask(Player const* player)
{
// Always use the player's actual race mask so the server BaseRep calculation
// matches what the client computes (client always uses the original race, not
// the current team). This is correct for both the race-change service (where
// getRace() already reflects the new race) and for game modes like Traitor
// that swap team without changing race (where the original race mask is the
// right baseline for reputation display and incremental gain alike).
return player->getRaceMask();
}
}
ReputationRank ReputationMgr::ReputationToRank(int32 standing)
{
int32 limit = Reputation_Cap + 1;
@@ -93,7 +107,7 @@ int32 ReputationMgr::GetBaseReputation(FactionEntry const* factionEntry) const
if (!factionEntry)
return 0;
uint32 raceMask = _player->getRaceMask();
uint32 raceMask = GetEffectiveReputationRaceMask(_player);
uint32 classMask = _player->getClassMask();
for (int i = 0; i < 4; i++)
{
@@ -147,7 +161,7 @@ uint32 ReputationMgr::GetDefaultStateFlags(FactionEntry const* factionEntry) con
if (!factionEntry)
return 0;
uint32 raceMask = _player->getRaceMask();
uint32 raceMask = GetEffectiveReputationRaceMask(_player);
uint32 classMask = _player->getClassMask();
for (int i = 0; i < 4; i++)
{
@@ -464,9 +478,17 @@ void ReputationMgr::SetVisible(FactionTemplateEntry const* factionTemplateEntry)
return;
if (FactionEntry const* factionEntry = sFactionStore.LookupEntry(factionTemplateEntry->faction))
{
if (_player->GetTeamId() != _player->GetTeamId(true))
{
SetVisible(factionEntry);
return;
}
// Never show factions of the opposing team
if (!(factionEntry->BaseRepRaceMask[1] & _player->getRaceMask() && factionEntry->BaseRepValue[1] == Reputation_Bottom))
SetVisible(factionEntry);
}
}
void ReputationMgr::SetVisible(FactionEntry const* factionEntry)
@@ -215,6 +215,11 @@ void ScriptMgr::OnPlayerLoadFromDB(Player* player)
CALL_ENABLED_HOOKS(PlayerScript, PLAYERHOOK_ON_LOAD_FROM_DB, script->OnPlayerLoadFromDB(player));
}
void ScriptMgr::OnPlayerReputationLoadFromDB(Player* player)
{
CALL_ENABLED_HOOKS(PlayerScript, PLAYERHOOK_ON_REPUTATION_LOAD_FROM_DB, script->OnPlayerReputationLoadFromDB(player));
}
void ScriptMgr::OnPlayerBeforeLogout(Player* player)
{
CALL_ENABLED_HOOKS(PlayerScript, PLAYERHOOK_ON_BEFORE_LOGOUT, script->OnPlayerBeforeLogout(player));
@@ -67,6 +67,7 @@ enum PlayerHook
PLAYERHOOK_ON_TEXT_EMOTE,
PLAYERHOOK_ON_SPELL_CAST,
PLAYERHOOK_ON_LOAD_FROM_DB,
PLAYERHOOK_ON_REPUTATION_LOAD_FROM_DB,
PLAYERHOOK_ON_LOGIN,
PLAYERHOOK_ON_BEFORE_LOGOUT,
PLAYERHOOK_ON_LOGOUT,
@@ -320,6 +321,7 @@ public:
// Called during data loading
virtual void OnPlayerLoadFromDB(Player* /*player*/) { };
virtual void OnPlayerReputationLoadFromDB(Player* /*player*/) { };
// Called when a player logs in.
virtual void OnPlayerLogin(Player* /*player*/) { }
+1
View File
@@ -346,6 +346,7 @@ public: /* PlayerScript */
void OnPlayerSpellCast(Player* player, Spell* spell, bool skipCheck);
void OnPlayerLogin(Player* player);
void OnPlayerLoadFromDB(Player* player);
void OnPlayerReputationLoadFromDB(Player* player);
void OnPlayerBeforeLogout(Player* player);
void OnPlayerLogout(Player* player);
void OnPlayerCreate(Player* player);