Right click lobby spawns to disable or remove players.

This commit is contained in:
Trevor Nichols
2020-07-19 11:11:27 +10:00
committed by abcdefg30
parent 13581c030d
commit d66e0bb22e
30 changed files with 165 additions and 45 deletions

View File

@@ -145,6 +145,7 @@ Also thanks to:
* Tirili * Tirili
* Tomas Einarsson (Mesacer) * Tomas Einarsson (Mesacer)
* Tom van Leth (tovl) * Tom van Leth (tovl)
* Trevor Nichols (ocdi)
* Tristan Keating (Kilkakon) * Tristan Keating (Kilkakon)
* Tristan Mühlbacher (MicroBit) * Tristan Mühlbacher (MicroBit)
* UnknownProgrammer * UnknownProgrammer

View File

@@ -35,6 +35,7 @@ namespace OpenRA
/// <summary>Gets the game's duration, from the time the game started until the replay recording stopped.</summary> /// <summary>Gets the game's duration, from the time the game started until the replay recording stopped.</summary>
public TimeSpan Duration { get { return EndTimeUtc > StartTimeUtc ? EndTimeUtc - StartTimeUtc : TimeSpan.Zero; } } public TimeSpan Duration { get { return EndTimeUtc > StartTimeUtc ? EndTimeUtc - StartTimeUtc : TimeSpan.Zero; } }
public IList<Player> Players { get; private set; } public IList<Player> Players { get; private set; }
public List<int> DisabledSpawnPoints = new List<int>();
public MapPreview MapPreview { get { return Game.ModData.MapCache[MapUid]; } } public MapPreview MapPreview { get { return Game.ModData.MapCache[MapUid]; } }
public IEnumerable<Player> HumanPlayers { get { return Players.Where(p => p.IsHuman); } } public IEnumerable<Player> HumanPlayers { get { return Players.Where(p => p.IsHuman); } }
public bool IsSinglePlayer { get { return HumanPlayers.Count() == 1; } } public bool IsSinglePlayer { get { return HumanPlayers.Count() == 1; } }

View File

@@ -56,7 +56,7 @@ namespace OpenRA.Network
"Mod", "Version", "ModTitle", "ModWebsite", "ModIcon32", "Mod", "Version", "ModTitle", "ModWebsite", "ModIcon32",
// Current server state // Current server state
"Map", "State", "MaxPlayers", "Protected", "Authentication" "Map", "State", "MaxPlayers", "Protected", "Authentication", "DisabledSpawnPoints"
}; };
public const int ProtocolVersion = 2; public const int ProtocolVersion = 2;
@@ -132,6 +132,9 @@ namespace OpenRA.Network
[FieldLoader.LoadUsing("LoadClients")] [FieldLoader.LoadUsing("LoadClients")]
public readonly GameClient[] Clients; public readonly GameClient[] Clients;
/// <summary>The list of spawnpoints that are disabled for this game</summary>
public readonly int[] DisabledSpawnPoints = { };
public string ModLabel { get { return "{0} ({1})".F(ModTitle, Version); } } public string ModLabel { get { return "{0} ({1})".F(ModTitle, Version); } }
static object LoadClients(MiniYaml yaml) static object LoadClients(MiniYaml yaml)
@@ -226,6 +229,7 @@ namespace OpenRA.Network
Protected = !string.IsNullOrEmpty(server.Settings.Password); Protected = !string.IsNullOrEmpty(server.Settings.Password);
Authentication = server.Settings.RequireAuthentication || server.Settings.ProfileIDWhitelist.Any(); Authentication = server.Settings.RequireAuthentication || server.Settings.ProfileIDWhitelist.Any();
Clients = server.LobbyInfo.Clients.Select(c => new GameClient(c)).ToArray(); Clients = server.LobbyInfo.Clients.Select(c => new GameClient(c)).ToArray();
DisabledSpawnPoints = server.LobbyInfo.DisabledSpawnPoints?.ToArray() ?? Array.Empty<int>();
} }
public string ToPOSTData(bool lanGame) public string ToPOSTData(bool lanGame)

View File

