Skip to content

Commit b053728

Browse files
Nyeriahclaude
andauthored
feat(WG): prioritize native faction, flip only the surplus at war start (#164)
The greedy per-join balance flipped players based on the live in-war counts at accept time, so a minority-faction player who accepted early could be morphed unnecessarily and (with team-lock) stay that way. Add a native-priority pass: snapshot the native split from the invited maps on the first join of a war, keep the minority faction fully native, and let the majority fill its fair share (floor(total/2)) native in accept order. Players past that share -- the latest to commit -- flip to even the sides, minimizing faction changes. Gated by CFBG.Battlefield.NativePriority.Enable (default 1); disabling it falls back to the previous greedy balance. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent c18f453 commit b053728

4 files changed

Lines changed: 70 additions & 5 deletions

File tree

conf/CFBG.conf.dist

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,16 @@
3232
# Default: 1 - (Enabled)
3333
# 0 - (Disabled)
3434
#
35+
# CFBG.Battlefield.NativePriority.Enable
36+
# Description: When assigning teams for a Wintergrasp war, keep the minority
37+
# faction entirely on its native side and morph only the
38+
# majority's surplus to even the teams. The majority fills its
39+
# fair share native in invite-accept order, so the players moved
40+
# are the last to commit. Disable to fall back to the per-join
41+
# greedy balance. Requires CFBG.Battlefield.Enable = 1.
42+
# Default: 1 - (Enabled)
43+
# 0 - (Disabled, greedy per-join balance)
44+
#
3545
# CFBG.Battlefield.ReapplyOnResurrect.Enable
3646
# Description: Re-apply a cross-faction player's assigned race/faction/morph
3747
# when they resurrect inside Wintergrasp, so the ghost->alive
@@ -100,6 +110,7 @@
100110
CFBG.Enable = 1
101111
CFBG.Battlefield.Enable = 1
102112
CFBG.Battlefield.TeamLock.Enable = 1
113+
CFBG.Battlefield.NativePriority.Enable = 1
103114
CFBG.Battlefield.ReapplyOnResurrect.Enable = 1
104115
CFBG.BalancedTeams = 1
105116
CFBG.BalancedTeams.Class.LowLevel = 0

src/CFBG.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ void CFBG::LoadConfig()
151151

152152
_IsEnableWGSystem = sConfigMgr->GetOption<bool>("CFBG.Battlefield.Enable", true);
153153
_IsEnableWGTeamLock = sConfigMgr->GetOption<bool>("CFBG.Battlefield.TeamLock.Enable", true);
154+
_IsEnableWGNativePriority = sConfigMgr->GetOption<bool>("CFBG.Battlefield.NativePriority.Enable", true);
154155
_IsEnableWGReapplyOnResurrect = sConfigMgr->GetOption<bool>("CFBG.Battlefield.ReapplyOnResurrect.Enable", true);
155156
_IsEnableAvgIlvl = sConfigMgr->GetOption<bool>("CFBG.Include.Avg.Ilvl.Enable", false);
156157
_IsEnableBalancedTeams = sConfigMgr->GetOption<bool>("CFBG.BalancedTeams", false);
@@ -635,6 +636,37 @@ void CFBG::SetWGWarAssignment(ObjectGuid guid, TeamId team)
635636
void CFBG::ClearWGWarAssignments()
636637
{
637638
_wgWarAssignmentStore.clear();
639+
_wgCensusValid = false;
640+
_wgMajorityNativeKept = 0;
641+
}
642+
643+
TeamId CFBG::ResolveWGWarTeam(Player* player, uint32 nativeAllianceInvited, uint32 nativeHordeInvited)
644+
{
645+
// Capture the native split once: at the first join PlayersInWar is empty
646+
// and nobody is faked, so the invited counts are the true native census.
647+
if (!_wgCensusValid)
648+
{
649+
_wgMajorityTeam = (nativeAllianceInvited >= nativeHordeInvited) ? TEAM_ALLIANCE : TEAM_HORDE;
650+
_wgMajorityFairShare = (nativeAllianceInvited + nativeHordeInvited) / 2;
651+
_wgMajorityNativeKept = 0;
652+
_wgCensusValid = true;
653+
}
654+
655+
TeamId realTeam = player->GetTeamId(true);
656+
657+
// Minority stays native.
658+
if (realTeam != _wgMajorityTeam)
659+
return realTeam;
660+
661+
// Majority keeps its fair share native in accept order; the rest (latest
662+
// to commit) flip.
663+
if (_wgMajorityNativeKept < _wgMajorityFairShare)
664+
{
665+
++_wgMajorityNativeKept;
666+
return realTeam;
667+
}
668+
669+
return (_wgMajorityTeam == TEAM_ALLIANCE) ? TEAM_HORDE : TEAM_ALLIANCE;
638670
}
639671

640672
void CFBG::DoForgetPlayersInList(Player* player)

src/CFBG.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ class CFBG
137137
inline bool IsEnableSystem() const { return _IsEnableSystem; }
138138
inline bool IsEnableWGSystem() const { return _IsEnableWGSystem; }
139139
inline bool IsEnableWGTeamLock() const { return _IsEnableWGTeamLock; }
140+
inline bool IsEnableWGNativePriority() const { return _IsEnableWGNativePriority; }
140141
inline bool IsEnableWGReapplyOnResurrect() const { return _IsEnableWGReapplyOnResurrect; }
141142
inline bool IsEnableAvgIlvl() const { return _IsEnableAvgIlvl; }
142143
inline bool IsEnableBalancedTeams() const { return _IsEnableBalancedTeams; }
@@ -167,6 +168,11 @@ class CFBG
167168
void SetWGWarAssignment(ObjectGuid guid, TeamId team);
168169
void ClearWGWarAssignments();
169170

171+
// Native-priority WG balancing: keep the minority native, flip only the
172+
// majority's surplus (latest accepters). Census captured on the first call
173+
// of a war, reset in ClearWGWarAssignments.
174+
TeamId ResolveWGWarTeam(Player* player, uint32 nativeAllianceInvited, uint32 nativeHordeInvited);
175+
170176
bool ShouldForgetInListPlayers(Player* player);
171177
bool IsPlayingNative(Player* player);
172178

@@ -214,6 +220,12 @@ class CFBG
214220

215221
std::unordered_map<Player*, FakePlayer> _fakePlayerStore;
216222
std::unordered_map<ObjectGuid, TeamId> _wgWarAssignmentStore;
223+
224+
// Per-war native census for ResolveWGWarTeam, reset in ClearWGWarAssignments.
225+
bool _wgCensusValid = false;
226+
TeamId _wgMajorityTeam = TEAM_ALLIANCE;
227+
uint32 _wgMajorityFairShare = 0;
228+
uint32 _wgMajorityNativeKept = 0;
217229
std::unordered_map<Player*, ObjectGuid> _fakeNamePlayersStore;
218230
std::unordered_map<Player*, bool> _forgetBGPlayersStore;
219231
std::unordered_map<Player*, bool> _forgetInListPlayersStore;
@@ -225,6 +237,7 @@ class CFBG
225237
bool _IsEnableSystem;
226238
bool _IsEnableWGSystem;
227239
bool _IsEnableWGTeamLock;
240+
bool _IsEnableWGNativePriority;
228241
bool _IsEnableWGReapplyOnResurrect;
229242
bool _IsEnableAvgIlvl;
230243
bool _IsEnableBalancedTeams;

src/CFBG_SC.cpp

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -289,13 +289,22 @@ class CFBG_Battlefield : public BattlefieldScript
289289

290290
if (locked)
291291
assignedTeam = *locked;
292+
else if (sCFBG->IsEnableWGNativePriority())
293+
{
294+
// Invited maps still hold the full native war population here:
295+
// core erases each accepter from InvitedPlayers only after this hook.
296+
uint32 allianceInvited = static_cast<uint32>(bf->GetInvitedPlayersMap(TEAM_ALLIANCE).size());
297+
uint32 hordeInvited = static_cast<uint32>(bf->GetInvitedPlayersMap(TEAM_HORDE).size());
298+
299+
assignedTeam = sCFBG->ResolveWGWarTeam(player, allianceInvited, hordeInvited);
300+
301+
if (sCFBG->IsEnableWGTeamLock())
302+
sCFBG->SetWGWarAssignment(player->GetGUID(), assignedTeam);
303+
}
292304
else
293305
{
294-
// Hook fires before core's PlayersInWar.insert, so the candidate is
295-
// counted in neither side here. Reading the live core set drops the
296-
// parallel bucket we used to maintain and removes the divergence path
297-
// where AddOrSetPlayerToCorrectBfGroup rejects the join after the
298-
// hook has already mutated module state.
306+
// Greedy fallback: candidate is in neither set yet (hook fires
307+
// before PlayersInWar.insert), so balance off the live core counts.
299308
uint32 allianceCount = static_cast<uint32>(bf->GetPlayersInWarSet(TEAM_ALLIANCE).size());
300309
uint32 hordeCount = static_cast<uint32>(bf->GetPlayersInWarSet(TEAM_HORDE).size());
301310

0 commit comments

Comments
 (0)