Right click lobby spawns to disable or remove players.
1
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
|
||||
|
||||
@@ -35,6 +35,7 @@ namespace OpenRA
|
||||
/// <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 IList<Player> Players { get; private set; }
|
||||
public List<int> DisabledSpawnPoints = new List<int>();
|
||||
public MapPreview MapPreview { get { return Game.ModData.MapCache[MapUid]; } }
|
||||
public IEnumerable<Player> HumanPlayers { get { return Players.Where(p => p.IsHuman); } }
|
||||
public bool IsSinglePlayer { get { return HumanPlayers.Count() == 1; } }
|
||||
|
||||
@@ -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;
|
||||
|
||||
/// <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); } }
|
||||
|
||||
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<int>();
|
||||
}
|
||||
|
||||
public string ToPOSTData(bool lanGame)
|
||||
|
||||
@@ -26,6 +26,8 @@ namespace OpenRA.Network
|
||||
// Keyed by the PlayerReference id that the slot corresponds to
|
||||
public Dictionary<string, Slot> Slots = new Dictionary<string, Slot>();
|
||||
|
||||
public List<int> DisabledSpawnPoints = new List<int>();
|
||||
|
||||
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<List<int>>("DisabledSpawnPoints", node.Value.Value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -267,7 +272,10 @@ namespace OpenRA.Network
|
||||
|
||||
public string Serialize()
|
||||
{
|
||||
var sessionData = new List<MiniYamlNode>();
|
||||
var sessionData = new List<MiniYamlNode>()
|
||||
{
|
||||
new MiniYamlNode("DisabledSpawnPoints", FieldSaver.FormatValue(DisabledSpawnPoints))
|
||||
};
|
||||
|
||||
foreach (var client in Clients)
|
||||
sessionData.Add(client.Serialize());
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -60,6 +60,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
MapPreview map;
|
||||
bool addBotOnMapLoad;
|
||||
bool disableTeamChat;
|
||||
bool insufficientPlayerSpawns;
|
||||
bool teamChat;
|
||||
bool updateDiscordStatus = true;
|
||||
Dictionary<int, SpawnOccupant> spawnOccupants = new Dictionary<int, SpawnOccupant>();
|
||||
@@ -136,6 +137,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
LobbyUtils.SelectSpawnPoint(orderManager, preview, mapPreview, mi))
|
||||
},
|
||||
{ "getSpawnOccupants", (Func<Dictionary<int, SpawnOccupant>>)(() => spawnOccupants) },
|
||||
{ "getDisabledSpawnPoints", (Func<List<int>>)(() => 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;
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -24,8 +24,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
int blinkTick;
|
||||
|
||||
[ObjectCreator.UseCtor]
|
||||
internal MapPreviewLogic(Widget widget, ModData modData, OrderManager orderManager, Func<MapPreview> getMap,
|
||||
Action<MapPreviewWidget, MapPreview, MouseInput> onMouseDown, Func<Dictionary<int, SpawnOccupant>> getSpawnOccupants, bool showUnoccupiedSpawnpoints)
|
||||
internal MapPreviewLogic(Widget widget, ModData modData, OrderManager orderManager, Func<MapPreview> getMap, Action<MapPreviewWidget, MapPreview, MouseInput> onMouseDown,
|
||||
Func<Dictionary<int, SpawnOccupant>> getSpawnOccupants, Func<List<int>> getDisabledSpawnPoints, bool showUnoccupiedSpawnpoints)
|
||||
{
|
||||
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);
|
||||
};
|
||||
|
||||
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<ButtonWidget>("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<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");
|
||||
preview.Preview = () => getMap();
|
||||
preview.OnMouseDown = mi => onMouseDown(preview, getMap(), mi);
|
||||
preview.SpawnOccupants = getSpawnOccupants;
|
||||
preview.DisabledSpawnPoints = getDisabledSpawnPoints;
|
||||
preview.ShowUnoccupiedSpawnpoints = showUnoccupiedSpawnpoints;
|
||||
|
||||
var titleLabel = parent.GetOrNull<LabelWithTooltipWidget>("MAP_TITLE");
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -87,12 +87,16 @@ namespace OpenRA.Mods.Common.Widgets.Logic
|
||||
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
|
||||
{
|
||||
{ "orderManager", null },
|
||||
{ "getMap", (Func<MapPreview>)(() => map) },
|
||||
{ "onMouseDown", (Action<MapPreviewWidget, MapPreview, MouseInput>)((preview, mapPreview, mi) => { }) },
|
||||
{ "getSpawnOccupants", (Func<Dictionary<int, SpawnOccupant>>)(() => spawnOccupants.Update(selectedReplay)) },
|
||||
{ "getDisabledSpawnPoints", (Func<List<int>>)(() => disabledSpawnPoints.Update(selectedReplay)) },
|
||||
{ "showUnoccupiedSpawnpoints", false },
|
||||
});
|
||||
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -57,6 +57,8 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
|
||||
public class MapPreviewWidget : Widget
|
||||
{
|
||||
static readonly int[] NoDisabledSpawnPoints = Array.Empty<int>();
|
||||
|
||||
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<TooltipContainerWidget> tooltipContainer;
|
||||
|
||||
readonly Sprite spawnClaimed, spawnUnclaimed;
|
||||
readonly Sprite spawnClaimed, spawnUnclaimed, spawnDisabled;
|
||||
readonly SpriteFont spawnFont;
|
||||
readonly Color spawnColor, spawnContrastColor;
|
||||
readonly int2 spawnLabelOffset;
|
||||
|
||||
public Func<MapPreview> Preview = () => null;
|
||||
public Func<Dictionary<int, SpawnOccupant>> SpawnOccupants = () => new Dictionary<int, SpawnOccupant>();
|
||||
public Func<IEnumerable<int>> DisabledSpawnPoints = () => NoDisabledSpawnPoints;
|
||||
public Action<MouseInput> 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<string>("SpawnFont")];
|
||||
spawnColor = ChromeMetrics.Get<Color>("SpawnColor");
|
||||
spawnContrastColor = ChromeMetrics.Get<Color>("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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
Before Width: | Height: | Size: 410 KiB After Width: | Height: | Size: 412 KiB |
|
Before Width: | Height: | Size: 735 KiB After Width: | Height: | Size: 995 KiB |
|
Before Width: | Height: | Size: 168 KiB After Width: | Height: | Size: 169 KiB |
@@ -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
|
||||
|
||||
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 80 KiB After Width: | Height: | Size: 99 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 27 KiB |
@@ -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
|
||||
|
||||
|
Before Width: | Height: | Size: 76 KiB After Width: | Height: | Size: 78 KiB |
|
Before Width: | Height: | Size: 133 KiB After Width: | Height: | Size: 156 KiB |
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 36 KiB |
@@ -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
|
||||
|
||||
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 78 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 17 KiB |