コード例 #1
0
        void CreateSurrenderButton()
        {
            if ((world.Type == WorldType.Shellmap || world.Type == WorldType.Editor) || world.LocalPlayer == null)
            {
                return;
            }

            if (world.Type != WorldType.Capmaign && world.Map.Visibility.HasFlag(MapVisibility.MissionSelector))
            {
                return;
            }

            Action onSurrender = () =>
            {
                world.IssueOrder(new Order("Surrender", world.LocalPlayer.PlayerActor, false));
                CloseMenu();
            };

            var button = AddButton("SURRENDER", "Surrender");

            button.IsDisabled = () => world.LocalPlayer.WinState != WinState.Undefined || hasError || leaving;
            button.OnClick    = () =>
            {
                hideMenu = true;
                ConfirmationDialogs.ButtonPrompt(
                    title: "Surrender",
                    text: "Are you sure you want to surrender?",
                    onConfirm: onSurrender,
                    onCancel: ShowMenu,
                    confirmText: "Surrender",
                    cancelText: "Stay");
            };
        }
コード例 #2
0
        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();
            }
        }
コード例 #3
0
        private void OpenWorldDominationPanel()
        {
            bool founded = File.Exists(Path.GetFullPath(Platform.ResolvePath("^Content/ra2/wdt.mix")));

            if (!founded)
            {
                ConfirmationDialogs.ButtonPrompt(
                    title: "Error",
                    text: "Can't find wdt.mix!",
                    confirmText: "Retry",
                    cancelText: "Quit",
                    onCancel: close,
                    onConfirm: OpenWorldDominationPanel);
            }
            else
            {
                SwitchMenu(MenuType.None);
                Game.OpenWindow("WDT_PANEL", new WidgetArgs
                {
                    { "onStart", () => { RemoveShellmapUI(); lastGameState = MenuPanel.Multiplayer; } },
                    { "onExit", () => SwitchMenu(MenuType.Multiplayer) },
                    { "directConnectHost", null },
                    { "directConnectPort", 0 },
                });
            }
        }
コード例 #4
0
        void CreateSurrenderButton()
        {
            if (world.Type != WorldType.Regular || isSinglePlayer || world.LocalPlayer == null)
            {
                return;
            }

            Action onSurrender = () =>
            {
                world.IssueOrder(new Order("Surrender", world.LocalPlayer.PlayerActor, false));
                CloseMenu();
            };

            var button = AddButton("SURRENDER", "Surrender");

            button.IsDisabled = () => world.LocalPlayer.WinState != WinState.Undefined || hasError || leaving;
            button.OnClick    = () =>
            {
                hideMenu = true;
                ConfirmationDialogs.ButtonPrompt(
                    title: "Surrender",
                    text: "Are you sure you want to surrender?",
                    onConfirm: onSurrender,
                    onCancel: ShowMenu,
                    confirmText: "Surrender",
                    cancelText: "Stay");
            };
        }
コード例 #5
0
        void CreateExitEditorButton()
        {
            if (world.Type != WorldType.Editor)
            {
                return;
            }

            var actionManager = world.WorldActor.Trait <EditorActionManager>();
            var button        = AddButton("EXIT_EDITOR", "Exit Map Editor");

            // Show dialog only if updated since last save
            button.OnClick = () =>
            {
                if (actionManager.HasUnsavedItems())
                {
                    hideMenu = true;
                    ConfirmationDialogs.ButtonPrompt(
                        title: "Exit Map Editor",
                        text: "Exit and lose all unsaved changes?",
                        onConfirm: OnQuit,
                        onCancel: ShowMenu);
                }
                else
                {
                    OnQuit();
                }
            };
        }
コード例 #6
0
        void PlayVideo(VideoPlayerWidget player, string video, PlayingVideo pv, Action onComplete = null)
        {
            if (!modData.DefaultFileSystem.Exists(video))
            {
                ConfirmationDialogs.ButtonPrompt(
                    title: "Video not installed",
                    text: "The game videos can be installed from the\n\"Manage Content\" menu in the mod chooser.",
                    cancelText: "Back",
                    onCancel: () => { });
            }
            else
            {
                StopVideo(player);

                playingVideo = pv;
                player.Load(video);

                // video playback runs asynchronously
                player.PlayThen(() =>
                {
                    StopVideo(player);
                    onComplete?.Invoke();
                });

                // Mute other distracting sounds
                MuteSounds();
            }
        }
コード例 #7
0
        static bool IncompatibleReplayDialog(string type, string name, Action onCancel)
        {
            var error = "It was recorded with an " + type;

            error += string.IsNullOrEmpty(name) ? "." : ":\n{0}".F(name);

            ConfirmationDialogs.ButtonPrompt("Incompatible Replay", error, onCancel: onCancel);

            return(false);
        }
