Fix the lobby slots.

- lobbyInfo.Slots is now a dictionary, keyed by the name of the PlayerReference that the slot is tied to.
 - LockRace/Color/Team is now specified on the slot, avoiding map lookups in the lobby
 - Observers are no longer tied to slots -> players will join as observers instead of crashing the game if there are no available slots
 - Observers are able to change their name and color
This commit is contained in:
Paul Chote
2011-06-17 19:03:57 +12:00
parent e934baa6e9
commit d2df5722b5
13 changed files with 588 additions and 317 deletions

View File

@@ -31,13 +31,13 @@ namespace OpenRA.Mods.RA
}
// create the players which are bound through slots.
foreach (var slot in w.LobbyInfo.Slots)
foreach (var kv in w.LobbyInfo.Slots)
{
var client = w.LobbyInfo.Clients.FirstOrDefault(c => c.Slot == slot.Index && slot.MapPlayer != null);
if (client == null && slot.Bot == null)
var client = w.LobbyInfo.ClientInSlot(kv.Key);
if (client == null && kv.Value.Bot == null)
continue;
var player = new Player(w, client, slot, w.Map.Players[slot.MapPlayer]);
var player = new Player(w, client, kv.Value, w.Map.Players[kv.Value.PlayerReference]);
w.AddPlayer(player);
if (client != null && client.Index == Game.LocalClientId)
@@ -60,8 +60,8 @@ namespace OpenRA.Mods.RA
if (p.World.LobbyInfo.Slots.Count > 0)
{
if (p.World.LobbyInfo.Slots[pc.Slot].Spectator) return Stance.Ally;
if (p.World.LobbyInfo.Slots[qc.Slot].Spectator) return Stance.Ally;
if (p.PlayerRef == null) return Stance.Ally;
if (q.PlayerRef == null) return Stance.Ally;
}
// Stances set via the player reference

View File

@@ -28,19 +28,17 @@ namespace OpenRA.Mods.RA
public void WorldLoaded(World world)
{
var taken = world.LobbyInfo.Clients.Where(c => c.SpawnPoint != 0 && c.Slot != -1)
var taken = world.LobbyInfo.Clients.Where(c => c.SpawnPoint != 0 && c.Slot != null)
.Select(c => world.Map.SpawnPoints.ElementAt(c.SpawnPoint - 1)).ToList();
var available = world.Map.SpawnPoints.Except(taken).ToList();
// Set spawn
foreach (var slot in world.LobbyInfo.Slots)
foreach (var kv in world.LobbyInfo.Slots)
{
if (slot.Spectator)
continue; // Skip spectator slots
var client = world.LobbyInfo.Clients.FirstOrDefault(c => c.Slot == slot.Index);
var player = FindPlayerInSlot(world, slot);
var player = FindPlayerInSlot(world, kv.Key);
if (player == null) continue;
var client = world.LobbyInfo.ClientInSlot(kv.Key);
var spid = (client == null || client.SpawnPoint == 0)
? ChooseSpawnPoint(world, available, taken)
: world.Map.SpawnPoints.ElementAt(client.SpawnPoint - 1);
@@ -59,9 +57,9 @@ namespace OpenRA.Mods.RA
Game.viewport.Center(Start[world.LocalPlayer]);
}
static Player FindPlayerInSlot(World world, Session.Slot slot)
static Player FindPlayerInSlot(World world, string pr)
{
return world.Players.FirstOrDefault(p => p.PlayerRef.Name == slot.MapPlayer);
return world.Players.FirstOrDefault(p => p.PlayerRef.Name == pr);
}
static int2 ChooseSpawnPoint(World world, List<int2> available, List<int2> taken)

View File

@@ -20,8 +20,6 @@ namespace OpenRA.Mods.RA.Server
{
public class LobbyCommands : ServerTrait, IInterpretCommand, INotifyServerStart
{
public static int MaxSpectators = 4; // How many spectators to allow // @todo Expose this as an option
public bool InterpretCommand(S server, Connection conn, Session.Client client, string cmd)
{
if (server.GameStarted)
@@ -77,33 +75,39 @@ namespace OpenRA.Mods.RA.Server
{ "slot",
s =>
{
int slot;
if (!int.TryParse(s, out slot)) { Log.Write("server", "Invalid slot: {0}", s ); return false; }
if (!server.lobbyInfo.Slots.ContainsKey(s))
{
Log.Write("server", "Invalid slot: {0}", s );
return false;
}
var slot = server.lobbyInfo.Slots[s];
var slotData = server.lobbyInfo.Slots.FirstOrDefault( x => x.Index == slot );
if (slotData == null || slotData.Closed || slotData.Bot != null
|| server.lobbyInfo.Clients.Any( c => c.Slot == slot ))
if (slot.Closed || slot.Bot != null ||
server.lobbyInfo.ClientInSlot(s) != null)
return false;
client.Slot = slot;
S.SyncClientToPlayerReference(client, slotData.MapPlayer != null ? server.Map.Players[slotData.MapPlayer] : null);
// if we're entering a spectator slot, relinquish our spawnpoint.
if (slotData.Spectator)
client.SpawnPoint = 0;
client.Slot = s;
S.SyncClientToPlayerReference(client, server.Map.Players[s]);
server.SyncLobbyInfo();
return true;
}},
{ "spectate",
s =>
{
client.Slot = null;
client.SpawnPoint = 0;
server.SyncLobbyInfo();
return true;
}},
{ "slot_close",
s =>
{
int slot;
if (!int.TryParse(s, out slot)) { Log.Write("server", "Invalid slot: {0}", s ); return false; }
var slotData = server.lobbyInfo.Slots.FirstOrDefault( x => x.Index == slot );
if (slotData == null)
if (!server.lobbyInfo.Slots.ContainsKey(s))
{
Log.Write("server", "Invalid slot: {0}", s );
return false;
}
if (conn.PlayerIndex != 0)
{
@@ -111,11 +115,8 @@ namespace OpenRA.Mods.RA.Server
return true;
}
slotData.Closed = true;
slotData.Bot = null;
/* kick any player that's in the slot */
var occupant = server.lobbyInfo.Clients.FirstOrDefault( c => c.Slot == slotData.Index );
// kick any player that's in the slot
var occupant = server.lobbyInfo.ClientInSlot(s);
if (occupant != null)
{
var occupantConn = server.conns.FirstOrDefault( c => c.PlayerIndex == occupant.Index );
@@ -125,6 +126,9 @@ namespace OpenRA.Mods.RA.Server
server.DropClient(occupantConn);
}
}
var slot = server.lobbyInfo.Slots[s];
slot.Closed = true;
slot.Bot = null;
server.SyncLobbyInfo();
return true;
@@ -132,12 +136,11 @@ namespace OpenRA.Mods.RA.Server
{ "slot_open",
s =>
{
int slot;
if (!int.TryParse(s, out slot)) { Log.Write("server", "Invalid slot: {0}", s ); return false; }
var slotData = server.lobbyInfo.Slots.FirstOrDefault( x => x.Index == slot );
if (slotData == null)
if (!server.lobbyInfo.Slots.ContainsKey(s))
{
Log.Write("server", "Invalid slot: {0}", s );
return false;
}
if (conn.PlayerIndex != 0)
{
@@ -145,8 +148,9 @@ namespace OpenRA.Mods.RA.Server
return true;
}
slotData.Closed = false;
slotData.Bot = null;
var slot = server.lobbyInfo.Slots[s];
slot.Closed = false;
slot.Bot = null;
server.SyncLobbyInfo();
return true;
@@ -162,12 +166,11 @@ namespace OpenRA.Mods.RA.Server
return true;
}
int slot;
if (!int.TryParse(parts[0], out slot)) { Log.Write("server", "Invalid slot: {0}", s ); return false; }
var slotData = server.lobbyInfo.Slots.FirstOrDefault( x => x.Index == slot );
if (slotData == null)
if (!server.lobbyInfo.Slots.ContainsKey(parts[0]))
{
Log.Write("server", "Invalid slot: {0}", parts[0] );
return false;
}
if (conn.PlayerIndex != 0)
{
@@ -175,8 +178,9 @@ namespace OpenRA.Mods.RA.Server
return true;
}
slotData.Closed = false;
slotData.Bot = string.Join(" ", parts.Skip(1).ToArray() );
var slot = server.lobbyInfo.Slots[parts[0]];
slot.Bot = string.Join(" ", parts.Skip(1).ToArray() );
slot.Closed = false;
server.SyncLobbyInfo();
return true;
@@ -192,14 +196,17 @@ namespace OpenRA.Mods.RA.Server
server.lobbyInfo.GlobalSettings.Map = s;
LoadMap(server);
// Reassign players into slots
int i = 0;
foreach(var c in server.lobbyInfo.Clients)
{
c.SpawnPoint = 0;
var slotData = server.lobbyInfo.Slots.FirstOrDefault( x => x.Index == c.Slot );
if (slotData != null && slotData.MapPlayer != null)
S.SyncClientToPlayerReference(c, server.Map.Players[slotData.MapPlayer]);
c.State = Session.ClientState.NotReady;
c.Slot = c.Slot == null || i >= server.lobbyInfo.Slots.Count ?
null : server.lobbyInfo.Slots.ElementAt(i++).Key;
if (c.Slot != null)
S.SyncClientToPlayerReference(c, server.Map.Players[c.Slot]);
}
server.SyncLobbyInfo();
@@ -234,16 +241,17 @@ namespace OpenRA.Mods.RA.Server
{ "kick",
s =>
{
if (conn.PlayerIndex != 0)
{
server.SendChatTo( conn, "Only the host can kick players" );
return true;
}
int slot;
int.TryParse( s, out slot );
int clientID;
int.TryParse( s, out clientID );
var connToKick = server.conns.SingleOrDefault( c => server.GetClient(c) != null && server.GetClient(c).Slot == slot);
var connToKick = server.conns.SingleOrDefault( c => server.GetClient(c) != null && server.GetClient(c).Index == clientID);
if (connToKick == null)
{
server.SendChatTo( conn, "Noone in that slot." );
@@ -252,9 +260,7 @@ namespace OpenRA.Mods.RA.Server
server.SendOrderTo(connToKick, "ServerError", "You have been kicked from the server");
server.DropClient(connToKick);
server.SyncLobbyInfo();
return true;
}},
};
@@ -275,9 +281,13 @@ namespace OpenRA.Mods.RA.Server
if (!pr.Playable) return null;
return new Session.Slot
{
MapPlayer = pr.Name,
Bot = null, /* todo: allow the map to specify a bot class? */
PlayerReference = pr.Name,
Bot = null,
Closed = false,
AllowBots = pr.AllowBots,
LockRace = pr.LockRace,
LockColor = pr.LockColor,
LockTeam = false
};
}
@@ -287,18 +297,7 @@ namespace OpenRA.Mods.RA.Server
server.lobbyInfo.Slots = server.Map.Players
.Select(p => MakeSlotFromPlayerReference(p.Value))
.Where(s => s != null)
.Select((s, i) => { s.Index = i; return s; })
.ToList();
// Generate slots for spectators
for (int i = 0; i < MaxSpectators; i++)
server.lobbyInfo.Slots.Add(new Session.Slot
{
Spectator = true,
Index = server.lobbyInfo.Slots.Count(),
MapPlayer = null,
Bot = null
});
.ToDictionary(s => s.PlayerReference, s => s);
}
}
}

View File

@@ -66,7 +66,7 @@ namespace OpenRA.Mods.RA.Server
server.lobbyInfo.Clients.Count,
string.Join(",", Game.CurrentMods.Select(f => "{0}@{1}".F(f.Key, f.Value.Version)).ToArray()),
server.lobbyInfo.GlobalSettings.Map,
server.lobbyInfo.Slots.Count( s => !s.Spectator )));
server.Map.PlayerCount));
if (isInitialPing)
{

View File

@@ -71,7 +71,7 @@ namespace OpenRA.Mods.RA.Server
return false;
}
if (server.lobbyInfo.Slots[client.Slot].Spectator)
if (client.Slot == null)
{
server.SendChatTo( conn, "Can't select a spawnpoint as a spectator" );
return false;

View File

@@ -22,7 +22,9 @@ namespace OpenRA.Mods.RA.Widgets.Logic
{
public class LobbyLogic
{
Widget lobby, LocalPlayerTemplate, RemotePlayerTemplate, EmptySlotTemplate, EmptySlotTemplateHost;
Widget lobby, LocalPlayerTemplate, RemotePlayerTemplate, EmptySlotTemplate, EmptySlotTemplateHost,
LocalSpectatorTemplate, RemoteSpectatorTemplate, NewSpectatorTemplate;
ScrollPanelWidget Players;
Dictionary<string, string> CountryNames;
string MapUid;
@@ -50,6 +52,9 @@ namespace OpenRA.Mods.RA.Widgets.Logic
RemotePlayerTemplate = Players.GetWidget("TEMPLATE_REMOTE");
EmptySlotTemplate = Players.GetWidget("TEMPLATE_EMPTY");
EmptySlotTemplateHost = Players.GetWidget("TEMPLATE_EMPTY_HOST");
LocalSpectatorTemplate = Players.GetWidget("TEMPLATE_LOCAL_SPECTATOR");
RemoteSpectatorTemplate = Players.GetWidget("TEMPLATE_REMOTE_SPECTATOR");
NewSpectatorTemplate = Players.GetWidget("TEMPLATE_NEW_SPECTATOR");
var mapPreview = lobby.GetWidget<MapPreviewWidget>("LOBBY_MAP_PREVIEW");
mapPreview.Map = () => Map;
@@ -201,11 +206,6 @@ namespace OpenRA.Mods.RA.Widgets.Logic
var title = Widget.RootWidget.GetWidget<LabelWidget>("LOBBY_TITLE");
title.Text = "OpenRA Multiplayer Lobby - " + orderManager.LobbyInfo.GlobalSettings.ServerName;
}
Session.Client GetClientInSlot(Session.Slot slot)
{
return orderManager.LobbyInfo.ClientInSlot( slot );
}
class SlotDropDownOption
{
@@ -221,20 +221,21 @@ namespace OpenRA.Mods.RA.Widgets.Logic
}
}
bool ShowSlotDropDown(DropDownButtonWidget dropdown, Session.Slot slot, bool showBotOptions)
bool ShowSlotDropDown(DropDownButtonWidget dropdown, Session.Slot slot)
{
var options = new List<SlotDropDownOption>()
{
new SlotDropDownOption("Open", "slot_open "+slot.Index, () => (!slot.Closed && slot.Bot == null)),
new SlotDropDownOption("Closed", "slot_close "+slot.Index, () => slot.Closed)
new SlotDropDownOption("Open", "slot_open "+slot.PlayerReference, () => (!slot.Closed && slot.Bot == null)),
new SlotDropDownOption("Closed", "slot_close "+slot.PlayerReference, () => slot.Closed)
};
if (showBotOptions)
if (slot.AllowBots)
foreach (var b in Rules.Info["player"].Traits.WithInterface<IBotInfo>().Select(t => t.Name))
{
var bot = b;
options.Add(new SlotDropDownOption("Bot: {0}".F(bot), "slot_bot {0} {1}".F(slot.Index, bot), () => slot.Bot == bot));
options.Add(new SlotDropDownOption("Bot: {0}".F(bot), "slot_bot {0} {1}".F(slot.PlayerReference, bot), () => slot.Bot == bot));
}
Func<SlotDropDownOption, ScrollItemWidget, ScrollItemWidget> setupItem = (o, itemTemplate) =>
{
var item = ScrollItemWidget.Setup(itemTemplate,
@@ -248,16 +249,12 @@ namespace OpenRA.Mods.RA.Widgets.Logic
return true;
}
bool ShowRaceDropDown(DropDownButtonWidget dropdown, Session.Slot slot)
bool ShowRaceDropDown(DropDownButtonWidget dropdown, Session.Client client)
{
if (Map.Players[slot.MapPlayer].LockRace)
return false;
var sr = GetClientInSlot(slot).Country;
Func<string, ScrollItemWidget, ScrollItemWidget> setupItem = (race, itemTemplate) =>
{
var item = ScrollItemWidget.Setup(itemTemplate,
() => sr == race,
() => client.Country == race,
() => orderManager.IssueOrder(Order.Command("race "+race)));
item.GetWidget<LabelWidget>("LABEL").GetText = () => CountryNames[race];
var flag = item.GetWidget<ImageWidget>("FLAG");
@@ -270,13 +267,12 @@ namespace OpenRA.Mods.RA.Widgets.Logic
return true;
}
bool ShowTeamDropDown(DropDownButtonWidget dropdown, Session.Slot slot)
bool ShowTeamDropDown(DropDownButtonWidget dropdown, Session.Client client)
{
var c = GetClientInSlot(slot);
Func<int, ScrollItemWidget, ScrollItemWidget> setupItem = (ii, itemTemplate) =>
{
var item = ScrollItemWidget.Setup(itemTemplate,
() => c.Team == ii,
() => client.Team == ii,
() => orderManager.IssueOrder(Order.Command("team "+ii)));
item.GetWidget<LabelWidget>("LABEL").GetText = () => ii == 0 ? "-" : ii.ToString();
return item;
@@ -287,11 +283,8 @@ namespace OpenRA.Mods.RA.Widgets.Logic
return true;
}
bool ShowColorDropDown(Session.Slot s, DropDownButtonWidget color)
bool ShowColorDropDown(DropDownButtonWidget color, Session.Client client)
{
if (Map.Players[s.MapPlayer].LockColor)
return false;
var colorChooser = Game.modData.WidgetLoader.LoadWidget( new WidgetArgs() { {"worldRenderer", worldRenderer} }, null, "COLOR_CHOOSER" );
var hueSlider = colorChooser.GetWidget<SliderWidget>("HUE_SLIDER");
hueSlider.SetOffset(orderManager.LocalClient.ColorRamp.H / 255f);
@@ -328,50 +321,32 @@ namespace OpenRA.Mods.RA.Widgets.Logic
// Todo: handle this nicer
Players.RemoveChildren();
foreach (var slot in orderManager.LobbyInfo.Slots)
foreach (var kv in orderManager.LobbyInfo.Slots)
{
var s = slot;
var c = GetClientInSlot(s);
var s = kv.Value;
var c = orderManager.LobbyInfo.ClientInSlot(kv.Key);
Widget template;
if (c == null)
{
if (Game.IsHost)
{
if (slot.Spectator)
{
template = EmptySlotTemplateHost.Clone();
var name = template.GetWidget<DropDownButtonWidget>("NAME");
name.GetText = () => s.Closed ? "Closed" : "Open";
name.OnMouseDown = _ => ShowSlotDropDown(name, s, false);
var btn = template.GetWidget<ButtonWidget>("JOIN");
btn.GetText = () => "Spectate in this slot";
}
else
{
template = EmptySlotTemplateHost.Clone();
var name = template.GetWidget<DropDownButtonWidget>("NAME");
name.GetText = () => s.Closed ? "Closed" : (s.Bot == null) ? "Open" : s.Bot;
name.OnMouseDown = _ => ShowSlotDropDown(name, s, Map.Players[ s.MapPlayer ].AllowBots);
}
template = EmptySlotTemplateHost.Clone();
var name = template.GetWidget<DropDownButtonWidget>("NAME");
name.GetText = () => s.Closed ? "Closed" : (s.Bot == null) ? "Open" : s.Bot;
name.OnMouseDown = _ => ShowSlotDropDown(name, s);
}
else
{
template = EmptySlotTemplate.Clone();
var name = template.GetWidget<LabelWidget>("NAME");
name.GetText = () => s.Closed ? "Closed" : (s.Bot == null) ? "Open" : s.Bot;
if (slot.Spectator)
{
var btn = template.GetWidget<ButtonWidget>("JOIN");
btn.GetText = () => "Spectate in this slot";
}
}
var join = template.GetWidget<ButtonWidget>("JOIN");
if (join != null)
{
join.OnMouseUp = _ => { orderManager.IssueOrder(Order.Command("slot " + s.Index)); return true; };
join.OnMouseUp = _ => { orderManager.IssueOrder(Order.Command("slot " + s.PlayerReference)); return true; };
join.IsVisible = () => !s.Closed && s.Bot == null && orderManager.LocalClient.State != Session.ClientState.Ready;
}
@@ -402,13 +377,15 @@ namespace OpenRA.Mods.RA.Widgets.Logic
name.OnLoseFocus = () => name.OnEnterKey();
var color = template.GetWidget<DropDownButtonWidget>("COLOR");
color.OnMouseUp = _ => ShowColorDropDown(s, color);
color.IsDisabled = () => s.LockColor;
color.OnMouseDown = _ => { if (s.LockColor) return true; return ShowColorDropDown(color, c); };
var colorBlock = color.GetWidget<ColorBlockWidget>("COLORBLOCK");
colorBlock.GetColor = () => c.ColorRamp.GetColor(0);
var faction = template.GetWidget<DropDownButtonWidget>("FACTION");
faction.OnMouseDown = _ => ShowRaceDropDown(faction, s);
faction.IsDisabled = () => s.LockRace;
faction.OnMouseDown = _ => { if (s.LockRace) return true; return ShowRaceDropDown(faction, c); };
var factionname = faction.GetWidget<LabelWidget>("FACTIONNAME");
factionname.GetText = () => CountryNames[c.Country];
@@ -417,7 +394,8 @@ namespace OpenRA.Mods.RA.Widgets.Logic
factionflag.GetImageCollection = () => "flags";
var team = template.GetWidget<DropDownButtonWidget>("TEAM");
team.OnMouseDown = _ => ShowTeamDropDown(team, s);
team.IsDisabled = () => s.LockTeam;
team.OnMouseDown = _ => { if (s.LockTeam) return true; return ShowTeamDropDown(team, c); };
team.GetText = () => (c.Team == 0) ? "-" : c.Team.ToString();
var status = template.GetWidget<CheckboxWidget>("STATUS");
@@ -426,14 +404,8 @@ namespace OpenRA.Mods.RA.Widgets.Logic
var spectator = template.GetWidget<LabelWidget>("SPECTATOR");
Session.Slot slot1 = slot;
color.IsVisible = () => !slot1.Spectator;
colorBlock.IsVisible = () => !slot1.Spectator;
faction.IsVisible = () => !slot1.Spectator;
factionname.IsVisible = () => !slot1.Spectator;
factionflag.IsVisible = () => !slot1.Spectator;
team.IsVisible = () => !slot1.Spectator;
spectator.IsVisible = () => slot1.Spectator || slot1.Bot != null;
Session.Slot ss = s;
spectator.IsVisible = () => ss.Bot != null;
}
else
{
@@ -459,13 +431,8 @@ namespace OpenRA.Mods.RA.Widgets.Logic
var spectator = template.GetWidget<LabelWidget>("SPECTATOR");
Session.Slot slot1 = slot;
color.IsVisible = () => !slot1.Spectator;
faction.IsVisible = () => !slot1.Spectator;
factionname.IsVisible = () => !slot1.Spectator;
factionflag.IsVisible = () => !slot1.Spectator;
team.IsVisible = () => !slot1.Spectator;
spectator.IsVisible = () => slot1.Spectator || slot1.Bot != null;
Session.Slot ss = s;
spectator.IsVisible = () => ss.Bot != null;
var kickButton = template.GetWidget<ButtonWidget>("KICK");
kickButton.IsVisible = () => Game.IsHost && c.Index != orderManager.LocalClient.Index;
@@ -476,10 +443,82 @@ namespace OpenRA.Mods.RA.Widgets.Logic
};
}
template.Id = "SLOT_{0}".F(s.Index);
template.IsVisible = () => true;
Players.AddChild(template);
}
}
// Add spectators
foreach (var client in orderManager.LobbyInfo.Clients.Where(client => client.Slot == null))
{
Widget template;
// Editable spectator
if (client.Index == orderManager.LocalClient.Index && client.State != Session.ClientState.Ready)
{
template = LocalSpectatorTemplate.Clone();
var name = template.GetWidget<TextFieldWidget>("NAME");
name.Text = client.Name;
name.OnEnterKey = () =>
{
name.Text = name.Text.Trim();
if (name.Text.Length == 0)
name.Text = client.Name;
name.LoseFocus();
if (name.Text == client.Name)
return true;
orderManager.IssueOrder(Order.Command("name " + name.Text));
Game.Settings.Player.Name = name.Text;
Game.Settings.Save();
return true;
};
name.OnLoseFocus = () => name.OnEnterKey();
var color = template.GetWidget<DropDownButtonWidget>("COLOR");
color.OnMouseDown = _ => ShowColorDropDown(color, client);
var colorBlock = color.GetWidget<ColorBlockWidget>("COLORBLOCK");
colorBlock.GetColor = () => client.ColorRamp.GetColor(0);
var status = template.GetWidget<CheckboxWidget>("STATUS");
status.IsChecked = () => client.State == Session.ClientState.Ready;
status.OnClick += CycleReady;
}
// Non-editable spectator
else
{
template = RemoteSpectatorTemplate.Clone();
template.GetWidget<LabelWidget>("NAME").GetText = () => client.Name;
var color = template.GetWidget<ColorBlockWidget>("COLOR");
color.GetColor = () => client.ColorRamp.GetColor(0);
var status = template.GetWidget<CheckboxWidget>("STATUS");
status.IsChecked = () => client.State == Session.ClientState.Ready;
if (client.Index == orderManager.LocalClient.Index)
status.OnClick += CycleReady;
var kickButton = template.GetWidget<ButtonWidget>("KICK");
kickButton.IsVisible = () => Game.IsHost && client.Index != orderManager.LocalClient.Index;
kickButton.OnMouseUp = mi =>
{
orderManager.IssueOrder(Order.Command("kick " + client.Index));
return true;
};
}
template.IsVisible = () => true;
Players.AddChild(template);
}
// Spectate button
if (orderManager.LocalClient.Slot != null && orderManager.LocalClient.State != Session.ClientState.Ready)
{
var spec = NewSpectatorTemplate.Clone();
var btn = spec.GetWidget<ButtonWidget>("SPECTATE");
btn.OnMouseUp = _ => { orderManager.IssueOrder(Order.Command("spectate")); return true; };
spec.IsVisible = () => true;
Players.AddChild(spec);
}
}
bool SpawnPointAvailable(int index) { return (index == 0) || orderManager.LobbyInfo.Clients.All(c => c.SpawnPoint != index); }