@@ -26,6 +26,8 @@ namespace OpenRA.Network
// Keyed by the PlayerReference id that the slot corresponds to // Keyed by the PlayerReference id that the slot corresponds to
public Dictionary<string, Slot> Slots = new Dictionary<string, Slot>(); public Dictionary<string, Slot> Slots = new Dictionary<string, Slot>();
public List<int> DisabledSpawnPoints = new List<int>();
public Global GlobalSettings = new Global(); public Global GlobalSettings = new Global();
public static string AnonymizeIP(IPAddress ip) public static string AnonymizeIP(IPAddress ip)
@@ -69,6 +71,9 @@ namespace OpenRA.Network
var s = Slot.Deserialize(node.Value); var s = Slot.Deserialize(node.Value);
session.Slots.Add(s.PlayerReference, s); session.Slots.Add(s.PlayerReference, s);
break; break;
case "DisabledSpawnPoints":
session.DisabledSpawnPoints = FieldLoader.GetValue<List<int>>("DisabledSpawnPoints", node.Value.Value);
break;
} }
} }
@@ -267,7 +272,10 @@ namespace OpenRA.Network
public string Serialize() public string Serialize()
{ {
var sessionData = new List<MiniYamlNode>(); var sessionData = new List<MiniYamlNode>()
{
new MiniYamlNode("DisabledSpawnPoints", FieldSaver.FormatValue(DisabledSpawnPoints))
};
foreach (var client in Clients) foreach (var client in Clients)
sessionData.Add(client.Serialize()); sessionData.Add(client.Serialize());

View File

@@ -321,6 +321,8 @@ namespace OpenRA
foreach (var player in Players) foreach (var player in Players)
gameInfo.AddPlayer(player, OrderManager.LobbyInfo); gameInfo.AddPlayer(player, OrderManager.LobbyInfo);
gameInfo.DisabledSpawnPoints = OrderManager.LobbyInfo.DisabledSpawnPoints;
var echo = OrderManager.Connection as EchoConnection; var echo = OrderManager.Connection as EchoConnection;
var rc = echo != null ? echo.Recorder : null; var rc = echo != null ? echo.Recorder : null;

View File

@@ -43,6 +43,7 @@ namespace OpenRA.Mods.Common.Server
{ "faction", Faction }, { "faction", Faction },
{ "team", Team }, { "team", Team },
{ "spawn", Spawn }, { "spawn", Spawn },
{ "clear_spawn", ClearPlayerSpawn },
{ "color", PlayerColor }, { "color", PlayerColor },
{ "sync_lobby", SyncLobby } { "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)) if (server.LobbyInfo.Slots.Any(sl => sl.Value.Required && server.LobbyInfo.ClientInSlot(sl.Key) == null))
return; 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(); server.StartGame();
} }
} }
@@ -170,6 +176,13 @@ namespace OpenRA.Mods.Common.Server
return true; 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(); server.StartGame();
return true; return true;
@@ -473,6 +486,8 @@ namespace OpenRA.Mods.Common.Server
if (c.Slot != null && !server.LobbyInfo.Slots[c.Slot].LockColor) if (c.Slot != null && !server.LobbyInfo.Slots[c.Slot].LockColor)
c.Color = c.PreferredColor = SanitizePlayerColor(server, c.Color, c.Index, conn); c.Color = c.PreferredColor = SanitizePlayerColor(server, c.Color, c.Index, conn);
server.LobbyInfo.DisabledSpawnPoints.Clear();
server.SyncLobbyInfo(); server.SyncLobbyInfo();
server.SendMessage("{0} changed the map to {1}.".F(client.Name, server.Map.Title)); 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) static bool Spawn(S server, Connection conn, Session.Client client, string s)
{ {
lock (server.LobbyInfo) lock (server.LobbyInfo)

View File

@@ -70,7 +70,7 @@ namespace OpenRA.Mods.Common.Traits
// Initialize the list of unoccupied spawn points for AssignSpawnLocations to pick from // Initialize the list of unoccupied spawn points for AssignSpawnLocations to pick from
state.SpawnLocations = map.SpawnPoints; 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) foreach (var kv in lobbyInfo.Slots)
{ {
var client = lobbyInfo.ClientInSlot(kv.Key); var client = lobbyInfo.ClientInSlot(kv.Key);
@@ -130,7 +130,7 @@ namespace OpenRA.Mods.Common.Traits
spawnLocations = spawns.ToArray(); spawnLocations = spawns.ToArray();
// Initialize the list of unoccupied spawn points for AssignSpawnLocations to pick from // 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) foreach (var kv in self.World.LobbyInfo.Slots)
{ {
var client = self.World.LobbyInfo.ClientInSlot(kv.Key); var client = self.World.LobbyInfo.ClientInSlot(kv.Key);

View File

@@ -60,6 +60,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
MapPreview map; MapPreview map;
bool addBotOnMapLoad; bool addBotOnMapLoad;
bool disableTeamChat; bool disableTeamChat;
bool insufficientPlayerSpawns;
bool teamChat; bool teamChat;
bool updateDiscordStatus = true; bool updateDiscordStatus = true;
Dictionary<int, SpawnOccupant> spawnOccupants = new Dictionary<int, SpawnOccupant>(); Dictionary<int, SpawnOccupant> spawnOccupants = new Dictionary<int, SpawnOccupant>();
@@ -136,6 +137,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
LobbyUtils.SelectSpawnPoint(orderManager, preview, mapPreview, mi)) LobbyUtils.SelectSpawnPoint(orderManager, preview, mapPreview, mi))
}, },
{ "getSpawnOccupants", (Func<Dictionary<int, SpawnOccupant>>)(() => spawnOccupants) }, { "getSpawnOccupants", (Func<Dictionary<int, SpawnOccupant>>)(() => spawnOccupants) },
{ "getDisabledSpawnPoints", (Func<List<int>>)(() => orderManager.LobbyInfo.DisabledSpawnPoints) },
{ "showUnoccupiedSpawnpoints", true }, { "showUnoccupiedSpawnpoints", true },
}); });
@@ -367,7 +369,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic
{ {
startGameButton.IsDisabled = () => configurationDisabled() || map.Status != MapStatus.Available || startGameButton.IsDisabled = () => configurationDisabled() || map.Status != MapStatus.Available ||
orderManager.LobbyInfo.Slots.Any(sl => sl.Value.Required && orderManager.LobbyInfo.ClientInSlot(sl.Key) == null) || 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 = () => startGameButton.OnClick = () =>
{ {
@@ -570,6 +573,9 @@ namespace OpenRA.Mods.Common.Widgets.Logic
c.Bot == null && c.Bot == null &&
c.Team == orderManager.LocalClient.Team); 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) if (disableTeamChat)
teamChat = false; teamChat = false;

View File

@@ -229,29 +229,48 @@ namespace OpenRA.Mods.Common.Widgets.Logic
public static void SelectSpawnPoint(OrderManager orderManager, MapPreviewWidget mapPreview, MapPreview preview, MouseInput mi) 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; return;
if (!orderManager.LocalClient.IsObserver && orderManager.LocalClient.State == Session.ClientState.Ready) if (mi.Button == MouseButton.Left)
return; SelectPlayerSpawnPoint(orderManager, mapPreview, preview, mi);
var spawnSize = ChromeProvider.GetImage("lobby-bits", "spawn-unclaimed").Size.XY; if (mi.Button == MouseButton.Right)
var selectedSpawn = preview.SpawnPoints ClearPlayerSpawnPoint(orderManager, mapPreview, preview, mi);
.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) static void SelectPlayerSpawnPoint(OrderManager orderManager, MapPreviewWidget mapPreview, MapPreview preview, MouseInput mi)
.FirstOrDefault(); {
var selectedSpawn = DetermineSelectedSpawnPoint(mapPreview, preview, mi);
var locals = orderManager.LobbyInfo.Clients.Where(c => c.Index == orderManager.LocalClient.Index || (Game.IsHost && c.Bot != null)); 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)); var playerToMove = locals.FirstOrDefault(c => ((selectedSpawn == 0) ^ (c.SpawnPoint == 0) && !c.IsObserver));
SetSpawnPoint(orderManager, playerToMove, selectedSpawn); 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); var selectedSpawn = DetermineSelectedSpawnPoint(mapPreview, preview, mi);
if (selectedSpawn == 0 || !owned) if (Game.IsHost || orderManager.LobbyInfo.Clients.FirstOrDefault(cc => cc.SpawnPoint == selectedSpawn) == orderManager.LocalClient)
orderManager.IssueOrder(Order.Command("spawn {0} {1}".F((playerToMove ?? orderManager.LocalClient).Index, selectedSpawn))); 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) 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( var spawnPoints = Enumerable.Range(0, map.SpawnPoints.Length + 1).Except(
orderManager.LobbyInfo.Clients.Where( 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); ShowSpawnDropDown(dropdown, c, orderManager, spawnPoints);
}; };
dropdown.GetText = () => (c.SpawnPoint == 0) ? "-" : Convert.ToChar('A' - 1 + c.SpawnPoint).ToString(); dropdown.GetText = () => (c.SpawnPoint == 0) ? "-" : Convert.ToChar('A' - 1 + c.SpawnPoint).ToString();