コード例 #8
0
ファイル: SettingsLogic.cs プロジェクト: reaperrr/OpenRA
        public SettingsLogic(Widget widget, Action onExit, ModData modData, WorldRenderer worldRenderer, Dictionary <string, MiniYaml> logicArgs)
        {
            this.worldRenderer = worldRenderer;
            this.modData       = modData;
            this.logicArgs     = logicArgs;

            panelContainer = widget.Get("SETTINGS_PANEL");
            tabContainer   = widget.Get("TAB_CONTAINER");

            RegisterSettingsPanel(PanelType.Display, InitDisplayPanel, ResetDisplayPanel, "DISPLAY_PANEL", "DISPLAY_TAB");
            RegisterSettingsPanel(PanelType.Audio, InitAudioPanel, ResetAudioPanel, "AUDIO_PANEL", "AUDIO_TAB");
            RegisterSettingsPanel(PanelType.Input, InitInputPanel, ResetInputPanel, "INPUT_PANEL", "INPUT_TAB");
            RegisterSettingsPanel(PanelType.Hotkeys, InitHotkeysPanel, ResetHotkeysPanel, "HOTKEYS_PANEL", "HOTKEYS_TAB");
            RegisterSettingsPanel(PanelType.Advanced, InitAdvancedPanel, ResetAdvancedPanel, "ADVANCED_PANEL", "ADVANCED_TAB");

            panelContainer.Get <ButtonWidget>("BACK_BUTTON").OnClick = () =>
            {
                leavePanelActions[settingsPanel]();
                var current = Game.Settings;
                current.Save();

                Action closeAndExit = () => { Ui.CloseWindow(); onExit(); };
                if (current.Sound.Device != OriginalSoundDevice ||
                    current.Graphics.Mode != OriginalGraphicsMode ||
                    current.Graphics.WindowedSize != OriginalGraphicsWindowedSize ||
                    current.Graphics.FullscreenSize != OriginalGraphicsFullscreenSize ||
                    current.Server.DiscoverNatDevices != OriginalServerDiscoverNatDevices)
                {
                    Action restart = () =>
                    {
                        var external = Game.ExternalMods[ExternalMod.MakeKey(Game.ModData.Manifest)];
                        Game.SwitchToExternalMod(external, null, closeAndExit);
                    };

                    ConfirmationDialogs.ButtonPrompt(
                        title: "Restart Now?",
                        text: "Some changes will not be applied until\nthe game is restarted. Restart now?",
                        onConfirm: restart,
                        onCancel: closeAndExit,
                        confirmText: "Restart Now",
                        cancelText: "Restart Later");
                }
                else
                {
                    closeAndExit();
                }
            };

            panelContainer.Get <ButtonWidget>("RESET_BUTTON").OnClick = () =>
            {
                resetPanelActions[settingsPanel]();
                Game.Settings.Save();
            };
        }
コード例 #9
0
        void CreateAndJoin()
        {
            var name = Settings.SanitizedServerName(panel.Get <TextFieldWidget>("SERVER_NAME").Text);
            int listenPort, externalPort;

            if (!Exts.TryParseIntegerInvariant(panel.Get <TextFieldWidget>("LISTEN_PORT").Text, out listenPort))
            {
                listenPort = 1234;
            }

            if (!Exts.TryParseIntegerInvariant(panel.Get <TextFieldWidget>("EXTERNAL_PORT").Text, out externalPort))
            {
                externalPort = 1234;
            }

            var passwordField = panel.GetOrNull <PasswordFieldWidget>("PASSWORD");
            var password      = passwordField != null ? passwordField.Text : "";

            // Save new settings
            Game.Settings.Server.Name             = name;
            Game.Settings.Server.ListenPort       = listenPort;
            Game.Settings.Server.ExternalPort     = externalPort;
            Game.Settings.Server.AdvertiseOnline  = advertiseOnline;
            Game.Settings.Server.AllowPortForward = allowPortForward;
            Game.Settings.Server.Map      = preview.Uid;
            Game.Settings.Server.Password = password;
            Game.Settings.Save();

            // Take a copy so that subsequent changes don't affect the server
            var settings = Game.Settings.Server.Clone();

            // Create and join the server
            try
            {
                Game.CreateServer(settings);
            }
            catch (System.Net.Sockets.SocketException e)
            {
                var message = "Could not listen on port {0}.".F(Game.Settings.Server.ListenPort);
                if (e.ErrorCode == 10048)                   // AddressAlreadyInUse (WSAEADDRINUSE)
                {
                    message += "\nCheck if the port is already being used.";
                }
                else
                {
                    message += "\nError is: \"{0}\" ({1})".F(e.Message, e.ErrorCode);
                }

                ConfirmationDialogs.ButtonPrompt("Server Creation Failed", message, onCancel: () => { }, cancelText: "Back");
                return;
            }

            ConnectionLogic.Connect(IPAddress.Loopback.ToString(), Game.Settings.Server.ListenPort, password, onCreate, onExit);
        }
コード例 #10
0
ファイル: IngameMenuLogic.cs プロジェクト: reaperrr/OpenRA
        void CreateAbortMissionButton()
        {
            if (world.Type != WorldType.Regular)
            {
                return;
            }

            var button = AddButton("ABORT_MISSION", world.IsGameOver ? "Leave" : "Abort Mission");

            button.OnClick = () =>
            {
                hideMenu = true;

                if (world.LocalPlayer == null || world.LocalPlayer.WinState != WinState.Won)
                {
                    Action restartAction = null;
                    var    iop           = world.WorldActor.TraitsImplementing <IObjectivesPanel>().FirstOrDefault();
                    var    exitDelay     = iop != null ? iop.ExitDelay : 0;

                    if (!world.LobbyInfo.GlobalSettings.Dedicated && world.LobbyInfo.NonBotClients.Count() == 1)
                    {
                        restartAction = () =>
                        {
                            Ui.CloseWindow();
                            if (mpe != null)
                            {
                                if (Game.IsCurrentWorld(world))
                                {
                                    mpe.Fade(MenuPaletteEffect.EffectType.Black);
                                }
                                exitDelay += 40 * mpe.Info.FadeLength;
                            }

                            Game.RunAfterDelay(exitDelay, Game.RestartGame);
                        };
                    }

                    ConfirmationDialogs.ButtonPrompt(
                        title: "Leave Mission",
                        text: "Leave this game and return to the menu?",
                        onConfirm: OnQuit,
                        onCancel: ShowMenu,
                        confirmText: "Leave",
                        cancelText: "Stay",
                        otherText: "Restart",
                        onOther: restartAction);
                }
                else
                {
                    OnQuit();
                }
            };
        }
コード例 #11
0
 void DeleteOneMap(string map, Action <string> after)
 {
     ConfirmationDialogs.ButtonPrompt(
         title: "Delete map",
         text: $"Delete the map '{modData.MapCache[map].Title}'?",
         onConfirm: () =>
     {
         var newUid = DeleteMap(map);
         after?.Invoke(newUid);
     },
         confirmText: "Delete",
         onCancel: () => { });
 }
コード例 #12
0
 void DeleteAllMaps(string[] maps, Action <string> after)
 {
     ConfirmationDialogs.ButtonPrompt(
         title: "Delete maps",
         text: "Delete all maps on this page?",
         onConfirm: () =>
     {
         maps.Do(m => DeleteMap(m));
         after?.Invoke(Game.ModData.MapCache.ChooseInitialMap(null, Game.CosmeticRandom));
     },
         confirmText: "Delete",
         onCancel: () => { });
 }
