Dynamically populate the ingame menu.

This commit is contained in:
Paul Chote
2019-04-14 21:03:13 +00:00
committed by reaperrr
parent 1cfb110d03
commit 877215c86a
4 changed files with 221 additions and 245 deletions

View File

@@ -10,6 +10,7 @@
#endregion #endregion
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using OpenRA.Graphics; using OpenRA.Graphics;
using OpenRA.Mods.Common.Scripting; using OpenRA.Mods.Common.Scripting;
@@ -21,27 +22,94 @@ namespace OpenRA.Mods.Common.Widgets.Logic
public class IngameMenuLogic : ChromeLogic public class IngameMenuLogic : ChromeLogic
{ {
readonly Widget menu; readonly Widget menu;
readonly Widget buttonContainer;
readonly ButtonWidget buttonTemplate;
readonly int2 buttonStride;
readonly List<ButtonWidget> buttons = new List<ButtonWidget>();
readonly ModData modData;
readonly Action onExit;
readonly World world;
readonly WorldRenderer worldRenderer;
readonly MenuPaletteEffect mpe;
bool hasError;
bool leaving;
bool hideMenu;
[ObjectCreator.UseCtor] [ObjectCreator.UseCtor]
public IngameMenuLogic(Widget widget, ModData modData, World world, Action onExit, WorldRenderer worldRenderer, IngameInfoPanel activePanel) public IngameMenuLogic(Widget widget, ModData modData, World world, Action onExit, WorldRenderer worldRenderer,
IngameInfoPanel activePanel, Dictionary<string, MiniYaml> logicArgs)
{ {
var leaving = false; this.modData = modData;
this.world = world;
this.worldRenderer = worldRenderer;
this.onExit = onExit;
var buttonHandlers = new Dictionary<string, Action>
{
{ "ABORT_MISSION", CreateAbortMissionButton },
{ "SURRENDER", CreateSurrenderButton },
{ "MUSIC", CreateMusicButton },
{ "SETTINGS", CreateSettingsButton },
{ "RESUME", CreateResumeButton },
{ "SAVE_MAP", CreateSaveMapButton },
{ "EXIT_EDITOR", CreateExitEditorButton }
};
menu = widget.Get("INGAME_MENU"); menu = widget.Get("INGAME_MENU");
var mpe = world.WorldActor.TraitOrDefault<MenuPaletteEffect>(); mpe = world.WorldActor.TraitOrDefault<MenuPaletteEffect>();
if (mpe != null) if (mpe != null)
mpe.Fade(mpe.Info.MenuEffect); mpe.Fade(mpe.Info.MenuEffect);
menu.Get<LabelWidget>("VERSION_LABEL").Text = modData.Manifest.Metadata.Version; menu.Get<LabelWidget>("VERSION_LABEL").Text = modData.Manifest.Metadata.Version;
var hideMenu = false; buttonContainer = menu.Get("MENU_BUTTONS");
menu.Get("MENU_BUTTONS").IsVisible = () => !hideMenu; buttonTemplate = buttonContainer.Get<ButtonWidget>("BUTTON_TEMPLATE");
buttonContainer.RemoveChild(buttonTemplate);
buttonContainer.IsVisible = () => !hideMenu;
MiniYaml buttonStrideNode;
if (logicArgs.TryGetValue("ButtonStride", out buttonStrideNode))
buttonStride = FieldLoader.GetValue<int2>("ButtonStride", buttonStrideNode.Value);
var scriptContext = world.WorldActor.TraitOrDefault<LuaScript>(); var scriptContext = world.WorldActor.TraitOrDefault<LuaScript>();
var hasError = scriptContext != null && scriptContext.FatalErrorOccurred; hasError = scriptContext != null && scriptContext.FatalErrorOccurred;
// TODO: Create a mechanism to do things like this cleaner. Also needed for scripted missions MiniYaml buttonsNode;
Action onQuit = () => if (logicArgs.TryGetValue("Buttons", out buttonsNode))
{ {
Action createHandler;
var buttonIds = FieldLoader.GetValue<string[]>("Buttons", buttonsNode.Value);
foreach (var button in buttonIds)
if (buttonHandlers.TryGetValue(button, out createHandler))
createHandler();
}
// Recenter the button container
if (buttons.Count > 0)
{
var expand = (buttons.Count - 1) * buttonStride;
buttonContainer.Bounds.X -= expand.X / 2;
buttonContainer.Bounds.Y -= expand.Y / 2;
buttonContainer.Bounds.Width += expand.X;
buttonContainer.Bounds.Height += expand.Y;
}
var panelRoot = widget.GetOrNull("PANEL_ROOT");
if (panelRoot != null && world.Type != WorldType.Editor)
{
var gameInfoPanel = Game.LoadWidget(world, "GAME_INFO_PANEL", panelRoot, new WidgetArgs()
{
{ "activePanel", activePanel }
});
gameInfoPanel.IsVisible = () => !hideMenu;
}
}
void OnQuit()
{
// TODO: Create a mechanism to do things like this cleaner. Also needed for scripted missions
if (world.Type == WorldType.Regular) if (world.Type == WorldType.Regular)
{ {
var moi = world.Map.Rules.Actors["player"].TraitInfoOrDefault<MissionObjectivesInfo>(); var moi = world.Map.Rules.Actors["player"].TraitInfoOrDefault<MissionObjectivesInfo>();
@@ -75,25 +143,48 @@ namespace OpenRA.Mods.Common.Widgets.Logic
Ui.ResetAll(); Ui.ResetAll();
Game.LoadShellMap(); Game.LoadShellMap();
}); });
}; }
Action closeMenu = () => void ShowMenu()
{
hideMenu = false;
}
void CloseMenu()
{ {
Ui.CloseWindow(); Ui.CloseWindow();
if (mpe != null) if (mpe != null)
mpe.Fade(MenuPaletteEffect.EffectType.None); mpe.Fade(MenuPaletteEffect.EffectType.None);
onExit(); onExit();
}; }
Action showMenu = () => hideMenu = false; ButtonWidget AddButton(string id, string text)
{
var button = buttonTemplate.Clone() as ButtonWidget;
var lastButton = buttons.LastOrDefault();
if (lastButton != null)
{
button.Bounds.X = lastButton.Bounds.X + buttonStride.X;
button.Bounds.Y = lastButton.Bounds.Y + buttonStride.Y;
}
var abortMissionButton = menu.Get<ButtonWidget>("ABORT_MISSION"); button.Id = id;
abortMissionButton.IsVisible = () => world.Type == WorldType.Regular; button.IsDisabled = () => leaving;
abortMissionButton.IsDisabled = () => leaving; button.GetText = () => text;
if (world.IsGameOver) buttonContainer.AddChild(button);
abortMissionButton.GetText = () => "Leave"; buttons.Add(button);
abortMissionButton.OnClick = () => return button;
}
void CreateAbortMissionButton()
{
if (world.Type != WorldType.Regular)
return;
var button = AddButton("ABORT_MISSION", world.IsGameOver ? "Leave" : "Abort Mission");
button.OnClick = () =>
{ {
hideMenu = true; hideMenu = true;
@@ -122,54 +213,87 @@ namespace OpenRA.Mods.Common.Widgets.Logic
ConfirmationDialogs.ButtonPrompt( ConfirmationDialogs.ButtonPrompt(
title: "Leave Mission", title: "Leave Mission",
text: "Leave this game and return to the menu?", text: "Leave this game and return to the menu?",
onConfirm: onQuit, onConfirm: OnQuit,
onCancel: showMenu, onCancel: ShowMenu,
confirmText: "Leave", confirmText: "Leave",
cancelText: "Stay", cancelText: "Stay",
otherText: "Restart", otherText: "Restart",
onOther: restartAction); onOther: restartAction);
} }
else else
onQuit(); OnQuit();
}; };
}
var exitEditorButton = menu.Get<ButtonWidget>("EXIT_EDITOR"); void CreateSurrenderButton()
exitEditorButton.IsVisible = () => world.Type == WorldType.Editor;
exitEditorButton.OnClick = () =>
{ {
hideMenu = true; if (world.Type != WorldType.Regular || world.Map.Visibility.HasFlag(MapVisibility.MissionSelector) || world.LocalPlayer == null)
ConfirmationDialogs.ButtonPrompt( return;
title: "Exit Map Editor",
text: "Exit and lose all unsaved changes?",
onConfirm: onQuit,
onCancel: showMenu);
};
Action onSurrender = () => Action onSurrender = () =>
{ {
world.IssueOrder(new Order("Surrender", world.LocalPlayer.PlayerActor, false)); world.IssueOrder(new Order("Surrender", world.LocalPlayer.PlayerActor, false));
closeMenu(); CloseMenu();
}; };
var surrenderButton = menu.Get<ButtonWidget>("SURRENDER");
surrenderButton.IsVisible = () => world.Type == WorldType.Regular; var button = AddButton("SURRENDER", "Surrender");
surrenderButton.IsDisabled = () => button.IsDisabled = () => world.LocalPlayer.WinState != WinState.Undefined || hasError || leaving;
world.LocalPlayer == null || world.LocalPlayer.WinState != WinState.Undefined || button.OnClick = () =>
world.Map.Visibility.HasFlag(MapVisibility.MissionSelector) || hasError;
surrenderButton.OnClick = () =>
{ {
hideMenu = true; hideMenu = true;
ConfirmationDialogs.ButtonPrompt( ConfirmationDialogs.ButtonPrompt(
title: "Surrender", title: "Surrender",
text: "Are you sure you want to surrender?", text: "Are you sure you want to surrender?",
onConfirm: onSurrender, onConfirm: onSurrender,
onCancel: showMenu, onCancel: ShowMenu,
confirmText: "Surrender", confirmText: "Surrender",
cancelText: "Stay"); cancelText: "Stay");
}; };
}
var saveMapButton = menu.Get<ButtonWidget>("SAVE_MAP"); void CreateMusicButton()
saveMapButton.IsVisible = () => world.Type == WorldType.Editor; {
saveMapButton.OnClick = () => var button = AddButton("MUSIC", "Music");
button.OnClick = () =>
{
hideMenu = true;
Ui.OpenWindow("MUSIC_PANEL", new WidgetArgs()
{
{ "onExit", () => hideMenu = false },
{ "world", world }
});
};
}
void CreateSettingsButton()
{
var button = AddButton("SETTINGS", "Settings");
button.OnClick = () =>
{
hideMenu = true;
Ui.OpenWindow("SETTINGS_PANEL", new WidgetArgs()
{
{ "world", world },
{ "worldRenderer", worldRenderer },
{ "onExit", () => hideMenu = false },
});
};
}
void CreateResumeButton()
{
var button = AddButton("RESUME", world.IsGameOver ? "Return to map" : "Resume");
button.Key = modData.Hotkeys["escape"];
button.OnClick = CloseMenu;
}
void CreateSaveMapButton()
{
if (world.Type != WorldType.Editor)
return;
var button = AddButton("SAVE_MAP", "Save Map");
button.OnClick = () =>
{ {
hideMenu = true; hideMenu = true;
var editorActorLayer = world.WorldActor.Trait<EditorActorLayer>(); var editorActorLayer = world.WorldActor.Trait<EditorActorLayer>();
@@ -182,49 +306,23 @@ namespace OpenRA.Mods.Common.Widgets.Logic
{ "actorDefinitions", editorActorLayer.Save() } { "actorDefinitions", editorActorLayer.Save() }
}); });
}; };
}
var musicButton = menu.Get<ButtonWidget>("MUSIC"); void CreateExitEditorButton()
musicButton.IsDisabled = () => leaving; {
musicButton.OnClick = () => if (world.Type != WorldType.Editor)
return;
var button = AddButton("EXIT_EDITOR", "Exit Map Editor");
button.OnClick = () =>
{ {
hideMenu = true; hideMenu = true;
Ui.OpenWindow("MUSIC_PANEL", new WidgetArgs() ConfirmationDialogs.ButtonPrompt(
{ title: "Exit Map Editor",
{ "onExit", () => hideMenu = false }, text: "Exit and lose all unsaved changes?",
{ "world", world } onConfirm: OnQuit,
}); onCancel: ShowMenu);
}; };
var settingsButton = widget.Get<ButtonWidget>("SETTINGS");
settingsButton.IsDisabled = () => leaving;
settingsButton.OnClick = () =>
{
hideMenu = true;
Ui.OpenWindow("SETTINGS_PANEL", new WidgetArgs()
{
{ "world", world },
{ "worldRenderer", worldRenderer },
{ "onExit", () => hideMenu = false },
});
};
var resumeButton = menu.Get<ButtonWidget>("RESUME");
resumeButton.IsDisabled = () => leaving;
if (world.IsGameOver)
resumeButton.GetText = () => "Return to map";
resumeButton.OnClick = closeMenu;
var panelRoot = widget.GetOrNull("PANEL_ROOT");
if (panelRoot != null && world.Type != WorldType.Editor)
{
var gameInfoPanel = Game.LoadWidget(world, "GAME_INFO_PANEL", panelRoot, new WidgetArgs()
{
{ "activePanel", activePanel }
});
gameInfoPanel.IsVisible = () => !hideMenu;
}
} }
} }
} }

