diff --git a/NetAF.Tests/Commands/Global/CommandList_Tests.cs b/NetAF.Tests/Commands/Global/CommandList_Tests.cs new file mode 100644 index 00000000..f143fc64 --- /dev/null +++ b/NetAF.Tests/Commands/Global/CommandList_Tests.cs @@ -0,0 +1,45 @@ +using NetAF.Assets; +using NetAF.Assets.Characters; +using NetAF.Assets.Locations; +using NetAF.Commands.Global; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using NetAF.Logic; +using NetAF.Logic.Modes; +using NetAF.Commands; + +namespace NetAF.Tests.Commands.Global +{ + [TestClass] + public class CommandList_Tests + { + [TestMethod] + public void GivenNullGame_WhenInvoke_ThenError() + { + var command = new CommandList(); + + var result = command.Invoke(null); + + Assert.AreEqual(ReactionResult.Error, result.Result); + } + + [TestMethod] + public void GivenValidGame_WhenInvoke_ThenGameModeChanged() + { + var room = new Room(Identifier.Empty, Description.Empty); + var character = new PlayableCharacter(Identifier.Empty, Description.Empty); + var item = new Item(new Identifier("A"), Description.Empty, true); + room.AddItem(item); + var region = new Region(string.Empty, string.Empty); + region.AddRoom(room, 0, 0, 0); + var overworld = new Overworld(string.Empty, string.Empty); + overworld.AddRegion(region); + var game = Game.Create(new GameInfo(string.Empty, string.Empty, string.Empty), string.Empty, AssetGenerator.Retained(overworld, character), GameEndConditions.NoEnd, TestGameConfiguration.Default).Invoke(); + game.ChangeMode(new AboutMode()); + var command = new CommandList(); + + var result = command.Invoke(game); + + Assert.AreEqual(ReactionResult.GameModeChanged, result.Result); + } + } +} diff --git a/NetAF.Tests/Commands/Global/Help_Tests.cs b/NetAF.Tests/Commands/Global/Help_Tests.cs index 363249e6..e285f7b0 100644 --- a/NetAF.Tests/Commands/Global/Help_Tests.cs +++ b/NetAF.Tests/Commands/Global/Help_Tests.cs @@ -15,7 +15,7 @@ public class Help_Tests [TestMethod] public void GivenNullGame_WhenInvoke_ThenError() { - var command = new Help(); + var command = new Help(End.CommandHelp); var result = command.Invoke(null); @@ -35,7 +35,7 @@ public void GivenValidGame_WhenInvoke_ThenGameModeChanged() overworld.AddRegion(region); var game = Game.Create(new GameInfo(string.Empty, string.Empty, string.Empty), string.Empty, AssetGenerator.Retained(overworld, character), GameEndConditions.NoEnd, TestGameConfiguration.Default).Invoke(); game.ChangeMode(new AboutMode()); - var command = new Help(); + var command = new Help(End.CommandHelp); var result = command.Invoke(game); diff --git a/NetAF.Tests/Extensions/CommandHelpExtensions_Tests.cs b/NetAF.Tests/Extensions/CommandHelpExtensions_Tests.cs deleted file mode 100644 index b4ebd221..00000000 --- a/NetAF.Tests/Extensions/CommandHelpExtensions_Tests.cs +++ /dev/null @@ -1,26 +0,0 @@ -using NetAF.Extensions; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using NetAF.Commands; - -namespace NetAF.Tests.Extensions -{ - [TestClass] - public class CommandHelpExtensions_Tests - { - [TestMethod] - public void GivenCommandAShortcutB_WhenFormattedToDisplayShortcut_ThenCommandIsAForwardSlashB() - { - var result = new CommandHelp("A", string.Empty, "B").FormattedToDisplayShortcut(); - - Assert.AreEqual("A/B", result.Command); - } - - [TestMethod] - public void GivenCommandAShortcutB_FormattedToDisplayShortcutAndVariable_ThenCommandIsAForwardSlashBSpaceUnderscoreUnderscore() - { - var result = new CommandHelp("A", string.Empty, "B").FormattedToDisplayShortcutAndVariable(); - - Assert.AreEqual("A/B __", result.Command); - } - } -} diff --git a/NetAF.Tests/Interpretation/GlobalCommandInterpreter_Tests.cs b/NetAF.Tests/Interpretation/GlobalCommandInterpreter_Tests.cs index ffc37f2e..f568b2f8 100644 --- a/NetAF.Tests/Interpretation/GlobalCommandInterpreter_Tests.cs +++ b/NetAF.Tests/Interpretation/GlobalCommandInterpreter_Tests.cs @@ -4,6 +4,7 @@ using NetAF.Interpretation; using NetAF.Logic; using Microsoft.VisualStudio.TestTools.UnitTesting; +using NetAF.Logic.Modes; namespace NetAF.Tests.Interpretation { @@ -67,13 +68,59 @@ public void GivenExit_WhenInterpret_ThenReturnTrue() } [TestMethod] - public void GivenHelp_WhenInterpret_ThenReturnTrue() + public void GivenHelpWithCommand_WhenInterpret_ThenReturnTrue() + { + var interpreter = new GlobalCommandInterpreter(); + var game = Game.Create(new GameInfo(string.Empty, string.Empty, string.Empty), string.Empty, AssetGenerator.Retained(overworld, new PlayableCharacter(string.Empty, string.Empty)), GameEndConditions.NoEnd, TestGameConfiguration.Default).Invoke(); + game.ChangeMode(new SceneMode()); + + var result = interpreter.Interpret($"{NetAF.Commands.Global.Help.CommandHelp.Command} {NetAF.Commands.Scene.Examine.CommandHelp.Command}", game); + + Assert.IsTrue(result.WasInterpretedSuccessfully); + } + + [TestMethod] + public void GivenHelpWithShortcut_WhenInterpret_ThenReturnTrue() + { + var interpreter = new GlobalCommandInterpreter(); + var game = Game.Create(new GameInfo(string.Empty, string.Empty, string.Empty), string.Empty, AssetGenerator.Retained(overworld, new PlayableCharacter(string.Empty, string.Empty)), GameEndConditions.NoEnd, TestGameConfiguration.Default).Invoke(); + game.ChangeMode(new SceneMode()); + + var result = interpreter.Interpret($"{NetAF.Commands.Global.Help.CommandHelp.Command} {NetAF.Commands.Scene.Examine.CommandHelp.Shortcut}", game); + + Assert.IsTrue(result.WasInterpretedSuccessfully); + } + + [TestMethod] + public void GivenHelpWithNoCommand_WhenInterpret_ThenReturnFalse() { var interpreter = new GlobalCommandInterpreter(); var game = Game.Create(new GameInfo(string.Empty, string.Empty, string.Empty), string.Empty, AssetGenerator.Retained(overworld, new PlayableCharacter(string.Empty, string.Empty)), GameEndConditions.NoEnd, TestGameConfiguration.Default).Invoke(); var result = interpreter.Interpret(NetAF.Commands.Global.Help.CommandHelp.Command, game); + Assert.IsFalse(result.WasInterpretedSuccessfully); + } + + [TestMethod] + public void GivenHelpWithUnknownCommand_WhenInterpret_ThenReturnFalse() + { + var interpreter = new GlobalCommandInterpreter(); + var game = Game.Create(new GameInfo(string.Empty, string.Empty, string.Empty), string.Empty, AssetGenerator.Retained(overworld, new PlayableCharacter(string.Empty, string.Empty)), GameEndConditions.NoEnd, TestGameConfiguration.Default).Invoke(); + + var result = interpreter.Interpret($"{NetAF.Commands.Global.Help.CommandHelp.Command} ABC", game); + + Assert.IsFalse(result.WasInterpretedSuccessfully); + } + + [TestMethod] + public void GivenCommands_WhenInterpret_ThenReturnTrue() + { + var interpreter = new GlobalCommandInterpreter(); + var game = Game.Create(new GameInfo(string.Empty, string.Empty, string.Empty), string.Empty, AssetGenerator.Retained(overworld, new PlayableCharacter(string.Empty, string.Empty)), GameEndConditions.NoEnd, TestGameConfiguration.Default).Invoke(); + + var result = interpreter.Interpret(NetAF.Commands.Global.CommandList.CommandHelp.Command, game); + Assert.IsTrue(result.WasInterpretedSuccessfully); } diff --git a/NetAF.Tests/Logic/Game_Tests.cs b/NetAF.Tests/Logic/Game_Tests.cs index 68043118..9e175cea 100644 --- a/NetAF.Tests/Logic/Game_Tests.cs +++ b/NetAF.Tests/Logic/Game_Tests.cs @@ -9,12 +9,28 @@ using NetAF.Logic.Modes; using NetAF.Logic.Configuration; using NetAF.Commands; +using NetAF.Commands.Scene; namespace NetAF.Tests.Logic { [TestClass] public class Game_Tests { + [TestMethod] + public void GivenEmptyRoom_WhenGetContextualCommands_ThenNotNullOrEmpty() + { + RegionMaker regionMaker = new(string.Empty, string.Empty); + Room room = new(string.Empty, string.Empty); + regionMaker[0, 0, 0] = room; + OverworldMaker overworldMaker = new(string.Empty, string.Empty, regionMaker); + var game = Game.Create(new(string.Empty, string.Empty, string.Empty), string.Empty, AssetGenerator.Retained(overworldMaker.Make(), new PlayableCharacter(string.Empty, string.Empty)), GameEndConditions.NoEnd, TestGameConfiguration.Default).Invoke(); + + var result = game.GetContextualCommands(); + + Assert.IsNotNull(result); + Assert.IsTrue(result.Length > 0); + } + [TestMethod] public void GivenEmptyRoom_WhenGetAllPlayerVisibleExaminables_ThenNotNull() { @@ -165,6 +181,21 @@ public void GivenSimpleGame_WhenChangeModeToAbout_ThenNoExceptionThrown() }); } + [TestMethod] + public void GivenSimpleGame_WhenChangeModeToCommandList_ThenNoExceptionThrown() + { + Assertions.NoExceptionThrown(() => + { + RegionMaker regionMaker = new(string.Empty, string.Empty); + Room room = new(string.Empty, string.Empty); + regionMaker[0, 0, 0] = room; + OverworldMaker overworldMaker = new(string.Empty, string.Empty, regionMaker); + var game = Game.Create(new(string.Empty, string.Empty, string.Empty), string.Empty, AssetGenerator.Retained(overworldMaker.Make(), new PlayableCharacter(string.Empty, string.Empty)), GameEndConditions.NoEnd, TestGameConfiguration.Default).Invoke(); + + game.ChangeMode(new CommandListMode([])); + }); + } + [TestMethod] public void GivenSimpleGame_WhenChangeModeToHelp_ThenNoExceptionThrown() { @@ -176,7 +207,7 @@ public void GivenSimpleGame_WhenChangeModeToHelp_ThenNoExceptionThrown() OverworldMaker overworldMaker = new(string.Empty, string.Empty, regionMaker); var game = Game.Create(new(string.Empty, string.Empty, string.Empty), string.Empty, AssetGenerator.Retained(overworldMaker.Make(), new PlayableCharacter(string.Empty, string.Empty)), GameEndConditions.NoEnd, TestGameConfiguration.Default).Invoke(); - game.ChangeMode(new HelpMode([])); + game.ChangeMode(new HelpMode(Take.CommandHelp)); }); } diff --git a/NetAF.Tests/Logic/Modes/CommandListMode_Tests.cs b/NetAF.Tests/Logic/Modes/CommandListMode_Tests.cs new file mode 100644 index 00000000..65cb4985 --- /dev/null +++ b/NetAF.Tests/Logic/Modes/CommandListMode_Tests.cs @@ -0,0 +1,31 @@ +using NetAF.Logic; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using NetAF.Assets.Characters; +using NetAF.Assets.Locations; +using NetAF.Utilities; +using NetAF.Logic.Modes; +using NetAF.Commands.Global; +using NetAF.Commands.Scene; + +namespace NetAF.Tests.Logic.Modes +{ + [TestClass] + public class CommandListMode_Tests + { + [TestMethod] + public void GivenNew_WhenRender_ThenNoExceptionThrown() + { + Assertions.NoExceptionThrown(() => + { + RegionMaker regionMaker = new(string.Empty, string.Empty); + Room room = new(string.Empty, string.Empty); + regionMaker[0, 0, 0] = room; + OverworldMaker overworldMaker = new(string.Empty, string.Empty, regionMaker); + var game = Game.Create(new(string.Empty, string.Empty, string.Empty), string.Empty, AssetGenerator.Retained(overworldMaker.Make(), new PlayableCharacter(string.Empty, string.Empty)), GameEndConditions.NoEnd, TestGameConfiguration.Default).Invoke(); + var mode = new CommandListMode([End.CommandHelp, Take.CommandHelp]); + + mode.Render(game); + }); + } + } +} diff --git a/NetAF.Tests/Logic/Modes/HelpMode_Tests.cs b/NetAF.Tests/Logic/Modes/HelpMode_Tests.cs index add422e8..8d8598a8 100644 --- a/NetAF.Tests/Logic/Modes/HelpMode_Tests.cs +++ b/NetAF.Tests/Logic/Modes/HelpMode_Tests.cs @@ -4,6 +4,7 @@ using NetAF.Assets.Locations; using NetAF.Utilities; using NetAF.Logic.Modes; +using NetAF.Commands.Global; namespace NetAF.Tests.Logic.Modes { @@ -20,7 +21,7 @@ public void GivenNew_WhenRender_ThenNoExceptionThrown() regionMaker[0, 0, 0] = room; OverworldMaker overworldMaker = new(string.Empty, string.Empty, regionMaker); var game = Game.Create(new(string.Empty, string.Empty, string.Empty), string.Empty, AssetGenerator.Retained(overworldMaker.Make(), new PlayableCharacter(string.Empty, string.Empty)), GameEndConditions.NoEnd, TestGameConfiguration.Default).Invoke(); - var mode = new HelpMode([]); + var mode = new HelpMode(End.CommandHelp); mode.Render(game); }); diff --git a/NetAF.Tests/Rendering/FrameBuilders/Console/ConsoleCommandListFrameBuilder_Tests.cs b/NetAF.Tests/Rendering/FrameBuilders/Console/ConsoleCommandListFrameBuilder_Tests.cs new file mode 100644 index 00000000..8aca9833 --- /dev/null +++ b/NetAF.Tests/Rendering/FrameBuilders/Console/ConsoleCommandListFrameBuilder_Tests.cs @@ -0,0 +1,24 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using NetAF.Assets; +using NetAF.Commands.Scene; +using NetAF.Rendering.FrameBuilders; +using NetAF.Rendering.FrameBuilders.Console; + +namespace NetAF.Tests.Rendering.FrameBuilders.Console +{ + [TestClass] + public class ConsoleCommandListFrameBuilder_Tests + { + [TestMethod] + public void GivenDefaults_WhenBuild_ThenNoException() + { + Assertions.NoExceptionThrown(() => + { + var gridStringBuilder = new GridStringBuilder(); + var builder = new ConsoleCommandListFrameBuilder(gridStringBuilder); + + builder.Build(string.Empty, string.Empty, [Take.CommandHelp], new Size(80, 50)); + }); + } + } +} diff --git a/NetAF.Tests/Rendering/FrameBuilders/Console/ConsoleHelpFrameBuilder_Tests.cs b/NetAF.Tests/Rendering/FrameBuilders/Console/ConsoleHelpFrameBuilder_Tests.cs index 710ea808..915763ed 100644 --- a/NetAF.Tests/Rendering/FrameBuilders/Console/ConsoleHelpFrameBuilder_Tests.cs +++ b/NetAF.Tests/Rendering/FrameBuilders/Console/ConsoleHelpFrameBuilder_Tests.cs @@ -9,14 +9,26 @@ namespace NetAF.Tests.Rendering.FrameBuilders.Console public class ConsoleHelpFrameBuilder_Tests { [TestMethod] - public void GivenDefaults_WhenBuild_ThenNoException() + public void GivenDefaultsWithNoInstructions_WhenBuild_ThenNoException() { Assertions.NoExceptionThrown(() => { var gridStringBuilder = new GridStringBuilder(); var builder = new ConsoleHelpFrameBuilder(gridStringBuilder); - builder.Build(string.Empty, string.Empty, [], new Size(80, 50)); + builder.Build(new NetAF.Commands.CommandHelp("Test", "Test 2"), new Size(80, 50)); + }); + } + + [TestMethod] + public void GivenDefaultsWithInstructions_WhenBuild_ThenNoException() + { + Assertions.NoExceptionThrown(() => + { + var gridStringBuilder = new GridStringBuilder(); + var builder = new ConsoleHelpFrameBuilder(gridStringBuilder); + + builder.Build(new NetAF.Commands.CommandHelp("Test", "Test 2", "Test 3."), new Size(80, 50)); }); } } diff --git a/NetAF.Tests/Utilities/StringUtilities_Tests.cs b/NetAF.Tests/Utilities/StringUtilities_Tests.cs index ab17ba0a..c9b6d944 100644 --- a/NetAF.Tests/Utilities/StringUtilities_Tests.cs +++ b/NetAF.Tests/Utilities/StringUtilities_Tests.cs @@ -175,5 +175,32 @@ public void GivenTwoAttributes_WhenConstructAttributesAsString_ThenTestColon1Tab Assert.AreEqual("Test: 1\tTest2: 1", result); } + + [TestMethod] + public void GivenEmptyString_WhenSplitTextToVerbAndNoun_ThenReturnEmptyVerbAndNoun() + { + StringUtilities.SplitTextToVerbAndNoun(string.Empty, out var verb, out var noun); + + Assert.AreEqual(string.Empty, verb); + Assert.AreEqual(string.Empty, noun); + } + + [TestMethod] + public void GivenABC_WhenSplitTextToVerbAndNoun_TheNounABCVerbEmpty() + { + StringUtilities.SplitTextToVerbAndNoun("ABC", out var verb, out var noun); + + Assert.AreEqual("ABC", verb); + Assert.AreEqual(string.Empty, noun); + } + + [TestMethod] + public void GivenABCSpaceXYZ_WhenSplitTextToVerbAndNoun_TheNounABCVerbXYZ() + { + StringUtilities.SplitTextToVerbAndNoun("ABC XYZ", out var verb, out var noun); + + Assert.AreEqual("ABC", verb); + Assert.AreEqual("XYZ", noun); + } } } \ No newline at end of file diff --git a/NetAF/Assets/Locations/Matrix.cs b/NetAF/Assets/Locations/Matrix.cs index 12c4cdf2..21159c16 100644 --- a/NetAF/Assets/Locations/Matrix.cs +++ b/NetAF/Assets/Locations/Matrix.cs @@ -1,6 +1,5 @@ using System; using System.Linq; -using System.Security.Cryptography.X509Certificates; namespace NetAF.Assets.Locations { diff --git a/NetAF/Commands/CommandHelp.cs b/NetAF/Commands/CommandHelp.cs index 4e784ae0..7e352e8d 100644 --- a/NetAF/Commands/CommandHelp.cs +++ b/NetAF/Commands/CommandHelp.cs @@ -9,7 +9,9 @@ namespace NetAF.Commands /// The command. /// The help. /// A shortcut for the command. - public sealed class CommandHelp(string command, string description, string shortcut = "") : IEquatable, IEquatable + /// A instructions on how to use the command. + /// A string overriding how the command should be displayed.. + public sealed class CommandHelp(string command, string description, string shortcut = "", string instructions = "", string displayAs = "") : IEquatable, IEquatable { #region Properties @@ -28,6 +30,21 @@ public sealed class CommandHelp(string command, string description, string short /// public string Shortcut { get; } = shortcut; + /// + /// Get the instructions of the command. + /// + public string Instructions { get; } = instructions; + + /// + /// Get how this command should be displayed. + /// + public string DisplayAs { get; } = displayAs; + + /// + /// Get a string representing the command as it should be displayed to the user. + /// + public string DisplayCommand => !string.IsNullOrEmpty(DisplayAs) ? DisplayAs : Command; + #endregion #region Implementation of IEquatable diff --git a/NetAF/Commands/Global/CommandList.cs b/NetAF/Commands/Global/CommandList.cs new file mode 100644 index 00000000..5f50bb54 --- /dev/null +++ b/NetAF/Commands/Global/CommandList.cs @@ -0,0 +1,37 @@ +using NetAF.Logic.Modes; + +namespace NetAF.Commands.Global +{ + /// + /// Represents the Commands command. + /// + public sealed class CommandList : ICommand + { + #region StaticProperties + + /// + /// Get the command help. + /// + public static CommandHelp CommandHelp { get; } = new("Commands", "View a list of commands"); + + #endregion + + #region Implementation of ICommand + + /// + /// Invoke the command. + /// + /// The game to invoke the command on. + /// The reaction. + public Reaction Invoke(Logic.Game game) + { + if (game == null) + return new(ReactionResult.Error, "No game specified."); + + game.ChangeMode(new CommandListMode(game.GetContextualCommands())); + return new(ReactionResult.GameModeChanged, string.Empty); + } + + #endregion + } +} \ No newline at end of file diff --git a/NetAF/Commands/Global/Help.cs b/NetAF/Commands/Global/Help.cs index 96a961a5..8e40ba90 100644 --- a/NetAF/Commands/Global/Help.cs +++ b/NetAF/Commands/Global/Help.cs @@ -1,20 +1,19 @@ using NetAF.Logic.Modes; -using System.Collections.Generic; -using System.Linq; namespace NetAF.Commands.Global { /// /// Represents the Help command. /// - public sealed class Help : ICommand + /// The command to display help for. + public sealed class Help(CommandHelp command) : ICommand { #region StaticProperties /// /// Get the command help. /// - public static CommandHelp CommandHelp { get; } = new("Help", "View game help"); + public static CommandHelp CommandHelp { get; } = new("Help", "View detailed help for a command"); #endregion @@ -30,15 +29,7 @@ public Reaction Invoke(Logic.Game game) if (game == null) return new(ReactionResult.Error, "No game specified."); - List commands = - [ - .. game.Configuration.Interpreter.SupportedCommands, - .. game.Configuration.Interpreter.GetContextualCommandHelp(game), - .. game.Mode.Interpreter?.SupportedCommands ?? [], - .. game.Mode.Interpreter?.GetContextualCommandHelp(game) ?? [], - ]; - - game.ChangeMode(new HelpMode([.. commands.Distinct()])); + game.ChangeMode(new HelpMode(command)); return new(ReactionResult.GameModeChanged, string.Empty); } diff --git a/NetAF/Commands/RegionMap/Pan.cs b/NetAF/Commands/RegionMap/Pan.cs index 373208d9..c94111c2 100644 --- a/NetAF/Commands/RegionMap/Pan.cs +++ b/NetAF/Commands/RegionMap/Pan.cs @@ -15,32 +15,32 @@ public sealed class Pan(Direction direction) : ICommand /// /// Get the command help for north. /// - public static CommandHelp NorthCommandHelp { get; } = new("North", "Pan north", "N"); + public static CommandHelp NorthCommandHelp { get; } = new("North", "Pan north", "N", displayAs: "North/N"); /// /// Get the command help for south. /// - public static CommandHelp SouthCommandHelp { get; } = new("South", "Pan south", "S"); + public static CommandHelp SouthCommandHelp { get; } = new("South", "Pan south", "S", displayAs: "South/S"); /// /// Get the command help for east. /// - public static CommandHelp EastCommandHelp { get; } = new("East", "Pan east", "E"); + public static CommandHelp EastCommandHelp { get; } = new("East", "Pan east", "E", displayAs: "East/E"); /// /// Get the command help for west. /// - public static CommandHelp WestCommandHelp { get; } = new("West", "Pan west", "W"); + public static CommandHelp WestCommandHelp { get; } = new("West", "Pan west", "W", displayAs: "West/W"); /// /// Get the command help for up. /// - public static CommandHelp UpCommandHelp { get; } = new("Up", "Pan up", "U"); + public static CommandHelp UpCommandHelp { get; } = new("Up", "Pan up", "U", displayAs: "Up/U"); /// /// Get the command help for down. /// - public static CommandHelp DownCommandHelp { get; } = new("Down", "Pan down", "D"); + public static CommandHelp DownCommandHelp { get; } = new("Down", "Pan down", "D", displayAs: "Down/D"); #endregion diff --git a/NetAF/Commands/RegionMap/PanReset.cs b/NetAF/Commands/RegionMap/PanReset.cs index 6624029a..a782acff 100644 --- a/NetAF/Commands/RegionMap/PanReset.cs +++ b/NetAF/Commands/RegionMap/PanReset.cs @@ -12,7 +12,7 @@ public sealed class PanReset : ICommand /// /// Get the command help. /// - public static CommandHelp CommandHelp { get; } = new("Reset", "Reset pan", "Z"); + public static CommandHelp CommandHelp { get; } = new("Reset", "Reset pan", "Z", displayAs: "Reset/Z"); #endregion diff --git a/NetAF/Commands/Scene/Drop.cs b/NetAF/Commands/Scene/Drop.cs index fe9b3a12..db768f17 100644 --- a/NetAF/Commands/Scene/Drop.cs +++ b/NetAF/Commands/Scene/Drop.cs @@ -13,7 +13,7 @@ public sealed class Drop(Item item) : ICommand /// /// Get the command help. /// - public static CommandHelp CommandHelp { get; } = new("Drop", "Drop an item", "R"); + public static CommandHelp CommandHelp { get; } = new("Drop", "Drop an item", "R", displayAs: "Drop/R __"); #endregion diff --git a/NetAF/Commands/Scene/Examine.cs b/NetAF/Commands/Scene/Examine.cs index b5cad198..df95905c 100644 --- a/NetAF/Commands/Scene/Examine.cs +++ b/NetAF/Commands/Scene/Examine.cs @@ -13,7 +13,7 @@ public sealed class Examine(IExaminable examinable) : ICommand /// /// Get the command help. /// - public static CommandHelp CommandHelp { get; } = new("Examine", "Examine something", "X"); + public static CommandHelp CommandHelp { get; } = new("Examine", "Examine something", "X", displayAs: "Examine/X __"); #endregion diff --git a/NetAF/Commands/Scene/Move.cs b/NetAF/Commands/Scene/Move.cs index 07b5d3b3..3fd2203a 100644 --- a/NetAF/Commands/Scene/Move.cs +++ b/NetAF/Commands/Scene/Move.cs @@ -1,5 +1,4 @@ -using NetAF.Assets; -using NetAF.Assets.Locations; +using NetAF.Assets.Locations; namespace NetAF.Commands.Scene { @@ -14,32 +13,32 @@ public sealed class Move(Direction direction) : ICommand /// /// Get the command help for north. /// - public static CommandHelp NorthCommandHelp { get; } = new("North", "Move north", "N"); + public static CommandHelp NorthCommandHelp { get; } = new("North", "Move north", "N", displayAs: "North/N"); /// /// Get the command help for south. /// - public static CommandHelp SouthCommandHelp { get; } = new("South", "Move south", "S"); + public static CommandHelp SouthCommandHelp { get; } = new("South", "Move south", "S", displayAs: "South/S"); /// /// Get the command help for east. /// - public static CommandHelp EastCommandHelp { get; } = new("East", "Move east", "E"); + public static CommandHelp EastCommandHelp { get; } = new("East", "Move east", "E", displayAs: "East/E"); /// /// Get the command help for west. /// - public static CommandHelp WestCommandHelp { get; } = new("West", "Move west", "W"); + public static CommandHelp WestCommandHelp { get; } = new("West", "Move west", "W", displayAs: "West/W"); /// /// Get the command help for up. /// - public static CommandHelp UpCommandHelp { get; } = new("Up", "Move up", "U"); + public static CommandHelp UpCommandHelp { get; } = new("Up", "Move up", "U", displayAs: "Up/U"); /// /// Get the command help for down. /// - public static CommandHelp DownCommandHelp { get; } = new("Down", "Move down", "D"); + public static CommandHelp DownCommandHelp { get; } = new("Down", "Move down", "D", displayAs: "Down/D"); #endregion diff --git a/NetAF/Commands/Scene/Take.cs b/NetAF/Commands/Scene/Take.cs index 1ad15c3e..fe99411d 100644 --- a/NetAF/Commands/Scene/Take.cs +++ b/NetAF/Commands/Scene/Take.cs @@ -13,7 +13,7 @@ public sealed class Take(Item item) : ICommand /// /// Get the command help. /// - public static CommandHelp CommandHelp { get; } = new("Take", "Take an item", "T"); + public static CommandHelp CommandHelp { get; } = new("Take", "Take an item", "T", displayAs: "Take/T __"); #endregion diff --git a/NetAF/Commands/Scene/Talk.cs b/NetAF/Commands/Scene/Talk.cs index dfc4b1f6..df43534c 100644 --- a/NetAF/Commands/Scene/Talk.cs +++ b/NetAF/Commands/Scene/Talk.cs @@ -14,7 +14,7 @@ public sealed class Talk(IConverser converser) : ICommand /// /// Get the command help. /// - public static CommandHelp TalkCommandHelp { get; } = new("Talk", "Talk to a character", "L"); + public static CommandHelp TalkCommandHelp { get; } = new("Talk", "Talk to a character", "L", displayAs: $"Talk/L to __"); /// /// Get the command help for to. diff --git a/NetAF/Commands/Scene/UseOn.cs b/NetAF/Commands/Scene/UseOn.cs index 4af74898..e6713ac1 100644 --- a/NetAF/Commands/Scene/UseOn.cs +++ b/NetAF/Commands/Scene/UseOn.cs @@ -19,12 +19,12 @@ public sealed class UseOn(Item item, IInteractWithItem target) : ICommand /// /// Get the command help. /// - public static CommandHelp UseCommandHelp { get; } = new("Use", "Use an item on the current room"); + public static CommandHelp UseCommandHelp { get; } = new("Use", "Use an item on the current room", displayAs: "Use __"); /// /// Get the command help for on. /// - public static CommandHelp OnCommandHelp { get; } = new("On", "Use an item on another item or character"); + public static CommandHelp OnCommandHelp { get; } = new("On", "Use an item on another item or character", displayAs: "Use __ on __"); #endregion diff --git a/NetAF/Extensions/CommandHelpExtensions.cs b/NetAF/Extensions/CommandHelpExtensions.cs deleted file mode 100644 index 04ac21a6..00000000 --- a/NetAF/Extensions/CommandHelpExtensions.cs +++ /dev/null @@ -1,34 +0,0 @@ -using NetAF.Commands; - -namespace NetAF.Extensions -{ - /// - /// Provides extension methods for CommandHelp. - /// - public static class CommandHelpExtensions - { - #region Extensions - - /// - /// Returns this CommandHelp formatted to display command in the format Command/Shortcut. - /// - /// The value. - /// The formatted CommandHelp. - public static CommandHelp FormattedToDisplayShortcut(this CommandHelp value) - { - return new($"{value.Command}/{value.Shortcut}", value.Description); - } - - /// - /// Returns this CommandHelp formatted to display command in the format Command/Shortcut __. - /// - /// The value. - /// The formatted CommandHelp. - public static CommandHelp FormattedToDisplayShortcutAndVariable(this CommandHelp value) - { - return new($"{value.Command}/{value.Shortcut} __", value.Description); - } - - #endregion - } -} \ No newline at end of file diff --git a/NetAF/Interpretation/FrameCommandInterpreter.cs b/NetAF/Interpretation/FrameCommandInterpreter.cs index df85a912..b8b2de54 100644 --- a/NetAF/Interpretation/FrameCommandInterpreter.cs +++ b/NetAF/Interpretation/FrameCommandInterpreter.cs @@ -16,8 +16,10 @@ public sealed class FrameCommandInterpreter : IInterpreter /// public static CommandHelp[] DefaultSupportedCommands { get; } = [ - new CommandHelp($"{CommandsOn.CommandHelp.Command} / {CommandsOff.CommandHelp.Command}", "Turn commands on/off"), - new CommandHelp($"{KeyOn.CommandHelp.Command} / {KeyOff.CommandHelp.Command} ", "Turn the key on/off") + CommandsOn.CommandHelp, + CommandsOff.CommandHelp, + KeyOn.CommandHelp, + KeyOff.CommandHelp ]; #endregion diff --git a/NetAF/Interpretation/GlobalCommandInterpreter.cs b/NetAF/Interpretation/GlobalCommandInterpreter.cs index 3f23b3a9..0140ea2d 100644 --- a/NetAF/Interpretation/GlobalCommandInterpreter.cs +++ b/NetAF/Interpretation/GlobalCommandInterpreter.cs @@ -1,6 +1,9 @@ using NetAF.Commands; using NetAF.Commands.Global; +using NetAF.Extensions; using NetAF.Logic; +using NetAF.Utilities; +using System; namespace NetAF.Interpretation { @@ -19,7 +22,9 @@ public sealed class GlobalCommandInterpreter : IInterpreter About.CommandHelp, Map.CommandHelp, Exit.CommandHelp, - New.CommandHelp + New.CommandHelp, + Help.CommandHelp, + CommandList.CommandHelp ]; #endregion @@ -39,19 +44,33 @@ public sealed class GlobalCommandInterpreter : IInterpreter /// The result of the interpretation. public InterpretationResult Interpret(string input, Game game) { - if (About.CommandHelp.Equals(input)) + StringUtilities.SplitTextToVerbAndNoun(input, out var verb, out var noun); + + if (About.CommandHelp.Equals(verb)) return new(true, new About()); - if (Exit.CommandHelp.Equals(input)) + if (Exit.CommandHelp.Equals(verb)) return new(true, new Exit()); - if (Help.CommandHelp.Equals(input)) - return new(true, new Help()); + if (Help.CommandHelp.Equals(verb)) + { + if (string.IsNullOrEmpty(noun)) + return InterpretationResult.Fail; + + var commands = game.GetContextualCommands(); + var command = Array.Find(commands, x => x.Command.InsensitiveEquals(noun) || x.Shortcut.InsensitiveEquals(noun)); + + if (command != null) + return new(true, new Help(command)); + } + + if (CommandList.CommandHelp.Equals(verb)) + return new(true, new CommandList()); - if (Map.CommandHelp.Equals(input)) + if (Map.CommandHelp.Equals(verb)) return new(true, new Map()); - if (New.CommandHelp.Equals(input)) + if (New.CommandHelp.Equals(verb)) return new(true, new New()); return InterpretationResult.Fail; diff --git a/NetAF/Interpretation/RegionMapCommandInterpreter.cs b/NetAF/Interpretation/RegionMapCommandInterpreter.cs index 3e0edfdd..d17823ee 100644 --- a/NetAF/Interpretation/RegionMapCommandInterpreter.cs +++ b/NetAF/Interpretation/RegionMapCommandInterpreter.cs @@ -2,7 +2,6 @@ using NetAF.Commands; using NetAF.Commands.Global; using NetAF.Commands.RegionMap; -using NetAF.Extensions; using NetAF.Logic; using NetAF.Logic.Modes; using System.Collections.Generic; @@ -21,13 +20,13 @@ public sealed class RegionMapCommandInterpreter : IInterpreter /// public static CommandHelp[] DefaultSupportedCommands { get; } = [ - Pan.NorthCommandHelp.FormattedToDisplayShortcut(), - Pan.SouthCommandHelp.FormattedToDisplayShortcut(), - Pan.EastCommandHelp.FormattedToDisplayShortcut(), - Pan.WestCommandHelp.FormattedToDisplayShortcut(), - Pan.UpCommandHelp.FormattedToDisplayShortcut(), - Pan.DownCommandHelp.FormattedToDisplayShortcut(), - PanReset.CommandHelp.FormattedToDisplayShortcut(), + Pan.NorthCommandHelp, + Pan.SouthCommandHelp, + Pan.EastCommandHelp, + Pan.WestCommandHelp, + Pan.UpCommandHelp, + Pan.DownCommandHelp, + PanReset.CommandHelp, End.CommandHelp, ]; @@ -87,25 +86,25 @@ public CommandHelp[] GetContextualCommandHelp(Game game) if (game.Mode is RegionMapMode regionMapMode) { if (RegionMapMode.CanPanToPosition(game.Overworld.CurrentRegion, Pan.GetPanPosition(regionMapMode.FocusPosition, Direction.North))) - commands.Add(Pan.NorthCommandHelp.FormattedToDisplayShortcut()); + commands.Add(Pan.NorthCommandHelp); if (RegionMapMode.CanPanToPosition(game.Overworld.CurrentRegion, Pan.GetPanPosition(regionMapMode.FocusPosition, Direction.South))) - commands.Add(Pan.SouthCommandHelp.FormattedToDisplayShortcut()); + commands.Add(Pan.SouthCommandHelp); if (RegionMapMode.CanPanToPosition(game.Overworld.CurrentRegion, Pan.GetPanPosition(regionMapMode.FocusPosition, Direction.East))) - commands.Add(Pan.EastCommandHelp.FormattedToDisplayShortcut()); + commands.Add(Pan.EastCommandHelp); if (RegionMapMode.CanPanToPosition(game.Overworld.CurrentRegion, Pan.GetPanPosition(regionMapMode.FocusPosition, Direction.West))) - commands.Add(Pan.WestCommandHelp.FormattedToDisplayShortcut()); + commands.Add(Pan.WestCommandHelp); if (RegionMapMode.CanPanToPosition(game.Overworld.CurrentRegion, Pan.GetPanPosition(regionMapMode.FocusPosition, Direction.Up))) - commands.Add(Pan.UpCommandHelp.FormattedToDisplayShortcut()); + commands.Add(Pan.UpCommandHelp); if (RegionMapMode.CanPanToPosition(game.Overworld.CurrentRegion, Pan.GetPanPosition(regionMapMode.FocusPosition, Direction.Down))) - commands.Add(Pan.DownCommandHelp.FormattedToDisplayShortcut()); + commands.Add(Pan.DownCommandHelp); if (!regionMapMode.FocusPosition.Equals(game.Overworld.CurrentRegion.GetPositionOfRoom(game.Overworld.CurrentRegion.CurrentRoom))) - commands.Add(PanReset.CommandHelp.FormattedToDisplayShortcut()); + commands.Add(PanReset.CommandHelp); commands.Add(new CommandHelp(End.CommandHelp.Command, "Finish looking at the map")); } diff --git a/NetAF/Interpretation/SceneCommandInterpreter.cs b/NetAF/Interpretation/SceneCommandInterpreter.cs index 2d721d21..527743b8 100644 --- a/NetAF/Interpretation/SceneCommandInterpreter.cs +++ b/NetAF/Interpretation/SceneCommandInterpreter.cs @@ -8,6 +8,7 @@ using NetAF.Extensions; using NetAF.Logic; using NetAF.Logic.Modes; +using NetAF.Utilities; namespace NetAF.Interpretation { @@ -38,11 +39,6 @@ public sealed class SceneCommandInterpreter : IInterpreter /// public const string Overworld = "Overworld"; - /// - /// Get a string representing a variable. - /// - private const string Variable = "__"; - #endregion #region StaticProperties @@ -52,67 +48,25 @@ public sealed class SceneCommandInterpreter : IInterpreter /// public static CommandHelp[] DefaultSupportedCommands { get; } = [ - Move.NorthCommandHelp.FormattedToDisplayShortcut(), - Move.EastCommandHelp.FormattedToDisplayShortcut(), - Move.SouthCommandHelp.FormattedToDisplayShortcut(), - Move.WestCommandHelp.FormattedToDisplayShortcut(), - Move.UpCommandHelp.FormattedToDisplayShortcut(), - Move.DownCommandHelp.FormattedToDisplayShortcut(), - Drop.CommandHelp.FormattedToDisplayShortcutAndVariable(), - Examine.CommandHelp.FormattedToDisplayShortcutAndVariable(), - Take.CommandHelp.FormattedToDisplayShortcutAndVariable(), + Move.NorthCommandHelp, + Move.EastCommandHelp, + Move.SouthCommandHelp, + Move.WestCommandHelp, + Move.UpCommandHelp, + Move.DownCommandHelp, + Drop.CommandHelp, + Examine.CommandHelp, + Take.CommandHelp, TakeAll.CommandHelp, - GetTalkToCommandHelp(), - UseOn.UseCommandHelp.FormattedToDisplayShortcutAndVariable(), - GetUseOnCommandHelp() + Talk.TalkCommandHelp, + UseOn.UseCommandHelp, + UseOn.OnCommandHelp ]; #endregion #region StaticMethods - /// - /// Get a command help for the talk to command. - /// - /// The command help. - private static CommandHelp GetTalkToCommandHelp() - { - return new($"{Talk.TalkCommandHelp.Command}/{Talk.TalkCommandHelp.Shortcut} {Talk.ToCommandHelp.Command.ToLower()} {Variable}", Talk.TalkCommandHelp.Description); - } - - /// - /// Get a command help for the use on command. - /// - /// The command help. - private static CommandHelp GetUseOnCommandHelp() - { - return new($"{UseOn.UseCommandHelp.Command} {Variable} {UseOn.OnCommandHelp.Command.ToLower()} {Variable}", UseOn.OnCommandHelp.Description); - } - - /// - /// Split text in to a verb and a noun. - /// - /// The text to split. - /// The verb. - /// The noun. - private static void SplitTextToVerbAndNoun(string text, out string verb, out string noun) - { - // if there is a space - if (text.IndexOf(" ", StringComparison.Ordinal) > -1) - { - // verb all text up to space - verb = text.Substring(0, text.IndexOf(" ", StringComparison.Ordinal)).Trim(); - - // noun is all text after space - noun = text.Substring(text.IndexOf(" ", StringComparison.Ordinal)).Trim(); - } - else - { - verb = text; - noun = string.Empty; - } - } - /// /// Try and parse the Drop command. /// @@ -122,7 +76,7 @@ private static void SplitTextToVerbAndNoun(string text, out string verb, out str /// True if the input could be parsed, else false. private static bool TryParseDropCommand(string text, Game game, out ICommand command) { - SplitTextToVerbAndNoun(text, out var verb, out var noun); + StringUtilities.SplitTextToVerbAndNoun(text, out var verb, out var noun); if (!Drop.CommandHelp.Equals(verb)) { @@ -144,7 +98,7 @@ private static bool TryParseDropCommand(string text, Game game, out ICommand com /// True if the input could be parsed, else false. private static bool TryParseTakeCommand(string text, Game game, out ICommand command) { - SplitTextToVerbAndNoun(text, out var verb, out var noun); + StringUtilities.SplitTextToVerbAndNoun(text, out var verb, out var noun); if (!Take.CommandHelp.Equals(verb)) { @@ -189,7 +143,7 @@ private static bool TryParseTakeCommand(string text, Game game, out ICommand com /// True if the input could be parsed, else false. private static bool TryParseTalkCommand(string text, Game game, out ICommand command) { - SplitTextToVerbAndNoun(text, out var verb, out var noun); + StringUtilities.SplitTextToVerbAndNoun(text, out var verb, out var noun); if (!Talk.TalkCommandHelp.Equals(verb)) { @@ -282,7 +236,7 @@ private static bool TryParseExamineCommandLocations(string noun, Game game, out /// True if the input could be parsed, else false. private static bool TryParseExamineCommand(string text, Game game, out ICommand command) { - SplitTextToVerbAndNoun(text, out var verb, out var noun); + StringUtilities.SplitTextToVerbAndNoun(text, out var verb, out var noun); if (!Examine.CommandHelp.Equals(verb)) { @@ -350,7 +304,7 @@ private static bool TryParseExamineCommand(string text, Game game, out ICommand /// True if the input could be parsed, else false. private static bool TryParseUseOnCommand(string text, Game game, out ICommand command) { - SplitTextToVerbAndNoun(text, out var verb, out var noun); + StringUtilities.SplitTextToVerbAndNoun(text, out var verb, out var noun); if (!UseOn.UseCommandHelp.Equals(verb)) { @@ -505,41 +459,41 @@ public CommandHelp[] GetContextualCommandHelp(Game game) List commands = []; if (game.Overworld.CurrentRegion.CurrentRoom.CanMove(Direction.North)) - commands.Add(Move.NorthCommandHelp.FormattedToDisplayShortcut()); + commands.Add(Move.NorthCommandHelp); if (game.Overworld.CurrentRegion.CurrentRoom.CanMove(Direction.East)) - commands.Add(Move.EastCommandHelp.FormattedToDisplayShortcut()); + commands.Add(Move.EastCommandHelp); if (game.Overworld.CurrentRegion.CurrentRoom.CanMove(Direction.South)) - commands.Add(Move.SouthCommandHelp.FormattedToDisplayShortcut()); + commands.Add(Move.SouthCommandHelp); if (game.Overworld.CurrentRegion.CurrentRoom.CanMove(Direction.West)) - commands.Add(Move.WestCommandHelp.FormattedToDisplayShortcut()); + commands.Add(Move.WestCommandHelp); if (game.Overworld.CurrentRegion.CurrentRoom.CanMove(Direction.Up)) - commands.Add(Move.UpCommandHelp.FormattedToDisplayShortcut()); + commands.Add(Move.UpCommandHelp); if (game.Overworld.CurrentRegion.CurrentRoom.CanMove(Direction.Down)) - commands.Add(Move.DownCommandHelp.FormattedToDisplayShortcut()); + commands.Add(Move.DownCommandHelp); - commands.Add(Examine.CommandHelp.FormattedToDisplayShortcutAndVariable()); + commands.Add(Examine.CommandHelp); if (game.Player.Items.Any()) - commands.Add(Drop.CommandHelp.FormattedToDisplayShortcutAndVariable()); + commands.Add(Drop.CommandHelp); if (game.Overworld.CurrentRegion.CurrentRoom.Items.Any()) { - commands.Add(Take.CommandHelp.FormattedToDisplayShortcutAndVariable()); + commands.Add(Take.CommandHelp); commands.Add(TakeAll.CommandHelp); } if (game.Player.CanConverse && game.Overworld.CurrentRegion.CurrentRoom.Characters.Any()) - commands.Add(GetTalkToCommandHelp()); + commands.Add(Talk.TalkCommandHelp); if (game.Overworld.CurrentRegion.CurrentRoom.Items.Any() || game.Player.Items.Any()) { - commands.Add(UseOn.UseCommandHelp.FormattedToDisplayShortcutAndVariable()); - commands.Add(GetUseOnCommandHelp()); + commands.Add(UseOn.UseCommandHelp); + commands.Add(UseOn.OnCommandHelp); } return [.. commands]; diff --git a/NetAF/Logic/Game.cs b/NetAF/Logic/Game.cs index f36d35d9..45254a51 100644 --- a/NetAF/Logic/Game.cs +++ b/NetAF/Logic/Game.cs @@ -378,6 +378,23 @@ public IExaminable[] GetAllPlayerVisibleExaminables() return [.. examinables]; } + /// + /// Get all commands that are valid in the current context. + /// + /// An array of all commands that are valid in the current context. + public CommandHelp[] GetContextualCommands() + { + List commands = + [ + .. Configuration.Interpreter.SupportedCommands, + .. Configuration.Interpreter.GetContextualCommandHelp(this), + .. Mode?.Interpreter?.SupportedCommands ?? [], + .. Mode?.Interpreter?.GetContextualCommandHelp(this) ?? [], + ]; + + return [.. commands.Distinct()]; + } + #endregion #region StaticMethods diff --git a/NetAF/Logic/Modes/CommandListMode.cs b/NetAF/Logic/Modes/CommandListMode.cs new file mode 100644 index 00000000..5032ca30 --- /dev/null +++ b/NetAF/Logic/Modes/CommandListMode.cs @@ -0,0 +1,36 @@ +using NetAF.Commands; +using NetAF.Interpretation; + +namespace NetAF.Logic.Modes +{ + /// + /// Provides a display mode for command list. + /// + /// The commands to display. + public sealed class CommandListMode(CommandHelp[] commands) : IGameMode + { + #region Implementation of IGameMode + + /// + /// Get the interpreter. + /// + public IInterpreter Interpreter { get; } + + /// + /// Get the type of mode this provides. + /// + public GameModeType Type { get; } = GameModeType.Information; + + /// + /// Render the current state of a game. + /// + /// The game. + public void Render(Game game) + { + var frame = game.Configuration.FrameBuilders.CommandListFrameBuilder.Build("Help", string.Empty, commands, game.Configuration.DisplaySize); + game.Configuration.Adapter.RenderFrame(frame); + } + + #endregion + } +} diff --git a/NetAF/Logic/Modes/HelpMode.cs b/NetAF/Logic/Modes/HelpMode.cs index a58f851e..d2dcc42f 100644 --- a/NetAF/Logic/Modes/HelpMode.cs +++ b/NetAF/Logic/Modes/HelpMode.cs @@ -6,8 +6,8 @@ namespace NetAF.Logic.Modes /// /// Provides a display mode for help. /// - /// The commands to display. - public sealed class HelpMode(CommandHelp[] commands) : IGameMode + /// The command to display. + public sealed class HelpMode(CommandHelp command) : IGameMode { #region Implementation of IGameMode @@ -27,7 +27,7 @@ public sealed class HelpMode(CommandHelp[] commands) : IGameMode /// The game. public void Render(Game game) { - var frame = game.Configuration.FrameBuilders.HelpFrameBuilder.Build("Help", string.Empty, commands, game.Configuration.DisplaySize); + var frame = game.Configuration.FrameBuilders.HelpFrameBuilder.Build(command, game.Configuration.DisplaySize); game.Configuration.Adapter.RenderFrame(frame); } diff --git a/NetAF/Logic/Modes/RegionMapMode.cs b/NetAF/Logic/Modes/RegionMapMode.cs index 6d6f9801..4e7acbbe 100644 --- a/NetAF/Logic/Modes/RegionMapMode.cs +++ b/NetAF/Logic/Modes/RegionMapMode.cs @@ -3,7 +3,6 @@ using NetAF.Interpretation; using System.Collections.Generic; using System.Linq; -using System.Runtime.InteropServices.ComTypes; namespace NetAF.Logic.Modes { diff --git a/NetAF/Rendering/FrameBuilders/Console/ConsoleCommandListFrameBuilder.cs b/NetAF/Rendering/FrameBuilders/Console/ConsoleCommandListFrameBuilder.cs new file mode 100644 index 00000000..087060c0 --- /dev/null +++ b/NetAF/Rendering/FrameBuilders/Console/ConsoleCommandListFrameBuilder.cs @@ -0,0 +1,98 @@ +using System.Linq; +using NetAF.Assets; +using NetAF.Commands; +using NetAF.Extensions; +using NetAF.Rendering.Frames; + +namespace NetAF.Rendering.FrameBuilders.Console +{ + /// + /// Provides a builder of command list frames. + /// + /// A builder to use for the string layout. + public sealed class ConsoleCommandListFrameBuilder(GridStringBuilder gridStringBuilder) : ICommandListFrameBuilder + { + #region Properties + + /// + /// Get or set the background color. + /// + public AnsiColor BackgroundColor { get; set; } + + /// + /// Get or set the border color. + /// + public AnsiColor BorderColor { get; set; } = AnsiColor.BrightBlack; + + /// + /// Get or set the title color. + /// + public AnsiColor TitleColor { get; set; } = AnsiColor.White; + + /// + /// Get or set the description color. + /// + public AnsiColor DescriptionColor { get; set; } = AnsiColor.White; + + /// + /// Get or set the command color. + /// + public AnsiColor CommandColor { get; set; } = AnsiColor.Green; + + /// + /// Get or set the description color. + /// + public AnsiColor CommandDescriptionColor { get; set; } = AnsiColor.Yellow; + + #endregion + + #region Implementation of ICommandListFrameBuilder + + /// + /// Build a frame. + /// + /// The title. + /// The description. + /// The command help. + /// The size of the frame. + public IFrame Build(string title, string description, CommandHelp[] commandHelp, Size size) + { + gridStringBuilder.Resize(size); + + gridStringBuilder.DrawBoundary(BorderColor); + + var availableWidth = size.Width - 4; + const int leftMargin = 2; + var padding = (commandHelp.Any() ? commandHelp.Max(x => x.DisplayCommand.Length) : 0) + 1; + + gridStringBuilder.DrawWrapped(title, leftMargin, 2, availableWidth, TitleColor, out _, out var lastY); + gridStringBuilder.DrawUnderline(leftMargin, lastY + 1, title.Length, TitleColor); + + if (!string.IsNullOrEmpty(description)) + gridStringBuilder.DrawCentralisedWrapped(description, lastY + 3, availableWidth, DescriptionColor, out _, out lastY); + + lastY += 2; + + foreach (var command in commandHelp) + { + if (lastY >= size.Height - 1) + break; + + if (!string.IsNullOrEmpty(command.DisplayCommand) && !string.IsNullOrEmpty(command.Description)) + { + gridStringBuilder.DrawWrapped(command.DisplayCommand, leftMargin, lastY + 1, availableWidth, CommandColor, out _, out lastY); + gridStringBuilder.DrawWrapped("-", leftMargin + padding, lastY, availableWidth, CommandColor, out _, out lastY); + gridStringBuilder.DrawWrapped(command.Description.EnsureFinishedSentence(), leftMargin + padding + 2, lastY, availableWidth, CommandDescriptionColor, out _, out lastY); + } + else if (!string.IsNullOrEmpty(command.DisplayCommand) && string.IsNullOrEmpty(command.Description)) + { + gridStringBuilder.DrawWrapped(command.DisplayCommand, leftMargin, lastY + 1, availableWidth, CommandColor, out _, out lastY); + } + } + + return new GridTextFrame(gridStringBuilder, 0, 0, BackgroundColor) { ShowCursor = false }; + } + + #endregion + } +} diff --git a/NetAF/Rendering/FrameBuilders/Console/ConsoleConversationFrameBuilder.cs b/NetAF/Rendering/FrameBuilders/Console/ConsoleConversationFrameBuilder.cs index 61d333f5..b7e87b01 100644 --- a/NetAF/Rendering/FrameBuilders/Console/ConsoleConversationFrameBuilder.cs +++ b/NetAF/Rendering/FrameBuilders/Console/ConsoleConversationFrameBuilder.cs @@ -156,7 +156,7 @@ public IFrame Build(string title, IConverser converser, CommandHelp[] contextual gridStringBuilder.DrawHorizontalDivider(lastY + linePadding, BorderColor); gridStringBuilder.DrawWrapped(CommandTitle, leftMargin, lastY + 4, availableWidth, ResponseColor, out _, out lastY); - var maxCommandLength = contextualCommands.Max(x => x.Command.Length); + var maxCommandLength = contextualCommands.Max(x => x.DisplayCommand.Length); const int padding = 4; var dashStartX = leftMargin + maxCommandLength + padding; var descriptionStartX = dashStartX + 2; @@ -164,7 +164,7 @@ public IFrame Build(string title, IConverser converser, CommandHelp[] contextual foreach (var contextualCommand in contextualCommands) { - gridStringBuilder.DrawWrapped(contextualCommand.Command, leftMargin, lastY + 1, availableWidth, ResponseColor, out _, out lastY); + gridStringBuilder.DrawWrapped(contextualCommand.DisplayCommand, leftMargin, lastY + 1, availableWidth, ResponseColor, out _, out lastY); gridStringBuilder.DrawWrapped("-", dashStartX, lastY, availableWidth, ResponseColor, out _, out lastY); gridStringBuilder.DrawWrapped(contextualCommand.Description, descriptionStartX, lastY, availableWidth, ResponseColor, out _, out lastY); } diff --git a/NetAF/Rendering/FrameBuilders/Console/ConsoleHelpFrameBuilder.cs b/NetAF/Rendering/FrameBuilders/Console/ConsoleHelpFrameBuilder.cs index 1eb4cb3e..92bd886b 100644 --- a/NetAF/Rendering/FrameBuilders/Console/ConsoleHelpFrameBuilder.cs +++ b/NetAF/Rendering/FrameBuilders/Console/ConsoleHelpFrameBuilder.cs @@ -1,5 +1,4 @@ -using System.Linq; -using NetAF.Assets; +using NetAF.Assets; using NetAF.Commands; using NetAF.Extensions; using NetAF.Rendering.Frames; @@ -24,16 +23,6 @@ public sealed class ConsoleHelpFrameBuilder(GridStringBuilder gridStringBuilder) /// public AnsiColor BorderColor { get; set; } = AnsiColor.BrightBlack; - /// - /// Get or set the title color. - /// - public AnsiColor TitleColor { get; set; } = AnsiColor.White; - - /// - /// Get or set the description color. - /// - public AnsiColor DescriptionColor { get; set; } = AnsiColor.White; - /// /// Get or set the command color. /// @@ -51,11 +40,9 @@ public sealed class ConsoleHelpFrameBuilder(GridStringBuilder gridStringBuilder) /// /// Build a frame. /// - /// The title. - /// The description. /// The command help. /// The size of the frame. - public IFrame Build(string title, string description, CommandHelp[] commandHelp, Size size) + public IFrame Build(CommandHelp commandHelp, Size size) { gridStringBuilder.Resize(size); @@ -63,32 +50,18 @@ public IFrame Build(string title, string description, CommandHelp[] commandHelp, var availableWidth = size.Width - 4; const int leftMargin = 2; - var padding = (commandHelp.Any() ? commandHelp.Max(x => x.Command.Length) : 0) + 1; - - gridStringBuilder.DrawWrapped(title, leftMargin, 2, availableWidth, TitleColor, out _, out var lastY); - gridStringBuilder.DrawUnderline(leftMargin, lastY + 1, title.Length, TitleColor); - - if (!string.IsNullOrEmpty(description)) - gridStringBuilder.DrawCentralisedWrapped(description, lastY + 3, availableWidth, DescriptionColor, out _, out lastY); - - lastY += 2; - - foreach (var command in commandHelp) - { - if (lastY >= size.Height - 1) - break; - - if (!string.IsNullOrEmpty(command.Command) && !string.IsNullOrEmpty(command.Description)) - { - gridStringBuilder.DrawWrapped(command.Command, leftMargin, lastY + 1, availableWidth, CommandColor, out _, out lastY); - gridStringBuilder.DrawWrapped("-", leftMargin + padding, lastY, availableWidth, CommandColor, out _, out lastY); - gridStringBuilder.DrawWrapped(command.Description.EnsureFinishedSentence(), leftMargin + padding + 2, lastY, availableWidth, CommandDescriptionColor, out _, out lastY); - } - else if (!string.IsNullOrEmpty(command.Command) && string.IsNullOrEmpty(command.Description)) - { - gridStringBuilder.DrawWrapped(command.Command, leftMargin, lastY + 1, availableWidth, CommandColor, out _, out lastY); - } - } + + gridStringBuilder.DrawWrapped(commandHelp.Command, leftMargin, 2, availableWidth, CommandColor, out _, out var lastY); + gridStringBuilder.DrawUnderline(leftMargin, lastY + 1, commandHelp.Command.Length, CommandColor); + + lastY += 3; + + var description = !string.IsNullOrEmpty(commandHelp.Instructions) ? commandHelp.Instructions : commandHelp.Description; + + gridStringBuilder.DrawWrapped(description.EnsureFinishedSentence(), leftMargin, lastY, availableWidth, CommandDescriptionColor, out _, out _); + + if (!string.IsNullOrEmpty(commandHelp.DisplayAs)) + gridStringBuilder.DrawWrapped($"Example: {commandHelp.DisplayAs}", leftMargin, lastY + 2, availableWidth, CommandDescriptionColor, out _, out _); return new GridTextFrame(gridStringBuilder, 0, 0, BackgroundColor) { ShowCursor = false }; } diff --git a/NetAF/Rendering/FrameBuilders/Console/ConsoleRegionMapFrameBuilder.cs b/NetAF/Rendering/FrameBuilders/Console/ConsoleRegionMapFrameBuilder.cs index 1298c38b..c8322e72 100644 --- a/NetAF/Rendering/FrameBuilders/Console/ConsoleRegionMapFrameBuilder.cs +++ b/NetAF/Rendering/FrameBuilders/Console/ConsoleRegionMapFrameBuilder.cs @@ -97,7 +97,7 @@ public IFrame Build(Region region, Point3D focusPosition, CommandHelp[] contextu gridStringBuilder.DrawHorizontalDivider(requiredYToFitAllCommands, BorderColor); gridStringBuilder.DrawWrapped(CommandTitle, leftMargin, requiredYToFitAllCommands + 2, availableWidth, CommandsColor, out _, out lastY); - var maxCommandLength = contextualCommands.Max(x => x.Command.Length); + var maxCommandLength = contextualCommands.Max(x => x.DisplayCommand.Length); const int padding = 4; var dashStartX = leftMargin + maxCommandLength + padding; var descriptionStartX = dashStartX + 2; @@ -106,7 +106,7 @@ public IFrame Build(Region region, Point3D focusPosition, CommandHelp[] contextu for (var index = 0; index < contextualCommands.Length; index++) { var contextualCommand = contextualCommands[index]; - gridStringBuilder.DrawWrapped(contextualCommand.Command, leftMargin, lastY + 1, availableWidth, CommandsColor, out _, out lastY); + gridStringBuilder.DrawWrapped(contextualCommand.DisplayCommand, leftMargin, lastY + 1, availableWidth, CommandsColor, out _, out lastY); gridStringBuilder.DrawWrapped("-", dashStartX, lastY, availableWidth, CommandsColor, out _, out lastY); gridStringBuilder.DrawWrapped(contextualCommand.Description.EnsureFinishedSentence(), descriptionStartX, lastY, availableWidth, CommandsColor, out _, out lastY); } diff --git a/NetAF/Rendering/FrameBuilders/Console/ConsoleSceneFrameBuilder.cs b/NetAF/Rendering/FrameBuilders/Console/ConsoleSceneFrameBuilder.cs index a4c1018a..883846fa 100644 --- a/NetAF/Rendering/FrameBuilders/Console/ConsoleSceneFrameBuilder.cs +++ b/NetAF/Rendering/FrameBuilders/Console/ConsoleSceneFrameBuilder.cs @@ -126,7 +126,7 @@ public IFrame Build(Room room, ViewPoint viewPoint, PlayableCharacter player, Co gridStringBuilder.DrawHorizontalDivider(lastY + linePadding, BorderColor); gridStringBuilder.DrawWrapped(CommandTitle, leftMargin, lastY + 4, availableWidth, CommandsColor, out _, out lastY); - var maxCommandLength = contextualCommands.Max(x => x.Command.Length); + var maxCommandLength = contextualCommands.Max(x => x.DisplayCommand.Length); const int padding = 4; var dashStartX = leftMargin + maxCommandLength + padding; var descriptionStartX = dashStartX + 2; @@ -135,7 +135,7 @@ public IFrame Build(Room room, ViewPoint viewPoint, PlayableCharacter player, Co for (var index = 0; index < contextualCommands.Length; index++) { var contextualCommand = contextualCommands[index]; - gridStringBuilder.DrawWrapped(contextualCommand.Command, leftMargin, lastY + 1, availableWidth, CommandsColor, out _, out lastY); + gridStringBuilder.DrawWrapped(contextualCommand.DisplayCommand, leftMargin, lastY + 1, availableWidth, CommandsColor, out _, out lastY); gridStringBuilder.DrawWrapped("-", dashStartX, lastY, availableWidth, CommandsColor, out _, out lastY); gridStringBuilder.DrawWrapped(contextualCommand.Description.EnsureFinishedSentence(), descriptionStartX, lastY, availableWidth, CommandsColor, out _, out lastY); diff --git a/NetAF/Rendering/FrameBuilders/FrameBuilderCollection.cs b/NetAF/Rendering/FrameBuilders/FrameBuilderCollection.cs index e9ae3fd0..488a0c99 100644 --- a/NetAF/Rendering/FrameBuilders/FrameBuilderCollection.cs +++ b/NetAF/Rendering/FrameBuilders/FrameBuilderCollection.cs @@ -6,13 +6,14 @@ /// The builder to use for building title frames. /// The builder to use for building scene frames. /// The builder to use for building region map frames. + /// The builder to use for building command list frames. /// The builder to use for building help frames. /// The builder to use for building completion frames. /// The builder to use for building game over frames. /// The builder to use for building about frames. /// The builder to use for building reaction frames. /// The builder to use for building conversation frames. - public class FrameBuilderCollection(ITitleFrameBuilder titleFrameBuilder, ISceneFrameBuilder sceneFrameBuilder, IRegionMapFrameBuilder regionMapFrameBuilder, IHelpFrameBuilder helpFrameBuilder, ICompletionFrameBuilder completionFrameBuilder, IGameOverFrameBuilder gameOverFrameBuilder, IAboutFrameBuilder aboutFrameBuilder, IReactionFrameBuilder reactionFrameBuilder, IConversationFrameBuilder conversationFrameBuilder) + public class FrameBuilderCollection(ITitleFrameBuilder titleFrameBuilder, ISceneFrameBuilder sceneFrameBuilder, IRegionMapFrameBuilder regionMapFrameBuilder, ICommandListFrameBuilder commandListFrameBuilder, IHelpFrameBuilder helpFrameBuilder, ICompletionFrameBuilder completionFrameBuilder, IGameOverFrameBuilder gameOverFrameBuilder, IAboutFrameBuilder aboutFrameBuilder, IReactionFrameBuilder reactionFrameBuilder, IConversationFrameBuilder conversationFrameBuilder) { #region Properties @@ -31,6 +32,11 @@ public class FrameBuilderCollection(ITitleFrameBuilder titleFrameBuilder, IScene /// public IRegionMapFrameBuilder RegionMapFrameBuilder { get; } = regionMapFrameBuilder; + /// + /// Get the builder to use for command list frames. + /// + public ICommandListFrameBuilder CommandListFrameBuilder { get; } = commandListFrameBuilder; + /// /// Get the builder to use for help frames. /// diff --git a/NetAF/Rendering/FrameBuilders/FrameBuilderCollections.cs b/NetAF/Rendering/FrameBuilders/FrameBuilderCollections.cs index 1e64beb9..acd2a180 100644 --- a/NetAF/Rendering/FrameBuilders/FrameBuilderCollections.cs +++ b/NetAF/Rendering/FrameBuilders/FrameBuilderCollections.cs @@ -20,6 +20,7 @@ public static FrameBuilderCollection Default new ConsoleTitleFrameBuilder(gridLayoutBuilder), new ConsoleSceneFrameBuilder(gridLayoutBuilder, new ConsoleRoomMapBuilder(gridLayoutBuilder)), new ConsoleRegionMapFrameBuilder(gridLayoutBuilder, new ConsoleRegionMapBuilder(gridLayoutBuilder)), + new ConsoleCommandListFrameBuilder(gridLayoutBuilder), new ConsoleHelpFrameBuilder(gridLayoutBuilder), new ConsoleCompletionFrameBuilder(gridLayoutBuilder), new ConsoleGameOverFrameBuilder(gridLayoutBuilder), diff --git a/NetAF/Rendering/FrameBuilders/ICommandListFrameBuilder.cs b/NetAF/Rendering/FrameBuilders/ICommandListFrameBuilder.cs new file mode 100644 index 00000000..2271c515 --- /dev/null +++ b/NetAF/Rendering/FrameBuilders/ICommandListFrameBuilder.cs @@ -0,0 +1,21 @@ +using NetAF.Assets; +using NetAF.Commands; +using NetAF.Rendering.Frames; + +namespace NetAF.Rendering.FrameBuilders +{ + /// + /// Represents any object that can build command list frames. + /// + public interface ICommandListFrameBuilder + { + /// + /// Build a frame. + /// + /// The title. + /// The description. + /// The command help. + /// The size of the frame. + IFrame Build(string title, string description, CommandHelp[] commandHelp, Size size); + } +} diff --git a/NetAF/Rendering/FrameBuilders/IHelpFrameBuilder.cs b/NetAF/Rendering/FrameBuilders/IHelpFrameBuilder.cs index 384f98f5..e79ff4da 100644 --- a/NetAF/Rendering/FrameBuilders/IHelpFrameBuilder.cs +++ b/NetAF/Rendering/FrameBuilders/IHelpFrameBuilder.cs @@ -12,10 +12,8 @@ public interface IHelpFrameBuilder /// /// Build a frame. /// - /// The title. - /// The description. /// The command help. /// The size of the frame. - IFrame Build(string title, string description, CommandHelp[] commandHelp, Size size); + IFrame Build(CommandHelp commandHelp, Size size); } } diff --git a/NetAF/Utilities/StringUtilities.cs b/NetAF/Utilities/StringUtilities.cs index 65c1c726..09eb1edc 100644 --- a/NetAF/Utilities/StringUtilities.cs +++ b/NetAF/Utilities/StringUtilities.cs @@ -1,8 +1,8 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using System.Text; using NetAF.Assets; -using NetAF.Assets.Attributes; using NetAF.Extensions; namespace NetAF.Utilities @@ -163,7 +163,7 @@ public static string ConstructExaminablesAsSentence(IExaminable[] examinables) /// /// The attributes. /// The sentence. - public static string ConstructAttributesAsString(Dictionary attributes) + public static string ConstructAttributesAsString(Dictionary attributes) { if (attributes?.Any() != true) return string.Empty; @@ -176,6 +176,30 @@ public static string ConstructAttributesAsString(Dictionary attr return builder.ToString(); } + /// + /// Split text in to a verb and a noun. + /// + /// The text to split. + /// The verb. + /// The noun. + public static void SplitTextToVerbAndNoun(string text, out string verb, out string noun) + { + // if there is a space + if (text.IndexOf(" ", StringComparison.Ordinal) > -1) + { + // verb all text up to space + verb = text.Substring(0, text.IndexOf(" ", StringComparison.Ordinal)).Trim(); + + // noun is all text after space + noun = text.Substring(text.IndexOf(" ", StringComparison.Ordinal)).Trim(); + } + else + { + verb = text; + noun = string.Empty; + } + } + #endregion } }