コード例 #13
0
ファイル: MapChooserLogic.cs プロジェクト: Mete0/anki-OpenRA
		void DeleteOneMap(string map, Action<string> after)
		{
			ConfirmationDialogs.ButtonPrompt(
				title: "Delete map",
				text: "Delete the map '{0}'?".F(modData.MapCache[map].Title),
				onConfirm: () =>
				{
					var newUid = DeleteMap(map);
					if (after != null)
						after(newUid);
				},
				confirmText: "Delete",
				onCancel: () => { });
		}
コード例 #14
0
        void CreateAndJoin()
        {
            var name = Game.Settings.SanitizedServerName(panel.Get <TextFieldWidget>("SERVER_NAME").Text);

            if (!Exts.TryParseIntegerInvariant(panel.Get <TextFieldWidget>("LISTEN_PORT").Text, out var listenPort))
            {
                listenPort = 1234;
            }

            var passwordField = panel.GetOrNull <PasswordFieldWidget>("PASSWORD");
            var password      = passwordField != null ? passwordField.Text : "";

            // Save new settings
            Game.Settings.Server.Name            = name;
            Game.Settings.Server.ListenPort      = listenPort;
            Game.Settings.Server.AdvertiseOnline = advertiseOnline;
            Game.Settings.Server.Map             = preview.Uid;
            Game.Settings.Server.Password        = password;
            Game.Settings.Save();

            // Take a copy so that subsequent changes don't affect the server
            var settings = Game.Settings.Server.Clone();

            // Create and join the server
            try
            {
                var endpoint = Game.CreateServer(settings);

                Ui.CloseWindow();
                ConnectionLogic.Connect(endpoint, password, onCreate, onExit);
            }
            catch (System.Net.Sockets.SocketException e)
            {
                var message = $"Could not listen on port {Game.Settings.Server.ListenPort}.";

                // AddressAlreadyInUse (WSAEADDRINUSE)
                if (e.ErrorCode == 10048)
                {
                    message += "\nCheck if the port is already being used.";
                }
                else
                {
                    message += $"\nError is: \"{e.Message}\" ({e.ErrorCode})";
                }

                ConfirmationDialogs.ButtonPrompt("Server Creation Failed", message, onCancel: () => { }, cancelText: "Back");
            }
        }
コード例 #15
0
        public SettingsLogic(Widget widget, Action onExit, ModData modData, WorldRenderer worldRenderer)
        {
            this.worldRenderer = worldRenderer;
            this.modData       = modData;

            panelContainer = widget.Get("SETTINGS_PANEL");
            tabContainer   = widget.Get("TAB_CONTAINER");

            RegisterSettingsPanel(PanelType.Display, InitDisplayPanel, ResetDisplayPanel, "DISPLAY_PANEL", "DISPLAY_TAB");
            RegisterSettingsPanel(PanelType.Audio, InitAudioPanel, ResetAudioPanel, "AUDIO_PANEL", "AUDIO_TAB");
            RegisterSettingsPanel(PanelType.Input, InitInputPanel, ResetInputPanel, "INPUT_PANEL", "INPUT_TAB");
            RegisterSettingsPanel(PanelType.Advanced, InitAdvancedPanel, ResetAdvancedPanel, "ADVANCED_PANEL", "ADVANCED_TAB");

            panelContainer.Get <ButtonWidget>("BACK_BUTTON").OnClick = () =>
            {
                leavePanelActions[settingsPanel]();
                var current = Game.Settings;
                current.Save();

                Action closeAndExit = () => { Ui.CloseWindow(); onExit(); };
                if (OriginalSoundDevice != current.Sound.Device ||
                    OriginalGraphicsMode != current.Graphics.Mode ||
                    OriginalGraphicsWindowedSize != current.Graphics.WindowedSize ||
                    OriginalGraphicsFullscreenSize != current.Graphics.FullscreenSize)
                {
                    ConfirmationDialogs.ButtonPrompt(
                        title: "Restart Now?",
                        text: "Some changes will not be applied until\nthe game is restarted. Restart now?",
                        onConfirm: Game.Restart,
                        onCancel: closeAndExit,
                        confirmText: "Restart Now",
                        cancelText: "Restart Later");
                }
                else
                {
                    closeAndExit();
                }
            };

            panelContainer.Get <ButtonWidget>("RESET_BUTTON").OnClick = () =>
            {
                resetPanelActions[settingsPanel]();
                Game.Settings.Save();
            };
        }
コード例 #16
0
        public static bool PromptConfirmReplayCompatibility(ReplayMetadata replayMeta, Action onCancel = null)
        {
            if (onCancel == null)
            {
                onCancel = DoNothing;
            }

            if (replayMeta == null)
            {
                ConfirmationDialogs.ButtonPrompt("Incompatible Replay", "Replay metadata could not be read.", onCancel: onCancel);
                return(false);
            }

            var version = replayMeta.GameInfo.Version;

            if (version == null)
            {
                return(IncompatibleReplayDialog("unknown version", version, onCancel));
            }

            var mod = replayMeta.GameInfo.Mod;

            if (mod == null)
            {
                return(IncompatibleReplayDialog("unknown mod", mod, onCancel));
            }

            if (!Game.Mods.ContainsKey(mod))
            {
                return(IncompatibleReplayDialog("unavailable mod", mod, onCancel));
            }

            if (Game.Mods[mod].Metadata.Version != version)
            {
                return(IncompatibleReplayDialog("incompatible version", version, onCancel));
            }

            if (replayMeta.GameInfo.MapPreview.Status != MapStatus.Available)
            {
                return(IncompatibleReplayDialog("unavailable map", replayMeta.GameInfo.MapUid, onCancel));
            }

            return(true);
        }
コード例 #17
0
ファイル: IngameMenuLogic.cs プロジェクト: zgrsgr/OpenRA-SC
        void CreateExitEditorButton()
        {
            if (world.Type != WorldType.Editor)
            {
                return;
            }

            var button = AddButton("EXIT_EDITOR", "Exit Map Editor");

            button.OnClick = () =>
            {
                hideMenu = true;
                ConfirmationDialogs.ButtonPrompt(
                    title: "Exit Map Editor",
                    text: "Exit and lose all unsaved changes?",
                    onConfirm: OnQuit,
                    onCancel: ShowMenu);
            };
        }
