From 030bd4b28d19d2a3bce7d4d82748999849ef6bd7 Mon Sep 17 00:00:00 2001 From: geckosoft Date: Sun, 31 Oct 2010 03:57:03 +0100 Subject: [PATCH] Core: Added basic support for Spectators TODO: Someone modify the files for cnc (chrome / rules) --- OpenRA.Game/Network/Session.cs | 2 +- OpenRA.Game/Player.cs | 2 +- OpenRA.Game/Server/Server.cs | 20 +- OpenRA.Game/Traits/Selectable.cs | 2 +- .../Widgets/Delegates/LobbyDelegate.cs | 65 +++- OpenRA.Game/World.cs | 2 +- OpenRA.Game/WorldUtils.cs | 1 + OpenRA.Mods.RA/CreateMPPlayers.cs | 14 +- OpenRA.Mods.RA/HackyAI.cs | 7 +- OpenRA.Mods.RA/MPStartLocations.cs | 6 +- OpenRA.Mods.RA/OpenRA.Mods.RA.csproj | 3 +- OpenRA.Mods.RA/OpenWidgetAtGameStart.cs | 10 +- OpenRA.Mods.RA/SpawnMPUnits.cs | 2 +- mods/cnc/rules/system.yaml | 2 + mods/ra/chrome.xml | 1 + mods/ra/chrome/gamelobby.yaml | 331 ++++++++++-------- mods/ra/chrome/ingame.yaml | 130 +++++++ mods/ra/rules/system.yaml | 4 +- 18 files changed, 418 insertions(+), 186 deletions(-) diff --git a/OpenRA.Game/Network/Session.cs b/OpenRA.Game/Network/Session.cs index 111531e7c7..fe8cc8c12f 100644 --- a/OpenRA.Game/Network/Session.cs +++ b/OpenRA.Game/Network/Session.cs @@ -56,7 +56,7 @@ namespace OpenRA.Network public string Bot; // trait name of the bot to initialize in this slot, or null otherwise. public bool Closed; // host has explicitly closed this slot. public string MapPlayer; // playerReference to bind against. - + public bool Spectator = false; // Spectating or not // todo: more stuff? } diff --git a/OpenRA.Game/Player.cs b/OpenRA.Game/Player.cs index 01ebeef752..da4e41254d 100644 --- a/OpenRA.Game/Player.cs +++ b/OpenRA.Game/Player.cs @@ -78,7 +78,7 @@ namespace OpenRA PlayerName = client.Name; InternalName = pr.Name; Country = world.GetCountries() - .FirstOrDefault(c => client != null && client.Country == c.Race) + .FirstOrDefault(c => client != null && client.Country == c.Race ) ?? world.GetCountries().Random(world.SharedRandom); ClientIndex = client.Index; diff --git a/OpenRA.Game/Server/Server.cs b/OpenRA.Game/Server/Server.cs index d265e4edde..68f6d84081 100644 --- a/OpenRA.Game/Server/Server.cs +++ b/OpenRA.Game/Server/Server.cs @@ -40,6 +40,9 @@ namespace OpenRA.Server const int MasterPingInterval = 60 * 3; // 3 minutes. server has a 5 minute TTL for games, so give ourselves a bit // of leeway. + + public static int MaxSpectators = 4; // How many spectators to allow // @todo Expose this as an option + static int lastPing = 0; static bool isInternetServer; static string masterServerUrl; @@ -135,6 +138,12 @@ namespace OpenRA.Server .Where(s => s != null) .Select((s, i) => { s.Index = i; return s; }) .ToList(); + + // Generate slots for spectators + for (int i = 0; i < MaxSpectators; i++) + { + lobbyInfo.Slots.Add(new Session.Slot { Spectator = true, Index = lobbyInfo.Slots.Count(), MapPlayer = null, Bot = null}); + } } /* lobby rework todo: @@ -283,6 +292,8 @@ namespace OpenRA.Server static void SyncClientToPlayerReference(Session.Client c, PlayerReference pr) { + if (pr == null) + return; if (pr.LockColor) { c.Color1 = pr.Color; @@ -358,6 +369,13 @@ namespace OpenRA.Server SyncLobbyInfo(); return true; }}, + { "spectator", + s => + { + // GetClient(conn).Slot = -1; /* observer */ + SyncLobbyInfo(); + return true; + }}, { "team", s => { @@ -411,7 +429,7 @@ namespace OpenRA.Server var cl = GetClient(conn); cl.Slot = slot; - SyncClientToPlayerReference(cl, Map.Players[slotData.MapPlayer]); + SyncClientToPlayerReference(cl, slotData.MapPlayer != null ? Map.Players[slotData.MapPlayer] : null); SyncLobbyInfo(); return true; diff --git a/OpenRA.Game/Traits/Selectable.cs b/OpenRA.Game/Traits/Selectable.cs index 7fc25e78c2..3798ec31b4 100644 --- a/OpenRA.Game/Traits/Selectable.cs +++ b/OpenRA.Game/Traits/Selectable.cs @@ -158,7 +158,7 @@ namespace OpenRA.Traits void DrawUnitPath(Actor self) { - if (!self.World.LocalPlayer.PlayerActor.Trait().PathDebug) return; + if (self.World.LocalPlayer == null ||!self.World.LocalPlayer.PlayerActor.Trait().PathDebug) return; var activity = self.GetCurrentActivity(); var mobile = self.TraitOrDefault(); diff --git a/OpenRA.Game/Widgets/Delegates/LobbyDelegate.cs b/OpenRA.Game/Widgets/Delegates/LobbyDelegate.cs index 8263bb25d1..9e90602e02 100755 --- a/OpenRA.Game/Widgets/Delegates/LobbyDelegate.cs +++ b/OpenRA.Game/Widgets/Delegates/LobbyDelegate.cs @@ -19,7 +19,7 @@ namespace OpenRA.Widgets.Delegates { public class LobbyDelegate : IWidgetDelegate { - Widget Players, LocalPlayerTemplate, RemotePlayerTemplate, EmptySlotTemplate, EmptySlotTemplateHost; + Widget Players, LocalPlayerTemplate, RemotePlayerTemplate, EmptySlotTemplate, EmptySlotTemplateHost, EmptySpectatorSlotTemplateHost; Dictionary CountryNames; string MapUid; @@ -44,6 +44,7 @@ namespace OpenRA.Widgets.Delegates RemotePlayerTemplate = Players.GetWidget("TEMPLATE_REMOTE"); EmptySlotTemplate = Players.GetWidget("TEMPLATE_EMPTY"); EmptySlotTemplateHost = Players.GetWidget("TEMPLATE_EMPTY_HOST"); + EmptySpectatorSlotTemplateHost = Players.GetWidget("TEMPLATE_EMPTY_SPECTATOR"); var mapPreview = lobby.GetWidget("LOBBY_MAP_PREVIEW"); mapPreview.Map = () => Map; @@ -71,6 +72,7 @@ namespace OpenRA.Widgets.Delegates }; CountryNames = Rules.Info["world"].Traits.WithInterface().ToDictionary(a => a.Race, a => a.Name); + CountryNames.Add("random", "Random"); var mapButton = lobby.GetWidget("CHANGEMAP_BUTTON"); @@ -232,25 +234,48 @@ namespace OpenRA.Widgets.Delegates { if (Game.IsHost) { - template = EmptySlotTemplateHost.Clone(); - var name = template.GetWidget("NAME"); - name.GetText = () => s.Closed ? "Closed" : (s.Bot == null)? "Open" : "Bot: " + s.Bot; - name.OnMouseUp = _ => + if (slot.Spectator) { - if (s.Closed) + template = EmptySlotTemplateHost.Clone(); + var btn = template.GetWidget("JOIN"); + var name = template.GetWidget("NAME"); + btn.GetText = () => "Spectate in this slot"; + name.GetText = () => s.Closed ? "Closed" : "Open"; + name.OnMouseUp = _ => { - s.Bot = null; - orderManager.IssueOrder(Order.Command("slot_open " + s.Index)); - } - else - { - if (s.Bot == null && Map.Players[s.MapPlayer].AllowBots) - orderManager.IssueOrder(Order.Command("slot_bot {0} HackyAI".F(s.Index))); + if (s.Closed) + { + orderManager.IssueOrder(Order.Command("slot_open " + s.Index)); + } else + { orderManager.IssueOrder(Order.Command("slot_close " + s.Index)); - } - return true; - }; + } + return true; + }; + } + else + { + template = EmptySlotTemplateHost.Clone(); + var name = template.GetWidget("NAME"); + name.GetText = () => s.Closed ? "Closed" : (s.Bot == null) ? "Open" : "Bot: " + s.Bot; + name.OnMouseUp = _ => + { + if (s.Closed) + { + s.Bot = null; + orderManager.IssueOrder(Order.Command("slot_open " + s.Index)); + } + else + { + if (s.Bot == null && Map.Players[s.MapPlayer].AllowBots) + orderManager.IssueOrder(Order.Command("slot_bot {0} HackyAI".F(s.Index))); + else + orderManager.IssueOrder(Order.Command("slot_close " + s.Index)); + } + return true; + }; + } } else { @@ -330,6 +355,14 @@ namespace OpenRA.Widgets.Delegates var status = template.GetWidget("STATUS"); status.Checked = () => c.State == Session.ClientState.Ready; status.OnMouseDown = CycleReady; + + 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; } else { diff --git a/OpenRA.Game/World.cs b/OpenRA.Game/World.cs index 4ad76473d6..30dc027bf6 100644 --- a/OpenRA.Game/World.cs +++ b/OpenRA.Game/World.cs @@ -39,7 +39,7 @@ namespace OpenRA public void AddPlayer(Player p) { players[p.Index] = p; } - int localPlayerIndex; + int localPlayerIndex = -999; public Player LocalPlayer { get { return players.ContainsKey(localPlayerIndex) ? players[localPlayerIndex] : null; } diff --git a/OpenRA.Game/WorldUtils.cs b/OpenRA.Game/WorldUtils.cs index 1cfc018262..6f402914b8 100755 --- a/OpenRA.Game/WorldUtils.cs +++ b/OpenRA.Game/WorldUtils.cs @@ -103,6 +103,7 @@ namespace OpenRA public static bool IsVisible(this Actor a, Player byPlayer) /* must never be relied on in synced code! */ { + if (byPlayer == null) return true; // Observer if (a.World.LocalPlayer != null && a.World.LocalPlayer.Shroud.Disabled) return true; diff --git a/OpenRA.Mods.RA/CreateMPPlayers.cs b/OpenRA.Mods.RA/CreateMPPlayers.cs index 52b859a98a..2ead882f7c 100644 --- a/OpenRA.Mods.RA/CreateMPPlayers.cs +++ b/OpenRA.Mods.RA/CreateMPPlayers.cs @@ -36,7 +36,7 @@ namespace OpenRA.Mods.RA // create the players which are bound through slots. foreach (var slot in w.LobbyInfo.Slots) { - var client = w.LobbyInfo.Clients.FirstOrDefault(c => c.Slot == slot.Index); + var client = w.LobbyInfo.Clients.FirstOrDefault(c => c.Slot == slot.Index && slot.MapPlayer != null); if (client != null) { /* spawn a real player in this slot. */ @@ -45,7 +45,7 @@ namespace OpenRA.Mods.RA if (client.Index == Game.LocalClientId) w.SetLocalPlayer(player.Index); // bind this one to the local player. } - else if (slot.Bot != null) + else if (slot.Bot != null && slot.MapPlayer != null) { /* spawn a bot in this slot, "owned" by the host */ @@ -80,6 +80,14 @@ namespace OpenRA.Mods.RA static Stance ChooseInitialStance(Player p, Player q) { if (p == q) return Stance.Ally; + var pc = GetClientForPlayer(p); + var qc = GetClientForPlayer(q); + + 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.Allies.Contains(q.InternalName)) return Stance.Ally; @@ -92,8 +100,6 @@ namespace OpenRA.Mods.RA if (p.IsBot ^ q.IsBot) return Stance.Enemy; // bots and humans hate each other - var pc = GetClientForPlayer(p); - var qc = GetClientForPlayer(q); return pc.Team != 0 && pc.Team == qc.Team ? Stance.Ally : Stance.Enemy; diff --git a/OpenRA.Mods.RA/HackyAI.cs b/OpenRA.Mods.RA/HackyAI.cs index 0930606016..69938874ff 100644 --- a/OpenRA.Mods.RA/HackyAI.cs +++ b/OpenRA.Mods.RA/HackyAI.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; using System.Linq; +using OpenRA.Network; using OpenRA.Traits; using XRandom = OpenRA.Thirdparty.Random; using OpenRA.FileFormats; @@ -232,6 +233,10 @@ namespace OpenRA.Mods.RA return !hackyAI.enabled; } + bool HasHumanPlayers() + { + return p.World.players.Any(a => !a.Value.IsBot && !a.Value.NonCombatant); + } int2? ChooseEnemyTarget() { // Criteria for picking an enemy: @@ -240,7 +245,7 @@ namespace OpenRA.Mods.RA // 3. not dead. var possibleTargets = world.WorldActor.Trait().Start - .Where(kv => kv.Key != p && IsHumanPlayer(kv.Key) + .Where(kv => kv.Key != p && (!HasHumanPlayers()|| IsHumanPlayer(kv.Key)) && p.WinState == WinState.Undefined) .Select(kv => kv.Value); diff --git a/OpenRA.Mods.RA/MPStartLocations.cs b/OpenRA.Mods.RA/MPStartLocations.cs index 713bab2b37..eec0bd460f 100755 --- a/OpenRA.Mods.RA/MPStartLocations.cs +++ b/OpenRA.Mods.RA/MPStartLocations.cs @@ -28,13 +28,15 @@ namespace OpenRA.Mods.RA public void WorldLoaded(World world) { - var taken = world.LobbyInfo.Clients.Where(c => c.SpawnPoint != 0) + var taken = world.LobbyInfo.Clients.Where(c => c.SpawnPoint != 0 && c.Slot != -1) .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) { + if (slot.Spectator) + continue; // Skip spectator slots var client = world.LobbyInfo.Clients.FirstOrDefault(c => c.Slot == slot.Index); var player = FindPlayerInSlot(world, slot); @@ -49,7 +51,7 @@ namespace OpenRA.Mods.RA // Explore allied shroud foreach (var p in Start) - if (p.Key == world.LocalPlayer || p.Key.Stances[world.LocalPlayer] == Stance.Ally) + if ((world.LocalPlayer != null ) &&(p.Key == world.LocalPlayer || p.Key.Stances[world.LocalPlayer] == Stance.Ally)) world.WorldActor.Trait().Explore(world, p.Value, world.WorldActor.Info.Traits.Get().InitialExploreRange); diff --git a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj index 0d956ad228..19940586ef 100644 --- a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj +++ b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj @@ -3,7 +3,7 @@ Debug AnyCPU - 9.0.21022 + 9.0.30729 2.0 {4A8A43B5-A9EF-4ED0-99DD-4BAB10A0DB6E} Library @@ -230,6 +230,7 @@ + diff --git a/OpenRA.Mods.RA/OpenWidgetAtGameStart.cs b/OpenRA.Mods.RA/OpenWidgetAtGameStart.cs index 2ad30c0650..ebe62612a6 100644 --- a/OpenRA.Mods.RA/OpenWidgetAtGameStart.cs +++ b/OpenRA.Mods.RA/OpenWidgetAtGameStart.cs @@ -14,8 +14,9 @@ namespace OpenRA.Mods.RA { public class OpenWidgetAtGameStartInfo : ITraitInfo { - public readonly string Widget = "INGAME_ROOT"; - + public readonly string Widget = "INGAME_ROOT"; + public readonly string ObserverWidget = ""; + public object Create(ActorInitializer init) { return new OpenWidgetAtGameStart(this); } } @@ -29,9 +30,10 @@ namespace OpenRA.Mods.RA public void WorldLoaded(World world) { - // Todo: custom observer ui? if (world.LocalPlayer != null) - world.OpenWindow(Info.Widget); + world.OpenWindow(Info.Widget); + else if (Info.ObserverWidget != null) + world.OpenWindow(Info.ObserverWidget); } } } \ No newline at end of file diff --git a/OpenRA.Mods.RA/SpawnMPUnits.cs b/OpenRA.Mods.RA/SpawnMPUnits.cs index 294994fc00..7e25bc02a5 100644 --- a/OpenRA.Mods.RA/SpawnMPUnits.cs +++ b/OpenRA.Mods.RA/SpawnMPUnits.cs @@ -26,7 +26,7 @@ namespace OpenRA.Mods.RA void SpawnUnitsForPlayer(Player p, int2 sp) { if (!p.PlayerRef.DefaultStartingUnits) - return; /* they don't want an mcv, the map provides something else for them. */ + return; /* they don't want an mcv, the map provides something else for them OR it is a spectator. */ p.World.CreateActor("mcv", new TypeDictionary { diff --git a/mods/cnc/rules/system.yaml b/mods/cnc/rules/system.yaml index 2f4d10df21..9428f8fdd4 100644 --- a/mods/cnc/rules/system.yaml +++ b/mods/cnc/rules/system.yaml @@ -71,6 +71,8 @@ Player: World: OpenWidgetAtGameStart: + Widget: INGAME_ROOT +# ObserverWidget: ScreenShaker: NukePaletteEffect: WaterPaletteRotation: diff --git a/mods/ra/chrome.xml b/mods/ra/chrome.xml index 0f4e28a6b8..de56a921f1 100644 --- a/mods/ra/chrome.xml +++ b/mods/ra/chrome.xml @@ -193,6 +193,7 @@ + diff --git a/mods/ra/chrome/gamelobby.yaml b/mods/ra/chrome/gamelobby.yaml index 9621d82735..96d22546f9 100644 --- a/mods/ra/chrome/gamelobby.yaml +++ b/mods/ra/chrome/gamelobby.yaml @@ -29,175 +29,204 @@ Background@SERVER_LOBBY: Y:4 Width:244 Height:244 - Container@PLAYERS: - Id:PLAYERS + ListBox@PLAYERSX: + Id:PLAYERSX X:20 Y:75 Width:500 - Height:200 + Height:227 Children: - Container@TEMPLATE_LOCAL: - Id:TEMPLATE_LOCAL - X:0 - Y:0 + Container@PLAYERS: + Id:PLAYERS + X:2 + Y:1 Width:500 - Height:30 - Visible:false + Height:2270 Children: - TextField@NAME: - Id:NAME - Text:Name - Width:139 - Height:25 + Container@TEMPLATE_LOCAL: + Id:TEMPLATE_LOCAL X:0 Y:0 - MaxLength:16 - Button@COLOR: - Id:COLOR - Width:65 - Height:25 - X:159 - Y:0 + Width:400 + Height:30 + Visible:false Children: - ColorBlock@COLORBLOCK: - Id:COLORBLOCK - X:5 + TextField@NAME: + Id:NAME + Text:Name + Width:139 + Height:25 + X:0 + Y:0 + MaxLength:16 + Button@COLOR: + Id:COLOR + Width:65 + Height:25 + X:159 + Y:0 + Children: + ColorBlock@COLORBLOCK: + Id:COLORBLOCK + X:5 + Y:7 + Width:PARENT_RIGHT-10 + Height:PARENT_BOTTOM-12 + Button@FACTION: + Id:FACTION + Width:110 + Height:25 + X:244 + Y:0 + Children: + Image@FACTIONFLAG: + Id:FACTIONFLAG + Width:30 + Height:15 + X:5 + Y:5 + Label@FACTIONNAME: + Id:FACTIONNAME + Text:Faction + Width:60 + Height:25 + X:40 + Y:0 + Button@TEAM: + Id:TEAM + Text:Team + Width:25 + Height:25 + X:374 + Y:0 + Checkbox@STATUS: + Id:STATUS + X:450 + Y:2 + Width:20 + Height:20 + Container@TEMPLATE_REMOTE: + Id:TEMPLATE_REMOTE + X:0 + Y:0 + Width:400 + Height:30 + Visible:false + Children: + Label@NAME: + Id:NAME + Text:Name + Width:139 + Height:25 + X:0 + Y:0 + ColorBlock@COLOR: + Id:COLOR + X:164 Y:7 - Width:PARENT_RIGHT-10 - Height:PARENT_BOTTOM-12 - Button@FACTION: - Id:FACTION - Width:110 - Height:25 - X:244 - Y:0 - Children: - Image@FACTIONFLAG: - Id:FACTIONFLAG - Width:30 - Height:15 - X:5 - Y:5 - Label@FACTIONNAME: - Id:FACTIONNAME - Text:Faction - Width:60 + Width:55 + Height:13 + Label@FACTION: + Id:FACTION + Width:110 Height:25 - X:40 + X:244 Y:0 - Button@TEAM: - Id:TEAM - Text:Team - Width:25 - Height:25 - X:374 - Y:0 - Checkbox@STATUS: - Id:STATUS - X:455 - Y:2 - Width:20 - Height:20 - Container@TEMPLATE_REMOTE: - Id:TEMPLATE_REMOTE - X:0 - Y:0 - Width:500 - Height:30 - Visible:false - Children: - Label@NAME: - Id:NAME - Text:Name - Width:139 - Height:25 - X:0 - Y:0 - ColorBlock@COLOR: - Id:COLOR - X:164 - Y:7 - Width:55 - Height:13 - Label@FACTION: - Id:FACTION - Width:110 - Height:25 - X:244 - Y:0 - Children: - Image@FACTIONFLAG: - Id:FACTIONFLAG - Width:30 - Height:15 - X:5 - Y:5 - Label@FACTIONNAME: - Id:FACTIONNAME - Text:Faction - Width:60 + Children: + Image@FACTIONFLAG: + Id:FACTIONFLAG + Width:30 + Height:15 + X:5 + Y:5 + Label@FACTIONNAME: + Id:FACTIONNAME + Text:Faction + Width:60 + Height:25 + X:40 + Y:0 + Label@TEAM: + Id:TEAM + Text:Team + Width:70 Height:25 - X:40 + X:351 Y:0 - Label@TEAM: - Id:TEAM - Text:Team - Width:70 - Height:25 - X:351 - Y:0 - Align:Center - Bold: false - Checkbox@STATUS: - Id:STATUS - X:455 - Y:2 - Width:20 - Height:20 - Container@TEMPLATE_EMPTY: - Id:TEMPLATE_EMPTY - X:0 - Y:0 - Width:500 - Height:30 - Visible:false - Children: - Label@NAME: - Id:NAME - Text:Name - Width:139 - Height:25 + Align:Center + Bold: false + Checkbox@STATUS: + Id:STATUS + X:455 + Y:2 + Width:20 + Height:20 + Container@TEMPLATE_EMPTY_SPECTATOR: + Id:TEMPLATE_EMPTY_SPECTATOR X:0 Y:0 - Button@JOIN: - Id:JOIN - Text:Play in this slot - Width:PARENT_RIGHT - 160 - Height:25 - X:160 - Y:0 - Container@TEMPLATE_EMPTY_HOST: - Id:TEMPLATE_EMPTY_HOST - X:0 - Y:0 - Width:500 - Height:30 - Visible:false - Children: - Button@NAME: -- TODO: replace with dropdown - Id:NAME - Text:Name - Width:155 - Height:25 + Width:400 + Height:30 + Visible:false + Children: + Button@NAME: + Id:NAME + Text:Name + Width:139 + Height:25 + X:0 + Y:0 + Button@JOIN: + Id:JOIN + Text:Spectate this match + Width:PARENT_RIGHT - 90 + Height:25 + X:160 + Y:0 + Container@TEMPLATE_EMPTY: + Id:TEMPLATE_EMPTY X:0 Y:0 - Button@JOIN: - Id:JOIN - Text:Play in this slot - Width:PARENT_RIGHT - 160 - Height:25 - X:160 + Width:400 + Height:30 + Visible:false + Children: + Label@NAME: + Id:NAME + Text:Name + Width:139 + Height:25 + X:0 + Y:0 + Button@JOIN: + Id:JOIN + Text:Play in this slot + Width:PARENT_RIGHT - 90 + Height:25 + X:160 + Y:0 + Container@TEMPLATE_EMPTY_HOST: + Id:TEMPLATE_EMPTY_HOST + X:0 Y:0 + Width:400 + Height:30 + Visible:false + Children: + Button@NAME: -- TODO: replace with dropdown + Id:NAME + Text:Name + Width:155 + Height:25 + X:0 + Y:0 + Button@JOIN: + Id:JOIN + Text:Play in this slot + Width:PARENT_RIGHT - 90 + Height:25 + X:160 + Y:0 Container@LABEL_CONTAINER: X:30 Y:45 @@ -240,7 +269,7 @@ Background@SERVER_LOBBY: Bold:True Label@LABEL_LOBBY_STATUS: Id:LABEL_LOBBY_STATUS - X:432 + X:420 Y:0 Width:70 Height:25 diff --git a/mods/ra/chrome/ingame.yaml b/mods/ra/chrome/ingame.yaml index fb18978a07..cca7f18b34 100644 --- a/mods/ra/chrome/ingame.yaml +++ b/mods/ra/chrome/ingame.yaml @@ -284,3 +284,133 @@ Container@INGAME_ROOT: Width:200 Height:20 Text: Give Exploration +Container@OBSERVER_ROOT: + Id:OBSERVER_ROOT + Visible:true + Delegate:IngameObserverChromeDelegate + Children: + WorldInteractionController: + X:0 + Y:0 + Width:WINDOW_RIGHT + Height:WINDOW_BOTTOM + ViewportScrollController: + X:0 + Y:0 + Width:WINDOW_RIGHT + Height:WINDOW_BOTTOM + Timer@GAME_TIMER: + Id:GAME_TIMER + X: WINDOW_RIGHT/2 + Y: 10 + Background@POSTGAME_BG: + Id:POSTGAME_BG + X:(WINDOW_RIGHT - WIDTH)/2 + Y:(WINDOW_BOTTOM - HEIGHT)/2 + Width:400 + Height:100 + Background:dialog4 + Visible:false + Children: + Label@TEXT: + Id:TEXT + X:(PARENT_RIGHT - WIDTH)/2 + Y:(PARENT_BOTTOM - HEIGHT)/2 + Width:200 + Height:40 + Align:Center + Bold:True + SpecialPowerBin@INGAME_POWERS_BIN: + Id:INGAME_POWERS_BIN + X:0 + Y:25 + Button@INGAME_OPTIONS_BUTTON: + Id:INGAME_OPTIONS_BUTTON + X:0 + Y:0 + Width:160 + Height:25 + Text:Options + Bold:True + WorldTooltip: + Background@INGAME_OPTIONS_BG: + Id:INGAME_OPTIONS_BG + X:(WINDOW_RIGHT - WIDTH)/2 + Y:(WINDOW_BOTTOM - HEIGHT)/2 + Width:300 + Height:320 + Visible:false + Children: + Label@LABEL_TITLE: + Id:LABEL_TITLE + X:(PARENT_RIGHT - WIDTH)/2 + Y:20 + Width:250 + Height:25 + Text:Options + Align:Center + Bold:True + Button@RESUME: + Id:RESUME + X:(PARENT_RIGHT - WIDTH)/2 + Y:60 + Width:160 + Height:25 + Text:Resume + Bold:True + Button@SETTINGS: + Id:SETTINGS + X:(PARENT_RIGHT - WIDTH)/2 + Y:100 + Width:160 + Height:25 + Text:Settings + Bold:True + Button@MUSIC: + Id:MUSIC + X:(PARENT_RIGHT - WIDTH)/2 + Y:140 + Width:160 + Height:25 + Text:Music + Bold:True + Button@SURRENDER: + Id:SURRENDER + X:(PARENT_RIGHT - WIDTH)/2 + Y:180 + Width:160 + Height:25 + Text:Surrender + Bold:True + Button@DISCONNECT: + Id:DISCONNECT + X:(PARENT_RIGHT - WIDTH)/2 + Y:220 + Width:160 + Height:25 + Text:Disconnect + Bold:True + Button@QUIT: + Id:QUIT + X:(PARENT_RIGHT - WIDTH)/2 + Y:260 + Width:160 + Height:25 + Text:Quit + Bold:True + ChatDisplay@CHAT_DISPLAY: + Id:CHAT_DISPLAY + X:250 + Y:WINDOW_BOTTOM - HEIGHT - 30 + Width: 760 + Height: 200 + ClickThrough: True + DrawBackground: False + RemoveTime:250 + ChatEntry@CHAT_ENTRY: + Id:CHAT_ENTRY + X:250 + Y:WINDOW_BOTTOM - HEIGHT + Width: 760 + Height: 30 + ClickThrough: True diff --git a/mods/ra/rules/system.yaml b/mods/ra/rules/system.yaml index 90cd719523..4f6dbec0dd 100644 --- a/mods/ra/rules/system.yaml +++ b/mods/ra/rules/system.yaml @@ -125,8 +125,10 @@ Player: PlayerColorPalette: BasePalette: terrain -World: +World: OpenWidgetAtGameStart: + Widget: INGAME_ROOT + ObserverWidget: OBSERVER_ROOT ScreenShaker: WaterPaletteRotation: ChronoshiftPaletteEffect: