Merge pull request #4901 from pchote/async-maps

Asynchronous map downloading.
This commit is contained in:
Matthias Mailänder
2014-03-21 12:01:11 +01:00
28 changed files with 1153 additions and 468 deletions

View File

@@ -27,6 +27,9 @@ NEW:
Fixed the game sometimes crashing when deploying and activating the guard cursor at the same time.
Build time is now set when an item reaches the front of a queue, instead of immediately when queued.
The attack cursor now changes if the target is out of range.
The server browser will now show map details from other mods if the maps are available online.
Fixed a crash when connecting to a server with an unavailable map.
Added a warning dialog when force starting a match.
Dune 2000:
Added the Atreides grenadier from the 1.06 patch.
Added randomized tiles for Sand and Rock terrain.

View File

@@ -531,46 +531,5 @@ namespace OpenRA
{
return orderManager != null && orderManager.world == world;
}
public static bool DownloadMap(string mapHash)
{
var mod = Game.modData.Manifest.Mod;
var dirPath = new[] { Platform.SupportDir, "maps", mod.Id }.Aggregate(Path.Combine);
var tempFile = Path.Combine(dirPath, Path.GetRandomFileName());
if (!Directory.Exists(dirPath))
Directory.CreateDirectory(dirPath);
foreach (var MapRepository in Game.Settings.Game.MapRepositories)
{
try
{
var url = MapRepository + mapHash;
var request = WebRequest.Create(url);
request.Method = "HEAD";
var res = request.GetResponse();
if (res.Headers["Content-Disposition"] == null)
continue;
var mapPath = Path.Combine(dirPath, res.Headers ["Content-Disposition"].Replace("attachment; filename = ", ""));
Log.Write("debug", "Trying to download map to '{0}' using {1}", mapPath, MapRepository);
WebClient webClient = new WebClient();
webClient.DownloadFile(url, tempFile);
File.Move(tempFile, mapPath);
Game.modData.MapCache[mapHash].UpdateFromMap(new Map(mapPath));
Log.Write("debug", "New map has been downloaded to '{0}'", mapPath);
return true;
}
catch (WebException e)
{
Log.Write("debug", "Could not download map '{0}' using {1}", mapHash, MapRepository);
Log.Write("debug", e.ToString());
File.Delete(tempFile);
continue;
}
}
return false;
}
}
}

View File

@@ -136,7 +136,7 @@ namespace OpenRA.GameRules
public bool TeamHealthColors = false;
public bool AllowDownloading = true;
public string[] MapRepositories = { "http://resource.openra.net/map/", "http://resource.ihptru.net:8080/map/" };
public string MapRepository = "http://resource.openra.net/map/";
}
public class KeySettings

View File

@@ -14,10 +14,11 @@ using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using OpenRA.FileFormats;
using OpenRA.Graphics;
using OpenRA.Widgets;
namespace OpenRA
{
@@ -57,6 +58,43 @@ namespace OpenRA
}
}
public void QueryRemoteMapDetails(IEnumerable<string> uids)
{
var maps = uids.Distinct()
.Select(uid => previews[uid])
.Where(p => p.Status == MapStatus.Unavailable)
.ToDictionary(p => p.Uid, p => p);
if (!maps.Any())
return;
foreach (var p in maps.Values)
p.UpdateRemoteSearch(MapStatus.Searching, null);
var url = Game.Settings.Game.MapRepository + "hash/" + string.Join(",", maps.Keys.ToArray()) + "/yaml";
Action<DownloadDataCompletedEventArgs, bool> onInfoComplete = (i, cancelled) =>
{
if (cancelled || i.Error != null)
{
Log.Write("debug", "Remote map query failed with error: {0}", i.Error != null ? i.Error.Message : "cancelled");
Log.Write("debug", "URL was: {0}", url);
foreach (var p in maps.Values)
p.UpdateRemoteSearch(MapStatus.Unavailable, null);
return;
}
var data = Encoding.UTF8.GetString(i.Result);
var yaml = MiniYaml.FromString(data);
foreach (var kv in yaml)
maps[kv.Key].UpdateRemoteSearch(MapStatus.DownloadAvailable, kv.Value);
};
new Download(url, _ => { }, onInfoComplete);
}
public static IEnumerable<string> FindMapsIn(string dir)
{
string[] noMaps = { };

View File

@@ -11,9 +11,11 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading;
using OpenRA.FileFormats;
using OpenRA.Graphics;
@@ -21,10 +23,26 @@ using OpenRA.Widgets;
namespace OpenRA
{
public enum MapStatus { Available, Unavailable }
public enum MapStatus { Available, Unavailable, Searching, DownloadAvailable, Downloading, DownloadError }
// Fields names must match the with the remote API
public class RemoteMapData
{
public readonly string title;
public readonly string author;
public readonly string map_type;
public readonly int players;
public readonly Rectangle bounds;
public readonly int[] spawnpoints = {};
public readonly string minimap;
public readonly bool downloading;
public readonly bool requires_upgrade;
}
public class MapPreview
{
static readonly List<CPos> NoSpawns = new List<CPos>();
MapCache cache;
public readonly string Uid;
public string Title { get; private set; }
@@ -37,6 +55,10 @@ namespace OpenRA
public Map Map { get; private set; }
public MapStatus Status { get; private set; }
Download download;
public long DownloadBytes { get; private set; }
public int DownloadPercentage { get; private set; }
Sprite minimap;
bool generatingMinimap;
public Sprite Minimap
@@ -62,7 +84,6 @@ namespace OpenRA
}
}
MapCache cache;
public MapPreview(string uid, MapCache cache)
{
this.cache = cache;
@@ -89,5 +110,116 @@ namespace OpenRA
CustomPreview = m.CustomPreview;
Status = MapStatus.Available;
}
public void UpdateRemoteSearch(MapStatus status, MiniYaml yaml)
{
// Update on the main thread to ensure consistency
Game.RunAfterTick(() =>
{
if (status == MapStatus.DownloadAvailable)
{
try
{
var r = FieldLoader.Load<RemoteMapData>(yaml);
// Map is not useable by the current version
if (!r.downloading || r.requires_upgrade)
{
Status = MapStatus.Unavailable;
return;
}
Title = r.title;
Type = r.map_type;
Author = r.author;
PlayerCount = r.players;
Bounds = r.bounds;
var spawns = new List<CPos>();
for (var j = 0; j < r.spawnpoints.Length; j += 2)
spawns.Add(new CPos(r.spawnpoints[j], r.spawnpoints[j+1]));
SpawnPoints = spawns;
CustomPreview = new Bitmap(new MemoryStream(Convert.FromBase64String(r.minimap)));
}
catch (Exception) {}
if (CustomPreview != null)
cache.CacheMinimap(this);
}
Status = status;
});
}
public void Install()
{
if (Status != MapStatus.DownloadAvailable || !Game.Settings.Game.AllowDownloading)
return;
Status = MapStatus.Downloading;
var baseMapPath = new[] { Platform.SupportDir, "maps", Game.modData.Manifest.Mod.Id }.Aggregate(Path.Combine);
// Create the map directory if it doesn't exist
if (!Directory.Exists(baseMapPath))
Directory.CreateDirectory(baseMapPath);
new Thread(() =>
{
// Request the filename from the server
// Run in a worker thread to avoid network delays
var mapUrl = Game.Settings.Game.MapRepository + Uid;
try
{
var request = WebRequest.Create(mapUrl);
request.Method = "HEAD";
var res = request.GetResponse();
// Map not found
if (res.Headers["Content-Disposition"] == null)
{
Status = MapStatus.DownloadError;
return;
}
var mapPath = Path.Combine(baseMapPath, res.Headers["Content-Disposition"].Replace("attachment; filename = ", ""));
Action<DownloadProgressChangedEventArgs> onDownloadProgress = i => { DownloadBytes = i.BytesReceived; DownloadPercentage = i.ProgressPercentage; };
Action<AsyncCompletedEventArgs, bool> onDownloadComplete = (i, cancelled) =>
{
download = null;
if (cancelled || i.Error != null)
{
Log.Write("debug", "Remote map download failed with error: {0}", i.Error != null ? i.Error.Message : "cancelled");
Log.Write("debug", "URL was: {0}", mapUrl);
Status = MapStatus.DownloadError;
return;
}
Log.Write("debug", "Downloaded map to '{0}'", mapPath);
Game.RunAfterTick(() => UpdateFromMap(new Map(mapPath)));
};
download = new Download(mapUrl, mapPath, onDownloadProgress, onDownloadComplete);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
Status = MapStatus.DownloadError;
}
}).Start();
}
public void CancelInstall()
{
if (download == null)
return;
download.Cancel();
download = null;
}
}
}