コード例 #18
0
        void CreateRestartButton()
        {
            if (world.Type != WorldType.Regular || !isSinglePlayer)
            {
                return;
            }

            var iop       = world.WorldActor.TraitsImplementing <IObjectivesPanel>().FirstOrDefault();
            var exitDelay = iop?.ExitDelay ?? 0;

            Action onRestart = () =>
            {
                Ui.CloseWindow();
                if (mpe != null)
                {
                    if (Game.IsCurrentWorld(world))
                    {
                        mpe.Fade(MenuPaletteEffect.EffectType.Black);
                    }
                    exitDelay += 40 * mpe.Info.FadeLength;
                }

                Game.RunAfterDelay(exitDelay, Game.RestartGame);
            };

            var button = AddButton("RESTART", "Restart");

            button.IsDisabled = () => hasError || leaving;
            button.OnClick    = () =>
            {
                hideMenu = true;
                ConfirmationDialogs.ButtonPrompt(
                    title: "Restart",
                    text: "Are you sure you want to restart?",
                    onConfirm: onRestart,
                    onCancel: ShowMenu,
                    confirmText: "Restart",
                    cancelText: "Stay");
            };
        }
コード例 #19
0
        void CreateSaveMapButton()
        {
            if (world.Type != WorldType.Editor)
            {
                return;
            }

            var button = AddButton("SAVE_MAP", "Save Map");

            button.OnClick = () =>
            {
                hideMenu = true;
                var editorActorLayer = world.WorldActor.Trait <EditorActorLayer>();
                var actionManager    = world.WorldActor.Trait <EditorActionManager>();

                var playerDefinitions = editorActorLayer.Players.ToMiniYaml();

                var playerCount = new MapPlayers(playerDefinitions).Players.Count;
                if (playerCount > MapPlayers.MaximumPlayerCount)
                {
                    ConfirmationDialogs.ButtonPrompt(
                        title: "Error: Max player count exceeded",
                        text: $"There are too many players defined ({playerCount}/{MapPlayers.MaximumPlayerCount}).",
                        onConfirm: ShowMenu,
                        confirmText: "Back");

                    return;
                }

                Ui.OpenWindow("SAVE_MAP_PANEL", new WidgetArgs()
                {
                    { "onSave", (Action <string>)(_ => { hideMenu = false; actionManager.Modified = false; }) },
                    { "onExit", () => hideMenu = false },
                    { "map", world.Map },
                    { "playerDefinitions", playerDefinitions },
                    { "actorDefinitions", editorActorLayer.Save() }
                });
            };
        }
コード例 #20
0
        void CreateAbortMissionButton()
        {
            if (world.Type != WorldType.Regular)
            {
                return;
            }

            var button = AddButton("ABORT_MISSION", world.IsGameOver ? "Leave" : "Abort Mission");

            button.OnClick = () =>
            {
                hideMenu = true;

                ConfirmationDialogs.ButtonPrompt(
                    title: "Leave Mission",
                    text: "Leave this game and return to the menu?",
                    onConfirm: OnQuit,
                    onCancel: ShowMenu,
                    confirmText: "Leave",
                    cancelText: "Stay");
            };
        }
コード例 #21
0
        void PlayVideoStack(WsaPlayerWidget player, Action onComplete = null)
        {
            if (player.VideoStackList.Count == 0)
            {
                onComplete();
                return;
            }
            string videowsa = player.VideoStackList.Dequeue();

            if (!modData.DefaultFileSystem.Exists(videowsa))
            {
                ConfirmationDialogs.ButtonPrompt(
                    title: "Video not installed",
                    text: "The game videos can be installed from the\n\"Manage Content\" menu in the mod chooser.",
                    cancelText: "Back",
                    onCancel: () => { });
            }
            else
            {
                //StopVideo(player);

                // = pv;
                player.Load(videowsa);

                // video playback runs asynchronously
                player.PlayThen(() =>
                {
                    //StopVideo(player);
                    if (onComplete != null)
                    {
                        onComplete();
                    }
                });

                // Mute other distracting sounds
                //MuteSounds();
            }
        }