View File

@@ -24,8 +24,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic
int blinkTick; int blinkTick;
[ObjectCreator.UseCtor] [ObjectCreator.UseCtor]
internal MapPreviewLogic(Widget widget, ModData modData, OrderManager orderManager, Func<MapPreview> getMap, internal MapPreviewLogic(Widget widget, ModData modData, OrderManager orderManager, Func<MapPreview> getMap, Action<MapPreviewWidget, MapPreview, MouseInput> onMouseDown,
Action<MapPreviewWidget, MapPreview, MouseInput> onMouseDown, Func<Dictionary<int, SpawnOccupant>> getSpawnOccupants, bool showUnoccupiedSpawnpoints) Func<Dictionary<int, SpawnOccupant>> getSpawnOccupants, Func<List<int>> getDisabledSpawnPoints, bool showUnoccupiedSpawnpoints)
{ {
var mapRepository = modData.Manifest.Get<WebServices>().MapRepository; var mapRepository = modData.Manifest.Get<WebServices>().MapRepository;
@@ -38,7 +38,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
return map.Status == MapStatus.Available && (!map.RulesLoaded || !map.InvalidCustomRules); 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"); var invalid = widget.GetOrNull("MAP_INVALID");
@@ -50,14 +50,14 @@ namespace OpenRA.Mods.Common.Widgets.Logic
return map.Status == MapStatus.Available && map.InvalidCustomRules; 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"); var download = widget.GetOrNull("MAP_DOWNLOADABLE");
if (download != null) if (download != null)
{ {
download.IsVisible = () => getMap().Status == MapStatus.DownloadAvailable; download.IsVisible = () => getMap().Status == MapStatus.DownloadAvailable;
SetupWidgets(download, getMap, onMouseDown, getSpawnOccupants, showUnoccupiedSpawnpoints); SetupWidgets(download, getMap, onMouseDown, getSpawnOccupants, getDisabledSpawnPoints, showUnoccupiedSpawnpoints);
var install = download.GetOrNull<ButtonWidget>("MAP_INSTALL"); var install = download.GetOrNull<ButtonWidget>("MAP_INSTALL");
if (install != null) if (install != null)
@@ -86,7 +86,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
return map.Status != MapStatus.Available && map.Status != MapStatus.DownloadAvailable; 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"); var statusSearching = progress.GetOrNull("MAP_STATUS_SEARCHING");
if (statusSearching != null) if (statusSearching != null)
@@ -172,12 +172,13 @@ namespace OpenRA.Mods.Common.Widgets.Logic
} }
void SetupWidgets(Widget parent, Func<MapPreview> getMap, void SetupWidgets(Widget parent, Func<MapPreview> getMap,
Action<MapPreviewWidget, MapPreview, MouseInput> onMouseDown, Func<Dictionary<int, SpawnOccupant>> getSpawnOccupants, bool showUnoccupiedSpawnpoints) Action<MapPreviewWidget, MapPreview, MouseInput> onMouseDown, Func<Dictionary<int, SpawnOccupant>> getSpawnOccupants, Func<List<int>> getDisabledSpawnPoints, bool showUnoccupiedSpawnpoints)
{ {
var preview = parent.Get<MapPreviewWidget>("MAP_PREVIEW"); var preview = parent.Get<MapPreviewWidget>("MAP_PREVIEW");
preview.Preview = () => getMap(); preview.Preview = () => getMap();
preview.OnMouseDown = mi => onMouseDown(preview, getMap(), mi); preview.OnMouseDown = mi => onMouseDown(preview, getMap(), mi);
preview.SpawnOccupants = getSpawnOccupants; preview.SpawnOccupants = getSpawnOccupants;
preview.DisabledSpawnPoints = getDisabledSpawnPoints;
preview.ShowUnoccupiedSpawnpoints = showUnoccupiedSpawnpoints; preview.ShowUnoccupiedSpawnpoints = showUnoccupiedSpawnpoints;
var titleLabel = parent.GetOrNull<LabelWithTooltipWidget>("MAP_TITLE"); var titleLabel = parent.GetOrNull<LabelWithTooltipWidget>("MAP_TITLE");

View File

@@ -58,7 +58,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
return; return;
} }
labelText = "Available spawn"; labelText = preview.DisabledSpawnPoints().Contains(preview.TooltipSpawnIndex) ? "Disabled spawn" : "Available spawn";
playerFaction = null; playerFaction = null;
playerTeam = 0; playerTeam = 0;
widget.Bounds.Height = singleHeight; widget.Bounds.Height = singleHeight;

View File

@@ -87,12 +87,16 @@ namespace OpenRA.Mods.Common.Widgets.Logic
return occupants; return occupants;
}); });
var noSpawns = new List<int>();
var disabledSpawnPoints = new CachedTransform<ReplayMetadata, List<int>>(r => r.GameInfo.DisabledSpawnPoints ?? noSpawns);
Ui.LoadWidget("MAP_PREVIEW", mapPreviewRoot, new WidgetArgs Ui.LoadWidget("MAP_PREVIEW", mapPreviewRoot, new WidgetArgs
{ {
{ "orderManager", null }, { "orderManager", null },
{ "getMap", (Func<MapPreview>)(() => map) }, { "getMap", (Func<MapPreview>)(() => map) },
{ "onMouseDown", (Action<MapPreviewWidget, MapPreview, MouseInput>)((preview, mapPreview, mi) => { }) }, { "onMouseDown", (Action<MapPreviewWidget, MapPreview, MouseInput>)((preview, mapPreview, mi) => { }) },
{ "getSpawnOccupants", (Func<Dictionary<int, SpawnOccupant>>)(() => spawnOccupants.Update(selectedReplay)) }, { "getSpawnOccupants", (Func<Dictionary<int, SpawnOccupant>>)(() => spawnOccupants.Update(selectedReplay)) },
{ "getDisabledSpawnPoints", (Func<List<int>>)(() => disabledSpawnPoints.Update(selectedReplay)) },
{ "showUnoccupiedSpawnpoints", false }, { "showUnoccupiedSpawnpoints", false },
}); });

