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

@@ -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)

View File

@@ -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);

View File

@@ -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;

View File

@@ -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();

View File

@@ -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");

View File

@@ -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;

View File

@@ -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 },
});

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));
mapPreview.SpawnOccupants = () => occupants;
mapPreview.DisabledSpawnPoints = () => server.DisabledSpawnPoints;
}
if (server == null || !server.Clients.Any())

View File

@@ -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;
}
}
}