View File

@@ -2,6 +2,8 @@ Container@INGAME_MENU:
Width: WINDOW_RIGHT Width: WINDOW_RIGHT
Height: WINDOW_BOTTOM Height: WINDOW_BOTTOM
Logic: IngameMenuLogic Logic: IngameMenuLogic
Buttons: EXIT_EDITOR, SAVE_MAP, ABORT_MISSION, SURRENDER, LOAD_GAME, SAVE_GAME, MUSIC, SETTINGS, RESUME
ButtonStride: 130, 0
Children: Children:
Image@EVA: Image@EVA:
X: WINDOW_RIGHT - 128 - 43 X: WINDOW_RIGHT - 128 - 43
@@ -24,49 +26,9 @@ Container@INGAME_MENU:
Container@MENU_BUTTONS: Container@MENU_BUTTONS:
X: (WINDOW_RIGHT - WIDTH) / 2 X: (WINDOW_RIGHT - WIDTH) / 2
Y: WINDOW_BOTTOM - 33 - HEIGHT - 10 Y: WINDOW_BOTTOM - 33 - HEIGHT - 10
Width: 740 Width: 120
Height: 35 Height: 35
Children: Children:
Button@ABORT_MISSION: Button@BUTTON_TEMPLATE:
X: 0 Width: 120
Y: 0
Width: 140
Height: 35 Height: 35
Text: Abort Mission
Button@EXIT_EDITOR:
X: 0
Y: 0
Width: 140
Height: 35
Text: Exit Map Editor
Button@SURRENDER:
X: 150
Y: 0
Width: 140
Height: 35
Text: Surrender
Button@SAVE_MAP:
X: 150
Y: 0
Width: 140
Height: 35
Text: Save Map
Button@MUSIC:
X: 300
Y: 0
Width: 140
Height: 35
Text: Music
Button@SETTINGS:
X: 450
Y: 0
Width: 140
Height: 35
Text: Settings
Button@RESUME:
Key: escape
X: 600
Y: 0
Width: 140
Height: 35
Text: Resume

