diff --git a/Fixer/Program.cs b/Fixer/Program.cs index a76099d18..03a10430d 100644 --- a/Fixer/Program.cs +++ b/Fixer/Program.cs @@ -431,7 +431,6 @@ private static void TestPwMatchMaker() { var server = new global::ZkLobbyServer.ZkLobbyServer("", new PlanetwarsEventCreator()); var mm = server.PlanetWarsMatchMaker; - mm.AttackerSideCounter = 1; mm.GenerateLobbyCommand(); // simulate defend phase with a formed squad diff --git a/Shared/LobbyClient/Protocol/Messages.cs b/Shared/LobbyClient/Protocol/Messages.cs index a9153458e..13544b461 100644 --- a/Shared/LobbyClient/Protocol/Messages.cs +++ b/Shared/LobbyClient/Protocol/Messages.cs @@ -674,7 +674,11 @@ public enum ModeType Defend = 2 } - public string AttackerFaction { get; set; } + /// + /// Distinct attacker faction shortcuts across all . In parallel-turn PW every + /// faction can be attacking simultaneously; each option also carries its own . + /// + public List AttackerFactions { get; set; } public DateTime Deadline { get; set; } @@ -690,6 +694,7 @@ public PwMatchCommand(ModeType mode) Mode = mode; Options = new List(); DefenderFactions = new List(); + AttackerFactions = new List(); } public class VoteOption @@ -705,6 +710,17 @@ public class VoteOption public bool CanSelectForBattle { get; set; } public bool PlayerIsAttacker { get; set; } public bool PlayerIsDefender { get; set; } + /// + /// Faction shortcut of the attacker. Together with forms the (planet, attacker) + /// key that identifies this attack slot. Clients must echo it back in . + /// + public string AttackerFaction { get; set; } + /// Average PW-WHR of the projected attacker squad (top-TeamSize volunteers). 0 when none. + public int AttackerAvgWhr { get; set; } + /// Average PW-WHR of the projected defender squad. Null in AttackCollect phase or when no volunteers. + public int? DefenderAvgWhr { get; set; } + /// Attacker win chance 0-100 derived from WHR delta. Null when either side is empty. + public int? WinChance { get; set; } } } @@ -712,12 +728,27 @@ public class VoteOption public class PwJoinPlanet { public int PlanetID { get; set; } + /// + /// Which attack slot the player is interacting with. Required — a planet can be attacked by multiple + /// factions simultaneously, each a separate slot. For an attacker this should equal the user's own + /// faction; for a defender it identifies which incoming attack to defend against. + /// + public string AttackerFaction { get; set; } + } + + /// + /// Client → server: cancel the player's current attack or defense commitment for the cycle. + /// + [Message(Origin.Client)] + public class PwCancel + { } [Message(Origin.Server)] public class PwRequestJoinPlanet { public int PlanetID { get; set; } + public string AttackerFaction { get; set; } } @@ -725,12 +756,14 @@ public class PwRequestJoinPlanet public class PwJoinPlanetSuccess { public int PlanetID { get; set; } + public string AttackerFaction { get; set; } } [Message(Origin.Server)] public class PwAttackingPlanet { public int PlanetID { get; set; } + public string AttackerFaction { get; set; } } [Message(Origin.Client)] diff --git a/Zero-K.info/Controllers/PlanetwarsController.cs b/Zero-K.info/Controllers/PlanetwarsController.cs index 3f9d77022..b2b1b9dcd 100644 --- a/Zero-K.info/Controllers/PlanetwarsController.cs +++ b/Zero-K.info/Controllers/PlanetwarsController.cs @@ -797,10 +797,11 @@ public ActionResult MatchMakerAttack(int planetID) { var db = new ZkDataContext(); var planet = db.Planets.Single(x => x.PlanetID == planetID); - if (Global.IsAccountAuthorized && Global.Account.CanPlayerPlanetWars() && planet.CanMatchMakerPlay(db.CurrentAccount().Faction)) + var account = db.CurrentAccount(); + if (Global.IsAccountAuthorized && Global.Account.CanPlayerPlanetWars() && account?.FactionID != null && planet.CanMatchMakerPlay(account.Faction)) { - Global.Server.PlanetWarsMatchMaker.AddAttackOption(planet); - Global.Server.RequestJoinPlanet(Global.Account.Name, planet.PlanetID); + Global.Server.PlanetWarsMatchMaker.AddAttackOption(planet, account.FactionID.Value); + Global.Server.RequestJoinPlanet(Global.Account.Name, planet.PlanetID, account.Faction.Shortcut); } return RedirectToAction("Planet", new { id = planetID }); } @@ -812,16 +813,17 @@ public ActionResult MatchMaker() var pwm = Global.Server.PlanetWarsMatchMaker; if (pwm != null) { - var state = Global.Server.PlanetWarsMatchMaker.GenerateLobbyCommand(); + // admin view gets a per-viewer command so per-option flags render correctly + var state = Global.Server.PlanetWarsMatchMaker.GenerateLobbyCommand(Global.Account?.Name, Global.Account?.Faction?.Shortcut); if (state != null) return View("PwMatchMaker", state); } return Content("Match maker offline"); } [Auth] - public ActionResult MatchMakerJoin(int planetID) + public ActionResult MatchMakerJoin(int planetID, string attackerFaction) { - Global.Server.RequestJoinPlanet(Global.Account.Name, planetID); + Global.Server.RequestJoinPlanet(Global.Account.Name, planetID, attackerFaction); return MatchMaker(); } diff --git a/Zero-K.info/Views/Planetwars/Planet.cshtml b/Zero-K.info/Views/Planetwars/Planet.cshtml index 6e2a5b484..d9e390319 100644 --- a/Zero-K.info/Views/Planetwars/Planet.cshtml +++ b/Zero-K.info/Views/Planetwars/Planet.cshtml @@ -38,7 +38,7 @@ -@if (Global.IsAccountAuthorized && db.CurrentAccount().CanPlayerPlanetWars() && (Global.Server.PlanetWarsMatchMaker != null && Global.Server?.PlanetWarsMatchMaker?.AttackingFaction?.FactionID == Global.FactionID) && Model.CanMatchMakerPlay(db.CurrentAccount().Faction)) +@if (Global.IsAccountAuthorized && db.CurrentAccount().CanPlayerPlanetWars() && Global.Server.PlanetWarsMatchMaker != null && Global.Server.PlanetWarsMatchMaker.Phase == ZeroKWeb.PwPhase.AttackCollect && Model.OwnerFactionID != Global.FactionID && Model.CanMatchMakerPlay(db.CurrentAccount().Faction)) { ATTACK PLANET } diff --git a/Zero-K.info/Views/Planetwars/PwMatchMaker.cshtml b/Zero-K.info/Views/Planetwars/PwMatchMaker.cshtml index 85ce5ba16..b33da51be 100644 --- a/Zero-K.info/Views/Planetwars/PwMatchMaker.cshtml +++ b/Zero-K.info/Views/Planetwars/PwMatchMaker.cshtml @@ -1,23 +1,21 @@ -@using LobbyClient +@using LobbyClient @using PlasmaShared @using ZeroKWeb @using ZkData @model LobbyClient.PwMatchCommand @{ PwMatchCommand pw = Model; - string text = ""; + string text; var db = new ZkDataContext(); if (pw.Mode == PwMatchCommand.ModeType.Attack) { - text = string.Format("{0} picks a planet to attack", pw.AttackerFaction); + text = "Pick a planet to attack"; } else { - var planetNames = string.Join(", ", pw.Options.Select(o => o.PlanetName)); - text = string.Format("{0} attacks {2}, {1} defends", pw.AttackerFaction, string.Join(",", pw.DefenderFactions), planetNames); + var planetNames = string.Join(", ", pw.Options.Select(o => o.PlanetName + " (" + (o.AttackerFaction ?? "?") + ")")); + text = string.Format("Defend options: {0}", planetNames); } - - bool canClick = (pw.Mode == PwMatchCommand.ModeType.Attack && pw.AttackerFaction == Global.Account.Faction.Shortcut) || (pw.Mode == PwMatchCommand.ModeType.Defend && pw.DefenderFactions.Contains(Global.Account.Faction.Shortcut)); }
@@ -30,14 +28,23 @@ foreach (PwMatchCommand.VoteOption opt in pw.Options) { - @if (canClick) + @if (opt.CanSelectForBattle) { - @Ajax.ActionLink("Join", "MatchMakerJoin", new { planetID = opt.PlanetID }, new AjaxOptions { UpdateTargetId = "matchMaker", InsertionMode = InsertionMode.Replace }) + @Ajax.ActionLink("Join", "MatchMakerJoin", new { planetID = opt.PlanetID, attackerFaction = opt.AttackerFaction }, new AjaxOptions { UpdateTargetId = "matchMaker", InsertionMode = InsertionMode.Replace }) } @Html.PrintPlanet(db.Planets.First(x => x.PlanetID == opt.PlanetID)) + [@opt.AttackerFaction] [@opt.Count/@opt.Needed] + @if (opt.WinChance != null) + { + (WHR A:@opt.AttackerAvgWhr D:@opt.DefenderAvgWhr, @opt.WinChance% attacker) + } + else if (opt.AttackerAvgWhr > 0) + { + (WHR @opt.AttackerAvgWhr) + }      } } -
\ No newline at end of file + diff --git a/ZeroKLobby/Notifications/PwBar.cs b/ZeroKLobby/Notifications/PwBar.cs index c346b9df1..d3ea2167b 100644 --- a/ZeroKLobby/Notifications/PwBar.cs +++ b/ZeroKLobby/Notifications/PwBar.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using System.Windows.Forms; using LobbyClient; @@ -59,9 +60,11 @@ void UpdateGui() deadline = DateTime.Now.AddSeconds(pw.DeadlineSeconds); timerLabel.Text = PlasmaShared.Utils.PrintTimeRemaining(pw.DeadlineSeconds); + var attackerFactionsText = string.Join(",", pw.AttackerFactions ?? new List()); + if (pw.Mode == PwMatchCommand.ModeType.Attack) { - headerLabel.Text = string.Format("{0} picks a planet to attack", pw.AttackerFaction); + headerLabel.Text = string.Format("{0} picks a planet to attack", attackerFactionsText); foreach (Button c in pnl.Controls.OfType