View File

@@ -427,6 +427,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
.ToDictionary(c => c.SpawnPoint, c => new SpawnOccupant(c, server.Mod != modData.Manifest.Id)); .ToDictionary(c => c.SpawnPoint, c => new SpawnOccupant(c, server.Mod != modData.Manifest.Id));
mapPreview.SpawnOccupants = () => occupants; mapPreview.SpawnOccupants = () => occupants;
mapPreview.DisabledSpawnPoints = () => server.DisabledSpawnPoints;
} }
if (server == null || !server.Clients.Any()) if (server == null || !server.Clients.Any())

View File

@@ -57,6 +57,8 @@ namespace OpenRA.Mods.Common.Widgets
public class MapPreviewWidget : Widget public class MapPreviewWidget : Widget
{ {
static readonly int[] NoDisabledSpawnPoints = Array.Empty<int>();
public readonly bool IgnoreMouseInput = false; public readonly bool IgnoreMouseInput = false;
public readonly bool ShowSpawnPoints = true; public readonly bool ShowSpawnPoints = true;
@@ -64,13 +66,14 @@ namespace OpenRA.Mods.Common.Widgets
public readonly string TooltipTemplate = "SPAWN_TOOLTIP"; public readonly string TooltipTemplate = "SPAWN_TOOLTIP";
readonly Lazy<TooltipContainerWidget> tooltipContainer; readonly Lazy<TooltipContainerWidget> tooltipContainer;
readonly Sprite spawnClaimed, spawnUnclaimed; readonly Sprite spawnClaimed, spawnUnclaimed, spawnDisabled;
readonly SpriteFont spawnFont; readonly SpriteFont spawnFont;
readonly Color spawnColor, spawnContrastColor; readonly Color spawnColor, spawnContrastColor;
readonly int2 spawnLabelOffset; readonly int2 spawnLabelOffset;
public Func<MapPreview> Preview = () => null; public Func<MapPreview> Preview = () => null;
public Func<Dictionary<int, SpawnOccupant>> SpawnOccupants = () => new Dictionary<int, SpawnOccupant>(); public Func<Dictionary<int, SpawnOccupant>> SpawnOccupants = () => new Dictionary<int, SpawnOccupant>();
public Func<IEnumerable<int>> DisabledSpawnPoints = () => NoDisabledSpawnPoints;
public Action<MouseInput> OnMouseDown = _ => { }; public Action<MouseInput> OnMouseDown = _ => { };
public int TooltipSpawnIndex = -1; public int TooltipSpawnIndex = -1;
public bool ShowUnoccupiedSpawnpoints = true; public bool ShowUnoccupiedSpawnpoints = true;
@@ -85,6 +88,7 @@ namespace OpenRA.Mods.Common.Widgets
spawnClaimed = ChromeProvider.GetImage("lobby-bits", "spawn-claimed"); spawnClaimed = ChromeProvider.GetImage("lobby-bits", "spawn-claimed");
spawnUnclaimed = ChromeProvider.GetImage("lobby-bits", "spawn-unclaimed"); spawnUnclaimed = ChromeProvider.GetImage("lobby-bits", "spawn-unclaimed");
spawnDisabled = ChromeProvider.GetImage("lobby-bits", "spawn-disabled") ?? spawnUnclaimed;
spawnFont = Game.Renderer.Fonts[ChromeMetrics.Get<string>("SpawnFont")]; spawnFont = Game.Renderer.Fonts[ChromeMetrics.Get<string>("SpawnFont")];
spawnColor = ChromeMetrics.Get<Color>("SpawnColor"); spawnColor = ChromeMetrics.Get<Color>("SpawnColor");
spawnContrastColor = ChromeMetrics.Get<Color>("SpawnContrastColor"); spawnContrastColor = ChromeMetrics.Get<Color>("SpawnContrastColor");
@@ -184,6 +188,7 @@ namespace OpenRA.Mods.Common.Widgets
{ {
var spawnPoints = preview.SpawnPoints; var spawnPoints = preview.SpawnPoints;
var occupants = SpawnOccupants(); var occupants = SpawnOccupants();
var disabledSpawnPoints = DisabledSpawnPoints();
var gridType = preview.GridType; var gridType = preview.GridType;
for (var i = 0; i < spawnPoints.Length; i++) 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". // Spawn numbers are 1 indexed with 0 meaning "random spawn".
var occupied = occupants.TryGetValue(i + 1, out var occupant); var occupied = occupants.TryGetValue(i + 1, out var occupant);
var disabled = disabledSpawnPoints.Contains(i + 1);
var pos = ConvertToPreview(p, gridType); var pos = ConvertToPreview(p, gridType);
var sprite = occupied ? spawnClaimed : spawnUnclaimed;
var sprite = disabled ? spawnDisabled : occupied ? spawnClaimed : spawnUnclaimed;
var offset = sprite.Size.XY.ToInt2() / 2; 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) 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); 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); Game.Renderer.RgbaSpriteRenderer.DrawSprite(sprite, pos - offset);
var number = Convert.ToChar('A' + spawnPoints.IndexOf(p)).ToString(); var number = Convert.ToChar('A' + spawnPoints.IndexOf(p)).ToString();
var textOffset = spawnFont.Measure(number) / 2 + spawnLabelOffset; var textOffset = spawnFont.Measure(number) / 2 + spawnLabelOffset;
spawnFont.DrawTextWithContrast(number, pos - textOffset, spawnColor, spawnContrastColor, 1); spawnFont.DrawTextWithContrast(number, pos - textOffset, spawnColor, spawnContrastColor, 1);
if (((pos - Viewport.LastMousePos).ToFloat2() / offset.ToFloat2()).LengthSquared <= 1)
TooltipSpawnIndex = spawnPoints.IndexOf(p) + 1;
} }
} }
} }