View File

@@ -2,6 +2,8 @@ Container@INGAME_MENU:
Width: WINDOW_RIGHT Width: WINDOW_RIGHT
Height: WINDOW_BOTTOM Height: WINDOW_BOTTOM
Logic: IngameMenuLogic Logic: IngameMenuLogic
Buttons: RESUME, LOAD_GAME, SAVE_GAME, SETTINGS, MUSIC, SURRENDER, ABORT_MISSION, SAVE_MAP, EXIT_EDITOR
ButtonStride: 0, 40
Children: Children:
Background@BORDER: Background@BORDER:
X: 0 - 15 X: 0 - 15
@@ -27,7 +29,7 @@ Container@INGAME_MENU:
X: 13 + (WINDOW_RIGHT - 522) / 4 - WIDTH / 2 X: 13 + (WINDOW_RIGHT - 522) / 4 - WIDTH / 2
Y: (WINDOW_BOTTOM - HEIGHT) / 2 Y: (WINDOW_BOTTOM - HEIGHT) / 2
Width: 200 Width: 200
Height: 295 Height: 120
Children: Children:
Label@LABEL_TITLE: Label@LABEL_TITLE:
X: (PARENT_RIGHT - WIDTH) / 2 X: (PARENT_RIGHT - WIDTH) / 2
@@ -37,53 +39,9 @@ Container@INGAME_MENU:
Text: Options Text: Options
Align: Center Align: Center
Font: Bold Font: Bold
Button@RESUME: Button@BUTTON_TEMPLATE:
X: (PARENT_RIGHT - WIDTH) / 2 X: (PARENT_RIGHT - WIDTH) / 2
Y: 60 Y: 60
Width: 140 Width: 140
Height: 30 Height: 30
Text: Resume
Font: Bold
Key: escape
Button@SETTINGS:
X: (PARENT_RIGHT - WIDTH) / 2
Y: 100
Width: 140
Height: 30
Text: Settings
Font: Bold
Button@MUSIC:
X: (PARENT_RIGHT - WIDTH) / 2
Y: 140
Width: 140
Height: 30
Text: Music
Font: Bold
Button@SURRENDER:
X: (PARENT_RIGHT - WIDTH) / 2
Y: 180
Width: 140
Height: 30
Text: Surrender
Font: Bold
Button@SAVE_MAP:
X: (PARENT_RIGHT - WIDTH) / 2
Y: 180
Width: 140
Height: 30
Text: Save Map
Font: Bold
Button@ABORT_MISSION:
X: (PARENT_RIGHT - WIDTH) / 2
Y: 220
Width: 140
Height: 30
Text: Abort Mission
Font: Bold
Button@EXIT_EDITOR:
X: (PARENT_RIGHT - WIDTH) / 2
Y: 220
Width: 140
Height: 30
Text: Exit Map Editor
Font: Bold Font: Bold