コード例 #22
0
        public GameInfoStatsLogic(Widget widget, World world, OrderManager orderManager, WorldRenderer worldRenderer, Action <bool> hideMenu)
        {
            var player      = world.LocalPlayer;
            var playerPanel = widget.Get <ScrollPanelWidget>("PLAYER_LIST");

            if (player != null && !player.NonCombatant)
            {
                var checkbox    = widget.Get <CheckboxWidget>("STATS_CHECKBOX");
                var statusLabel = widget.Get <LabelWidget>("STATS_STATUS");

                checkbox.IsChecked    = () => player.WinState != WinState.Undefined;
                checkbox.GetCheckType = () => player.WinState == WinState.Won ?
                                        "checked" : "crossed";

                if (player.HasObjectives)
                {
                    var mo = player.PlayerActor.Trait <MissionObjectives>();
                    checkbox.GetText = () => mo.Objectives.First().Description;
                }

                statusLabel.GetText = () => player.WinState == WinState.Won ? "Accomplished" :
                                      player.WinState == WinState.Lost ? "Failed" : "In progress";
                statusLabel.GetColor = () => player.WinState == WinState.Won ? Color.LimeGreen :
                                       player.WinState == WinState.Lost ? Color.Red : Color.White;
            }
            else
            {
                // Expand the stats window to cover the hidden objectives
                var objectiveGroup = widget.Get("OBJECTIVE");
                var statsHeader    = widget.Get("STATS_HEADERS");

                objectiveGroup.Visible     = false;
                statsHeader.Bounds.Y      -= objectiveGroup.Bounds.Height;
                playerPanel.Bounds.Y      -= objectiveGroup.Bounds.Height;
                playerPanel.Bounds.Height += objectiveGroup.Bounds.Height;
            }

            var teamTemplate      = playerPanel.Get <ScrollItemWidget>("TEAM_TEMPLATE");
            var playerTemplate    = playerPanel.Get("PLAYER_TEMPLATE");
            var spectatorTemplate = playerPanel.Get("SPECTATOR_TEMPLATE");

            playerPanel.RemoveChildren();

            var teams = world.Players.Where(p => !p.NonCombatant && p.Playable)
                        .Select(p => (Player: p, PlayerStatistics: p.PlayerActor.TraitOrDefault <PlayerStatistics>()))
                        .OrderByDescending(p => p.PlayerStatistics?.Experience ?? 0)
                        .GroupBy(p => (world.LobbyInfo.ClientWithIndex(p.Player.ClientIndex) ?? new Session.Client()).Team)
                        .OrderByDescending(g => g.Sum(gg => gg.PlayerStatistics?.Experience ?? 0));

            foreach (var t in teams)
            {
                if (teams.Count() > 1)
                {
                    var teamHeader = ScrollItemWidget.Setup(teamTemplate, () => true, () => { });
                    teamHeader.Get <LabelWidget>("TEAM").GetText = () => t.Key == 0 ? "No Team" : "Team {0}".F(t.Key);
                    var teamRating       = teamHeader.Get <LabelWidget>("TEAM_SCORE");
                    var scoreCache       = new CachedTransform <int, string>(s => s.ToString());
                    var teamMemberScores = t.Select(tt => tt.PlayerStatistics).Where(s => s != null).ToArray().Select(s => s.Experience);
                    teamRating.GetText = () => scoreCache.Update(teamMemberScores.Sum());

                    playerPanel.AddChild(teamHeader);
                }

                foreach (var p in t.ToList())
                {
                    var pp     = p.Player;
                    var client = world.LobbyInfo.ClientWithIndex(pp.ClientIndex);
                    var item   = playerTemplate.Clone();
                    LobbyUtils.SetupProfileWidget(item, client, orderManager, worldRenderer);

                    var nameLabel = item.Get <LabelWidget>("NAME");
                    WidgetUtils.BindPlayerNameAndStatus(nameLabel, pp);
                    nameLabel.GetColor = () => pp.Color;

                    var flag = item.Get <ImageWidget>("FACTIONFLAG");
                    flag.GetImageCollection = () => "flags";
                    if (player == null || player.RelationshipWith(pp) == PlayerRelationship.Ally || player.WinState != WinState.Undefined)
                    {
                        flag.GetImageName = () => pp.Faction.InternalName;
                        var factionName = pp.Faction.Name != pp.DisplayFaction.Name ? "{0} ({1})".F(pp.DisplayFaction.Name, pp.Faction.Name) : pp.Faction.Name;
                        item.Get <LabelWidget>("FACTION").GetText = () => factionName;
                    }
                    else
                    {
                        flag.GetImageName = () => pp.DisplayFaction.InternalName;
                        item.Get <LabelWidget>("FACTION").GetText = () => pp.DisplayFaction.Name;
                    }

                    var scoreCache = new CachedTransform <int, string>(s => s.ToString());
                    item.Get <LabelWidget>("SCORE").GetText = () => scoreCache.Update(p.PlayerStatistics?.Experience ?? 0);

                    playerPanel.AddChild(item);
                }
            }

            var spectators = orderManager.LobbyInfo.Clients.Where(c => c.IsObserver).ToList();

            if (spectators.Any())
            {
                var spectatorHeader = ScrollItemWidget.Setup(teamTemplate, () => true, () => { });
                spectatorHeader.Get <LabelWidget>("TEAM").GetText = () => "Spectators";

                playerPanel.AddChild(spectatorHeader);

                foreach (var client in spectators)
                {
                    var item = spectatorTemplate.Clone();
                    LobbyUtils.SetupProfileWidget(item, client, orderManager, worldRenderer);

                    var nameLabel = item.Get <LabelWidget>("NAME");
                    var nameFont  = Game.Renderer.Fonts[nameLabel.Font];

                    var suffixLength = new CachedTransform <string, int>(s => nameFont.Measure(s).X);
                    var name         = new CachedTransform <(string Name, string Suffix), string>(c =>
                                                                                                  WidgetUtils.TruncateText(c.Name, nameLabel.Bounds.Width - suffixLength.Update(c.Suffix), nameFont) + c.Suffix);

                    nameLabel.GetText = () =>
                    {
                        var suffix = client.State == Session.ClientState.Disconnected ? " (Gone)" : "";
                        return(name.Update((client.Name, suffix)));
                    };

                    var kickButton = item.Get <ButtonWidget>("KICK");
                    kickButton.IsVisible = () => Game.IsHost && client.Index != orderManager.LocalClient.Index && client.State != Session.ClientState.Disconnected;
                    kickButton.OnClick   = () =>
                    {
                        hideMenu(true);
                        ConfirmationDialogs.ButtonPrompt(
                            title: "Kick {0}?".F(client.Name),
                            text: "They will not be able to rejoin this game.",
                            onConfirm: () =>
                        {
                            orderManager.IssueOrder(Order.Command("kick {0} {1}".F(client.Index, false)));
                            hideMenu(false);
                        },
                            onCancel: () => hideMenu(false),
                            confirmText: "Kick");
                    };

                    playerPanel.AddChild(item);
                }
            }
        }
コード例 #23
0
        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, world);
            }

            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();
        }
