Add save/load browser and buttons to the UI.

This commit is contained in:
Paul Chote
2019-04-20 10:33:55 +00:00
committed by reaperrr
parent 3a693d8def
commit de9649df27
13 changed files with 679 additions and 1 deletions

View File

@@ -636,6 +636,7 @@
<Compile Include="Widgets\ExponentialSliderWidget.cs" /> <Compile Include="Widgets\ExponentialSliderWidget.cs" />
<Compile Include="Widgets\Logic\Ingame\GameSaveLoadingLogic.cs" /> <Compile Include="Widgets\Logic\Ingame\GameSaveLoadingLogic.cs" />
<Compile Include="Widgets\Logic\MusicHotkeyLogic.cs" /> <Compile Include="Widgets\Logic\MusicHotkeyLogic.cs" />
<Compile Include="Widgets\Logic\GameSaveBrowserLogic.cs" />
<Compile Include="WorldExtensions.cs" /> <Compile Include="WorldExtensions.cs" />
<Compile Include="UtilityCommands\GetMapHashCommand.cs" /> <Compile Include="UtilityCommands\GetMapHashCommand.cs" />
<Compile Include="UtilityCommands\Glob.cs" /> <Compile Include="UtilityCommands\Glob.cs" />

View File

@@ -0,0 +1,360 @@
#region Copyright & License Information
/*
* Copyright 2007-2019 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, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using OpenRA.Network;
using OpenRA.Widgets;
namespace OpenRA.Mods.Common.Widgets.Logic
{
public class GameSaveBrowserLogic : ChromeLogic
{
readonly Widget panel;
readonly ScrollPanelWidget gameList;
readonly TextFieldWidget saveTextField;
readonly List<string> games = new List<string>();
readonly Action onStart;
readonly Action onExit;
readonly ModData modData;
readonly bool isSavePanel;
readonly string baseSavePath;
readonly string defaultSaveFilename;
string selectedSave;
[ObjectCreator.UseCtor]
public GameSaveBrowserLogic(Widget widget, ModData modData, Action onExit, Action onStart, bool isSavePanel, World world)
{
panel = widget;
this.modData = modData;
this.onStart = onStart;
this.onExit = onExit;
this.isSavePanel = isSavePanel;
Game.BeforeGameStart += OnGameStart;
panel.Get<ButtonWidget>("CANCEL_BUTTON").OnClick = () =>
{
Ui.CloseWindow();
onExit();
};
gameList = panel.Get<ScrollPanelWidget>("GAME_LIST");
var gameTemplate = panel.Get<ScrollItemWidget>("GAME_TEMPLATE");
var newTemplate = panel.Get<ScrollItemWidget>("NEW_TEMPLATE");
var mod = modData.Manifest;
baseSavePath = Platform.ResolvePath(Platform.SupportDirPrefix, "Saves", mod.Id, mod.Metadata.Version);
// Avoid filename conflicts when creating new saves
if (isSavePanel)
{
panel.Get("SAVE_TITLE").IsVisible = () => true;
defaultSaveFilename = world.Map.Title;
var filenameAttempt = 0;
while (true)
{
if (!File.Exists(Path.Combine(baseSavePath, defaultSaveFilename + ".orasav")))
break;
defaultSaveFilename = world.Map.Title + " ({0})".F(++filenameAttempt);
}
var saveButton = panel.Get<ButtonWidget>("SAVE_BUTTON");
saveButton.OnClick = () => { Save(world); };
saveButton.IsVisible = () => true;
var saveWidgets = panel.Get("SAVE_WIDGETS");
saveTextField = saveWidgets.Get<TextFieldWidget>("SAVE_TEXTFIELD");
gameList.Bounds.Height -= saveWidgets.Bounds.Height;
saveWidgets.IsVisible = () => true;
}
else
{
panel.Get("LOAD_TITLE").IsVisible = () => true;
var loadButton = panel.Get<ButtonWidget>("LOAD_BUTTON");
loadButton.IsVisible = () => true;
loadButton.IsDisabled = () => selectedSave == null;
loadButton.OnClick = () => { Load(); };
}
if (Directory.Exists(baseSavePath))
LoadGames(gameTemplate, newTemplate);
var renameButton = panel.Get<ButtonWidget>("RENAME_BUTTON");
renameButton.IsDisabled = () => selectedSave == null;
renameButton.OnClick = () =>
{
var initialName = Path.GetFileNameWithoutExtension(selectedSave);
var invalidChars = Path.GetInvalidFileNameChars();
ConfirmationDialogs.TextInputPrompt(
"Rename Save",
"Enter a new file name:",
initialName,
onAccept: newName => Rename(initialName, newName),
onCancel: null,
acceptText: "Rename",
cancelText: null,
inputValidator: newName =>
{
if (newName == initialName)
return false;
if (string.IsNullOrWhiteSpace(newName))
return false;
if (newName.IndexOfAny(invalidChars) >= 0)
return false;
if (File.Exists(Path.Combine(baseSavePath, newName)))
return false;
return true;
});
};
var deleteButton = panel.Get<ButtonWidget>("DELETE_BUTTON");
deleteButton.IsDisabled = () => selectedSave == null;
deleteButton.OnClick = () =>
{
ConfirmationDialogs.ButtonPrompt(
title: "Delete selected game save?",
text: "Delete '{0}'?".F(Path.GetFileNameWithoutExtension(selectedSave)),
onConfirm: () =>
{
Delete(selectedSave);
if (!games.Any() && !isSavePanel)
{
Ui.CloseWindow();
onExit();
}
else
SelectFirstVisible();
},
confirmText: "Delete",
onCancel: () => { });
};
var deleteAllButton = panel.Get<ButtonWidget>("DELETE_ALL_BUTTON");
deleteAllButton.IsDisabled = () => !games.Any();
deleteAllButton.OnClick = () =>
{
ConfirmationDialogs.ButtonPrompt(
title: "Delete all game saves?",
text: "Delete {0} game saves?".F(games.Count),
onConfirm: () =>
{
foreach (var s in games.ToList())
Delete(s);
Ui.CloseWindow();
onExit();
},
confirmText: "Delete All",
onCancel: () => { });
};
SelectFirstVisible();
}
void LoadGames(ScrollItemWidget gameTemplate, ScrollItemWidget newTemplate)
{
gameList.RemoveChildren();
if (isSavePanel)
{
var item = ScrollItemWidget.Setup(newTemplate,
() => selectedSave == null,
() => Select(null),
() => { });
gameList.AddChild(item);
}
var savePaths = Directory.GetFiles(baseSavePath, "*.orasav", SearchOption.AllDirectories)
.OrderByDescending(p => File.GetLastWriteTime(p))
.ToList();
foreach (var savePath in savePaths)
{
games.Add(savePath);
// Create the item manually so the click handlers can refer to itself
// This simplifies the rename handling (only needs to update ItemKey)
var item = gameTemplate.Clone() as ScrollItemWidget;
item.ItemKey = savePath;
item.IsVisible = () => true;
item.IsSelected = () => selectedSave == item.ItemKey;
item.OnClick = () => Select(item.ItemKey);
item.OnDoubleClick = Load;
var title = Path.GetFileNameWithoutExtension(savePath);
item.Get<LabelWidget>("TITLE").GetText = () => title;
var date = File.GetLastWriteTime(savePath).ToString("yyyy-MM-dd HH:mm:ss");
item.Get<LabelWidget>("DATE").GetText = () => date;
gameList.AddChild(item);
}
}
void Rename(string oldName, string newName)
{
try
{
var oldPath = Path.Combine(baseSavePath, oldName + ".orasav");
var newPath = Path.Combine(baseSavePath, newName + ".orasav");
File.Move(oldPath, newPath);
games[games.IndexOf(oldPath)] = newPath;
foreach (var c in gameList.Children)
{
var item = c as ScrollItemWidget;
if (item == null || item.ItemKey != oldPath)
continue;
item.ItemKey = newPath;
item.Get<LabelWidget>("TITLE").GetText = () => newName;
}
if (selectedSave == oldPath)
selectedSave = newPath;
}
catch (Exception ex)
{
Console.WriteLine(ex);
Log.Write("debug", ex.ToString());
}
}
void Delete(string savePath)
{
try
{
File.Delete(savePath);
}
catch (Exception ex)
{
Game.Debug("Failed to delete save file '{0}'. See the logs for details.", savePath);
Log.Write("debug", ex.ToString());
return;
}
if (savePath == selectedSave)
Select(null);
var item = gameList.Children
.Select(c => c as ScrollItemWidget)
.FirstOrDefault(c => c.ItemKey == savePath);
gameList.RemoveChild(item);
games.Remove(savePath);
}
void SelectFirstVisible()
{
Select(isSavePanel ? null : games.FirstOrDefault());
}
void Select(string savePath)
{
selectedSave = savePath;
if (isSavePanel)
saveTextField.Text = savePath == null ? defaultSaveFilename :
Path.GetFileNameWithoutExtension(savePath);
}
void Load()
{
if (selectedSave == null)
return;
// Parse the save to find the map UID
var save = new GameSave(selectedSave);
var map = modData.MapCache[save.GlobalSettings.Map];
if (map.Status != MapStatus.Available)
return;
var orders = new List<Order>()
{
new Order("LoadGameSave", null, false)
{
IsImmediate = true,
TargetString = Path.GetFileName(selectedSave)
},
Order.Command("state {0}".F(Session.ClientState.Ready))
};
Game.CreateAndStartLocalServer(map.Uid, orders);
}
void Save(World world)
{
var filename = saveTextField.Text + ".orasav";
var testPath = Platform.ResolvePath(
Platform.SupportDirPrefix,
"Saves",
modData.Manifest.Id,
modData.Manifest.Metadata.Version,
filename);
Action inner = () =>
{
world.RequestGameSave(filename);
Ui.CloseWindow();
onExit();
};
if (selectedSave != null || File.Exists(testPath))
{
ConfirmationDialogs.ButtonPrompt(
title: "Overwrite save game?",
text: "Overwrite {0}?".F(saveTextField.Text),
onConfirm: inner,
confirmText: "Overwrite",
onCancel: () => { });
}
else
inner();
}
void OnGameStart()
{
Ui.CloseWindow();
onStart();
}
bool disposed;
protected override void Dispose(bool disposing)
{
if (disposing && !disposed)
{
disposed = true;
Game.BeforeGameStart -= OnGameStart;
}
base.Dispose(disposing);
}
public static bool IsLoadPanelEnabled(Manifest mod)
{
var baseSavePath = Platform.ResolvePath(Platform.SupportDirPrefix, "Saves", mod.Id, mod.Metadata.Version);
if (!Directory.Exists(baseSavePath))
return false;
return Directory.GetFiles(baseSavePath, "*.orasav", SearchOption.AllDirectories).Any();
}
}
}

View File

@@ -49,6 +49,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic
{ {
{ "ABORT_MISSION", CreateAbortMissionButton }, { "ABORT_MISSION", CreateAbortMissionButton },
{ "SURRENDER", CreateSurrenderButton }, { "SURRENDER", CreateSurrenderButton },
{ "LOAD_GAME", CreateLoadGameButton },
{ "SAVE_GAME", CreateSaveGameButton },
{ "MUSIC", CreateMusicButton }, { "MUSIC", CreateMusicButton },
{ "SETTINGS", CreateSettingsButton }, { "SETTINGS", CreateSettingsButton },
{ "RESUME", CreateResumeButton }, { "RESUME", CreateResumeButton },
@@ -251,6 +253,46 @@ namespace OpenRA.Mods.Common.Widgets.Logic
}; };
} }
void CreateLoadGameButton()
{
if (world.Type != WorldType.Regular || !world.LobbyInfo.GlobalSettings.GameSavesEnabled || world.IsReplay)
return;
var button = AddButton("LOAD_GAME", "Load Game");
button.IsDisabled = () => leaving || !GameSaveBrowserLogic.IsLoadPanelEnabled(modData.Manifest);
button.OnClick = () =>
{
hideMenu = true;
Ui.OpenWindow("GAMESAVE_BROWSER_PANEL", new WidgetArgs
{
{ "onExit", () => hideMenu = false },
{ "onStart", CloseMenu },
{ "isSavePanel", false },
{ "world", null }
});
};
}
void CreateSaveGameButton()
{
if (world.Type != WorldType.Regular || !world.LobbyInfo.GlobalSettings.GameSavesEnabled || world.IsReplay)
return;
var button = AddButton("SAVE_GAME", "Save Game");
button.IsDisabled = () => hasError || leaving || !world.Players.Any(p => p.Playable && p.WinState == WinState.Undefined);
button.OnClick = () =>
{
hideMenu = true;
Ui.OpenWindow("GAMESAVE_BROWSER_PANEL", new WidgetArgs
{
{ "onExit", () => hideMenu = false },
{ "onStart", () => { } },
{ "isSavePanel", true },
{ "world", world }
});
};
}
void CreateMusicButton() void CreateMusicButton()
{ {
var button = AddButton("MUSIC", "Music"); var button = AddButton("MUSIC", "Music");

View File

@@ -28,7 +28,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
{ {
protected enum MenuType { Main, Singleplayer, Extras, MapEditor, SystemInfoPrompt, None } protected enum MenuType { Main, Singleplayer, Extras, MapEditor, SystemInfoPrompt, None }
protected enum MenuPanel { None, Missions, Skirmish, Multiplayer, MapEditor, Replays } protected enum MenuPanel { None, Missions, Skirmish, Multiplayer, MapEditor, Replays, GameSaves }
protected MenuType menuType = MenuType.Main; protected MenuType menuType = MenuType.Main;
readonly Widget rootMenu; readonly Widget rootMenu;
@@ -127,6 +127,10 @@ namespace OpenRA.Mods.Common.Widgets.Logic
skirmishButton.OnClick = StartSkirmishGame; skirmishButton.OnClick = StartSkirmishGame;
skirmishButton.Disabled = !hasMaps; skirmishButton.Disabled = !hasMaps;
var loadButton = singleplayerMenu.Get<ButtonWidget>("LOAD_BUTTON");
loadButton.IsDisabled = () => !GameSaveBrowserLogic.IsLoadPanelEnabled(modData.Manifest);
loadButton.OnClick = OpenGameSaveBrowserPanel;
singleplayerMenu.Get<ButtonWidget>("BACK_BUTTON").OnClick = () => SwitchMenu(MenuType.Main); singleplayerMenu.Get<ButtonWidget>("BACK_BUTTON").OnClick = () => SwitchMenu(MenuType.Main);
// Extras menu // Extras menu
@@ -500,6 +504,18 @@ namespace OpenRA.Mods.Common.Widgets.Logic
}); });
} }
void OpenGameSaveBrowserPanel()
{
SwitchMenu(MenuType.None);
Ui.OpenWindow("GAMESAVE_BROWSER_PANEL", new WidgetArgs
{
{ "onExit", () => SwitchMenu(MenuType.Singleplayer) },
{ "onStart", () => { RemoveShellmapUI(); lastGameState = MenuPanel.GameSaves; } },
{ "isSavePanel", false },
{ "world", null }
});
}
protected override void Dispose(bool disposing) protected override void Dispose(bool disposing)
{ {
if (disposing) if (disposing)
@@ -535,6 +551,10 @@ namespace OpenRA.Mods.Common.Widgets.Logic
case MenuPanel.MapEditor: case MenuPanel.MapEditor:
SwitchMenu(MenuType.MapEditor); SwitchMenu(MenuType.MapEditor);
break; break;
case MenuPanel.GameSaves:
SwitchMenu(MenuType.Singleplayer);
break;
} }
lastGameState = MenuPanel.None; lastGameState = MenuPanel.None;

View File

@@ -0,0 +1,115 @@
Container@GAMESAVE_BROWSER_PANEL:
Logic: GameSaveBrowserLogic
X: (WINDOW_RIGHT - WIDTH) / 2
Y: (WINDOW_BOTTOM - HEIGHT) / 2
Width: 600
Height: 400
Children:
Label@LOAD_TITLE:
Width: PARENT_RIGHT
Y: 0 - 25
Font: BigBold
Contrast: true
Align: Center
Text: Load game
Visible: False
Label@SAVE_TITLE:
Width: PARENT_RIGHT
Y: 0 - 25
Font: BigBold
Contrast: true
Align: Center
Text: Save game
Visible: False
Background@bg:
Width: PARENT_RIGHT
Height: PARENT_BOTTOM
Background: panel-black
Children:
ScrollPanel@GAME_LIST:
X: 10
Y: 10
Width: PARENT_RIGHT - 20
Height: PARENT_BOTTOM - 20
Children:
ScrollItem@NEW_TEMPLATE:
Width: PARENT_RIGHT - 27
Height: 25
X: 2
Visible: false
Children:
Label@TITLE:
Width: PARENT_RIGHT
Height: PARENT_BOTTOM
Align: Center
Text: [CREATE NEW FILE]
ScrollItem@GAME_TEMPLATE:
Width: PARENT_RIGHT - 27
Height: 25
X: 2
Visible: false
Children:
Label@TITLE:
X: 10
Width: PARENT_RIGHT - 200 - 10
Height: 25
Label@DATE:
X: PARENT_RIGHT - WIDTH - 10
Width: 200
Height: 25
Align: Right
Container@SAVE_WIDGETS:
X: 10
Y: PARENT_BOTTOM - 35
Width: PARENT_RIGHT - 20
Height: 35
Visible: False
Children:
TextField@SAVE_TEXTFIELD:
Width: PARENT_RIGHT
Height: 25
Type: Filename
Button@CANCEL_BUTTON:
Key: escape
X: 0
Y: PARENT_BOTTOM - 1
Width: 100
Height: 35
Text: Back
Button@DELETE_ALL_BUTTON:
X: PARENT_RIGHT - 330 - WIDTH
Y: PARENT_BOTTOM - 1
Width: 100
Height: 35
Text: Delete All
Button@DELETE_BUTTON:
X: PARENT_RIGHT - 220 - WIDTH
Y: PARENT_BOTTOM - 1
Width: 100
Height: 35
Text: Delete
Key: Delete
Button@RENAME_BUTTON:
X: PARENT_RIGHT - 110 - WIDTH
Y: PARENT_BOTTOM - 1
Width: 100
Height: 35
Text: Rename
Key: F2
Button@LOAD_BUTTON:
Key: return
X: PARENT_RIGHT - WIDTH
Y: PARENT_BOTTOM - 1
Width: 100
Height: 35
Text: Load
Visible: False
Button@SAVE_BUTTON:
Key: return
X: PARENT_RIGHT - WIDTH
Y: PARENT_BOTTOM - 1
Width: 100
Height: 35
Text: Save
Visible: False
TooltipContainer@TOOLTIP_CONTAINER:

View File

@@ -119,6 +119,12 @@ Container@MENU_BACKGROUND:
Width: 140 Width: 140
Height: 35 Height: 35
Text: Missions Text: Missions
Button@LOAD_BUTTON:
X: 300
Y: 0
Width: 140
Height: 35
Text: Load
Button@BACK_BUTTON: Button@BACK_BUTTON:
Key: escape Key: escape
X: 450 X: 450

View File

@@ -107,6 +107,7 @@ ChromeLayout:
cnc|chrome/color-picker.yaml cnc|chrome/color-picker.yaml
cnc|chrome/mapchooser.yaml cnc|chrome/mapchooser.yaml
cnc|chrome/replaybrowser.yaml cnc|chrome/replaybrowser.yaml
cnc|chrome/gamesave-browser.yaml
cnc|chrome/gamesave-loading.yaml cnc|chrome/gamesave-loading.yaml
cnc|chrome/ingame.yaml cnc|chrome/ingame.yaml
cnc|chrome/ingame-chat.yaml cnc|chrome/ingame-chat.yaml

View File

@@ -0,0 +1,116 @@
Background@GAMESAVE_BROWSER_PANEL:
Logic: GameSaveBrowserLogic
X: (WINDOW_RIGHT - WIDTH) / 2
Y: (WINDOW_BOTTOM - HEIGHT) / 2
Width: 600
Height: 400
Children:
Label@LOAD_TITLE:
Width: PARENT_RIGHT
Y: 20
Height: 25
Font: Bold
Align: Center
Text: Load game
Visible: False
Label@SAVE_TITLE:
Width: PARENT_RIGHT
Y: 20
Height: 25
Font: Bold
Align: Center
Text: Save game
Visible: False
ScrollPanel@GAME_LIST:
X: 20
Y: 50
Width: PARENT_RIGHT - 40
Height: PARENT_BOTTOM - 102
Children:
ScrollItem@NEW_TEMPLATE:
Width: PARENT_RIGHT - 27
Height: 25
X: 2
Visible: false
Children:
Label@TITLE:
Width: PARENT_RIGHT
Height: PARENT_BOTTOM
Align: Center
Text: [CREATE NEW FILE]
ScrollItem@GAME_TEMPLATE:
Width: PARENT_RIGHT - 27
Height: 25
X: 2
Visible: false
Children:
Label@TITLE:
X: 10
Width: PARENT_RIGHT - 200 - 10
Height: 25
Label@DATE:
X: PARENT_RIGHT - WIDTH - 10
Width: 200
Height: 25
Align: Right
Container@SAVE_WIDGETS:
X: 20
Y: PARENT_BOTTOM - 77
Width: PARENT_RIGHT - 40
Height: 32
Visible: False
Children:
TextField@SAVE_TEXTFIELD:
Width: PARENT_RIGHT
Height: 25
Type: Filename
Button@CANCEL_BUTTON:
Key: escape
X: 20
Y: PARENT_BOTTOM - 45
Width: 100
Height: 25
Text: Back
Font: Bold
Button@DELETE_ALL_BUTTON:
X: PARENT_RIGHT - 350 - WIDTH
Y: PARENT_BOTTOM - 45
Width: 100
Height: 25
Text: Delete All
Font: Bold
Button@DELETE_BUTTON:
X: PARENT_RIGHT - 240 - WIDTH
Y: PARENT_BOTTOM - 45
Width: 100
Height: 25
Text: Delete
Font: Bold
Key: Delete
Button@RENAME_BUTTON:
X: PARENT_RIGHT - 130 - WIDTH
Y: PARENT_BOTTOM - 45
Width: 100
Height: 25
Text: Rename
Font: Bold
Key: F2
Button@LOAD_BUTTON:
Key: return
X: PARENT_RIGHT - WIDTH - 20
Y: PARENT_BOTTOM - 45
Width: 100
Height: 25
Text: Load
Font: Bold
Visible: False
Button@SAVE_BUTTON:
Key: return
X: PARENT_RIGHT - WIDTH - 20
Y: PARENT_BOTTOM - 45
Width: 100
Height: 25
Text: Save
Font: Bold
Visible: False
TooltipContainer@TOOLTIP_CONTAINER:

View File

@@ -114,6 +114,13 @@ Container@MAINMENU:
Height: 30 Height: 30
Text: Missions Text: Missions
Font: Bold Font: Bold
Button@LOAD_BUTTON:
X: PARENT_RIGHT / 2 - WIDTH / 2
Y: 140
Width: 140
Height: 30
Text: Load
Font: Bold
Button@BACK_BUTTON: Button@BACK_BUTTON:
X: PARENT_RIGHT / 2 - WIDTH / 2 X: PARENT_RIGHT / 2 - WIDTH / 2
Key: escape Key: escape

View File

@@ -101,6 +101,13 @@ Container@MAINMENU:
Height: 30 Height: 30
Text: Missions Text: Missions
Font: Bold Font: Bold
Button@LOAD_BUTTON:
X: PARENT_RIGHT / 2 - WIDTH / 2
Y: 140
Width: 140
Height: 30
Text: Load
Font: Bold
Button@BACK_BUTTON: Button@BACK_BUTTON:
X: PARENT_RIGHT / 2 - WIDTH / 2 X: PARENT_RIGHT / 2 - WIDTH / 2
Key: escape Key: escape

View File

@@ -107,6 +107,7 @@ ChromeLayout:
common|chrome/confirmation-dialogs.yaml common|chrome/confirmation-dialogs.yaml
common|chrome/editor.yaml common|chrome/editor.yaml
common|chrome/replaybrowser.yaml common|chrome/replaybrowser.yaml
common|chrome/gamesave-browser.yaml
common|chrome/gamesave-loading.yaml common|chrome/gamesave-loading.yaml
Weapons: Weapons:

View File

@@ -114,6 +114,7 @@ ChromeLayout:
common|chrome/multiplayer-directconnect.yaml common|chrome/multiplayer-directconnect.yaml
common|chrome/connection.yaml common|chrome/connection.yaml
common|chrome/replaybrowser.yaml common|chrome/replaybrowser.yaml
common|chrome/gamesave-browser.yaml
ra|chrome/gamesave-loading.yaml ra|chrome/gamesave-loading.yaml
common|chrome/dropdowns.yaml common|chrome/dropdowns.yaml
common|chrome/musicplayer.yaml common|chrome/musicplayer.yaml

View File

@@ -163,6 +163,7 @@ ChromeLayout:
common|chrome/multiplayer-directconnect.yaml common|chrome/multiplayer-directconnect.yaml
common|chrome/connection.yaml common|chrome/connection.yaml
common|chrome/replaybrowser.yaml common|chrome/replaybrowser.yaml
common|chrome/gamesave-browser.yaml
common|chrome/gamesave-loading.yaml common|chrome/gamesave-loading.yaml
ts|chrome/dropdowns.yaml ts|chrome/dropdowns.yaml
common|chrome/musicplayer.yaml common|chrome/musicplayer.yaml