diff --git a/AUTHORS b/AUTHORS
index 07efee67ae..3cd8f10102 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -145,6 +145,7 @@ Also thanks to:
* Tirili
* Tomas Einarsson (Mesacer)
* Tom van Leth (tovl)
+ * Trevor Nichols (ocdi)
* Tristan Keating (Kilkakon)
* Tristan Mühlbacher (MicroBit)
* UnknownProgrammer
diff --git a/OpenRA.Game/GameInformation.cs b/OpenRA.Game/GameInformation.cs
index 5ee57ec54b..57765eb28a 100644
--- a/OpenRA.Game/GameInformation.cs
+++ b/OpenRA.Game/GameInformation.cs
@@ -35,6 +35,7 @@ namespace OpenRA
/// Gets the game's duration, from the time the game started until the replay recording stopped.
public TimeSpan Duration { get { return EndTimeUtc > StartTimeUtc ? EndTimeUtc - StartTimeUtc : TimeSpan.Zero; } }
public IList Players { get; private set; }
+ public List DisabledSpawnPoints = new List();
public MapPreview MapPreview { get { return Game.ModData.MapCache[MapUid]; } }
public IEnumerable HumanPlayers { get { return Players.Where(p => p.IsHuman); } }
public bool IsSinglePlayer { get { return HumanPlayers.Count() == 1; } }
diff --git a/OpenRA.Game/Network/GameServer.cs b/OpenRA.Game/Network/GameServer.cs
index e442d1e60c..4468beaf38 100644
--- a/OpenRA.Game/Network/GameServer.cs
+++ b/OpenRA.Game/Network/GameServer.cs
@@ -56,7 +56,7 @@ namespace OpenRA.Network
"Mod", "Version", "ModTitle", "ModWebsite", "ModIcon32",
// Current server state
- "Map", "State", "MaxPlayers", "Protected", "Authentication"
+ "Map", "State", "MaxPlayers", "Protected", "Authentication", "DisabledSpawnPoints"
};
public const int ProtocolVersion = 2;
@@ -132,6 +132,9 @@ namespace OpenRA.Network
[FieldLoader.LoadUsing("LoadClients")]
public readonly GameClient[] Clients;
+ /// The list of spawnpoints that are disabled for this game
+ public readonly int[] DisabledSpawnPoints = { };
+
public string ModLabel { get { return "{0} ({1})".F(ModTitle, Version); } }
static object LoadClients(MiniYaml yaml)
@@ -226,6 +229,7 @@ namespace OpenRA.Network
Protected = !string.IsNullOrEmpty(server.Settings.Password);
Authentication = server.Settings.RequireAuthentication || server.Settings.ProfileIDWhitelist.Any();
Clients = server.LobbyInfo.Clients.Select(c => new GameClient(c)).ToArray();
+ DisabledSpawnPoints = server.LobbyInfo.DisabledSpawnPoints?.ToArray() ?? Array.Empty();
}
public string ToPOSTData(bool lanGame)
diff --git a/OpenRA.Game/Network/Session.cs b/OpenRA.Game/Network/Session.cs
index 0db45b31dc..9e439e9328 100644
--- a/OpenRA.Game/Network/Session.cs
+++ b/OpenRA.Game/Network/Session.cs
@@ -26,6 +26,8 @@ namespace OpenRA.Network
// Keyed by the PlayerReference id that the slot corresponds to
public Dictionary Slots = new Dictionary();
+ public List DisabledSpawnPoints = new List();
+
public Global GlobalSettings = new Global();
public static string AnonymizeIP(IPAddress ip)
@@ -69,6 +71,9 @@ namespace OpenRA.Network
var s = Slot.Deserialize(node.Value);
session.Slots.Add(s.PlayerReference, s);
break;
+ case "DisabledSpawnPoints":
+ session.DisabledSpawnPoints = FieldLoader.GetValue>("DisabledSpawnPoints", node.Value.Value);
+ break;
}
}
@@ -267,7 +272,10 @@ namespace OpenRA.Network
public string Serialize()
{
- var sessionData = new List();
+ var sessionData = new List()
+ {
+ new MiniYamlNode("DisabledSpawnPoints", FieldSaver.FormatValue(DisabledSpawnPoints))
+ };
foreach (var client in Clients)
sessionData.Add(client.Serialize());
diff --git a/OpenRA.Game/World.cs b/OpenRA.Game/World.cs
index 1e0255ba68..eb6f01e12a 100644
--- a/OpenRA.Game/World.cs
+++ b/OpenRA.Game/World.cs
@@ -321,6 +321,8 @@ namespace OpenRA
foreach (var player in Players)
gameInfo.AddPlayer(player, OrderManager.LobbyInfo);
+ gameInfo.DisabledSpawnPoints = OrderManager.LobbyInfo.DisabledSpawnPoints;
+
var echo = OrderManager.Connection as EchoConnection;
var rc = echo != null ? echo.Recorder : null;
diff --git a/OpenRA.Mods.Common/ServerTraits/LobbyCommands.cs b/OpenRA.Mods.Common/ServerTraits/LobbyCommands.cs
index 4f8a1a9638..afdda0b8f2 100644
--- a/OpenRA.Mods.Common/ServerTraits/LobbyCommands.cs
+++ b/OpenRA.Mods.Common/ServerTraits/LobbyCommands.cs
@@ -43,6 +43,7 @@ namespace OpenRA.Mods.Common.Server
{ "faction", Faction },
{ "team", Team },
{ "spawn", Spawn },
+ { "clear_spawn", ClearPlayerSpawn },
{ "color", PlayerColor },
{ "sync_lobby", SyncLobby }
};
@@ -123,6 +124,11 @@ namespace OpenRA.Mods.Common.Server
if (server.LobbyInfo.Slots.Any(sl => sl.Value.Required && server.LobbyInfo.ClientInSlot(sl.Key) == null))
return;
+ // Can't have insufficient spawns
+ var availableSpawnPointCount = server.Map.SpawnPoints.Length - server.LobbyInfo.DisabledSpawnPoints.Count;
+ if (availableSpawnPointCount < server.LobbyInfo.Clients.Count(c => !c.IsObserver))
+ return;
+
server.StartGame();
}
}
@@ -170,6 +176,13 @@ namespace OpenRA.Mods.Common.Server
return true;
}
+ var availableSpawnPointCount = server.Map.SpawnPoints.Length - server.LobbyInfo.DisabledSpawnPoints.Count;
+ if (availableSpawnPointCount < server.LobbyInfo.Clients.Count(c => !c.IsObserver))
+ {
+ server.SendOrderTo(conn, "Message", "Unable to start the game until more spawn points are enabled.");
+ return true;
+ }
+
server.StartGame();
return true;
@@ -473,6 +486,8 @@ namespace OpenRA.Mods.Common.Server
if (c.Slot != null && !server.LobbyInfo.Slots[c.Slot].LockColor)
c.Color = c.PreferredColor = SanitizePlayerColor(server, c.Color, c.Index, conn);
+ server.LobbyInfo.DisabledSpawnPoints.Clear();
+
server.SyncLobbyInfo();
server.SendMessage("{0} changed the map to {1}.".F(client.Name, server.Map.Title));
@@ -811,6 +826,45 @@ namespace OpenRA.Mods.Common.Server
}
}
+ static bool ClearPlayerSpawn(S server, Connection conn, Session.Client client, string s)
+ {
+ var spawnPoint = Exts.ParseIntegerInvariant(s);
+ if (spawnPoint == 0)
+ return true;
+
+ var existingClient = server.LobbyInfo.Clients.FirstOrDefault(cc => cc.SpawnPoint == spawnPoint);
+ if (client != existingClient && !client.IsAdmin)
+ {
+ server.SendOrderTo(conn, "Message", "Only admins can clear spawn points.");
+ return true;
+ }
+
+ // Clearing a selected spawn point removes the player
+ if (existingClient != null)
+ {
+ // Prevent a map-defined lock spawn from being affected
+ if (existingClient.Slot != null && server.LobbyInfo.Slots[existingClient.Slot].LockSpawn)
+ return true;
+
+ existingClient.SpawnPoint = 0;
+ if (existingClient.State == Session.ClientState.Ready)
+ existingClient.State = Session.ClientState.NotReady;
+
+ server.SyncLobbyClients();
+ return true;
+ }
+
+ // Clearing an empty spawn point prevents it from being selected
+ // Clearing a disabled spawn restores it for use
+ if (!server.LobbyInfo.DisabledSpawnPoints.Contains(spawnPoint))
+ server.LobbyInfo.DisabledSpawnPoints.Add(spawnPoint);
+ else
+ server.LobbyInfo.DisabledSpawnPoints.Remove(spawnPoint);
+
+ server.SyncLobbyInfo();
+ return true;
+ }
+
static bool Spawn(S server, Connection conn, Session.Client client, string s)
{
lock (server.LobbyInfo)
diff --git a/OpenRA.Mods.Common/Traits/World/MPStartLocations.cs b/OpenRA.Mods.Common/Traits/World/MPStartLocations.cs
index a91e1aeaf8..7044ee3aed 100644
--- a/OpenRA.Mods.Common/Traits/World/MPStartLocations.cs
+++ b/OpenRA.Mods.Common/Traits/World/MPStartLocations.cs
@@ -70,7 +70,7 @@ namespace OpenRA.Mods.Common.Traits
// Initialize the list of unoccupied spawn points for AssignSpawnLocations to pick from
state.SpawnLocations = map.SpawnPoints;
- state.AvailableSpawnPoints = Enumerable.Range(1, map.SpawnPoints.Length).ToList();
+ state.AvailableSpawnPoints = Enumerable.Range(1, map.SpawnPoints.Length).Except(lobbyInfo.DisabledSpawnPoints).ToList();
foreach (var kv in lobbyInfo.Slots)
{
var client = lobbyInfo.ClientInSlot(kv.Key);
@@ -130,7 +130,7 @@ namespace OpenRA.Mods.Common.Traits
spawnLocations = spawns.ToArray();
// Initialize the list of unoccupied spawn points for AssignSpawnLocations to pick from
- availableSpawnPoints = Enumerable.Range(1, spawnLocations.Length).ToList();
+ availableSpawnPoints = Enumerable.Range(1, spawnLocations.Length).Except(self.World.LobbyInfo.DisabledSpawnPoints).ToList();
foreach (var kv in self.World.LobbyInfo.Slots)
{
var client = self.World.LobbyInfo.ClientInSlot(kv.Key);
diff --git a/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyLogic.cs
index da9757196a..bba9bdc2c8 100644
--- a/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyLogic.cs
+++ b/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyLogic.cs
@@ -60,6 +60,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
MapPreview map;
bool addBotOnMapLoad;
bool disableTeamChat;
+ bool insufficientPlayerSpawns;
bool teamChat;
bool updateDiscordStatus = true;
Dictionary spawnOccupants = new Dictionary();
@@ -136,6 +137,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
LobbyUtils.SelectSpawnPoint(orderManager, preview, mapPreview, mi))
},
{ "getSpawnOccupants", (Func>)(() => spawnOccupants) },
+ { "getDisabledSpawnPoints", (Func>)(() => orderManager.LobbyInfo.DisabledSpawnPoints) },
{ "showUnoccupiedSpawnpoints", true },
});
@@ -367,7 +369,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic
{
startGameButton.IsDisabled = () => configurationDisabled() || map.Status != MapStatus.Available ||
orderManager.LobbyInfo.Slots.Any(sl => sl.Value.Required && orderManager.LobbyInfo.ClientInSlot(sl.Key) == null) ||
- (!orderManager.LobbyInfo.GlobalSettings.EnableSingleplayer && orderManager.LobbyInfo.NonBotPlayers.Count() < 2);
+ (!orderManager.LobbyInfo.GlobalSettings.EnableSingleplayer && orderManager.LobbyInfo.NonBotPlayers.Count() < 2) ||
+ insufficientPlayerSpawns;
startGameButton.OnClick = () =>
{
@@ -570,6 +573,9 @@ namespace OpenRA.Mods.Common.Widgets.Logic
c.Bot == null &&
c.Team == orderManager.LocalClient.Team);
+ var availableSpawnPointCount = map.SpawnPoints.Length - orderManager.LobbyInfo.DisabledSpawnPoints.Count;
+ insufficientPlayerSpawns = availableSpawnPointCount < orderManager.LobbyInfo.Clients.Count(c => !c.IsObserver);
+
if (disableTeamChat)
teamChat = false;
diff --git a/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyUtils.cs b/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyUtils.cs
index d3b004415b..4bba88bc68 100644
--- a/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyUtils.cs
+++ b/OpenRA.Mods.Common/Widgets/Logic/Lobby/LobbyUtils.cs
@@ -229,29 +229,48 @@ namespace OpenRA.Mods.Common.Widgets.Logic
public static void SelectSpawnPoint(OrderManager orderManager, MapPreviewWidget mapPreview, MapPreview preview, MouseInput mi)
{
- if (mi.Button != MouseButton.Left)
+ if (orderManager.LocalClient.State == Session.ClientState.Ready)
return;
- if (!orderManager.LocalClient.IsObserver && orderManager.LocalClient.State == Session.ClientState.Ready)
- return;
+ if (mi.Button == MouseButton.Left)
+ SelectPlayerSpawnPoint(orderManager, mapPreview, preview, mi);
- var spawnSize = ChromeProvider.GetImage("lobby-bits", "spawn-unclaimed").Size.XY;
- var selectedSpawn = preview.SpawnPoints
- .Select((sp, i) => (SpawnLocation: mapPreview.ConvertToPreview(sp, preview.GridType), Index: i))
- .Where(a => ((a.SpawnLocation - mi.Location).ToFloat2() / spawnSize * 2).LengthSquared <= 1)
- .Select(a => a.Index + 1)
- .FirstOrDefault();
+ if (mi.Button == MouseButton.Right)
+ ClearPlayerSpawnPoint(orderManager, mapPreview, preview, mi);
+ }
+
+ static void SelectPlayerSpawnPoint(OrderManager orderManager, MapPreviewWidget mapPreview, MapPreview preview, MouseInput mi)
+ {
+ var selectedSpawn = DetermineSelectedSpawnPoint(mapPreview, preview, mi);
var locals = orderManager.LobbyInfo.Clients.Where(c => c.Index == orderManager.LocalClient.Index || (Game.IsHost && c.Bot != null));
var playerToMove = locals.FirstOrDefault(c => ((selectedSpawn == 0) ^ (c.SpawnPoint == 0) && !c.IsObserver));
SetSpawnPoint(orderManager, playerToMove, selectedSpawn);
}
- private static void SetSpawnPoint(OrderManager orderManager, Session.Client playerToMove, int selectedSpawn)
+ static void ClearPlayerSpawnPoint(OrderManager orderManager, MapPreviewWidget mapPreview, MapPreview preview, MouseInput mi)
{
- var owned = orderManager.LobbyInfo.Clients.Any(c => c.SpawnPoint == selectedSpawn);
- if (selectedSpawn == 0 || !owned)
- orderManager.IssueOrder(Order.Command("spawn {0} {1}".F((playerToMove ?? orderManager.LocalClient).Index, selectedSpawn)));
+ var selectedSpawn = DetermineSelectedSpawnPoint(mapPreview, preview, mi);
+ if (Game.IsHost || orderManager.LobbyInfo.Clients.FirstOrDefault(cc => cc.SpawnPoint == selectedSpawn) == orderManager.LocalClient)
+ orderManager.IssueOrder(Order.Command("clear_spawn {0}".F(selectedSpawn)));
+ }
+
+ static int DetermineSelectedSpawnPoint(MapPreviewWidget mapPreview, MapPreview preview, MouseInput mi)
+ {
+ var spawnSize = ChromeProvider.GetImage("lobby-bits", "spawn-unclaimed").Size.XY;
+ var selectedSpawn = preview.SpawnPoints
+ .Select((sp, i) => (SpawnLocation: mapPreview.ConvertToPreview(sp, preview.GridType), Index: i))
+ .Where(a => ((a.SpawnLocation - mi.Location).ToFloat2() / spawnSize * 2).LengthSquared <= 1)
+ .Select(a => a.Index + 1)
+ .FirstOrDefault();
+ return selectedSpawn;
+ }
+
+ static void SetSpawnPoint(OrderManager orderManager, Session.Client playerToMove, int selectedSpawnPoint)
+ {
+ var owned = orderManager.LobbyInfo.Clients.Any(c => c.SpawnPoint == selectedSpawnPoint) || orderManager.LobbyInfo.DisabledSpawnPoints.Contains(selectedSpawnPoint);
+ if (selectedSpawnPoint == 0 || !owned)
+ orderManager.IssueOrder(Order.Command("spawn {0} {1}".F((playerToMove ?? orderManager.LocalClient).Index, selectedSpawnPoint)));
}
public static Color LatencyColor(Session.ClientPing ping)
@@ -537,7 +556,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic
{
var spawnPoints = Enumerable.Range(0, map.SpawnPoints.Length + 1).Except(
orderManager.LobbyInfo.Clients.Where(
- client => client != c && client.SpawnPoint != 0).Select(client => client.SpawnPoint));
+ client => client != c && client.SpawnPoint != 0).Select(client => client.SpawnPoint))
+ .Except(orderManager.LobbyInfo.DisabledSpawnPoints);
ShowSpawnDropDown(dropdown, c, orderManager, spawnPoints);
};
dropdown.GetText = () => (c.SpawnPoint == 0) ? "-" : Convert.ToChar('A' - 1 + c.SpawnPoint).ToString();
diff --git a/OpenRA.Mods.Common/Widgets/Logic/Lobby/MapPreviewLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Lobby/MapPreviewLogic.cs
index 06e0ec1b1c..346d719af0 100644
--- a/OpenRA.Mods.Common/Widgets/Logic/Lobby/MapPreviewLogic.cs
+++ b/OpenRA.Mods.Common/Widgets/Logic/Lobby/MapPreviewLogic.cs
@@ -24,8 +24,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic
int blinkTick;
[ObjectCreator.UseCtor]
- internal MapPreviewLogic(Widget widget, ModData modData, OrderManager orderManager, Func getMap,
- Action onMouseDown, Func> getSpawnOccupants, bool showUnoccupiedSpawnpoints)
+ internal MapPreviewLogic(Widget widget, ModData modData, OrderManager orderManager, Func getMap, Action onMouseDown,
+ Func> getSpawnOccupants, Func> getDisabledSpawnPoints, bool showUnoccupiedSpawnpoints)
{
var mapRepository = modData.Manifest.Get().MapRepository;
@@ -38,7 +38,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
return map.Status == MapStatus.Available && (!map.RulesLoaded || !map.InvalidCustomRules);
};
- SetupWidgets(available, getMap, onMouseDown, getSpawnOccupants, showUnoccupiedSpawnpoints);
+ SetupWidgets(available, getMap, onMouseDown, getSpawnOccupants, getDisabledSpawnPoints, showUnoccupiedSpawnpoints);
}
var invalid = widget.GetOrNull("MAP_INVALID");
@@ -50,14 +50,14 @@ namespace OpenRA.Mods.Common.Widgets.Logic
return map.Status == MapStatus.Available && map.InvalidCustomRules;
};
- SetupWidgets(invalid, getMap, onMouseDown, getSpawnOccupants, showUnoccupiedSpawnpoints);
+ SetupWidgets(invalid, getMap, onMouseDown, getSpawnOccupants, getDisabledSpawnPoints, showUnoccupiedSpawnpoints);
}
var download = widget.GetOrNull("MAP_DOWNLOADABLE");
if (download != null)
{
download.IsVisible = () => getMap().Status == MapStatus.DownloadAvailable;
- SetupWidgets(download, getMap, onMouseDown, getSpawnOccupants, showUnoccupiedSpawnpoints);
+ SetupWidgets(download, getMap, onMouseDown, getSpawnOccupants, getDisabledSpawnPoints, showUnoccupiedSpawnpoints);
var install = download.GetOrNull("MAP_INSTALL");
if (install != null)
@@ -86,7 +86,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
return map.Status != MapStatus.Available && map.Status != MapStatus.DownloadAvailable;
};
- SetupWidgets(progress, getMap, onMouseDown, getSpawnOccupants, showUnoccupiedSpawnpoints);
+ SetupWidgets(progress, getMap, onMouseDown, getSpawnOccupants, getDisabledSpawnPoints, showUnoccupiedSpawnpoints);
var statusSearching = progress.GetOrNull("MAP_STATUS_SEARCHING");
if (statusSearching != null)
@@ -172,12 +172,13 @@ namespace OpenRA.Mods.Common.Widgets.Logic
}
void SetupWidgets(Widget parent, Func getMap,
- Action onMouseDown, Func> getSpawnOccupants, bool showUnoccupiedSpawnpoints)
+ Action onMouseDown, Func> getSpawnOccupants, Func> getDisabledSpawnPoints, bool showUnoccupiedSpawnpoints)
{
var preview = parent.Get("MAP_PREVIEW");
preview.Preview = () => getMap();
preview.OnMouseDown = mi => onMouseDown(preview, getMap(), mi);
preview.SpawnOccupants = getSpawnOccupants;
+ preview.DisabledSpawnPoints = getDisabledSpawnPoints;
preview.ShowUnoccupiedSpawnpoints = showUnoccupiedSpawnpoints;
var titleLabel = parent.GetOrNull("MAP_TITLE");
diff --git a/OpenRA.Mods.Common/Widgets/Logic/Lobby/SpawnSelectorTooltipLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Lobby/SpawnSelectorTooltipLogic.cs
index f9f17c8e98..2114898d80 100644
--- a/OpenRA.Mods.Common/Widgets/Logic/Lobby/SpawnSelectorTooltipLogic.cs
+++ b/OpenRA.Mods.Common/Widgets/Logic/Lobby/SpawnSelectorTooltipLogic.cs
@@ -58,7 +58,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
return;
}
- labelText = "Available spawn";
+ labelText = preview.DisabledSpawnPoints().Contains(preview.TooltipSpawnIndex) ? "Disabled spawn" : "Available spawn";
playerFaction = null;
playerTeam = 0;
widget.Bounds.Height = singleHeight;
diff --git a/OpenRA.Mods.Common/Widgets/Logic/ReplayBrowserLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/ReplayBrowserLogic.cs
index 94f0949369..65ef6d4b9d 100644
--- a/OpenRA.Mods.Common/Widgets/Logic/ReplayBrowserLogic.cs
+++ b/OpenRA.Mods.Common/Widgets/Logic/ReplayBrowserLogic.cs
@@ -87,12 +87,16 @@ namespace OpenRA.Mods.Common.Widgets.Logic
return occupants;
});
+ var noSpawns = new List();
+ var disabledSpawnPoints = new CachedTransform>(r => r.GameInfo.DisabledSpawnPoints ?? noSpawns);
+
Ui.LoadWidget("MAP_PREVIEW", mapPreviewRoot, new WidgetArgs
{
{ "orderManager", null },
{ "getMap", (Func)(() => map) },
{ "onMouseDown", (Action)((preview, mapPreview, mi) => { }) },
{ "getSpawnOccupants", (Func>)(() => spawnOccupants.Update(selectedReplay)) },
+ { "getDisabledSpawnPoints", (Func>)(() => disabledSpawnPoints.Update(selectedReplay)) },
{ "showUnoccupiedSpawnpoints", false },
});
diff --git a/OpenRA.Mods.Common/Widgets/Logic/ServerListLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/ServerListLogic.cs
index 369bbae2d0..8eb1aef40b 100644
--- a/OpenRA.Mods.Common/Widgets/Logic/ServerListLogic.cs
+++ b/OpenRA.Mods.Common/Widgets/Logic/ServerListLogic.cs
@@ -427,6 +427,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
.ToDictionary(c => c.SpawnPoint, c => new SpawnOccupant(c, server.Mod != modData.Manifest.Id));
mapPreview.SpawnOccupants = () => occupants;
+ mapPreview.DisabledSpawnPoints = () => server.DisabledSpawnPoints;
}
if (server == null || !server.Clients.Any())
diff --git a/OpenRA.Mods.Common/Widgets/MapPreviewWidget.cs b/OpenRA.Mods.Common/Widgets/MapPreviewWidget.cs
index b44ff03cc2..ca2887f93f 100644
--- a/OpenRA.Mods.Common/Widgets/MapPreviewWidget.cs
+++ b/OpenRA.Mods.Common/Widgets/MapPreviewWidget.cs
@@ -57,6 +57,8 @@ namespace OpenRA.Mods.Common.Widgets
public class MapPreviewWidget : Widget
{
+ static readonly int[] NoDisabledSpawnPoints = Array.Empty();
+
public readonly bool IgnoreMouseInput = false;
public readonly bool ShowSpawnPoints = true;
@@ -64,13 +66,14 @@ namespace OpenRA.Mods.Common.Widgets
public readonly string TooltipTemplate = "SPAWN_TOOLTIP";
readonly Lazy tooltipContainer;
- readonly Sprite spawnClaimed, spawnUnclaimed;
+ readonly Sprite spawnClaimed, spawnUnclaimed, spawnDisabled;
readonly SpriteFont spawnFont;
readonly Color spawnColor, spawnContrastColor;
readonly int2 spawnLabelOffset;
public Func Preview = () => null;
public Func> SpawnOccupants = () => new Dictionary();
+ public Func> DisabledSpawnPoints = () => NoDisabledSpawnPoints;
public Action OnMouseDown = _ => { };
public int TooltipSpawnIndex = -1;
public bool ShowUnoccupiedSpawnpoints = true;
@@ -85,6 +88,7 @@ namespace OpenRA.Mods.Common.Widgets
spawnClaimed = ChromeProvider.GetImage("lobby-bits", "spawn-claimed");
spawnUnclaimed = ChromeProvider.GetImage("lobby-bits", "spawn-unclaimed");
+ spawnDisabled = ChromeProvider.GetImage("lobby-bits", "spawn-disabled") ?? spawnUnclaimed;
spawnFont = Game.Renderer.Fonts[ChromeMetrics.Get("SpawnFont")];
spawnColor = ChromeMetrics.Get("SpawnColor");
spawnContrastColor = ChromeMetrics.Get("SpawnContrastColor");
@@ -184,6 +188,7 @@ namespace OpenRA.Mods.Common.Widgets
{
var spawnPoints = preview.SpawnPoints;
var occupants = SpawnOccupants();
+ var disabledSpawnPoints = DisabledSpawnPoints();
var gridType = preview.GridType;
for (var i = 0; i < spawnPoints.Length; i++)
{
@@ -191,21 +196,30 @@ namespace OpenRA.Mods.Common.Widgets
// Spawn numbers are 1 indexed with 0 meaning "random spawn".
var occupied = occupants.TryGetValue(i + 1, out var occupant);
+ var disabled = disabledSpawnPoints.Contains(i + 1);
var pos = ConvertToPreview(p, gridType);
- var sprite = occupied ? spawnClaimed : spawnUnclaimed;
+
+ var sprite = disabled ? spawnDisabled : occupied ? spawnClaimed : spawnUnclaimed;
var offset = sprite.Size.XY.ToInt2() / 2;
+ if (((pos - Viewport.LastMousePos).ToFloat2() / offset.ToFloat2()).LengthSquared <= 1)
+ TooltipSpawnIndex = spawnPoints.IndexOf(p) + 1;
+
+ if (disabled)
+ {
+ Game.Renderer.RgbaSpriteRenderer.DrawSprite(spawnDisabled, pos - offset);
+ continue;
+ }
+
if (occupied)
WidgetUtils.FillEllipseWithColor(new Rectangle(pos.X - offset.X + 1, pos.Y - offset.Y + 1, (int)sprite.Size.X - 2, (int)sprite.Size.Y - 2), occupant.Color);
Game.Renderer.RgbaSpriteRenderer.DrawSprite(sprite, pos - offset);
+
var number = Convert.ToChar('A' + spawnPoints.IndexOf(p)).ToString();
var textOffset = spawnFont.Measure(number) / 2 + spawnLabelOffset;
spawnFont.DrawTextWithContrast(number, pos - textOffset, spawnColor, spawnContrastColor, 1);
-
- if (((pos - Viewport.LastMousePos).ToFloat2() / offset.ToFloat2()).LengthSquared <= 1)
- TooltipSpawnIndex = spawnPoints.IndexOf(p) + 1;
}
}
}
diff --git a/mods/cnc/chrome.yaml b/mods/cnc/chrome.yaml
index 9bfaacc1ee..81c81f6cac 100644
--- a/mods/cnc/chrome.yaml
+++ b/mods/cnc/chrome.yaml
@@ -306,8 +306,9 @@ music:
lobby-bits:
Inherits: ^Chrome
Regions:
- spawn-unclaimed: 777, 423, 19, 19
- spawn-claimed: 777, 443, 19, 19
+ spawn-claimed: 744, 215, 18, 18
+ spawn-unclaimed: 744, 234, 18, 18
+ spawn-disabled: 744, 253, 18, 18
admin: 938, 0, 6, 5
colorpicker: 887, 0, 14, 14
huepicker: 904, 0, 7, 15
diff --git a/mods/cnc/uibits/chrome-2x.png b/mods/cnc/uibits/chrome-2x.png
index 0963ef7b21..7c49f0c5b9 100644
Binary files a/mods/cnc/uibits/chrome-2x.png and b/mods/cnc/uibits/chrome-2x.png differ
diff --git a/mods/cnc/uibits/chrome-3x.png b/mods/cnc/uibits/chrome-3x.png
index 35b269f5b3..2ce438b6da 100644
Binary files a/mods/cnc/uibits/chrome-3x.png and b/mods/cnc/uibits/chrome-3x.png differ
diff --git a/mods/cnc/uibits/chrome.png b/mods/cnc/uibits/chrome.png
index d9aeedcc6d..97e460b5b8 100644
Binary files a/mods/cnc/uibits/chrome.png and b/mods/cnc/uibits/chrome.png differ
diff --git a/mods/d2k/chrome.yaml b/mods/d2k/chrome.yaml
index 657a7da9bd..ac8091b071 100644
--- a/mods/d2k/chrome.yaml
+++ b/mods/d2k/chrome.yaml
@@ -241,10 +241,11 @@ dialog4:
lobby-bits:
Inherits: ^Glyphs
Regions:
- spawn-unclaimed: 91, 119, 22, 22
- spawn-claimed: 68, 119, 22, 22
+ spawn-claimed: 27, 119, 22, 22
+ spawn-unclaimed: 50, 119, 22, 22
+ spawn-disabled: 73, 119, 22, 22
admin: 170, 0, 6, 5
- colorpicker: 68, 119, 22, 22
+ colorpicker: 27, 119, 22, 22
huepicker: 136, 0, 7, 15
kick: 153, 0, 11, 11
protected: 0, 17, 12, 13
diff --git a/mods/d2k/uibits/glyphs-2x.png b/mods/d2k/uibits/glyphs-2x.png
index 57ab80a7c4..44afbc20d9 100644
Binary files a/mods/d2k/uibits/glyphs-2x.png and b/mods/d2k/uibits/glyphs-2x.png differ
diff --git a/mods/d2k/uibits/glyphs-3x.png b/mods/d2k/uibits/glyphs-3x.png
index ea75d0b8ba..5cf0ea5bb5 100644
Binary files a/mods/d2k/uibits/glyphs-3x.png and b/mods/d2k/uibits/glyphs-3x.png differ
diff --git a/mods/d2k/uibits/glyphs.png b/mods/d2k/uibits/glyphs.png
index 9d22f2724f..2530ce2579 100644
Binary files a/mods/d2k/uibits/glyphs.png and b/mods/d2k/uibits/glyphs.png differ
diff --git a/mods/ra/chrome.yaml b/mods/ra/chrome.yaml
index a3a6a191ab..39b475b37f 100644
--- a/mods/ra/chrome.yaml
+++ b/mods/ra/chrome.yaml
@@ -135,7 +135,7 @@ sidebar-bits:
production-tooltip-time: 136, 51, 16, 16
production-tooltip-power: 102, 51, 16, 16
production-tooltip-cost: 68, 51, 16, 16
- indicator-muted: 68, 145, 26, 24
+ indicator-muted: 221, 17, 26, 24
commandbar:
Inherits: ^Sidebar
@@ -326,8 +326,9 @@ dialog5:
lobby-bits:
Inherits: ^Glyphs
Regions:
- spawn-unclaimed: 91, 119, 22, 22
spawn-claimed: 68, 119, 22, 22
+ spawn-unclaimed: 91, 119, 22, 22
+ spawn-disabled: 114, 119, 22, 22
admin: 170, 0, 6, 5
colorpicker: 68, 119, 22, 22
huepicker: 136, 0, 7, 15
@@ -362,9 +363,9 @@ strategic:
Inherits: ^Glyphs
Regions:
unowned: 68, 119, 22, 22
- critical_unowned: 114, 119, 22, 22
- enemy_owned: 137, 119, 22, 22
- player_owned: 183, 119, 22, 22
+ critical_unowned: 137, 119, 22, 22
+ enemy_owned: 160, 119, 22, 22
+ player_owned: 160, 142, 22, 22
flags:
Inherits: ^Glyphs
diff --git a/mods/ra/uibits/glyphs-2x.png b/mods/ra/uibits/glyphs-2x.png
index 30fc3b12be..095928302f 100644
Binary files a/mods/ra/uibits/glyphs-2x.png and b/mods/ra/uibits/glyphs-2x.png differ
diff --git a/mods/ra/uibits/glyphs-3x.png b/mods/ra/uibits/glyphs-3x.png
index 28d48c67d0..1aa93b2175 100644
Binary files a/mods/ra/uibits/glyphs-3x.png and b/mods/ra/uibits/glyphs-3x.png differ
diff --git a/mods/ra/uibits/glyphs.png b/mods/ra/uibits/glyphs.png
index 29b7334497..da163f54a0 100644
Binary files a/mods/ra/uibits/glyphs.png and b/mods/ra/uibits/glyphs.png differ
diff --git a/mods/ts/chrome.yaml b/mods/ts/chrome.yaml
index e06908ddfa..dd33ca3386 100644
--- a/mods/ts/chrome.yaml
+++ b/mods/ts/chrome.yaml
@@ -458,10 +458,11 @@ flags:
lobby-bits:
Inherits: ^Glyphs
Regions:
- spawn-unclaimed: 91, 119, 22, 22
- spawn-claimed: 68, 119, 22, 22
+ spawn-claimed: 27, 119, 22, 22
+ spawn-unclaimed: 50, 119, 22, 22
+ spawn-disabled: 73, 119, 22, 22
admin: 170, 0, 6, 5
- colorpicker: 68, 119, 22, 22
+ colorpicker: 27, 119, 22, 22
huepicker: 136, 0, 7, 15
kick: 153, 0, 11, 11
protected: 0, 17, 12, 13
diff --git a/mods/ts/uibits/glyphs-2x.png b/mods/ts/uibits/glyphs-2x.png
index dd8d867777..d68a440c7e 100644
Binary files a/mods/ts/uibits/glyphs-2x.png and b/mods/ts/uibits/glyphs-2x.png differ
diff --git a/mods/ts/uibits/glyphs-3x.png b/mods/ts/uibits/glyphs-3x.png
index 1ecb77456b..f73511e49f 100644
Binary files a/mods/ts/uibits/glyphs-3x.png and b/mods/ts/uibits/glyphs-3x.png differ
diff --git a/mods/ts/uibits/glyphs.png b/mods/ts/uibits/glyphs.png
index b3cbc79629..1c16c776db 100644
Binary files a/mods/ts/uibits/glyphs.png and b/mods/ts/uibits/glyphs.png differ