コード例 #24
0
ファイル: ReplayBrowserLogic.cs プロジェクト: zhao212/OpenRA
        void SetupManagement()
        {
            var renameButton = panel.Get <ButtonWidget>("MNG_RENSEL_BUTTON");

            renameButton.IsDisabled = () => selectedReplay == null;
            renameButton.OnClick    = () =>
            {
                var r             = selectedReplay;
                var initialName   = Path.GetFileNameWithoutExtension(r.FilePath);
                var directoryName = Path.GetDirectoryName(r.FilePath);
                var invalidChars  = Path.GetInvalidFileNameChars();

                ConfirmationDialogs.TextInputPrompt(
                    "Rename Replay",
                    "Enter a new file name:",
                    initialName,
                    onAccept: newName => RenameReplay(r, 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(directoryName, newName)))
                    {
                        return(false);
                    }

                    return(true);
                });
            };

            Action <ReplayMetadata, Action> onDeleteReplay = (r, after) =>
            {
                ConfirmationDialogs.ButtonPrompt(
                    title: "Delete selected replay?",
                    text: "Delete replay '{0}'?".F(Path.GetFileNameWithoutExtension(r.FilePath)),
                    onConfirm: () =>
                {
                    DeleteReplay(r);
                    after?.Invoke();
                },
                    confirmText: "Delete",
                    onCancel: () => { });
            };

            var deleteButton = panel.Get <ButtonWidget>("MNG_DELSEL_BUTTON");

            deleteButton.IsDisabled = () => selectedReplay == null;
            deleteButton.OnClick    = () =>
            {
                onDeleteReplay(selectedReplay, () =>
                {
                    if (selectedReplay == null)
                    {
                        SelectFirstVisibleReplay();
                    }
                });
            };

            var deleteAllButton = panel.Get <ButtonWidget>("MNG_DELALL_BUTTON");

            deleteAllButton.IsDisabled = () => replayState.Count(kvp => kvp.Value.Visible) == 0;
            deleteAllButton.OnClick    = () =>
            {
                var list = replayState.Where(kvp => kvp.Value.Visible).Select(kvp => kvp.Key).ToList();
                if (list.Count == 0)
                {
                    return;
                }

                if (list.Count == 1)
                {
                    onDeleteReplay(list[0], () => { if (selectedReplay == null)
                                                    {
                                                        SelectFirstVisibleReplay();
                                                    }
                                   });
                    return;
                }

                ConfirmationDialogs.ButtonPrompt(
                    title: "Delete all selected replays?",
                    text: "Delete {0} replays?".F(list.Count),
                    onConfirm: () =>
                {
                    list.ForEach(DeleteReplay);
                    if (selectedReplay == null)
                    {
                        SelectFirstVisibleReplay();
                    }
                },
                    confirmText: "Delete All",
                    onCancel: () => { });
            };
        }
コード例 #25
0
        public SettingsLogic(Widget widget, Action onExit, WorldRenderer worldRenderer, Dictionary <string, MiniYaml> logicArgs)
        {
            panelContainer = widget.Get("PANEL_CONTAINER");
            var panelTemplate = panelContainer.Get <ContainerWidget>("PANEL_TEMPLATE");

            panelContainer.RemoveChild(panelTemplate);

            tabContainer = widget.Get("SETTINGS_TAB_CONTAINER");
            tabTemplate  = tabContainer.Get <ButtonWidget>("BUTTON_TEMPLATE");
            tabContainer.RemoveChild(tabTemplate);

            if (logicArgs.TryGetValue("ButtonStride", out var buttonStrideNode))
            {
                buttonStride = FieldLoader.GetValue <int2>("ButtonStride", buttonStrideNode.Value);
            }

            if (logicArgs.TryGetValue("Panels", out var settingsPanels))
            {
                panels = settingsPanels.ToDictionary(kv => kv.Value);

                foreach (var panel in panels)
                {
                    var container = panelTemplate.Clone() as ContainerWidget;
                    container.Id = panel.Key;
                    panelContainer.AddChild(container);

                    Game.LoadWidget(worldRenderer.World, panel.Key, container, new WidgetArgs()
                    {
                        { "registerPanel", (Action <string, string, Func <Widget, Func <bool> >, Func <Widget, Action> >)RegisterSettingsPanel },
                        { "panelID", panel.Key },
                        { "label", panel.Value }
                    });
                }
            }

            widget.Get <ButtonWidget>("BACK_BUTTON").OnClick = () =>
            {
                needsRestart |= leavePanelActions[activePanel]();
                var current = Game.Settings;
                current.Save();

                Action closeAndExit = () => { Ui.CloseWindow(); onExit(); };
                if (needsRestart)
                {
                    Action noRestart = () => ConfirmationDialogs.ButtonPrompt(
                        title: "Restart Required",
                        text: "Some changes will not be applied until\nthe game is restarted.",
                        onCancel: closeAndExit,
                        cancelText: "Continue");

                    if (!Game.ExternalMods.TryGetValue(ExternalMod.MakeKey(Game.ModData.Manifest), out var external))
                    {
                        noRestart();
                        return;
                    }

                    ConfirmationDialogs.ButtonPrompt(
                        title: "Restart Now?",
                        text: "Some changes will not be applied until\nthe game is restarted. Restart now?",
                        onConfirm: () => Game.SwitchToExternalMod(external, null, noRestart),
                        onCancel: closeAndExit,
                        confirmText: "Restart Now",
                        cancelText: "Restart Later");
                }
                else
                {
                    closeAndExit();
                }
            };

            widget.Get <ButtonWidget>("RESET_BUTTON").OnClick = () =>
            {
                Action reset = () =>
                {
                    resetPanelActions[activePanel]();
                    Game.Settings.Save();
                };

                ConfirmationDialogs.ButtonPrompt(
                    title: $"Reset \"{panels[activePanel]}\"",
                    text: "Are you sure you want to reset\nall settings in this panel?",
                    onConfirm: reset,
                    onCancel: () => { },
                    confirmText: "Reset",
                    cancelText: "Cancel");
            };
        }
コード例 #26
0
        public IngameMenuLogic(Widget widget, ModData modData, World world, Action onExit, WorldRenderer worldRenderer, IngameInfoPanel activePanel)
        {
            var leaving = false;

            menu = widget.Get("INGAME_MENU");
            var mpe = world.WorldActor.TraitOrDefault <MenuPaletteEffect>();

            if (mpe != null)
            {
                mpe.Fade(mpe.Info.MenuEffect);
            }

            menu.Get <LabelWidget>("VERSION_LABEL").Text = modData.Manifest.Metadata.Version;

            var hideMenu = false;

            menu.Get("MENU_BUTTONS").IsVisible = () => !hideMenu;

            var scriptContext = world.WorldActor.TraitOrDefault <LuaScript>();
            var hasError      = scriptContext != null && scriptContext.FatalErrorOccurred;

            // TODO: Create a mechanism to do things like this cleaner. Also needed for scripted missions
            Action onQuit = () =>
            {
                if (world.Type == WorldType.Regular)
                {
                    Game.Sound.PlayNotification(world.Map.Rules, null, "Speech", "Leave", world.LocalPlayer == null ? null : world.LocalPlayer.Faction.InternalName);
                }

                leaving = true;

                var iop       = world.WorldActor.TraitsImplementing <IObjectivesPanel>().FirstOrDefault();
                var exitDelay = iop != null ? iop.ExitDelay : 0;
                if (mpe != null)
                {
                    Game.RunAfterDelay(exitDelay, () =>
                    {
                        if (Game.IsCurrentWorld(world))
                        {
                            mpe.Fade(MenuPaletteEffect.EffectType.Black);
                        }
                    });
                    exitDelay += 40 * mpe.Info.FadeLength;
                }

                Game.RunAfterDelay(exitDelay, () =>
                {
                    if (!Game.IsCurrentWorld(world))
                    {
                        return;
                    }

                    Game.Disconnect();
                    Ui.ResetAll();
                    Game.LoadShellMap();
                });
            };

            Action closeMenu = () =>
            {
                Ui.CloseWindow();
                if (mpe != null)
                {
                    mpe.Fade(MenuPaletteEffect.EffectType.None);
                }
                onExit();
            };

            Action showMenu = () => hideMenu = false;

            var abortMissionButton = menu.Get <ButtonWidget>("ABORT_MISSION");

            abortMissionButton.IsVisible  = () => world.Type == WorldType.Regular;
            abortMissionButton.IsDisabled = () => leaving;
            if (world.IsGameOver)
            {
                abortMissionButton.GetText = () => "Leave";
            }

            abortMissionButton.OnClick = () =>
            {
                hideMenu = true;

                if (world.LocalPlayer == null || world.LocalPlayer.WinState != WinState.Won)
                {
                    Action restartAction = null;
                    var    iop           = world.WorldActor.TraitsImplementing <IObjectivesPanel>().FirstOrDefault();
                    var    exitDelay     = iop != null ? iop.ExitDelay : 0;

                    if (world.LobbyInfo.IsSinglePlayer)
                    {
                        restartAction = () =>
                        {
                            Ui.CloseWindow();
                            if (mpe != null)
                            {
                                if (Game.IsCurrentWorld(world))
                                {
                                    mpe.Fade(MenuPaletteEffect.EffectType.Black);
                                }
                                exitDelay += 40 * mpe.Info.FadeLength;
                            }

                            Game.RunAfterDelay(exitDelay, Game.RestartGame);
                        };
                    }

                    ConfirmationDialogs.ButtonPrompt(
                        title: "Leave Mission",
                        text: "Leave this game and return to the menu?",
                        onConfirm: onQuit,
                        onCancel: showMenu,
                        confirmText: "Leave",
                        cancelText: "Stay",
                        otherText: "Restart",
                        onOther: restartAction);
                }
                else
                {
                    onQuit();
                }
            };

            var exitEditorButton = menu.Get <ButtonWidget>("EXIT_EDITOR");

            exitEditorButton.IsVisible = () => world.Type == WorldType.Editor;
            exitEditorButton.OnClick   = () =>
            {
                hideMenu = true;
                ConfirmationDialogs.ButtonPrompt(
                    title: "Exit Map Editor",
                    text: "Exit and lose all unsaved changes?",
                    onConfirm: onQuit,
                    onCancel: showMenu);
            };

            Action onSurrender = () =>
            {
                world.IssueOrder(new Order("Surrender", world.LocalPlayer.PlayerActor, false));
                closeMenu();
            };
            var surrenderButton = menu.Get <ButtonWidget>("SURRENDER");

            surrenderButton.IsVisible  = () => world.Type == WorldType.Regular;
            surrenderButton.IsDisabled = () =>
                                         world.LocalPlayer == null || world.LocalPlayer.WinState != WinState.Undefined ||
                                         world.Map.Visibility.HasFlag(MapVisibility.MissionSelector) || hasError;
            surrenderButton.OnClick = () =>
            {
                hideMenu = true;
                ConfirmationDialogs.ButtonPrompt(
                    title: "Surrender",
                    text: "Are you sure you want to surrender?",
                    onConfirm: onSurrender,
                    onCancel: showMenu,
                    confirmText: "Surrender",
                    cancelText: "Stay");
            };

            var saveMapButton = menu.Get <ButtonWidget>("SAVE_MAP");

            saveMapButton.IsVisible = () => world.Type == WorldType.Editor;
            saveMapButton.OnClick   = () =>
            {
                hideMenu = true;
                var editorActorLayer = world.WorldActor.Trait <EditorActorLayer>();
                Ui.OpenWindow("SAVE_MAP_PANEL", new WidgetArgs()
                {
                    { "onSave", (Action <string>)(_ => hideMenu = false) },
                    { "onExit", () => hideMenu = false },
                    { "map", world.Map },
                    { "playerDefinitions", editorActorLayer.Players.ToMiniYaml() },
                    { "actorDefinitions", editorActorLayer.Save() }
                });
            };

            var musicButton = menu.Get <ButtonWidget>("MUSIC");

            musicButton.IsDisabled = () => leaving;
            musicButton.OnClick    = () =>
            {
                hideMenu = true;
                Ui.OpenWindow("MUSIC_PANEL", new WidgetArgs()
                {
                    { "onExit", () => hideMenu = false },
                    { "world", world }
                });
            };

            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;
            }
        }
コード例 #27
0
        public SaveMapLogic(Widget widget, ModData modData, Action <string> onSave, Action onExit,
                            Map map, List <MiniYamlNode> playerDefinitions, List <MiniYamlNode> actorDefinitions)
        {
            var title = widget.Get <TextFieldWidget>("TITLE");

            title.Text = map.Title;

            var author = widget.Get <TextFieldWidget>("AUTHOR");

            author.Text = map.Author;

            var visibilityPanel   = Ui.LoadWidget("MAP_SAVE_VISIBILITY_PANEL", null, new WidgetArgs());
            var visOptionTemplate = visibilityPanel.Get <CheckboxWidget>("VISIBILITY_TEMPLATE");

            visibilityPanel.RemoveChildren();

            foreach (MapVisibility visibilityOption in Enum.GetValues(typeof(MapVisibility)))
            {
                // To prevent users from breaking the game only show the 'Shellmap' option when it is already set.
                if (visibilityOption == MapVisibility.Shellmap && !map.Visibility.HasFlag(visibilityOption))
                {
                    continue;
                }

                var checkbox = (CheckboxWidget)visOptionTemplate.Clone();
                checkbox.GetText   = () => visibilityOption.ToString();
                checkbox.IsChecked = () => map.Visibility.HasFlag(visibilityOption);
                checkbox.OnClick   = () => map.Visibility ^= visibilityOption;
                visibilityPanel.AddChild(checkbox);
            }

            var visibilityDropdown = widget.Get <DropDownButtonWidget>("VISIBILITY_DROPDOWN");

            visibilityDropdown.OnMouseDown = _ =>
            {
                visibilityDropdown.RemovePanel();
                visibilityDropdown.AttachPanel(visibilityPanel);
            };

            var           writableDirectories = new List <SaveDirectory>();
            SaveDirectory selectedDirectory   = null;

            var directoryDropdown = widget.Get <DropDownButtonWidget>("DIRECTORY_DROPDOWN");
            {
                Func <SaveDirectory, ScrollItemWidget, ScrollItemWidget> setupItem = (option, template) =>
                {
                    var item = ScrollItemWidget.Setup(template,
                                                      () => selectedDirectory == option,
                                                      () => selectedDirectory = option);
                    item.Get <LabelWidget>("LABEL").GetText = () => option.DisplayName;
                    return(item);
                };

                foreach (var kv in modData.MapCache.MapLocations)
                {
                    var folder = kv.Key as Folder;
                    if (folder == null)
                    {
                        continue;
                    }

                    try
                    {
                        using (var fs = File.Create(Path.Combine(folder.Name, ".testwritable"), 1, FileOptions.DeleteOnClose))
                        {
                            // Do nothing: we just want to test whether we can create the file
                        }

                        writableDirectories.Add(new SaveDirectory(folder, kv.Value.ToString(), kv.Value));
                    }
                    catch
                    {
                        // Directory is not writable
                    }
                }

                if (map.Package != null)
                {
                    selectedDirectory = writableDirectories.FirstOrDefault(k => k.Folder.Contains(map.Package.Name));
                    if (selectedDirectory == null)
                    {
                        selectedDirectory = writableDirectories.FirstOrDefault(k => Directory.GetDirectories(k.Folder.Name).Any(f => f.Contains(map.Package.Name)));
                    }
                }

                // Prioritize MapClassification.User directories over system directories
                if (selectedDirectory == null)
                {
                    selectedDirectory = writableDirectories.OrderByDescending(kv => kv.Classification).First();
                }

                directoryDropdown.GetText = () => selectedDirectory?.DisplayName ?? "";
                directoryDropdown.OnClick = () =>
                                            directoryDropdown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 210, writableDirectories, setupItem);
            }

            var mapIsUnpacked = map.Package != null && map.Package is Folder;

            var filename = widget.Get <TextFieldWidget>("FILENAME");

            filename.Text = map.Package == null ? "" : mapIsUnpacked?Path.GetFileName(map.Package.Name) : Path.GetFileNameWithoutExtension(map.Package.Name);

            var fileType = mapIsUnpacked ? MapFileType.Unpacked : MapFileType.OraMap;

            var fileTypes = new Dictionary <MapFileType, MapFileTypeInfo>()
            {
                { MapFileType.OraMap, new MapFileTypeInfo {
                      Extension = ".oramap", UiLabel = ".oramap"
                  } },
                { MapFileType.Unpacked, new MapFileTypeInfo {
                      Extension = "", UiLabel = "(unpacked)"
                  } }
            };

            var typeDropdown = widget.Get <DropDownButtonWidget>("TYPE_DROPDOWN");
            {
                Func <KeyValuePair <MapFileType, MapFileTypeInfo>, ScrollItemWidget, ScrollItemWidget> setupItem = (option, template) =>
                {
                    var item = ScrollItemWidget.Setup(template,
                                                      () => fileType == option.Key,
                                                      () => { typeDropdown.Text = option.Value.UiLabel; fileType = option.Key; });
                    item.Get <LabelWidget>("LABEL").GetText = () => option.Value.UiLabel;
                    return(item);
                };

                typeDropdown.Text = fileTypes[fileType].UiLabel;

                typeDropdown.OnClick = () =>
                                       typeDropdown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 210, fileTypes, setupItem);
            }

            var close = widget.Get <ButtonWidget>("BACK_BUTTON");

            close.OnClick = () => { Ui.CloseWindow(); onExit(); };

            var save = widget.Get <ButtonWidget>("SAVE_BUTTON");

            save.OnClick = () =>
            {
                if (string.IsNullOrEmpty(filename.Text))
                {
                    return;
                }

                map.Title  = title.Text;
                map.Author = author.Text;

                if (actorDefinitions != null)
                {
                    map.ActorDefinitions = actorDefinitions;
                }

                if (playerDefinitions != null)
                {
                    map.PlayerDefinitions = playerDefinitions;
                }

                map.RequiresMod = modData.Manifest.Id;

                var combinedPath = Platform.ResolvePath(Path.Combine(selectedDirectory.Folder.Name, filename.Text + fileTypes[fileType].Extension));

                try
                {
                    if (!(map.Package is IReadWritePackage package) || package.Name != combinedPath)
                    {
                        selectedDirectory.Folder.Delete(combinedPath);
                        if (fileType == MapFileType.OraMap)
                        {
                            package = ZipFileLoader.Create(combinedPath);
                        }
                        else
                        {
                            package = new Folder(combinedPath);
                        }
                    }

                    map.Save(package);

                    Console.WriteLine("Saved current map at {0}", combinedPath);
                    Ui.CloseWindow();

                    onSave(map.Uid);
                }
                catch (Exception e)
                {
                    Log.Write("debug", "Failed to save map at {0}: {1}", combinedPath, e.Message);
                    Log.Write("debug", "{0}", e.StackTrace);

                    ConfirmationDialogs.ButtonPrompt(
                        title: "Failed to save map",
                        text: "See debug.log for details.",
                        onConfirm: () => { },
                        confirmText: "OK");
                }
            };
        }