View File

@@ -81,7 +81,7 @@ namespace OpenRA.Network
get { return Clients.Count(c => c.Bot == null) == 1; }
}
public enum ClientState { NotReady, Ready, Disconnected = 1000 }
public enum ClientState { NotReady, Invalid, Ready, Disconnected = 1000 }
public class Client
{
@@ -93,13 +93,14 @@ namespace OpenRA.Network
public int SpawnPoint;
public string Name;
public string IpAddress;
public ClientState State;
public ClientState State = ClientState.Invalid;
public int Team;
public string Slot; // slot ID, or null for observer
public string Bot; // Bot type, null for real clients
public int BotControllerClientIndex; // who added the bot to the slot
public bool IsAdmin;
public bool IsReady { get { return State == ClientState.Ready; } }
public bool IsInvalid { get { return State == ClientState.Invalid; } }
public bool IsObserver { get { return Slot == null; } }
public int Latency = -1;
public int LatencyJitter = -1;

View File

@@ -130,7 +130,7 @@ namespace OpenRA.Network
Country = "random",
SpawnPoint = 0,
Team = 0,
State = Session.ClientState.NotReady
State = Session.ClientState.Invalid
};
var response = new HandshakeResponse()

View File

@@ -272,7 +272,7 @@ namespace OpenRA.Server
Country = "random",
SpawnPoint = 0,
Team = 0,
State = Session.ClientState.NotReady,
State = Session.ClientState.Invalid,
IsAdmin = !LobbyInfo.Clients.Any(c1 => c1.IsAdmin)
};
@@ -572,19 +572,31 @@ namespace OpenRA.Server
public void StartGame()
{
State = ServerState.GameStarted;
listener.Stop();
Console.WriteLine("Game started");
foreach (var c in Conns)
foreach (var d in Conns)
DispatchOrdersToClient(c, d.PlayerIndex, 0x7FFFFFFF, new byte[] { 0xBF });
// Drop any unvalidated clients
foreach (var c in PreConns.ToArray())
DropClient(c);
// Drop any players who are not ready
foreach (var c in Conns.ToArray())
{
if (GetClient(c).IsInvalid)
{
SendOrderTo(c, "ServerError", "You have been kicked from the server");
DropClient(c);
}
}
SyncLobbyInfo();
State = ServerState.GameStarted;
foreach (var c in Conns)
foreach (var d in Conns)
DispatchOrdersToClient(c, d.PlayerIndex, 0x7FFFFFFF, new byte[] { 0xBF });
DispatchOrders(null, 0,
new ServerOrder("StartGame", "").Serialize());

View File

@@ -8,6 +8,7 @@
*/
#endregion
using System;
using System.Drawing;
namespace OpenRA.Widgets
@@ -17,45 +18,57 @@ namespace OpenRA.Widgets
public int Percentage = 0;
public bool Indeterminate = false;
public Func<int> GetPercentage;
public Func<bool> IsIndeterminate;
// Indeterminant bar properties
float offset = 0f;
float tickStep = 0.04f;
public ProgressBarWidget() {}
protected ProgressBarWidget(ProgressBarWidget widget)
: base(widget)
public ProgressBarWidget()
{
Percentage = widget.Percentage;
GetPercentage = () => Percentage;
IsIndeterminate = () => Indeterminate;
}
protected ProgressBarWidget(ProgressBarWidget other)
: base(other)
{
Percentage = other.Percentage;
GetPercentage = other.GetPercentage;
IsIndeterminate = other.IsIndeterminate;
}
public override void Draw()
{
var rb = RenderBounds;
var percentage = GetPercentage();
WidgetUtils.DrawPanel("progressbar-bg", rb);
Rectangle barRect = Indeterminate ?
Rectangle barRect = wasIndeterminate ?
new Rectangle(rb.X + 2 + (int)(0.75*offset*(rb.Width - 4)), rb.Y + 2, (rb.Width - 4) / 4, rb.Height - 4) :
new Rectangle(rb.X + 2, rb.Y + 2, Percentage * (rb.Width - 4) / 100, rb.Height - 4);
new Rectangle(rb.X + 2, rb.Y + 2, percentage * (rb.Width - 4) / 100, rb.Height - 4);
if (barRect.Width > 0)
WidgetUtils.DrawPanel("progressbar-thumb", barRect);
}
bool wasIndeterminate;
public override void Tick()
{
if (Indeterminate)
var indeterminate = IsIndeterminate();
if (indeterminate != wasIndeterminate)
offset = 0f;
if (indeterminate)
{
offset += tickStep;
offset = offset.Clamp(0f, 1f);
if (offset == 0f || offset == 1f)
tickStep *= -1;
}
}
public void SetIndeterminate(bool value)
{
Indeterminate = value;
offset = 0f;
wasIndeterminate = indeterminate;
}
public override Widget Clone() { return new ProgressBarWidget(this); }

View File

@@ -487,6 +487,7 @@
<Compile Include="RemoveImmediately.cs" />
<Compile Include="Attack\AttackFollow.cs" />
<Compile Include="Attack\AttackGarrisoned.cs" />
<Compile Include="Widgets\Logic\LobbyMapPreviewLogic.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\OpenRA.FileFormats\OpenRA.FileFormats.csproj">

View File

@@ -44,7 +44,7 @@ namespace OpenRA.Mods.RA.Server
server.SendOrderTo(conn, "Message", "Cannot change state when game started. ({0})".F(cmd));
return false;
}
else if (client.State == Session.ClientState.Ready && !(cmd == "ready" || cmd == "startgame"))
else if (client.State == Session.ClientState.Ready && !(cmd.StartsWith("state") || cmd == "startgame"))
{
server.SendOrderTo(conn, "Message", "Cannot change state when marked as ready.");
return false;
@@ -75,14 +75,17 @@ namespace OpenRA.Mods.RA.Server
var dict = new Dictionary<string, Func<string, bool>>
{
{ "ready",
{ "state",
s =>
{
// if we're downloading, we can't ready up.
if (client.State == Session.ClientState.NotReady)
client.State = Session.ClientState.Ready;
else if (client.State == Session.ClientState.Ready)
client.State = Session.ClientState.NotReady;
var state = Session.ClientState.Invalid;
if (!Enum<Session.ClientState>.TryParse(s, false, out state))
{
server.SendOrderTo(conn, "Message", "Malformed state command");
return true;
}
client.State = state;
Log.Write("server", "Player @{0} is {1}",
conn.socket.RemoteEndPoint, client.State);
@@ -290,6 +293,10 @@ namespace OpenRA.Mods.RA.Server
LoadMap(server);
SetDefaultDifficulty(server);
// Reset client states
foreach (var c in server.LobbyInfo.Clients)
c.State = Session.ClientState.Invalid;
// Reassign players into new slots based on their old slots:
// - Observers remain as observers
// - Players who now lack a slot are made observers
@@ -303,7 +310,6 @@ namespace OpenRA.Mods.RA.Server
continue;
c.SpawnPoint = 0;
c.State = Session.ClientState.NotReady;
c.Slot = i < slots.Length ? slots[i++] : null;
if (c.Slot != null)
{

View File

@@ -43,7 +43,7 @@ namespace OpenRA.Mods.RA.Widgets.Logic
void ShowDownloadDialog()
{
statusLabel.GetText = () => "Initializing...";
progressBar.SetIndeterminate(true);
progressBar.Indeterminate = true;
var retryButton = panel.Get<ButtonWidget>("RETRY_BUTTON");
retryButton.IsVisible = () => false;
@@ -55,9 +55,7 @@ namespace OpenRA.Mods.RA.Widgets.Logic
Action<DownloadProgressChangedEventArgs> onDownloadProgress = i =>
{
if (progressBar.Indeterminate)
progressBar.SetIndeterminate(false);
progressBar.Indeterminate = false;
progressBar.Percentage = i.ProgressPercentage;
statusLabel.GetText = () => "Downloading {1}/{2} kB ({0}%)".F(i.ProgressPercentage, i.BytesReceived / 1024, i.TotalBytesToReceive / 1024);
};
@@ -91,7 +89,7 @@ namespace OpenRA.Mods.RA.Widgets.Logic
// Automatically extract
statusLabel.GetText = () => "Extracting...";
progressBar.SetIndeterminate(true);
progressBar.Indeterminate = true;
if (InstallUtils.ExtractZip(file, dest, onExtractProgress, onError))
{
Game.RunAfterTick(() =>

View File

@@ -23,12 +23,14 @@ namespace OpenRA.Mods.RA.Widgets.Logic
{
static readonly Action DoNothing = () => { };
public MapPreview Map = MapCache.UnknownMap;
readonly Action onStart;
readonly Action onExit;
readonly OrderManager orderManager;
readonly bool skirmishMode;
enum PanelType { Players, Options, Kick }
enum PanelType { Players, Options, Kick, ForceStart }
PanelType panel = PanelType.Players;
Widget lobby;
@@ -41,7 +43,6 @@ namespace OpenRA.Mods.RA.Widgets.Logic
ScrollPanelWidget players;
Dictionary<string, string> countryNames;
MapPreview preview = MapCache.UnknownMap;
ColorPreviewManagerWidget colorPreview;
@@ -108,6 +109,12 @@ namespace OpenRA.Mods.RA.Widgets.Logic
if (name != null)
name.GetText = () => orderManager.LobbyInfo.GlobalSettings.ServerName;
Ui.LoadWidget("LOBBY_MAP_PREVIEW", lobby.Get("MAP_PREVIEW_ROOT"), new WidgetArgs
{
{ "orderManager", orderManager },
{ "lobby", this }
});
UpdateCurrentMap();
players = Ui.LoadWidget<ScrollPanelWidget>("LOBBY_PLAYER_BIN", lobby.Get("PLAYER_BIN_ROOT"), new WidgetArgs());
players.IsVisible = () => panel == PanelType.Players;
@@ -125,30 +132,14 @@ namespace OpenRA.Mods.RA.Widgets.Logic
colorPreview = lobby.Get<ColorPreviewManagerWidget>("COLOR_MANAGER");
colorPreview.Color = Game.Settings.Player.Color;
var mapPreview = lobby.Get<MapPreviewWidget>("MAP_PREVIEW");
mapPreview.Preview = () => preview;
mapPreview.OnMouseDown = mi => LobbyUtils.SelectSpawnPoint(orderManager, mapPreview, preview, mi);
mapPreview.SpawnClients = () => LobbyUtils.GetSpawnClients(orderManager, preview);
var mapTitle = lobby.GetOrNull<LabelWidget>("MAP_TITLE");
if (mapTitle != null)
mapTitle.GetText = () => preview.Title;
var mapType = lobby.GetOrNull<LabelWidget>("MAP_TYPE");
if (mapType != null)
mapType.GetText = () => preview.Type;
var mapAuthor = lobby.GetOrNull<LabelWidget>("MAP_AUTHOR");
if (mapAuthor != null)
mapAuthor.GetText = () => "Created by {0}".F(preview.Author);
countryNames = Rules.Info["world"].Traits.WithInterface<CountryInfo>()
.Where(c => c.Selectable)
.ToDictionary(a => a.Race, a => a.Name);
countryNames.Add("random", "Any");
var gameStarting = false;
Func<bool> configurationDisabled = () => !Game.IsHost || gameStarting || panel == PanelType.Kick ||
Func<bool> configurationDisabled = () => !Game.IsHost || gameStarting ||
panel == PanelType.Kick || panel == PanelType.ForceStart ||
orderManager.LocalClient == null || orderManager.LocalClient.IsReady;
var mapButton = lobby.GetOrNull<ButtonWidget>("CHANGEMAP_BUTTON");
@@ -166,7 +157,7 @@ namespace OpenRA.Mods.RA.Widgets.Logic
Ui.OpenWindow("MAPCHOOSER_PANEL", new WidgetArgs()
{
{ "initialMap", preview.Uid },
{ "initialMap", Map.Uid },
{ "onExit", DoNothing },
{ "onSelect", onSelect }
});
@@ -271,10 +262,17 @@ namespace OpenRA.Mods.RA.Widgets.Logic
optionsBin.IsVisible = () => panel == PanelType.Options;
var optionsButton = lobby.Get<ButtonWidget>("OPTIONS_BUTTON");
optionsButton.IsDisabled = () => panel == PanelType.Kick;
optionsButton.IsDisabled = () => panel == PanelType.Kick || panel == PanelType.ForceStart;
optionsButton.GetText = () => panel == PanelType.Options ? "Players" : "Options";
optionsButton.OnClick = () => panel = (panel == PanelType.Options) ? PanelType.Players : PanelType.Options;
// Force start panel
Action startGame = () =>
{
gameStarting = true;
orderManager.IssueOrder(Order.Command("startgame"));
};
var startGameButton = lobby.GetOrNull<ButtonWidget>("START_GAME_BUTTON");
if (startGameButton != null)
{
@@ -282,25 +280,33 @@ namespace OpenRA.Mods.RA.Widgets.Logic
orderManager.LobbyInfo.Slots.Any(sl => sl.Value.Required && orderManager.LobbyInfo.ClientInSlot(sl.Key) == null);
startGameButton.OnClick = () =>
{
gameStarting = true;
orderManager.IssueOrder(Order.Command("startgame"));
Func<KeyValuePair<string, Session.Slot>, bool> notReady = sl =>
{
var cl = orderManager.LobbyInfo.ClientInSlot(sl.Key);
// Bots and admins don't count
return cl != null && !cl.IsAdmin && cl.Bot == null && !cl.IsReady;
};
if (orderManager.LobbyInfo.Slots.Any(notReady))
panel = PanelType.ForceStart;
else
startGame();
};
}
var statusCheckbox = lobby.GetOrNull<CheckboxWidget>("STATUS_CHECKBOX");
if (statusCheckbox != null)
{
statusCheckbox.IsHighlighted = () => !statusCheckbox.IsChecked() &&
orderManager.LobbyInfo.FirstEmptySlot() == null &&
orderManager.LocalFrameNumber / 25 % 2 == 0;
}
var forceStartBin = Ui.LoadWidget("FORCE_START_DIALOG", lobby, new WidgetArgs());
forceStartBin.IsVisible = () => panel == PanelType.ForceStart;
forceStartBin.Get("KICK_WARNING").IsVisible = () => orderManager.LobbyInfo.Clients.Any(c => c.IsInvalid);
forceStartBin.Get<ButtonWidget>("OK_BUTTON").OnClick = startGame;
forceStartBin.Get<ButtonWidget>("CANCEL_BUTTON").OnClick = () => panel = PanelType.Players;
// Options panel
var allowCheats = optionsBin.GetOrNull<CheckboxWidget>("ALLOWCHEATS_CHECKBOX");
if (allowCheats != null)
{
allowCheats.IsChecked = () => orderManager.LobbyInfo.GlobalSettings.AllowCheats;
allowCheats.IsDisabled = () => preview.Status != MapStatus.Available || preview.Map.Options.Cheats.HasValue || configurationDisabled();
allowCheats.IsDisabled = () => Map.Status != MapStatus.Available || Map.Map.Options.Cheats.HasValue || configurationDisabled();
allowCheats.OnClick = () => orderManager.IssueOrder(Order.Command(
"allowcheats {0}".F(!orderManager.LobbyInfo.GlobalSettings.AllowCheats)));
}
@@ -309,7 +315,7 @@ namespace OpenRA.Mods.RA.Widgets.Logic
if (crates != null)
{
crates.IsChecked = () => orderManager.LobbyInfo.GlobalSettings.Crates;
crates.IsDisabled = () => preview.Status != MapStatus.Available || preview.Map.Options.Crates.HasValue || configurationDisabled();
crates.IsDisabled = () => Map.Status != MapStatus.Available || Map.Map.Options.Crates.HasValue || configurationDisabled();
crates.OnClick = () => orderManager.IssueOrder(Order.Command(
"crates {0}".F(!orderManager.LobbyInfo.GlobalSettings.Crates)));
}
@@ -318,7 +324,7 @@ namespace OpenRA.Mods.RA.Widgets.Logic
if (allybuildradius != null)
{
allybuildradius.IsChecked = () => orderManager.LobbyInfo.GlobalSettings.AllyBuildRadius;
allybuildradius.IsDisabled = () => preview.Status != MapStatus.Available || preview.Map.Options.AllyBuildRadius.HasValue || configurationDisabled();
allybuildradius.IsDisabled = () => Map.Status != MapStatus.Available || Map.Map.Options.AllyBuildRadius.HasValue || configurationDisabled();
allybuildradius.OnClick = () => orderManager.IssueOrder(Order.Command(
"allybuildradius {0}".F(!orderManager.LobbyInfo.GlobalSettings.AllyBuildRadius)));
}
@@ -327,7 +333,7 @@ namespace OpenRA.Mods.RA.Widgets.Logic
if (fragileAlliance != null)
{
fragileAlliance.IsChecked = () => orderManager.LobbyInfo.GlobalSettings.FragileAlliances;
fragileAlliance.IsDisabled = () => preview.Status != MapStatus.Available || preview.Map.Options.FragileAlliances.HasValue || configurationDisabled();
fragileAlliance.IsDisabled = () => Map.Status != MapStatus.Available || Map.Map.Options.FragileAlliances.HasValue || configurationDisabled();
fragileAlliance.OnClick = () => orderManager.IssueOrder(Order.Command(
"fragilealliance {0}".F(!orderManager.LobbyInfo.GlobalSettings.FragileAlliances)));
}
@@ -335,12 +341,12 @@ namespace OpenRA.Mods.RA.Widgets.Logic
var difficulty = optionsBin.GetOrNull<DropDownButtonWidget>("DIFFICULTY_DROPDOWNBUTTON");
if (difficulty != null)
{
difficulty.IsVisible = () => preview.Status == MapStatus.Available && preview.Map.Options.Difficulties.Any();
difficulty.IsDisabled = () => preview.Status != MapStatus.Available || configurationDisabled();
difficulty.IsVisible = () => Map.Status == MapStatus.Available && Map.Map.Options.Difficulties.Any();
difficulty.IsDisabled = () => Map.Status != MapStatus.Available || configurationDisabled();
difficulty.GetText = () => orderManager.LobbyInfo.GlobalSettings.Difficulty;
difficulty.OnMouseDown = _ =>
{
var options = preview.Map.Options.Difficulties.Select(d => new DropDownOption
var options = Map.Map.Options.Difficulties.Select(d => new DropDownOption
{
Title = d,
IsSelected = () => orderManager.LobbyInfo.GlobalSettings.Difficulty == d,
@@ -372,8 +378,8 @@ namespace OpenRA.Mods.RA.Widgets.Logic
var classes = Rules.Info["world"].Traits.WithInterface<MPStartUnitsInfo>()
.Select(a => a.Class).Distinct();
startingUnits.IsDisabled = () => preview.Status != MapStatus.Available || !preview.Map.Options.ConfigurableStartingUnits || configurationDisabled();
startingUnits.GetText = () => preview.Status != MapStatus.Available || !preview.Map.Options.ConfigurableStartingUnits ? "Not Available" : className(orderManager.LobbyInfo.GlobalSettings.StartingUnitsClass);
startingUnits.IsDisabled = () => Map.Status != MapStatus.Available || !Map.Map.Options.ConfigurableStartingUnits || configurationDisabled();
startingUnits.GetText = () => Map.Status != MapStatus.Available || !Map.Map.Options.ConfigurableStartingUnits ? "Not Available" : className(orderManager.LobbyInfo.GlobalSettings.StartingUnitsClass);
startingUnits.OnMouseDown = _ =>
{
var options = classes.Select(c => new DropDownOption
@@ -399,8 +405,8 @@ namespace OpenRA.Mods.RA.Widgets.Logic
var startingCash = optionsBin.GetOrNull<DropDownButtonWidget>("STARTINGCASH_DROPDOWNBUTTON");
if (startingCash != null)
{
startingCash.IsDisabled = () => preview.Status != MapStatus.Available || preview.Map.Options.StartingCash.HasValue || configurationDisabled();
startingCash.GetText = () => preview.Status != MapStatus.Available || preview.Map.Options.StartingCash.HasValue ? "Not Available" : "${0}".F(orderManager.LobbyInfo.GlobalSettings.StartingCash);
startingCash.IsDisabled = () => Map.Status != MapStatus.Available || Map.Map.Options.StartingCash.HasValue || configurationDisabled();
startingCash.GetText = () => Map.Status != MapStatus.Available || Map.Map.Options.StartingCash.HasValue ? "Not Available" : "${0}".F(orderManager.LobbyInfo.GlobalSettings.StartingCash);
startingCash.OnMouseDown = _ =>
{
var options = Rules.Info["player"].Traits.Get<PlayerResourcesInfo>().SelectableCash.Select(c => new DropDownOption
@@ -425,7 +431,7 @@ namespace OpenRA.Mods.RA.Widgets.Logic
if (enableShroud != null)
{
enableShroud.IsChecked = () => orderManager.LobbyInfo.GlobalSettings.Shroud;
enableShroud.IsDisabled = () => preview.Status != MapStatus.Available || preview.Map.Options.Shroud.HasValue || configurationDisabled();
enableShroud.IsDisabled = () => Map.Status != MapStatus.Available || Map.Map.Options.Shroud.HasValue || configurationDisabled();
enableShroud.OnClick = () => orderManager.IssueOrder(Order.Command(
"shroud {0}".F(!orderManager.LobbyInfo.GlobalSettings.Shroud)));
}
@@ -434,7 +440,7 @@ namespace OpenRA.Mods.RA.Widgets.Logic
if (enableFog != null)
{
enableFog.IsChecked = () => orderManager.LobbyInfo.GlobalSettings.Fog;
enableFog.IsDisabled = () => preview.Status != MapStatus.Available || preview.Map.Options.Fog.HasValue || configurationDisabled();
enableFog.IsDisabled = () => Map.Status != MapStatus.Available || Map.Map.Options.Fog.HasValue || configurationDisabled();
enableFog.OnClick = () => orderManager.IssueOrder(Order.Command(
"fog {0}".F(!orderManager.LobbyInfo.GlobalSettings.Fog)));
}
@@ -533,26 +539,24 @@ namespace OpenRA.Mods.RA.Widgets.Logic
void UpdateCurrentMap()
{
var uid = orderManager.LobbyInfo.GlobalSettings.Map;
if (preview.Uid == uid)
if (Map.Uid == uid)
return;
preview = Game.modData.MapCache[uid];
if (preview.Status != MapStatus.Available)
Map = Game.modData.MapCache[uid];
if (Map.Status == MapStatus.Available)
{
if (Game.Settings.Game.AllowDownloading)
{
Game.DownloadMap(uid);
Game.Debug("A new map has been downloaded...");
}
else
throw new InvalidOperationException("Server's new map doesn't exist on your system and Downloading turned off");
}
// Tell the server that we have the map
orderManager.IssueOrder(Order.Command("state {0}".F(Session.ClientState.NotReady)));
// Restore default starting cash if the last map set it to something invalid
var pri = Rules.Info["player"].Traits.Get<PlayerResourcesInfo>();
if (!preview.Map.Options.StartingCash.HasValue && !pri.SelectableCash.Contains(orderManager.LobbyInfo.GlobalSettings.StartingCash))
if (!Map.Map.Options.StartingCash.HasValue && !pri.SelectableCash.Contains(orderManager.LobbyInfo.GlobalSettings.StartingCash))
orderManager.IssueOrder(Order.Command("startingcash {0}".F(pri.DefaultCash)));
}
else if (Game.Settings.Game.AllowDownloading)
Game.modData.MapCache.QueryRemoteMapDetails(new [] { uid });
}
void UpdatePlayerList()
{
@@ -600,8 +604,8 @@ namespace OpenRA.Mods.RA.Widgets.Logic
LobbyUtils.SetupEditableColorWidget(template, slot, client, orderManager, colorPreview);
LobbyUtils.SetupEditableFactionWidget(template, slot, client, orderManager, countryNames);
LobbyUtils.SetupEditableTeamWidget(template, slot, client, orderManager, preview.SpawnPoints.Count);
LobbyUtils.SetupEditableReadyWidget(template, slot, client, orderManager);
LobbyUtils.SetupEditableTeamWidget(template, slot, client, orderManager, Map);
LobbyUtils.SetupEditableReadyWidget(template, slot, client, orderManager, Map);
}
else
{

View File

@@ -0,0 +1,148 @@
#region Copyright & License Information
/*
* Copyright 2007-2011 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using OpenRA.Network;
using OpenRA.Traits;
using OpenRA.Widgets;
namespace OpenRA.Mods.RA.Widgets.Logic
{
public class LobbyMapPreviewLogic
{
[ObjectCreator.UseCtor]
internal LobbyMapPreviewLogic(Widget widget, OrderManager orderManager, LobbyLogic lobby)
{
var available = widget.GetOrNull("MAP_AVAILABLE");
if (available != null)
{
available.IsVisible = () => lobby.Map.Status == MapStatus.Available;
var preview = available.Get<MapPreviewWidget>("MAP_PREVIEW");
preview.Preview = () => lobby.Map;
preview.OnMouseDown = mi => LobbyUtils.SelectSpawnPoint(orderManager, preview, lobby.Map, mi);
preview.SpawnClients = () => LobbyUtils.GetSpawnClients(orderManager, lobby.Map);
var title = available.GetOrNull<LabelWidget>("MAP_TITLE");
if (title != null)
title.GetText = () => lobby.Map.Title;
var type = available.GetOrNull<LabelWidget>("MAP_TYPE");
if (type != null)
type.GetText = () => lobby.Map.Type;
var author = available.GetOrNull<LabelWidget>("MAP_AUTHOR");
if (author != null)
author.GetText = () => "Created by {0}".F(lobby.Map.Author);
}
var download = widget.GetOrNull("MAP_DOWNLOADABLE");
if (download != null)
{
download.IsVisible = () => lobby.Map.Status == MapStatus.DownloadAvailable;
var preview = download.Get<MapPreviewWidget>("MAP_PREVIEW");
preview.Preview = () => lobby.Map;
preview.OnMouseDown = mi => LobbyUtils.SelectSpawnPoint(orderManager, preview, lobby.Map, mi);
preview.SpawnClients = () => LobbyUtils.GetSpawnClients(orderManager, lobby.Map);
var title = download.GetOrNull<LabelWidget>("MAP_TITLE");
if (title != null)
title.GetText = () => lobby.Map.Title;
var type = download.GetOrNull<LabelWidget>("MAP_TYPE");
if (type != null)
type.GetText = () => lobby.Map.Type;
var author = download.GetOrNull<LabelWidget>("MAP_AUTHOR");
if (author != null)
author.GetText = () => "Created by {0}".F(lobby.Map.Author);
var install = download.GetOrNull<ButtonWidget>("MAP_INSTALL");
if (install != null)
install.OnClick = () => lobby.Map.Install();
}
var progress = widget.GetOrNull("MAP_PROGRESS");
if (progress != null)
{
progress.IsVisible = () => lobby.Map.Status != MapStatus.Available && lobby.Map.Status != MapStatus.DownloadAvailable;
var preview = progress.Get<MapPreviewWidget>("MAP_PREVIEW");
preview.Preview = () => lobby.Map;
preview.OnMouseDown = mi => LobbyUtils.SelectSpawnPoint(orderManager, preview, lobby.Map, mi);
preview.SpawnClients = () => LobbyUtils.GetSpawnClients(orderManager, lobby.Map);
var title = progress.GetOrNull<LabelWidget>("MAP_TITLE");
if (title != null)
title.GetText = () => lobby.Map.Title;
var type = progress.GetOrNull<LabelWidget>("MAP_TYPE");
if (type != null)
type.GetText = () => lobby.Map.Type;
var statusSearching = progress.GetOrNull("MAP_STATUS_SEARCHING");
if (statusSearching != null)
statusSearching.IsVisible = () => lobby.Map.Status == MapStatus.Searching;
var statusUnavailable = progress.GetOrNull("MAP_STATUS_UNAVAILABLE");
if (statusUnavailable != null)
statusUnavailable.IsVisible = () => lobby.Map.Status == MapStatus.Unavailable;
var statusError = progress.GetOrNull("MAP_STATUS_ERROR");
if (statusError != null)
statusError.IsVisible = () => lobby.Map.Status == MapStatus.DownloadError;
var statusDownloading = progress.GetOrNull<LabelWidget>("MAP_STATUS_DOWNLOADING");
if (statusDownloading != null)
{
statusDownloading.IsVisible = () => lobby.Map.Status == MapStatus.Downloading;
statusDownloading.GetText = () =>
{
if (lobby.Map.DownloadBytes == 0)
return "Connecting...";
// Server does not provide the total file length
if (lobby.Map.DownloadPercentage == 0)
return "Downloading {0} kB".F(lobby.Map.DownloadBytes / 1024);
return "Downloading {0} kB ({1}%)".F(lobby.Map.DownloadBytes / 1024, lobby.Map.DownloadPercentage);
};
}
var retry = progress.GetOrNull<ButtonWidget>("MAP_RETRY");
if (retry != null)
{
retry.IsVisible = () => lobby.Map.Status == MapStatus.DownloadError || lobby.Map.Status == MapStatus.Unavailable;
retry.OnClick = () =>
{
if (lobby.Map.Status == MapStatus.DownloadError)
lobby.Map.Install();
else if (lobby.Map.Status == MapStatus.Unavailable)
Game.modData.MapCache.QueryRemoteMapDetails(new [] { lobby.Map.Uid });
};
retry.GetText = () => lobby.Map.Status == MapStatus.DownloadError ? "Retry Install" : "Retry Search";
}
var progressbar = progress.GetOrNull<ProgressBarWidget>("MAP_PROGRESSBAR");
if (progressbar != null)
{
progressbar.IsIndeterminate = () => lobby.Map.DownloadPercentage == 0;
progressbar.GetPercentage = () => lobby.Map.DownloadPercentage;
progressbar.IsVisible = () => !retry.IsVisible();
}
}
}
}
}

View File

@@ -363,11 +363,11 @@ namespace OpenRA.Mods.RA.Widgets.Logic
factionflag.GetImageCollection = () => "flags";
}
public static void SetupEditableTeamWidget(Widget parent, Session.Slot s, Session.Client c, OrderManager orderManager, int teamCount)
public static void SetupEditableTeamWidget(Widget parent, Session.Slot s, Session.Client c, OrderManager orderManager, MapPreview map)
{
var dropdown = parent.Get<DropDownButtonWidget>("TEAM");
dropdown.IsDisabled = () => s.LockTeam || orderManager.LocalClient.IsReady;
dropdown.OnMouseDown = _ => ShowTeamDropDown(dropdown, c, orderManager, teamCount);
dropdown.OnMouseDown = _ => ShowTeamDropDown(dropdown, c, orderManager, map.PlayerCount);
dropdown.GetText = () => (c.Team == 0) ? "-" : c.Team.ToString();
}
@@ -376,13 +376,15 @@ namespace OpenRA.Mods.RA.Widgets.Logic
parent.Get<LabelWidget>("TEAM").GetText = () => (c.Team == 0) ? "-" : c.Team.ToString();
}
public static void SetupEditableReadyWidget(Widget parent, Session.Slot s, Session.Client c, OrderManager orderManager)
public static void SetupEditableReadyWidget(Widget parent, Session.Slot s, Session.Client c, OrderManager orderManager, MapPreview map)
{
var status = parent.Get<CheckboxWidget>("STATUS_CHECKBOX");
status.IsChecked = () => orderManager.LocalClient.IsReady || c.Bot != null;
status.IsVisible = () => true;
status.IsDisabled = () => c.Bot != null;
status.OnClick = () => orderManager.IssueOrder(Order.Command("ready"));
status.IsDisabled = () => c.Bot != null || map.Status != MapStatus.Available;
var state = orderManager.LocalClient.IsReady ? Session.ClientState.NotReady : Session.ClientState.Ready;
status.OnClick = () => orderManager.IssueOrder(Order.Command("state {0}".F(state)));
}
public static void SetupReadyWidget(Widget parent, Session.Slot s, Session.Client c)

View File

@@ -147,30 +147,6 @@ namespace OpenRA.Mods.RA.Widgets.Logic
{
List<Widget> rows = new List<Widget>();
Game.RunAfterTick(() =>
{
serverList.RemoveChildren();
currentServer = null;
if (games == null)
{
searchStatus = SearchStatus.Failed;
return;
}
if (!games.Any())
{
searchStatus = SearchStatus.NoGames;
return;
}
currentServer = games.FirstOrDefault();
searchStatus = SearchStatus.Hidden;
foreach (var row in rows)
serverList.AddChild(row);
});
foreach (var loop in games.OrderByDescending(g => g.CanJoin()).ThenByDescending(g => g.Players))
{
var game = loop;
@@ -241,6 +217,34 @@ namespace OpenRA.Mods.RA.Widgets.Logic
if (!Filtered(game))
rows.Add(item);
}
Game.RunAfterTick(() =>
{
serverList.RemoveChildren();
currentServer = null;
if (games == null)
{
searchStatus = SearchStatus.Failed;
return;
}
if (!games.Any())
{
searchStatus = SearchStatus.NoGames;
return;
}
currentServer = games.FirstOrDefault();
searchStatus = SearchStatus.Hidden;
// Search for any unknown maps
if (Game.Settings.Game.AllowDownloading)
Game.modData.MapCache.QueryRemoteMapDetails(games.Where(g => !Filtered(g)).Select(g => g.Map));
foreach (var row in rows)
serverList.AddChild(row);
});
}
void OpenLobby()

View File

@@ -0,0 +1,45 @@
Background@COLOR_CHOOSER:
Logic:ColorPickerLogic
Background:panel-black
Width:234
Height:105
Children:
Background@HUEBG:
Background:panel-black
X:5
Y:5
Width:148
Height:13
Children:
HueSlider@HUE:
X:2
Y:2
Width:144
Height:9
Ticks:5
Background@MIXERBG:
Background:panel-black
X:5
Y:23
Width:148
Height:76
Children:
ColorMixer@MIXER:
X:2
Y:2
Width:144
Height:72
ShpImage@FACT:
X:153
Y:1
Width:80
Height:73
Image:fact
Palette:colorpicker
Button@RANDOM_BUTTON:
Key:tab
X:158
Y:74
Width:70
Height:25
Text:Random

View File

@@ -1,49 +1,3 @@
Background@COLOR_CHOOSER:
Logic:ColorPickerLogic
Background:panel-black
Width:234
Height:105
Children:
Background@HUEBG:
Background:panel-black
X:5
Y:5
Width:148
Height:13
Children:
HueSlider@HUE:
X:2
Y:2
Width:144
Height:9
Ticks:5
Background@MIXERBG:
Background:panel-black
X:5
Y:23
Width:148
Height:76
Children:
ColorMixer@MIXER:
X:2
Y:2
Width:144
Height:72
ShpImage@FACT:
X:153
Y:1
Width:80
Height:73
Image:fact
Palette:colorpicker
Button@RANDOM_BUTTON:
Key:tab
X:158
Y:74
Width:70
Height:25
Text:Random
ScrollPanel@LABEL_DROPDOWN_TEMPLATE:
Width:DROPDOWN_WIDTH
Background:panel-black
@@ -146,187 +100,3 @@ Container@CONFIRM_PROMPT:
Width:140
Height:35
Text:Confirm
Background@KICK_CLIENT_DIALOG:
X:15
Y:30
Width:501
Height:219
Logic:KickClientLogic
Background:scrollpanel-bg
Children:
Label@TITLE:
X:0
Y:40
Width:PARENT_RIGHT
Height:25
Font:Bold
Align:Center
Label@TEXTA:
X:0
Y:67
Width:PARENT_RIGHT
Height:25
Font:Regular
Align:Center
Text:You may also apply a temporary ban, preventing
Label@TEXTB:
X:0
Y:85
Width:PARENT_RIGHT
Height:25
Font:Regular
Align:Center
Text:them from joining for the remainder of this game.
Checkbox@PREVENT_REJOINING_CHECKBOX:
X:(PARENT_RIGHT - WIDTH)/2
Y:120
Width:150
Height:20
Font:Regular
Text:Temporarily Ban
Button@OK_BUTTON:
X:(PARENT_RIGHT - WIDTH)/2 + 75
Y:155
Width:120
Height:25
Text:Kick
Font:Bold
Button@CANCEL_BUTTON:
X:(PARENT_RIGHT - WIDTH)/2 - 75
Y:155
Width:120
Height:25
Text:Cancel
Font:Bold
Background@KICK_SPECTATORS_DIALOG:
X:15
Y:30
Width:501
Height:219
Logic:KickSpectatorsLogic
Background:scrollpanel-bg
Children:
Label@TITLE:
X:0
Y:40
Width:PARENT_RIGHT
Height:25
Font:Bold
Align:Center
Text:Kick Spectators
Label@TEXT:
X:0
Y:85
Width:PARENT_RIGHT
Height:25
Font:Regular
Align:Center
Button@OK_BUTTON:
X:(PARENT_RIGHT - WIDTH)/2 + 75
Y:155
Width:120
Height:25
Text:Ok
Font:Bold
Button@CANCEL_BUTTON:
X:(PARENT_RIGHT - WIDTH)/2 - 75
Y:155
Width:120
Height:25
Text:Cancel
Font:Bold
Background@LOBBY_OPTIONS_BIN:
X:15
Y:30
Width:501
Height:219
Background:scrollpanel-bg
Children:
Label@TITLE:
X:0
Y:30
Width:PARENT_RIGHT
Height:25
Font:Bold
Align:Center
Text: Map Options
Container:
X:30
Y:65
Width: PARENT_RIGHT-60
Height: PARENT_BOTTOM-75
Children:
Checkbox@SHROUD_CHECKBOX:
Width:230
Height:20
Font:Regular
Text:Shroud
Checkbox@FOG_CHECKBOX:
Y:35
Width:230
Height:20
Font:Regular
Text:Fog of War
Checkbox@CRATES_CHECKBOX:
X:160
Width:230
Height:20
Font:Regular
Text:Crates Appear
Checkbox@ALLYBUILDRADIUS_CHECKBOX:
X:160
Y:35
Width:230
Height:20
Font:Regular
Text:Build off Ally ConYards
Checkbox@ALLOWCHEATS_CHECKBOX:
X:325
Width:230
Height:20
Font:Regular
Text:Debug Menu
Label@STARTINGCASH_DESC:
X:10
Y:72
Width:70
Height:25
Font:Regular
Text:Starting Cash:
Align:Right
DropDownButton@STARTINGCASH_DROPDOWNBUTTON:
X:85
Y:72
Width:120
Height:25
Font:Regular
Text:$5000
Label@STARTINGUNITS_DESC:
X:PARENT_RIGHT - WIDTH - 135
Y:72
Width:120
Height:25
Text:Starting Units:
Align:Right
DropDownButton@STARTINGUNITS_DROPDOWNBUTTON:
X:PARENT_RIGHT - WIDTH + 10
Y:72
Width:140
Height:25
Font:Regular
Label@DIFFICULTY_DESC:
X:PARENT_RIGHT - WIDTH - 135
Y:107
Width:120
Height:25
Text:Mission Difficulty:
Align:Right
DropDownButton@DIFFICULTY_DROPDOWNBUTTON:
X:PARENT_RIGHT - WIDTH + 10
Y:107
Width:140
Height:25
Font:Regular

View File

@@ -0,0 +1,248 @@
Background@KICK_CLIENT_DIALOG:
X:15
Y:30
Width:501
Height:219
Logic:KickClientLogic
Background:scrollpanel-bg
Children:
Label@TITLE:
X:0
Y:40
Width:PARENT_RIGHT
Height:25
Font:Bold
Align:Center
Label@TEXTA:
X:0
Y:67
Width:PARENT_RIGHT
Height:25
Font:Regular
Align:Center
Text:You may also apply a temporary ban, preventing
Label@TEXTB:
X:0
Y:85
Width:PARENT_RIGHT
Height:25
Font:Regular
Align:Center
Text:them from joining for the remainder of this game.
Checkbox@PREVENT_REJOINING_CHECKBOX:
X:(PARENT_RIGHT - WIDTH)/2
Y:120
Width:150
Height:20
Font:Regular
Text:Temporarily Ban
Button@OK_BUTTON:
X:(PARENT_RIGHT - WIDTH)/2 + 75
Y:155
Width:120
Height:25
Text:Kick
Font:Bold
Button@CANCEL_BUTTON:
X:(PARENT_RIGHT - WIDTH)/2 - 75
Y:155
Width:120
Height:25
Text:Cancel
Font:Bold
Background@KICK_SPECTATORS_DIALOG:
X:15
Y:30
Width:501
Height:219
Logic:KickSpectatorsLogic
Background:scrollpanel-bg
Children:
Label@TITLE:
X:0
Y:40
Width:PARENT_RIGHT
Height:25
Font:Bold
Align:Center
Text:Kick Spectators
Label@TEXT:
X:0
Y:85
Width:PARENT_RIGHT
Height:25
Font:Regular
Align:Center
Button@OK_BUTTON:
X:(PARENT_RIGHT - WIDTH)/2 + 75
Y:155
Width:120
Height:25
Text:Ok
Font:Bold
Button@CANCEL_BUTTON:
X:(PARENT_RIGHT - WIDTH)/2 - 75
Y:155
Width:120
Height:25
Text:Cancel
Font:Bold
Background@LOBBY_OPTIONS_BIN:
X:15
Y:30
Width:501
Height:219
Background:scrollpanel-bg
Children:
Label@TITLE:
X:0
Y:30
Width:PARENT_RIGHT
Height:25
Font:Bold
Align:Center
Text: Map Options
Container:
X:30
Y:65
Width: PARENT_RIGHT-60
Height: PARENT_BOTTOM-75
Children:
Checkbox@SHROUD_CHECKBOX:
Width:230
Height:20
Font:Regular
Text:Shroud
Checkbox@FOG_CHECKBOX:
Y:35
Width:230
Height:20
Font:Regular
Text:Fog of War
Checkbox@CRATES_CHECKBOX:
X:160
Width:230
Height:20
Font:Regular
Text:Crates Appear
Checkbox@ALLYBUILDRADIUS_CHECKBOX:
X:160
Y:35
Width:230
Height:20
Font:Regular
Text:Build off Ally ConYards
Checkbox@ALLOWCHEATS_CHECKBOX:
X:325
Width:230
Height:20
Font:Regular
Text:Debug Menu
Label@STARTINGCASH_DESC:
X:10
Y:72
Width:70
Height:25
Font:Regular
Text:Starting Cash:
Align:Right
DropDownButton@STARTINGCASH_DROPDOWNBUTTON:
X:85
Y:72
Width:120
Height:25
Font:Regular
Text:$5000
Label@STARTINGUNITS_DESC:
X:PARENT_RIGHT - WIDTH - 135
Y:72
Width:120
Height:25
Text:Starting Units:
Align:Right
DropDownButton@STARTINGUNITS_DROPDOWNBUTTON:
X:PARENT_RIGHT - WIDTH + 10
Y:72
Width:140
Height:25
Font:Regular
Label@DIFFICULTY_DESC:
X:PARENT_RIGHT - WIDTH - 135
Y:107
Width:120
Height:25
Text:Mission Difficulty:
Align:Right
DropDownButton@DIFFICULTY_DROPDOWNBUTTON:
X:PARENT_RIGHT - WIDTH + 10
Y:107
Width:140
Height:25
Font:Regular
Background@FORCE_START_DIALOG:
X:15
Y:30
Width:501
Height:219
Background:scrollpanel-bg
Children:
Label@TITLE:
X:0
Y:40
Width:PARENT_RIGHT
Height:25
Font:Bold
Align:Center
Text: Start Game?
Label@TEXTA:
X:0
Y:67
Width:PARENT_RIGHT
Height:25
Font:Regular
Align:Center
Text:One or more players are not yet ready.
Label@TEXTB:
X:0
Y:85
Width:PARENT_RIGHT
Height:25
Font:Regular
Align:Center
Text:Are you sure that you want to force start the game?
Container@KICK_WARNING:
Width:PARENT_RIGHT
Children:
Label@KICK_WARNING_A:
X:0
Y:106
Width:PARENT_RIGHT
Height:25
Font:Bold
Align:Center
Text:One or more clients are missing the selected
Label@KICK_WARNING_B:
X:0
Y:123
Width:PARENT_RIGHT
Height:25
Font:Bold
Align:Center
Text:map, and will be kicked from the server.
Button@OK_BUTTON:
X:(PARENT_RIGHT - WIDTH)/2 + 75
Y:155
Width:120
Height:25
Text:Start
Font:Bold
Button@CANCEL_BUTTON:
X:(PARENT_RIGHT - WIDTH)/2 - 75
Y:155
Width:120
Height:25
Text:Cancel
Font:Bold

View File

@@ -0,0 +1,145 @@
Container@LOBBY_MAP_PREVIEW:
Logic:LobbyMapPreviewLogic
X:PARENT_RIGHT-15-WIDTH
Y:30
Width:194
Height:250
Children:
Container@MAP_AVAILABLE:
Width:PARENT_RIGHT
Height:PARENT_BOTTOM
Children:
Background@MAP_BG:
Width:PARENT_RIGHT
Height:194
Background:panel-gray
Children:
MapPreview@MAP_PREVIEW:
X:1
Y:1
Width:PARENT_RIGHT-2
Height:PARENT_BOTTOM-2
TooltipContainer:TOOLTIP_CONTAINER
Label@MAP_TITLE:
Y:197
Width:PARENT_RIGHT
Height:25
Font:Bold
Align:Center
Label@MAP_TYPE:
Y:212
Width:PARENT_RIGHT
Height:25
Font:TinyBold
Align:Center
Label@MAP_AUTHOR:
Y:225
Width:PARENT_RIGHT
Height:25
Font:Tiny
Align:Center
Container@MAP_DOWNLOADABLE:
Width:PARENT_RIGHT
Height:PARENT_BOTTOM
Children:
Background@MAP_BG:
Width:PARENT_RIGHT
Height:164
Background:panel-gray
Children:
MapPreview@MAP_PREVIEW:
X:1
Y:1
Width:PARENT_RIGHT-2
Height:PARENT_BOTTOM-2
TooltipContainer:TOOLTIP_CONTAINER
Label@MAP_TITLE:
Y:167
Width:PARENT_RIGHT
Height:25
Font:Bold
Align:Center
Label@MAP_TYPE:
Y:184
Width:PARENT_RIGHT
Height:25
Font:TinyBold
Align:Center
Label@MAP_AUTHOR:
Y:197
Width:PARENT_RIGHT
Height:25
Font:Tiny
Align:Center
Button@MAP_INSTALL:
Y:224
Width:PARENT_RIGHT
Height:25
Text:Install Map
Container@MAP_PROGRESS:
Width:PARENT_RIGHT
Height:PARENT_BOTTOM
Children:
Background@MAP_BG:
Width:PARENT_RIGHT
Height:164
Background:panel-gray
Children:
MapPreview@MAP_PREVIEW:
X:1
Y:1
Width:PARENT_RIGHT-2
Height:PARENT_BOTTOM-2
TooltipContainer:TOOLTIP_CONTAINER
Label@MAP_TITLE:
Y:167
Width:PARENT_RIGHT
Height:25
Font:Bold
Align:Center
Label@MAP_STATUS_SEARCHING:
Y:197
Width:PARENT_RIGHT
Height:25
Font:Tiny
Align:Center
Text: Searching OpenRA Resource Center...
Container@MAP_STATUS_UNAVAILABLE:
Width:PARENT_RIGHT
Children:
Label@a:
Y:184
Width:PARENT_RIGHT
Height:25
Font:Tiny
Align:Center
Text:This map was not found on the
Label@b:
Y:197
Width:PARENT_RIGHT
Height:25
Font:Tiny
Align:Center
Text:OpenRA Resource Center
Label@MAP_STATUS_ERROR:
Y:197
Width:PARENT_RIGHT
Height:25
Font:Tiny
Align:Center
Text:An error occurred during installation
Label@MAP_STATUS_DOWNLOADING:
Y:197
Width:PARENT_RIGHT
Height:25
Font:Tiny
Align:Center
ProgressBar@MAP_PROGRESSBAR:
Y:224
Width:PARENT_RIGHT
Height:25
Indeterminate:True
Button@MAP_RETRY:
Y:224
Width:PARENT_RIGHT
Height:25

View File

@@ -17,40 +17,9 @@ Container@SERVER_LOBBY:
Height:500
Background:panel-black
Children:
Background@MAP_BG:
X:PARENT_RIGHT-15-WIDTH
Y:30
Width:194
Height:194
Background:panel-gray
Children:
MapPreview@MAP_PREVIEW:
X:1
Y:1
Width:192
Height:192
TooltipContainer:TOOLTIP_CONTAINER
Label@MAP_TITLE:
X:PARENT_RIGHT-15-WIDTH
Y:227
Width:194
Height:25
Font:Bold
Align:Center
Label@MAP_TYPE:
X:PARENT_RIGHT-15-WIDTH
Y:242
Width:194
Height:25
Font:TinyBold
Align:Center
Label@MAP_AUTHOR:
X:PARENT_RIGHT-15-WIDTH
Y:255
Width:194
Height:25
Font:Tiny
Align:Center
Container@MAP_PREVIEW_ROOT:
Width:PARENT_RIGHT
Height:PARENT_BOTTOM
Container@LABEL_CONTAINER:
X:20
Y:5

View File

@@ -79,8 +79,11 @@ ChromeLayout:
mods/cnc/chrome/createserver.yaml
mods/cnc/chrome/directconnect.yaml
mods/cnc/chrome/lobby.yaml
mods/cnc/chrome/lobby-mappreview.yaml
mods/cnc/chrome/lobby-playerbin.yaml
mods/cnc/chrome/lobby-dialogs.yaml
mods/cnc/chrome/connection.yaml
mods/cnc/chrome/color-picker.yaml
mods/cnc/chrome/mapchooser.yaml
mods/cnc/chrome/replaybrowser.yaml
mods/cnc/chrome/ingame.yaml

View File

@@ -69,6 +69,7 @@ ChromeLayout:
mods/ra/chrome/settings.yaml
mods/ra/chrome/credits.yaml
mods/ra/chrome/lobby.yaml
mods/ra/chrome/lobby-mappreview.yaml
mods/d2k/chrome/lobby-playerbin.yaml
mods/ra/chrome/lobby-dialogs.yaml
mods/d2k/chrome/color-picker.yaml

View File

@@ -179,3 +179,68 @@ Background@LOBBY_OPTIONS_BIN:
Width:140
Height:25
Font:Regular
Background@FORCE_START_DIALOG:
X:20
Y:67
Width:535
Height:235
Background:dialog3
Children:
Label@TITLE:
X:0
Y:40
Width:PARENT_RIGHT
Height:25
Font:Bold
Align:Center
Text: Start Game?
Label@TEXTA:
X:0
Y:67
Width:PARENT_RIGHT
Height:25
Font:Regular
Align:Center
Text:One or more players are not yet ready.
Label@TEXTB:
X:0
Y:85
Width:PARENT_RIGHT
Height:25
Font:Regular
Align:Center
Text:Are you sure that you want to force start the game?
Container@KICK_WARNING:
Width:PARENT_RIGHT
Children:
Label@KICK_WARNING_A:
X:0
Y:106
Width:PARENT_RIGHT
Height:25
Font:Bold
Align:Center
Text:One or more clients are missing the selected
Label@KICK_WARNING_B:
X:0
Y:123
Width:PARENT_RIGHT
Height:25
Font:Bold
Align:Center
Text:map, and will be kicked from the server.
Button@OK_BUTTON:
X:(PARENT_RIGHT - WIDTH)/2 + 75
Y:155
Width:120
Height:25
Text:Start
Font:Bold
Button@CANCEL_BUTTON:
X:(PARENT_RIGHT - WIDTH)/2 - 75
Y:155
Width:120
Height:25
Text:Cancel
Font:Bold

View File

@@ -0,0 +1,147 @@
Container@LOBBY_MAP_PREVIEW:
Logic:LobbyMapPreviewLogic
X:PARENT_RIGHT-20-WIDTH
Y:67
Width:214
Height:250
Children:
Container@MAP_AVAILABLE:
Width:PARENT_RIGHT
Height:PARENT_BOTTOM
Children:
Background@MAP_BG:
Width:PARENT_RIGHT
Height:214
Background:dialog3
Children:
MapPreview@MAP_PREVIEW:
X:1
Y:1
Width:PARENT_RIGHT-2
Height:PARENT_BOTTOM-2
TooltipContainer:TOOLTIP_CONTAINER
Label@MAP_TITLE:
Y:215
Width:PARENT_RIGHT
Height:25
Font:Bold
Align:Center
Label@MAP_TYPE:
Y:232
Width:PARENT_RIGHT
Height:25
Font:TinyBold
Align:Center
Label@MAP_AUTHOR:
Y:245
Width:PARENT_RIGHT
Height:25
Font:Tiny
Align:Center
Container@MAP_DOWNLOADABLE:
Width:PARENT_RIGHT
Height:PARENT_BOTTOM
Children:
Background@MAP_BG:
Width:PARENT_RIGHT
Height:182
Background:dialog3
Children:
MapPreview@MAP_PREVIEW:
X:1
Y:1
Width:PARENT_RIGHT-2
Height:PARENT_BOTTOM-2
TooltipContainer:TOOLTIP_CONTAINER
Label@MAP_TITLE:
Y:185
Width:PARENT_RIGHT
Height:25
Font:Bold
Align:Center
Label@MAP_TYPE:
Y:202
Width:PARENT_RIGHT
Height:25
Font:TinyBold
Align:Center
Label@MAP_AUTHOR:
Y:215
Width:PARENT_RIGHT
Height:25
Font:Tiny
Align:Center
Button@MAP_INSTALL:
Y:242
Width:PARENT_RIGHT
Height:25
Font:Bold
Text:Install Map
Container@MAP_PROGRESS:
Width:PARENT_RIGHT
Height:PARENT_BOTTOM
Children:
Background@MAP_BG:
Width:PARENT_RIGHT
Height:182
Background:dialog3
Children:
MapPreview@MAP_PREVIEW:
X:1
Y:1
Width:PARENT_RIGHT-2
Height:PARENT_BOTTOM-2
TooltipContainer:TOOLTIP_CONTAINER
Label@MAP_TITLE:
Y:185
Width:PARENT_RIGHT
Height:25
Font:Bold
Align:Center
Label@MAP_STATUS_SEARCHING:
Y:215
Width:PARENT_RIGHT
Height:25
Font:Tiny
Align:Center
Text: Searching OpenRA Resource Center...
Container@MAP_STATUS_UNAVAILABLE:
Width:PARENT_RIGHT
Children:
Label@a:
Y:202
Width:PARENT_RIGHT
Height:25
Font:Tiny
Align:Center
Text:This map was not found on the
Label@b:
Y:215
Width:PARENT_RIGHT
Height:25
Font:Tiny
Align:Center
Text:OpenRA Resource Center
Label@MAP_STATUS_ERROR:
Y:215
Width:PARENT_RIGHT
Height:25
Font:Tiny
Align:Center
Text:An error occurred during installation
Label@MAP_STATUS_DOWNLOADING:
Y:215
Width:PARENT_RIGHT
Height:25
Font:Tiny
Align:Center
ProgressBar@MAP_PROGRESSBAR:
Y:242
Width:PARENT_RIGHT
Height:25
Indeterminate:True
Button@MAP_RETRY:
Y:242
Width:PARENT_RIGHT
Height:25
Font:Bold

View File

@@ -13,40 +13,9 @@ Background@SERVER_LOBBY:
Width:PARENT_RIGHT
Height:20
Font:Bold
Background@MAP_BG:
X:PARENT_RIGHT-20-WIDTH
Y:67
Width:214
Height:214
Background:dialog3
Children:
MapPreview@MAP_PREVIEW:
X:2
Y:2
Width:210
Height:210
TooltipContainer:TOOLTIP_CONTAINER
Label@MAP_TITLE:
X:PARENT_RIGHT-20-WIDTH
Y:282
Width:214
Height:25
Font:Bold
Align:Center
Label@MAP_TYPE:
X:PARENT_RIGHT-20-WIDTH
Y:297
Width:214
Height:25
Font:TinyBold
Align:Center
Label@MAP_AUTHOR:
X:PARENT_RIGHT-20-WIDTH
Y:310
Width:214
Height:25
Font:Tiny
Align:Center
Container@MAP_PREVIEW_ROOT:
Width:PARENT_RIGHT
Height:PARENT_BOTTOM
Container@LABEL_CONTAINER:
X:25
Y:40

View File

@@ -83,6 +83,7 @@ ChromeLayout:
mods/ra/chrome/settings.yaml
mods/ra/chrome/credits.yaml
mods/ra/chrome/lobby.yaml
mods/ra/chrome/lobby-mappreview.yaml
mods/ra/chrome/lobby-playerbin.yaml
mods/ra/chrome/lobby-dialogs.yaml
mods/ra/chrome/color-picker.yaml

View File

@@ -108,6 +108,7 @@ ChromeLayout:
mods/ra/chrome/settings.yaml
mods/ra/chrome/credits.yaml
mods/ra/chrome/lobby.yaml
mods/ra/chrome/lobby-mappreview.yaml
mods/ra/chrome/lobby-playerbin.yaml
mods/ra/chrome/lobby-dialogs.yaml
mods/ts/chrome/color-picker.yaml