Skip to content

Commit 1655a5f

Browse files
committed
simplify code
2 parents 8efe8b7 + 958ed30 commit 1655a5f

3 files changed

Lines changed: 69 additions & 47 deletions

File tree

Shared/LobbyClient/Protocol/Messages.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -674,7 +674,11 @@ public enum ModeType
674674
Defend = 2
675675
}
676676

677-
public string AttackerFaction { get; set; }
677+
/// <summary>
678+
/// Distinct attacker faction shortcuts across all <see cref="Options"/>. In parallel-turn PW every
679+
/// faction can be attacking simultaneously; each option also carries its own <see cref="VoteOption.AttackerFaction"/>.
680+
/// </summary>
681+
public List<string> AttackerFactions { get; set; }
678682

679683
public DateTime Deadline { get; set; }
680684

@@ -690,6 +694,7 @@ public PwMatchCommand(ModeType mode)
690694
Mode = mode;
691695
Options = new List<VoteOption>();
692696
DefenderFactions = new List<string>();
697+
AttackerFactions = new List<string>();
693698
}
694699

695700
public class VoteOption

ZeroKLobby/Notifications/PwBar.cs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Collections.Generic;
23
using System.Linq;
34
using System.Windows.Forms;
45
using LobbyClient;
@@ -59,9 +60,11 @@ void UpdateGui()
5960
deadline = DateTime.Now.AddSeconds(pw.DeadlineSeconds);
6061
timerLabel.Text = PlasmaShared.Utils.PrintTimeRemaining(pw.DeadlineSeconds);
6162

63+
var attackerFactionsText = string.Join(",", pw.AttackerFactions ?? new List<string>());
64+
6265
if (pw.Mode == PwMatchCommand.ModeType.Attack)
6366
{
64-
headerLabel.Text = string.Format("{0} picks a planet to attack", pw.AttackerFaction);
67+
headerLabel.Text = string.Format("{0} picks a planet to attack", attackerFactionsText);
6568

6669
foreach (Button c in pnl.Controls.OfType<Button>().ToList()) pnl.Controls.Remove(c);
6770

@@ -72,7 +75,7 @@ void UpdateGui()
7275
var but = new Button { Text = string.Format("{0} [{1}/{2}]", opt.PlanetName, opt.Count, opt.Needed), AutoSize = true };
7376
Program.ToolTip.SetMap(but, opt.Map);
7477

75-
if (pw.AttackerFaction == tas.MyUser.Faction) // NOTE this is for cases where nightwatch self faction info is delayed
78+
if (pw.AttackerFactions != null && pw.AttackerFactions.Contains(tas.MyUser.Faction))
7679
{
7780
AddButtonClick(opt, but);
7881
}
@@ -83,7 +86,7 @@ void UpdateGui()
8386
else if (pw.Mode == PwMatchCommand.ModeType.Defend)
8487
{
8588
headerLabel.Text = string.Format("{0} attacks, {1} defends",
86-
pw.AttackerFaction,
89+
attackerFactionsText,
8790
string.Join(",", pw.DefenderFactions));
8891

8992
foreach (Button c in pnl.Controls.OfType<Button>().ToList()) pnl.Controls.Remove(c);

ZkLobbyServer/SpringieInterface/PlanetWarsMatchMaker.cs

Lines changed: 57 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ private async void TimerOnElapsed(object sender, ElapsedEventArgs elapsedEventAr
120120
case PwPhase.AttackCollect:
121121
if (DateTime.UtcNow > GetAttackDeadline())
122122
{
123+
await ApplyTurnEndChargeBump();
123124
RunSquadFormation();
124125
if (FormedSquads.Any())
125126
{
@@ -150,7 +151,6 @@ private async void TimerOnElapsed(object sender, ElapsedEventArgs elapsedEventAr
150151
RunDefenderAssignment();
151152
await LaunchAllBattles();
152153
RunGalaxyTick();
153-
await ApplyTurnEndChargeBump();
154154
}
155155
catch (Exception ex)
156156
{
@@ -172,7 +172,14 @@ private async void TimerOnElapsed(object sender, ElapsedEventArgs elapsedEventAr
172172
}
173173

174174

175-
// ===================== SQUAD FORMATION (PIERCING) =====================
175+
// ===================== SQUAD FORMATION =====================
176+
// Phase 1: planets with >= TeamSize attackers form one squad with ALL their attackers.
177+
// Phase 2: repeatedly complete a squad for the highest-ranked straggler whose planet
178+
// the pool can still fill, pulling other stragglers to him as fillers. Skip
179+
// leaders whose planet is too big for the pool; stop when none qualify.
180+
// Phase 3: any remaining stragglers join the strongest existing squad (drawn by its
181+
// highest-ranked member). If no squad was formed at all, they are dropped.
182+
// Result: at most one squad per planet.
176183

177184
private void RunSquadFormation()
178185
{
@@ -237,66 +244,65 @@ private void FormSquadsForFaction(int attackerFactionId, List<AttackOption> fact
237244
}
238245
}
239246

240-
var pool = new HashSet<string>(playerOption.Keys);
241-
242-
// Pass 1: form squads from any planet with >= TeamSize volunteers, top-WHR first
243-
bool formed;
244-
do
247+
// Phase 1: self-sufficient planets form one squad with all their attackers
248+
foreach (var opt in factionOptions)
245249
{
246-
formed = false;
247-
foreach (var opt in factionOptions)
250+
var available = opt.Attackers.Where(playerOption.ContainsKey).ToList();
251+
if (available.Count >= opt.TeamSize)
248252
{
249-
var available = opt.Attackers.Where(pool.Contains).OrderByDescending(x => playerWhr.Get(x)).ToList();
250-
while (available.Count >= opt.TeamSize)
251-
{
252-
var squad = CreateSquadFromOption(opt);
253-
squad.Attackers = available.Take(opt.TeamSize).ToList();
254-
FormedSquads.Add(squad);
255-
foreach (var p in squad.Attackers) pool.Remove(p);
256-
available = available.Skip(opt.TeamSize).ToList();
257-
formed = true;
258-
}
253+
var squad = CreateSquadFromOption(opt);
254+
squad.Attackers = available;
255+
squad.TeamSize = available.Count;
256+
FormedSquads.Add(squad);
257+
foreach (var p in available) playerOption.Remove(p);
259258
}
260-
} while (formed);
259+
}
261260

262-
// Pass 2: piercing — top PW-Rank player pulls others to their chosen planet
263-
while (pool.Count > 0)
261+
// Phase 2: piercing — pick the top-ranked straggler whose planet's TeamSize
262+
// the pool can still satisfy; skip leaders whose planet is too big.
263+
while (playerOption.Count > 0)
264264
{
265-
var leader = pool
265+
var leader = playerOption.Keys
266+
.Where(x => playerOption[x].TeamSize <= playerOption.Count)
266267
.OrderBy(x => playerRoleOrder.GetOrDefault(x, int.MaxValue))
267268
.ThenByDescending(x => playerWhr.Get(x))
268-
.First();
269+
.FirstOrDefault();
270+
271+
if (leader == null) break; // no straggler's planet fits the remaining pool
269272

270273
var leaderOption = playerOption[leader];
271-
if (pool.Count < leaderOption.TeamSize) break;
272274

273-
var fillers = pool
275+
var fillers = playerOption.Keys
274276
.Where(x => x != leader)
275277
.OrderByDescending(x => playerWhr.Get(x))
276278
.Take(leaderOption.TeamSize - 1)
277279
.ToList();
278280

279-
if (fillers.Count < leaderOption.TeamSize - 1) break;
280-
281281
var squad = CreateSquadFromOption(leaderOption);
282-
squad.Attackers = new List<string> { leader };
282+
squad.Attackers.Add(leader);
283283
squad.Attackers.AddRange(fillers);
284+
squad.TeamSize = squad.Attackers.Count;
284285
FormedSquads.Add(squad);
285286

286-
pool.Remove(leader);
287-
foreach (var p in fillers) pool.Remove(p);
287+
playerOption.Remove(leader);
288+
foreach (var p in fillers) playerOption.Remove(p);
288289
}
289290

290-
// Pass 3: absorb leftovers into an existing squad on their original planet
291-
foreach (var name in pool.ToList())
291+
// Phase 3: remaining stragglers all merge into the strongest existing squad
292+
// in THIS faction's pass (each faction's squads are independent).
293+
if (playerOption.Count > 0)
292294
{
293-
var originalOption = playerOption[name];
294-
var squad = FormedSquads.FirstOrDefault(s => s.PlanetID == originalOption.PlanetID && s.AttackerFactionID == attackerFactionId);
295-
if (squad != null)
295+
var strongest = FormedSquads
296+
.Where(s => s.AttackerFactionID == attackerFactionId)
297+
.OrderBy(s => s.Attackers.Min(a => playerRoleOrder.GetOrDefault(a, int.MaxValue)))
298+
.ThenByDescending(s => s.Attackers.Max(a => playerWhr.Get(a)))
299+
.FirstOrDefault();
300+
301+
if (strongest != null)
296302
{
297-
squad.Attackers.Add(name);
298-
squad.TeamSize = squad.Attackers.Count;
299-
pool.Remove(name);
303+
strongest.Attackers.AddRange(playerOption.Keys);
304+
strongest.TeamSize = strongest.Attackers.Count;
305+
playerOption.Clear();
300306
}
301307
}
302308
}
@@ -360,7 +366,7 @@ private void RunDefenderAssignment()
360366
// floating pool fills deficit on other squads where the defender's faction is eligible
361367
floatingPool = floatingPool.OrderByDescending(x => defenderWhr.Get(x)).Distinct().ToList();
362368

363-
// cache per-squad defending factions and floating-pool faction IDs
369+
// cache per-squad defending factions (GetDefendingFactions opens its own DB context, expensive inside a loop)
364370
var squadDefendingFactions = new Dictionary<AttackOption, HashSet<int>>();
365371
foreach (var squad in FormedSquads)
366372
squadDefendingFactions[squad] = GetDefendingFactions(squad).Select(f => f.FactionID).ToHashSet();
@@ -435,7 +441,7 @@ private async Task LaunchAllBattles()
435441
// (StartGame returned false, or setup threw) refund the commitment.
436442
var attackerNamesToChargeSpend = new List<string>();
437443

438-
// one battle per squad — no merging across attacker factions
444+
// one battle per squad — no merging across attacker factions (each (planet, attacker-faction) is its own slot)
439445
foreach (var squad in FormedSquads.ToList())
440446
{
441447
squad.Attackers = squad.Attackers.Where(x => server.ConnectedUsers.ContainsKey(x)).ToList();
@@ -839,6 +845,14 @@ private PwMatchCommand StampLobbyCommand(List<OptionSnapshot> snapshots, PwPhase
839845
if (playerFaction != null)
840846
playerFactionId = factions.FirstOrDefault(f => f.Shortcut == playerFaction)?.FactionID;
841847

848+
// Distinct attacker factions across all options — populated from the shared snapshot so it
849+
// matches the per-option AttackerFaction data the client sees in both phases.
850+
var attackerFactionShortcuts = snapshots
851+
.Select(s => s.AttackerFactionShortcut)
852+
.Where(x => !string.IsNullOrEmpty(x))
853+
.Distinct()
854+
.ToList();
855+
842856
if (phase == PwPhase.AttackCollect)
843857
{
844858
// All factions' options are shown to every viewer (parity with pre-parallel-turn UX, where
@@ -869,7 +883,7 @@ private PwMatchCommand StampLobbyCommand(List<OptionSnapshot> snapshots, PwPhase
869883
Options = options,
870884
Deadline = deadline,
871885
DeadlineSeconds = (int)deadline.Subtract(DateTime.UtcNow).TotalSeconds,
872-
AttackerFaction = playerFaction,
886+
AttackerFactions = attackerFactionShortcuts,
873887
};
874888
}
875889
else // DefendCollect
@@ -912,7 +926,7 @@ private PwMatchCommand StampLobbyCommand(List<OptionSnapshot> snapshots, PwPhase
912926
Options = options,
913927
Deadline = deadline,
914928
DeadlineSeconds = (int)deadline.Subtract(DateTime.UtcNow).TotalSeconds,
915-
AttackerFaction = null,
929+
AttackerFactions = attackerFactionShortcuts,
916930
DefenderFactions = allDefenderShortcuts.ToList(),
917931
};
918932
}

0 commit comments

Comments
 (0)