View File

@@ -306,8 +306,9 @@ music:
lobby-bits: lobby-bits:
Inherits: ^Chrome Inherits: ^Chrome
Regions: Regions:
spawn-unclaimed: 777, 423, 19, 19 spawn-claimed: 744, 215, 18, 18
spawn-claimed: 777, 443, 19, 19 spawn-unclaimed: 744, 234, 18, 18
spawn-disabled: 744, 253, 18, 18
admin: 938, 0, 6, 5 admin: 938, 0, 6, 5
colorpicker: 887, 0, 14, 14 colorpicker: 887, 0, 14, 14
huepicker: 904, 0, 7, 15 huepicker: 904, 0, 7, 15

Binary file not shown.

Before

Width:  |  Height:  |  Size: 410 KiB

After

Width:  |  Height:  |  Size: 412 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 735 KiB

After

Width:  |  Height:  |  Size: 995 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 168 KiB

After

Width:  |  Height:  |  Size: 169 KiB

View File

@@ -241,10 +241,11 @@ dialog4:
lobby-bits: lobby-bits:
Inherits: ^Glyphs Inherits: ^Glyphs
Regions: Regions:
spawn-unclaimed: 91, 119, 22, 22 spawn-claimed: 27, 119, 22, 22
spawn-claimed: 68, 119, 22, 22 spawn-unclaimed: 50, 119, 22, 22
spawn-disabled: 73, 119, 22, 22
admin: 170, 0, 6, 5 admin: 170, 0, 6, 5
colorpicker: 68, 119, 22, 22 colorpicker: 27, 119, 22, 22
huepicker: 136, 0, 7, 15 huepicker: 136, 0, 7, 15
kick: 153, 0, 11, 11 kick: 153, 0, 11, 11
protected: 0, 17, 12, 13 protected: 0, 17, 12, 13

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 27 KiB