View File

@@ -2,6 +2,8 @@ Container@INGAME_MENU:
Width: WINDOW_RIGHT Width: WINDOW_RIGHT
Height: WINDOW_BOTTOM Height: WINDOW_BOTTOM
Logic: IngameMenuLogic Logic: IngameMenuLogic
Buttons: RESUME, LOAD_GAME, SAVE_GAME, SETTINGS, MUSIC, SURRENDER, ABORT_MISSION, SAVE_MAP, EXIT_EDITOR
ButtonStride: 0, 40
Children: Children:
Label@VERSION_LABEL: Label@VERSION_LABEL:
X: WINDOW_RIGHT - 10 X: WINDOW_RIGHT - 10
@@ -14,7 +16,7 @@ Container@INGAME_MENU:
X: 13 + (WINDOW_RIGHT - 522) / 4 - WIDTH / 2 X: 13 + (WINDOW_RIGHT - 522) / 4 - WIDTH / 2
Y: (WINDOW_BOTTOM - HEIGHT) / 2 Y: (WINDOW_BOTTOM - HEIGHT) / 2
Width: 200 Width: 200
Height: 295 Height: 120
Children: Children:
Label@LABEL_TITLE: Label@LABEL_TITLE:
X: (PARENT_RIGHT - WIDTH) / 2 X: (PARENT_RIGHT - WIDTH) / 2
@@ -24,53 +26,9 @@ Container@INGAME_MENU:
Text: Options Text: Options
Align: Center Align: Center
Font: Bold Font: Bold
Button@RESUME: Button@BUTTON_TEMPLATE:
X: (PARENT_RIGHT - WIDTH) / 2 X: (PARENT_RIGHT - WIDTH) / 2
Y: 60 Y: 60
Width: 140 Width: 140
Height: 30 Height: 30
Text: Resume
Font: Bold
Key: escape
Button@SETTINGS:
X: (PARENT_RIGHT - WIDTH) / 2
Y: 100
Width: 140
Height: 30
Text: Settings
Font: Bold
Button@MUSIC:
X: (PARENT_RIGHT - WIDTH) / 2
Y: 140
Width: 140
Height: 30
Text: Music
Font: Bold
Button@SURRENDER:
X: (PARENT_RIGHT - WIDTH) / 2
Y: 180
Width: 140
Height: 30
Text: Surrender
Font: Bold
Button@SAVE_MAP:
X: (PARENT_RIGHT - WIDTH) / 2
Y: 180
Width: 140
Height: 30
Text: Save Map
Font: Bold
Button@ABORT_MISSION:
X: (PARENT_RIGHT - WIDTH) / 2
Y: 220
Width: 140
Height: 30
Text: Abort Mission
Font: Bold
Button@EXIT_EDITOR:
X: (PARENT_RIGHT - WIDTH) / 2
Y: 220
Width: 140
Height: 30
Text: Exit Map Editor
Font: Bold Font: Bold