diff --git a/NetAF.Tests/Interpretation/CustomCommandInterpreter_Tests.cs b/NetAF.Tests/Interpretation/CustomCommandInterpreter_Tests.cs index 12ae5354..cb862701 100644 --- a/NetAF.Tests/Interpretation/CustomCommandInterpreter_Tests.cs +++ b/NetAF.Tests/Interpretation/CustomCommandInterpreter_Tests.cs @@ -94,7 +94,6 @@ public void GivenValidCustomCommandThatIsNotPlayerVisibleButIsStillInterpreted_W interpreter.Interpret("Test", game); } - [TestMethod] public void GivenValidCustomCommandThatIsNotPlayerVisibleAndIsNotStillInterpreted_WhenInterpret_ThenResultWasInterpretedSuccessfullyIsFalse() { @@ -118,5 +117,29 @@ public void GivenValidCustomCommandThatIsNotPlayerVisibleAndIsNotStillInterprete Assert.IsFalse(result.WasInterpretedSuccessfully); } + + [TestMethod] + public void GivenValidCustomCommandAndSingleArgument_WhenInterpret_ThenResultWasInterpretedSuccessfullyIsTrue() + { + var interpreter = new CustomCommandInterpreter(); + CustomCommand[] commands = + [ + new CustomCommand(new("Two word", string.Empty), true, true, (_, _) => + { + return new(ReactionResult.Error, string.Empty); + }) + ]; + var overworld = new Overworld(Identifier.Empty, Description.Empty, commands); + var region = new Region(Identifier.Empty, Description.Empty); + region.AddRoom(new(Identifier.Empty, Description.Empty, [new Exit(Direction.North)]), 0, 0, 0); + region.AddRoom(new(Identifier.Empty, Description.Empty, [new Exit(Direction.South)]), 0, 1, 0); + overworld.AddRegion(region); + 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.Overworld.CurrentRegion.Enter(); + + var result = interpreter.Interpret("Two word args", game); + + Assert.IsTrue(result.WasInterpretedSuccessfully); + } } } diff --git a/NetAF.Tests/Interpretation/GameCommandInterpreter_Tests.cs b/NetAF.Tests/Interpretation/SceneCommandInterpreter_Tests.cs similarity index 88% rename from NetAF.Tests/Interpretation/GameCommandInterpreter_Tests.cs rename to NetAF.Tests/Interpretation/SceneCommandInterpreter_Tests.cs index 5ec090ac..fd246521 100644 --- a/NetAF.Tests/Interpretation/GameCommandInterpreter_Tests.cs +++ b/NetAF.Tests/Interpretation/SceneCommandInterpreter_Tests.cs @@ -9,7 +9,7 @@ namespace NetAF.Tests.Interpretation { [TestClass] - public class GameCommandInterpreter_Tests + public class SceneCommandInterpreter_Tests { [TestInitialize] public void Setup() @@ -245,5 +245,37 @@ public void GivenUseOn_WhenInterpret_ThenReturnTrue() Assert.IsTrue(result.WasInterpretedSuccessfully); } + + [TestMethod] + public void GivenUseOnWhenBothItemsAreTwoWords_WhenInterpret_ThenReturnTrue() + { + var interpreter = new SceneCommandInterpreter(); + 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($"{UseOn.UseCommandHelp.Command} test tube {UseOn.OnCommandHelp.Command} test egg", game); + + Assert.IsTrue(result.WasInterpretedSuccessfully); + } + + [TestMethod] + public void GivenInterpreter_WhenGetSupportedCommands_ThenReturnArrayWithSomeItems() + { + var interpreter = new SceneCommandInterpreter(); + + var result = interpreter.SupportedCommands; + + Assert.IsTrue(result.Length > 0); + } + + [TestMethod] + public void GivenInterpreter_WhenGetContextualCommandHelp_ThenReturnArrayWithSomeItems() + { + var interpreter = new SceneCommandInterpreter(); + 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.GetContextualCommandHelp(game); + + Assert.IsTrue(result.Length > 0); + } } } diff --git a/NetAF.Tests/Serialization/Assets/AttributeAndValueSerialization_Tests.cs b/NetAF.Tests/Serialization/Assets/AttributeAndValueSerialization_Tests.cs index 4be09d2c..0ea78358 100644 --- a/NetAF.Tests/Serialization/Assets/AttributeAndValueSerialization_Tests.cs +++ b/NetAF.Tests/Serialization/Assets/AttributeAndValueSerialization_Tests.cs @@ -1,6 +1,8 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using NetAF.Assets.Attributes; +using NetAF.Serialization; using NetAF.Serialization.Assets; +using System.Collections.Generic; namespace NetAF.Tests.Serialization.Assets { @@ -63,7 +65,7 @@ public void GivenRestore_ThenNoExceptionThrown() Assertions.NoExceptionThrown(() => { AttributeAndValueSerialization value = new(); - value.Restore(new System.Collections.Generic.KeyValuePair()); + ((IObjectSerialization>)value).Restore(new KeyValuePair()); }); } } diff --git a/NetAF.Tests/Serialization/Assets/AttributeManagerSerialization_Tests.cs b/NetAF.Tests/Serialization/Assets/AttributeManagerSerialization_Tests.cs index 14166c89..3c7142ca 100644 --- a/NetAF.Tests/Serialization/Assets/AttributeManagerSerialization_Tests.cs +++ b/NetAF.Tests/Serialization/Assets/AttributeManagerSerialization_Tests.cs @@ -1,5 +1,6 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using NetAF.Assets.Attributes; +using NetAF.Serialization; using NetAF.Serialization.Assets; using System.Linq; @@ -27,7 +28,7 @@ public void Given0AttributesButARestorationWith1Attribute_WhenRestore_ThenValues attributeManager2.Add(new Attribute("a", "b", 1, 10), 5); var serialization = AttributeManagerSerialization.FromAttributeManager(attributeManager2); - serialization.Restore(attributeManager1); + ((IObjectSerialization)serialization).Restore(attributeManager1); Assert.AreEqual(1, attributeManager1.Count); } @@ -40,7 +41,7 @@ public void Given0Attributes_WhenRestoreFromARestorationWith1Attribute_When_Rest attributeManager2.Add(new Attribute("a", "b", 1, 10), 5); var serialization = AttributeManagerSerialization.FromAttributeManager(attributeManager2); - serialization.Restore(attributeManager1); + ((IObjectSerialization)serialization).Restore(attributeManager1); var attributeDictionary = attributeManager2.GetAsDictionary(); var attribute = attributeDictionary.ElementAt(0).Key; var count = attributeDictionary.ElementAt(0).Value; diff --git a/NetAF.Tests/Serialization/Assets/CharacterSerialization_Tests.cs b/NetAF.Tests/Serialization/Assets/CharacterSerialization_Tests.cs index 2fb12211..2dd239a6 100644 --- a/NetAF.Tests/Serialization/Assets/CharacterSerialization_Tests.cs +++ b/NetAF.Tests/Serialization/Assets/CharacterSerialization_Tests.cs @@ -1,6 +1,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using NetAF.Assets; using NetAF.Assets.Characters; +using NetAF.Serialization; using NetAF.Serialization.Assets; namespace NetAF.Tests.Serialization.Assets @@ -58,7 +59,7 @@ public void GivenCharacterThatIsAlive_WhenRestoreFromCharacterThatIsNotAlive_The character2.Kill(); CharacterSerialization serialization = CharacterSerialization.FromCharacter(character2); - serialization.Restore(character); + ((IObjectSerialization)serialization).Restore(character); Assert.IsFalse(character.IsAlive); } diff --git a/NetAF.Tests/Serialization/Assets/ConversationSerialization_Tests.cs b/NetAF.Tests/Serialization/Assets/ConversationSerialization_Tests.cs index c6b8bdfb..6977a738 100644 --- a/NetAF.Tests/Serialization/Assets/ConversationSerialization_Tests.cs +++ b/NetAF.Tests/Serialization/Assets/ConversationSerialization_Tests.cs @@ -1,5 +1,6 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using NetAF.Conversations; +using NetAF.Serialization; using NetAF.Serialization.Assets; namespace NetAF.Tests.Serialization.Assets @@ -48,7 +49,7 @@ public void GivenAConversation_WhenRestoreFromWithNoCurrentParagraph_ThenCurrent Conversation conversation2 = new(); ConversationSerialization serialization = ConversationSerialization.FromConversation(conversation2); - serialization.Restore(conversation); + ((IObjectSerialization)serialization).Restore(conversation); Assert.IsNull(conversation.CurrentParagraph); } @@ -62,7 +63,7 @@ public void GivenAConversation_WhenRestoreFromWithCurrentParagraph1_ThenCurrentP conversation2.Next(null); ConversationSerialization serialization = ConversationSerialization.FromConversation(conversation2); - serialization.Restore(conversation); + ((IObjectSerialization)serialization).Restore(conversation); Assert.AreEqual(conversation.Paragraphs[1], conversation.CurrentParagraph); } diff --git a/NetAF.Tests/Serialization/Assets/ExaminableSerialization_Tests.cs b/NetAF.Tests/Serialization/Assets/ExaminableSerialization_Tests.cs index e3faaa14..13e3f976 100644 --- a/NetAF.Tests/Serialization/Assets/ExaminableSerialization_Tests.cs +++ b/NetAF.Tests/Serialization/Assets/ExaminableSerialization_Tests.cs @@ -2,6 +2,7 @@ using NetAF.Assets; using NetAF.Assets.Attributes; using NetAF.Commands; +using NetAF.Serialization; using NetAF.Serialization.Assets; namespace NetAF.Tests.Serialization.Assets @@ -67,7 +68,7 @@ public void GivenAnExaminable_WhenRestoreFrom_ThenIsPlayerVisibleSetCorrectly() Item item2 = new(string.Empty, string.Empty) { IsPlayerVisible = true }; ExaminableSerialization serialization = ExaminableSerialization.FromIExaminable(item2); - serialization.Restore(item); + ((IObjectSerialization)serialization).Restore(item); Assert.IsTrue(item.IsPlayerVisible); } @@ -80,7 +81,7 @@ public void GivenAnExaminable_WhenRestoreFrom_ThenAttributesSetCorrectly() item2.Attributes.Add(new Attribute(string.Empty, string.Empty, 0, 1), 1); ExaminableSerialization serialization = ExaminableSerialization.FromIExaminable(item2); - serialization.Restore(item); + ((IObjectSerialization)serialization).Restore(item); Assert.AreEqual(1, item.Attributes.Count); } diff --git a/NetAF.Tests/Serialization/Assets/ExitSerialization_Tests.cs b/NetAF.Tests/Serialization/Assets/ExitSerialization_Tests.cs index 655cf5a1..e4a06bdb 100644 --- a/NetAF.Tests/Serialization/Assets/ExitSerialization_Tests.cs +++ b/NetAF.Tests/Serialization/Assets/ExitSerialization_Tests.cs @@ -1,5 +1,6 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using NetAF.Assets.Locations; +using NetAF.Serialization; using NetAF.Serialization.Assets; namespace NetAF.Tests.Serialization.Assets @@ -24,7 +25,7 @@ public void GivenExitThatIsUnlocked_WhenRestoreFromExitThatIsLocked_ThenIsLocked Exit exit2 = new(Direction.North, true); ExitSerialization serialization = ExitSerialization.FromExit(exit2); - serialization.Restore(exit); + ((IObjectSerialization)serialization).Restore(exit); Assert.IsTrue(exit.IsLocked); } diff --git a/NetAF.Tests/Serialization/Assets/ItemSerialization_Tests.cs b/NetAF.Tests/Serialization/Assets/ItemSerialization_Tests.cs index ee49fd94..70545777 100644 --- a/NetAF.Tests/Serialization/Assets/ItemSerialization_Tests.cs +++ b/NetAF.Tests/Serialization/Assets/ItemSerialization_Tests.cs @@ -1,6 +1,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using NetAF.Assets; using NetAF.Assets.Attributes; +using NetAF.Serialization; using NetAF.Serialization.Assets; namespace NetAF.Tests.Serialization.Assets @@ -16,7 +17,7 @@ public void GivenAnExaminable_WhenRestoreFrom_ThenAttributesSetCorrectly() item2.Attributes.Add(new Attribute(string.Empty, string.Empty, 0, 1), 1); ItemSerialization serialization = ItemSerialization.FromItem(item2); - serialization.Restore(item); + ((IObjectSerialization)serialization).Restore(item); Assert.AreEqual(1, item.Attributes.Count); } diff --git a/NetAF.Tests/Serialization/Assets/NonPlayableCharacterSerialization_Tests.cs b/NetAF.Tests/Serialization/Assets/NonPlayableCharacterSerialization_Tests.cs index d6f51ff1..d243981e 100644 --- a/NetAF.Tests/Serialization/Assets/NonPlayableCharacterSerialization_Tests.cs +++ b/NetAF.Tests/Serialization/Assets/NonPlayableCharacterSerialization_Tests.cs @@ -1,6 +1,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using NetAF.Assets.Characters; using NetAF.Conversations; +using NetAF.Serialization; using NetAF.Serialization.Assets; namespace NetAF.Tests.Serialization.Assets @@ -36,7 +37,7 @@ public void GivenCharacter_WhenRestoreFromCharacterConversationElement0_ThenCurr character2.Conversation.Next(null); NonPlayableCharacterSerialization serialization = NonPlayableCharacterSerialization.FromNonPlayableCharacter(character2); - serialization.Restore(character); + ((IObjectSerialization)serialization).Restore(character); Assert.IsNotNull(character.Conversation.CurrentParagraph); } diff --git a/NetAF.Tests/Serialization/Assets/OverworldSerialization_Tests.cs b/NetAF.Tests/Serialization/Assets/OverworldSerialization_Tests.cs index 6086317f..71ed2ce1 100644 --- a/NetAF.Tests/Serialization/Assets/OverworldSerialization_Tests.cs +++ b/NetAF.Tests/Serialization/Assets/OverworldSerialization_Tests.cs @@ -1,5 +1,6 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using NetAF.Assets.Locations; +using NetAF.Serialization; using NetAF.Serialization.Assets; namespace NetAF.Tests.Serialization.Assets @@ -51,7 +52,7 @@ public void GivenOverworldWith2Regions_WhenRestoreFromOverworldWhereSecondRegion overworld2.Move(overworld2.Regions[1]); OverworldSerialization serialization = OverworldSerialization.FromOverworld(overworld2); - serialization.Restore(overworld); + ((IObjectSerialization)serialization).Restore(overworld); Assert.AreEqual("TARGET", overworld.CurrentRegion.Identifier.Name); } diff --git a/NetAF.Tests/Serialization/Assets/PlayableCharacterLocationSerialization_Tests.cs b/NetAF.Tests/Serialization/Assets/PlayableCharacterLocationSerialization_Tests.cs index d161e31e..3a717865 100644 --- a/NetAF.Tests/Serialization/Assets/PlayableCharacterLocationSerialization_Tests.cs +++ b/NetAF.Tests/Serialization/Assets/PlayableCharacterLocationSerialization_Tests.cs @@ -1,5 +1,6 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using NetAF.Logic; +using NetAF.Serialization; using NetAF.Serialization.Assets; namespace NetAF.Tests.Serialization.Assets @@ -44,7 +45,7 @@ public void GivenEmptyPlayableCharacterLocation_WhenRestoreFrom_ThenPlayableChar PlayableCharacterLocation location2 = new(string.Empty, string.Empty, string.Empty); PlayableCharacterLocationSerialization serialization = PlayableCharacterLocationSerialization.FromPlayableCharacterLocation(location); - serialization.Restore(location2); + ((IObjectSerialization)serialization).Restore(location2); Assert.AreEqual("a", location2.PlayerIdentifier); Assert.AreEqual("b", location2.RegionIdentifier); diff --git a/NetAF.Tests/Serialization/Assets/RegionSerialization_Tests.cs b/NetAF.Tests/Serialization/Assets/RegionSerialization_Tests.cs index 09934389..04c114e5 100644 --- a/NetAF.Tests/Serialization/Assets/RegionSerialization_Tests.cs +++ b/NetAF.Tests/Serialization/Assets/RegionSerialization_Tests.cs @@ -1,5 +1,6 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using NetAF.Assets.Locations; +using NetAF.Serialization; using NetAF.Serialization.Assets; namespace NetAF.Tests.Serialization.Assets @@ -54,7 +55,7 @@ public void GivenRegionWith2Rooms_WhenRestoreFromRegionWhereSecondRoomIsCurrentR region2.Move(Direction.North); RegionSerialization serialization = RegionSerialization.FromRegion(region2); - serialization.Restore(region); + ((IObjectSerialization)serialization).Restore(region); Assert.AreEqual("TARGET", region.CurrentRoom.Identifier.Name); } diff --git a/NetAF.Tests/Serialization/Assets/RoomSerialization_Tests.cs b/NetAF.Tests/Serialization/Assets/RoomSerialization_Tests.cs index 3700e205..0ffa66da 100644 --- a/NetAF.Tests/Serialization/Assets/RoomSerialization_Tests.cs +++ b/NetAF.Tests/Serialization/Assets/RoomSerialization_Tests.cs @@ -1,5 +1,6 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using NetAF.Assets.Locations; +using NetAF.Serialization; using NetAF.Serialization.Assets; namespace NetAF.Tests.Serialization.Assets @@ -102,7 +103,7 @@ public void GivenRoomThatHasNotBeenVisited_WhenRestoreFromRoomThatHasBeenVisited room2.MovedInto(Direction.North); RoomSerialization serialization = RoomSerialization.FromRoom(room2); - serialization.Restore(room); + ((IObjectSerialization)serialization).Restore(room); Assert.IsTrue(room.HasBeenVisited); } diff --git a/NetAF.Tests/Serialization/CustomCommandSerialization_Tests.cs b/NetAF.Tests/Serialization/CustomCommandSerialization_Tests.cs index 08b87bcd..f8d6ce0a 100644 --- a/NetAF.Tests/Serialization/CustomCommandSerialization_Tests.cs +++ b/NetAF.Tests/Serialization/CustomCommandSerialization_Tests.cs @@ -35,7 +35,7 @@ public void GivenACustomCommand_WhenRestoreFrom_ThenIsPlayerVisibleSetCorrectly( CustomCommandSerialization serialization = CustomCommandSerialization.FromCustomCommand(command1); - serialization.Restore(command2); + ((IObjectSerialization)serialization).Restore(command2); Assert.IsTrue(command2.IsPlayerVisible); } diff --git a/NetAF.Tests/Serialization/GameSerialization_Tests.cs b/NetAF.Tests/Serialization/GameSerialization_Tests.cs index 6cea0d4e..cf2f31af 100644 --- a/NetAF.Tests/Serialization/GameSerialization_Tests.cs +++ b/NetAF.Tests/Serialization/GameSerialization_Tests.cs @@ -97,7 +97,7 @@ public void GivenAGame_WhenRestoreFromSerializedAndPlayerTookAnItemFromARoom_The GameSerialization serialization = GameSerialization.FromGame(game2); - serialization.Restore(game); + ((IObjectSerialization)serialization).Restore(game); Assert.AreEqual(1, game.Player.Items.Length); Assert.AreEqual(0, room.Items.Length); @@ -126,7 +126,7 @@ public void GivenAGame_WhenRestoreFromSerializedAndPlayerDroppedAnItemInARoom_Th GameSerialization serialization = GameSerialization.FromGame(game2); - serialization.Restore(game); + ((IObjectSerialization)serialization).Restore(game); Assert.AreEqual(0, game.Player.Items.Length); Assert.AreEqual(1, room.Items.Length); @@ -162,7 +162,7 @@ public void GivenAGame_WhenRestoreFromSerializedAndCharacterMovedFromRoom1ToRoom GameSerialization serialization = GameSerialization.FromGame(game2); - serialization.Restore(game); + ((IObjectSerialization)serialization).Restore(game); Assert.AreEqual(0, roomA.Characters.Length); Assert.AreEqual(1, roomB.Characters.Length); diff --git a/NetAF/Assets/Characters/Character.cs b/NetAF/Assets/Characters/Character.cs index fbfe3332..d1e0e79c 100644 --- a/NetAF/Assets/Characters/Character.cs +++ b/NetAF/Assets/Characters/Character.cs @@ -152,7 +152,7 @@ void IRestoreFromObjectSerialization.RestoreFrom(Charact foreach (var item in Items) { var itemSerialization = Array.Find(serialization.Items, x => item.Identifier.Equals(x.Identifier)); - itemSerialization?.Restore(item); + ((IObjectSerialization)itemSerialization)?.Restore(item); } } diff --git a/NetAF/Assets/Locations/Overworld.cs b/NetAF/Assets/Locations/Overworld.cs index f88c2a7e..9a55b61a 100644 --- a/NetAF/Assets/Locations/Overworld.cs +++ b/NetAF/Assets/Locations/Overworld.cs @@ -161,7 +161,7 @@ void IRestoreFromObjectSerialization.RestoreFrom(Overwor foreach (var region in Regions) { var regionSerialization = Array.Find(serialization.Regions, x => region.Identifier.Equals(x.Identifier)); - regionSerialization?.Restore(region); + ((IObjectSerialization)regionSerialization)?.Restore(region); } CurrentRegion = Array.Find(Regions, x => x.Identifier.Equals(serialization.CurrentRegion)); diff --git a/NetAF/Assets/Locations/Region.cs b/NetAF/Assets/Locations/Region.cs index cd56e4a9..b2528223 100644 --- a/NetAF/Assets/Locations/Region.cs +++ b/NetAF/Assets/Locations/Region.cs @@ -369,7 +369,7 @@ void IRestoreFromObjectSerialization.RestoreFrom(RegionSeri foreach (var room in rooms) { var roomSerialization = Array.Find(serialization.Rooms, x => room.Identifier.Equals(x.Identifier)); - roomSerialization?.Restore(room); + ((IObjectSerialization)roomSerialization)?.Restore(room); } CurrentRoom = Array.Find(rooms, x => x.Identifier.Equals(serialization.CurrentRoom)); diff --git a/NetAF/Assets/Locations/Room.cs b/NetAF/Assets/Locations/Room.cs index e9e094a3..4fb7cf64 100644 --- a/NetAF/Assets/Locations/Room.cs +++ b/NetAF/Assets/Locations/Room.cs @@ -529,19 +529,19 @@ void IRestoreFromObjectSerialization.RestoreFrom(RoomSerializ foreach (var exit in Exits) { var exitSerialization = Array.Find(serialization.Exits, x => exit.Identifier.Equals(x.Identifier)); - exitSerialization?.Restore(exit); + ((IObjectSerialization)exitSerialization)?.Restore(exit); } foreach (var item in Items) { var itemSerialization = Array.Find(serialization.Items, x => item.Identifier.Equals(x.Identifier)); - itemSerialization?.Restore(item); + ((IObjectSerialization)itemSerialization)?.Restore(item); } foreach (var character in Characters) { var characterSerialization = Array.Find(serialization.Characters, x => character.Identifier.Equals(x.Identifier)); - characterSerialization?.Restore(character); + ((IObjectSerialization)characterSerialization)?.Restore(character); } } diff --git a/NetAF/Commands/CustomCommand.cs b/NetAF/Commands/CustomCommand.cs index c25dffe0..666852f4 100644 --- a/NetAF/Commands/CustomCommand.cs +++ b/NetAF/Commands/CustomCommand.cs @@ -1,5 +1,6 @@ using NetAF.Assets; using NetAF.Serialization; +using System; namespace NetAF.Commands { @@ -10,7 +11,7 @@ namespace NetAF.Commands /// If this is visible to the player. /// If this command can be interpreted when the IsPlayerVisible is false. /// The callback to invoke when this command is invoked. - public class CustomCommand(CommandHelp help, bool isPlayerVisible, bool interpretIfNotPlayerVisible, CustomCommandCallback callback) : ICommand, IPlayerVisible, IRestoreFromObjectSerialization + public class CustomCommand(CommandHelp help, bool isPlayerVisible, bool interpretIfNotPlayerVisible, CustomCommandCallback callback) : ICommand, IPlayerVisible, IRestoreFromObjectSerialization, ICloneable { #region Properties @@ -71,5 +72,18 @@ void IRestoreFromObjectSerialization.RestoreFrom(Cus } #endregion + + #region Implementation of ICloneable + + /// + /// Creates a new object that is a copy of the current instance. + /// + /// A new object that is a copy of this instance. + public object Clone() + { + return new CustomCommand(Help, IsPlayerVisible, InterpretIfNotPlayerVisible, Callback) { Arguments = Arguments }; + } + + #endregion } } diff --git a/NetAF/Commands/Persistence/Load.cs b/NetAF/Commands/Persistence/Load.cs index 7d9db0ba..56d07119 100644 --- a/NetAF/Commands/Persistence/Load.cs +++ b/NetAF/Commands/Persistence/Load.cs @@ -1,4 +1,6 @@ -using NetAF.Persistence.Json; +using NetAF.Logic; +using NetAF.Persistence.Json; +using NetAF.Serialization; namespace NetAF.Commands.Persistence { @@ -24,14 +26,14 @@ public Load() : base(new CommandHelp("Load", "Load the game state from a file.", /// The game to load. /// The arguments. The file path must be the first element in the array. /// The reaction. - private static Reaction LoadGameFromFile(Logic.Game game, string[] args) + private static Reaction LoadGameFromFile(Game game, string[] args) { var result = JsonSave.FromFile(args[0], out var restorePoint, out var message); if (!result) return new(ReactionResult.Error, $"Failed to load: {message}"); - restorePoint.Game.Restore(game); + ((IObjectSerialization)restorePoint.Game).Restore(game); return new(ReactionResult.Inform, $"Loaded."); } diff --git a/NetAF/Interpretation/CustomCommandInterpreter.cs b/NetAF/Interpretation/CustomCommandInterpreter.cs index 632b654c..4f799900 100644 --- a/NetAF/Interpretation/CustomCommandInterpreter.cs +++ b/NetAF/Interpretation/CustomCommandInterpreter.cs @@ -2,8 +2,8 @@ using System.Collections.Generic; using System.Linq; using NetAF.Commands; -using NetAF.Extensions; using NetAF.Logic; +using NetAF.Utilities; namespace NetAF.Interpretation { @@ -12,6 +12,44 @@ namespace NetAF.Interpretation /// public sealed class CustomCommandInterpreter : IInterpreter { + #region StaticMethods + + /// + /// Score commands against input to find closest match. + /// + /// The input. + /// The commands to score. + /// A dictionary containing the commands and their scores. The higher the score, the closer the match. + private static Dictionary ScoreCommandsAgainstInput(string input, CustomCommand[] commands) + { + Dictionary scores = []; + var upperCaseInput = input.ToUpper(); + + foreach (var command in commands) + { + var upperCaseCommand = $"{command.Help.Command.ToUpper()} "; + var upperCaseShortcut = $"{command.Help.Shortcut.ToUpper()} "; + int score = 0; + + for (var i = 0; i < upperCaseInput.Length; i++) + { + score = i; + + if (upperCaseCommand.Length < i + 1 && upperCaseShortcut.Length < i + 1) + break; + + if (upperCaseCommand[i] != upperCaseInput[i] && upperCaseShortcut[i] != upperCaseInput[i]) + break; + } + + scores.Add(command, score); + } + + return scores; + } + + #endregion + #region Implementation of IInterpreter /// @@ -29,29 +67,35 @@ public InterpretationResult Interpret(string input, Game game) { if (string.IsNullOrEmpty(input)) return InterpretationResult.Fail; - - var entries = input.Split(" ".ToCharArray(), StringSplitOptions.None); - var commandName = entries[0]; - var args = entries.Remove(commandName); + input = StringUtilities.PreenInput(input); + List commands = []; + // compile commands foreach (var examinable in game.GetAllPlayerVisibleExaminables().Where(x => x.Commands != null)) commands.AddRange(examinable.Commands.Where(x => x.IsPlayerVisible || x.InterpretIfNotPlayerVisible)); - // check looking for just command, not including args - var command = commands.Find(x => x.Help.Command.InsensitiveEquals(commandName)); + if (commands.Count == 0) + return InterpretationResult.Fail; - if (command != null) - { - command.Arguments = args; - return new InterpretationResult(true, command); - } + // score all and find highest scoring command + var scores = ScoreCommandsAgainstInput(input, [.. commands]); + var match = scores.OrderByDescending(x => x.Value).First(); + + // no matches + if (match.Value == 0) + return InterpretationResult.Fail; - // maybe the command had a space in it? - command = commands.Find(x => x.Help.Command.InsensitiveEquals(input)); + // remove either input or shortcut + if (input.StartsWith(match.Key.Help.Command, StringComparison.InvariantCultureIgnoreCase)) + input = input.Remove(0, match.Key.Help.Command.Length); + else if (input.StartsWith(match.Key.Help.Shortcut, StringComparison.InvariantCultureIgnoreCase)) + input = input.Remove(0, match.Key.Help.Shortcut.Length); - return command == null ? InterpretationResult.Fail : new InterpretationResult(true, command); + var command = match.Key.Clone() as CustomCommand; + command.Arguments = StringUtilities.PreenInput(input).Split(' ', StringSplitOptions.RemoveEmptyEntries); + return new InterpretationResult(true, command); } /// diff --git a/NetAF/Logic/Game.cs b/NetAF/Logic/Game.cs index 7a48e49f..9f193716 100644 --- a/NetAF/Logic/Game.cs +++ b/NetAF/Logic/Game.cs @@ -495,7 +495,7 @@ void IRestoreFromObjectSerialization.RestoreFrom(GameSerializ Player = Array.Find(Catalog.Players, x => x.Identifier.Equals(serialization.ActivePlayerIdentifier)); // restore overworld - serialization.Overworld.Restore(Overworld); + ((IObjectSerialization)serialization.Overworld).Restore(Overworld); // restore player locations inactivePlayerLocations.Clear(); diff --git a/NetAF/Serialization/Assets/AttributeAndValueSerialization.cs b/NetAF/Serialization/Assets/AttributeAndValueSerialization.cs index 772a23dc..78ff5430 100644 --- a/NetAF/Serialization/Assets/AttributeAndValueSerialization.cs +++ b/NetAF/Serialization/Assets/AttributeAndValueSerialization.cs @@ -64,7 +64,7 @@ public static AttributeAndValueSerialization FromAttributeAndValue(KeyValuePair< /// Restore an instance from this serialization. /// /// The KeyValuePair to restore. - public void Restore(KeyValuePair attributeAndValue) + void IObjectSerialization>.Restore(KeyValuePair attributeAndValue) { // cannot restore as readonly } diff --git a/NetAF/Serialization/Assets/AttributeManagerSerialization.cs b/NetAF/Serialization/Assets/AttributeManagerSerialization.cs index 8c2422a7..52c277ee 100644 --- a/NetAF/Serialization/Assets/AttributeManagerSerialization.cs +++ b/NetAF/Serialization/Assets/AttributeManagerSerialization.cs @@ -46,7 +46,7 @@ public static AttributeManagerSerialization FromAttributeManager(AttributeManage /// Restore an instance from this serialization. /// /// The attribute manager to restore. - public void Restore(AttributeManager attributeManager) + void IObjectSerialization.Restore(AttributeManager attributeManager) { ((IRestoreFromObjectSerialization)attributeManager).RestoreFrom(this); } diff --git a/NetAF/Serialization/Assets/CharacterSerialization.cs b/NetAF/Serialization/Assets/CharacterSerialization.cs index 656cca45..1885ec75 100644 --- a/NetAF/Serialization/Assets/CharacterSerialization.cs +++ b/NetAF/Serialization/Assets/CharacterSerialization.cs @@ -50,7 +50,7 @@ public static CharacterSerialization FromCharacter(Character character) /// Restore an instance from this serialization. /// /// The character to restore. - public void Restore(Character character) + void IObjectSerialization.Restore(Character character) { ((IRestoreFromObjectSerialization)character).RestoreFrom(this); } diff --git a/NetAF/Serialization/Assets/ConversationSerialization.cs b/NetAF/Serialization/Assets/ConversationSerialization.cs index 8aee51d1..216fd64a 100644 --- a/NetAF/Serialization/Assets/ConversationSerialization.cs +++ b/NetAF/Serialization/Assets/ConversationSerialization.cs @@ -49,7 +49,7 @@ public static ConversationSerialization FromConversation(Conversation conversati /// Restore an instance from this serialization. /// /// The conversation to restore. - public void Restore(Conversation conversation) + void IObjectSerialization.Restore(Conversation conversation) { ((IRestoreFromObjectSerialization)conversation).RestoreFrom(this); } diff --git a/NetAF/Serialization/Assets/ExaminableSerialization.cs b/NetAF/Serialization/Assets/ExaminableSerialization.cs index c6cf3d03..cce84f38 100644 --- a/NetAF/Serialization/Assets/ExaminableSerialization.cs +++ b/NetAF/Serialization/Assets/ExaminableSerialization.cs @@ -58,7 +58,7 @@ public static ExaminableSerialization FromIExaminable(IExaminable examinable) /// Restore an instance from this serialization. /// /// The examinable to restore. - public virtual void Restore(IExaminable examinable) + void IObjectSerialization.Restore(IExaminable examinable) { ((IRestoreFromObjectSerialization)examinable).RestoreFrom(this); } diff --git a/NetAF/Serialization/Assets/ExitSerialization.cs b/NetAF/Serialization/Assets/ExitSerialization.cs index 71c89df9..626321c4 100644 --- a/NetAF/Serialization/Assets/ExitSerialization.cs +++ b/NetAF/Serialization/Assets/ExitSerialization.cs @@ -44,7 +44,7 @@ public static ExitSerialization FromExit(Exit exit) /// Restore an instance from this serialization. /// /// The exit to restore. - public void Restore(Exit exit) + void IObjectSerialization.Restore(Exit exit) { ((IRestoreFromObjectSerialization)exit).RestoreFrom(this); } diff --git a/NetAF/Serialization/Assets/ItemSerialization.cs b/NetAF/Serialization/Assets/ItemSerialization.cs index 5aedaf64..9cb626a5 100644 --- a/NetAF/Serialization/Assets/ItemSerialization.cs +++ b/NetAF/Serialization/Assets/ItemSerialization.cs @@ -34,7 +34,7 @@ public static ItemSerialization FromItem(Item item) /// Restore an instance from this serialization. /// /// The item to restore. - public void Restore(Item item) + void IObjectSerialization.Restore(Item item) { ((IRestoreFromObjectSerialization)item).RestoreFrom(this); } diff --git a/NetAF/Serialization/Assets/NonPlayableCharacterSerialization.cs b/NetAF/Serialization/Assets/NonPlayableCharacterSerialization.cs index 640e95f5..11798aa6 100644 --- a/NetAF/Serialization/Assets/NonPlayableCharacterSerialization.cs +++ b/NetAF/Serialization/Assets/NonPlayableCharacterSerialization.cs @@ -46,7 +46,7 @@ public static NonPlayableCharacterSerialization FromNonPlayableCharacter(NonPlay /// Restore an instance from this serialization. /// /// The character to restore. - public void Restore(NonPlayableCharacter character) + void IObjectSerialization.Restore(NonPlayableCharacter character) { ((IRestoreFromObjectSerialization)character).RestoreFrom(this); } diff --git a/NetAF/Serialization/Assets/OverworldSerialization.cs b/NetAF/Serialization/Assets/OverworldSerialization.cs index d028ba48..f556f35a 100644 --- a/NetAF/Serialization/Assets/OverworldSerialization.cs +++ b/NetAF/Serialization/Assets/OverworldSerialization.cs @@ -50,7 +50,7 @@ public static OverworldSerialization FromOverworld(Overworld overworld) /// Restore an instance from this serialization. /// /// The overworld to restore. - public void Restore(Overworld overworld) + void IObjectSerialization.Restore(Overworld overworld) { ((IRestoreFromObjectSerialization)overworld).RestoreFrom(this); } diff --git a/NetAF/Serialization/Assets/PlayableCharacterLocationSerialization.cs b/NetAF/Serialization/Assets/PlayableCharacterLocationSerialization.cs index 0428a322..faed5ec6 100644 --- a/NetAF/Serialization/Assets/PlayableCharacterLocationSerialization.cs +++ b/NetAF/Serialization/Assets/PlayableCharacterLocationSerialization.cs @@ -51,7 +51,7 @@ public static PlayableCharacterLocationSerialization FromPlayableCharacterLocati /// Restore an instance from this serialization. /// /// The attribute to restore. - public void Restore(PlayableCharacterLocation location) + void IObjectSerialization.Restore(PlayableCharacterLocation location) { ((IRestoreFromObjectSerialization)location).RestoreFrom(this); } diff --git a/NetAF/Serialization/Assets/RegionSerialization.cs b/NetAF/Serialization/Assets/RegionSerialization.cs index 71bb5252..e5349522 100644 --- a/NetAF/Serialization/Assets/RegionSerialization.cs +++ b/NetAF/Serialization/Assets/RegionSerialization.cs @@ -56,7 +56,7 @@ public static RegionSerialization FromRegion(Region region) /// Restore an instance from this serialization. /// /// The region to restore. - public void Restore(Region region) + void IObjectSerialization.Restore(Region region) { ((IRestoreFromObjectSerialization)region).RestoreFrom(this); } diff --git a/NetAF/Serialization/Assets/RoomSerialization.cs b/NetAF/Serialization/Assets/RoomSerialization.cs index 07584f73..319c00da 100644 --- a/NetAF/Serialization/Assets/RoomSerialization.cs +++ b/NetAF/Serialization/Assets/RoomSerialization.cs @@ -62,7 +62,7 @@ public static RoomSerialization FromRoom(Room room) /// Restore an instance from this serialization. /// /// The room to restore. - public void Restore(Room room) + void IObjectSerialization.Restore(Room room) { ((IRestoreFromObjectSerialization)room).RestoreFrom(this); } diff --git a/NetAF/Serialization/CustomCommandSerialization.cs b/NetAF/Serialization/CustomCommandSerialization.cs index ab8389ca..738c61ca 100644 --- a/NetAF/Serialization/CustomCommandSerialization.cs +++ b/NetAF/Serialization/CustomCommandSerialization.cs @@ -45,7 +45,7 @@ public static CustomCommandSerialization FromCustomCommand(CustomCommand customC /// Restore an instance from this serialization. /// /// The command to restore. - public virtual void Restore(CustomCommand command) + void IObjectSerialization.Restore(CustomCommand command) { ((IRestoreFromObjectSerialization)command).RestoreFrom(this); } diff --git a/NetAF/Serialization/GameSerialization.cs b/NetAF/Serialization/GameSerialization.cs index 070bb099..c411073d 100644 --- a/NetAF/Serialization/GameSerialization.cs +++ b/NetAF/Serialization/GameSerialization.cs @@ -59,7 +59,7 @@ public static GameSerialization FromGame(Game game) /// Restore an instance from this serialization. /// /// The asset to restore. - public void Restore(Game game) + void IObjectSerialization.Restore(Game game) { ((IRestoreFromObjectSerialization)game).RestoreFrom(this); } diff --git a/NetAF/Serialization/IObjectSerialization.cs b/NetAF/Serialization/IObjectSerialization.cs index adfe6971..b793fa1e 100644 --- a/NetAF/Serialization/IObjectSerialization.cs +++ b/NetAF/Serialization/IObjectSerialization.cs @@ -4,7 +4,7 @@ /// Represents any object that is a serialization of another object. /// /// The type of object that this serialization represents. - public interface IObjectSerialization + internal interface IObjectSerialization { /// /// Restore an instance from this serialization.