View File

@@ -135,7 +135,7 @@ sidebar-bits:
production-tooltip-time: 136, 51, 16, 16 production-tooltip-time: 136, 51, 16, 16
production-tooltip-power: 102, 51, 16, 16 production-tooltip-power: 102, 51, 16, 16
production-tooltip-cost: 68, 51, 16, 16 production-tooltip-cost: 68, 51, 16, 16
indicator-muted: 68, 145, 26, 24 indicator-muted: 221, 17, 26, 24
commandbar: commandbar:
Inherits: ^Sidebar Inherits: ^Sidebar
@@ -326,8 +326,9 @@ dialog5:
lobby-bits: lobby-bits:
Inherits: ^Glyphs Inherits: ^Glyphs
Regions: Regions:
spawn-unclaimed: 91, 119, 22, 22
spawn-claimed: 68, 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 admin: 170, 0, 6, 5
colorpicker: 68, 119, 22, 22 colorpicker: 68, 119, 22, 22
huepicker: 136, 0, 7, 15 huepicker: 136, 0, 7, 15
@@ -362,9 +363,9 @@ strategic:
Inherits: ^Glyphs Inherits: ^Glyphs
Regions: Regions:
unowned: 68, 119, 22, 22 unowned: 68, 119, 22, 22
critical_unowned: 114, 119, 22, 22 critical_unowned: 137, 119, 22, 22
enemy_owned: 137, 119, 22, 22 enemy_owned: 160, 119, 22, 22
player_owned: 183, 119, 22, 22 player_owned: 160, 142, 22, 22
flags: flags:
Inherits: ^Glyphs Inherits: ^Glyphs

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 133 KiB

After

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 36 KiB

View File

@@ -458,10 +458,11 @@ flags:
lobby-bits: lobby-bits:
Inherits: ^Glyphs Inherits: ^Glyphs
Regions: Regions:
spawn-unclaimed: 91, 119, 22, 22 spawn-claimed: 27, 119, 22, 22
spawn-claimed: 68, 119, 22, 22 spawn-unclaimed: 50, 119, 22, 22
spawn-disabled: 73, 119, 22, 22
admin: 170, 0, 6, 5 admin: 170, 0, 6, 5
colorpicker: 68, 119, 22, 22 colorpicker: 27, 119, 22, 22
huepicker: 136, 0, 7, 15 huepicker: 136, 0, 7, 15
kick: 153, 0, 11, 11 kick: 153, 0, 11, 11
protected: 0, 17, 12, 13 protected: 0, 17, 12, 13

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 17 KiB