using System.Diagnostics.CodeAnalysis; using System.Text.RegularExpressions; using MultiversalDiplomacy.Model; using static MultiversalDiplomacy.Model.OrderRegex; namespace MultiversalDiplomacy.Script; /// /// A script handler for modifying a game before it begins. /// public class SetupScriptHandler(World world, bool strict = false) : IScriptHandler { public string Prompt => "setup> "; public World World { get; private set; } = world; /// /// Whether unsuccessful commands should terminate the script. /// public bool Strict { get; } = strict; public IScriptHandler? HandleInput(string input) { var args = input.Split(' ', StringSplitOptions.RemoveEmptyEntries); if (args.Length == 0) { return this; } var command = args[0]; switch (command) { case "help": case "?": Console.WriteLine("commands:"); Console.WriteLine(" begin: complete setup and start the game (alias: ---)"); Console.WriteLine(" list : list things in a game category"); Console.WriteLine(" option : set a game option"); Console.WriteLine(" unit [location]: add a unit to the game"); Console.WriteLine(" may be \"province/location\""); break; case "begin": case "---": return new GameScriptHandler(World, Strict); case "list" when args.Length == 1: Console.WriteLine("usage:"); Console.WriteLine(" list powers: the powers in the game"); Console.WriteLine(" list units: units created so far"); break; case "list" when args[1] == "powers": Console.WriteLine("Powers:"); foreach (string powerName in World.Powers) { Console.WriteLine($" {powerName}"); } break; case "list" when args[1] == "units": Console.WriteLine("Units:"); foreach (Unit unit in World.Units) { Console.WriteLine($" {unit}"); } break; case "option" when args.Length < 3: throw new NotImplementedException("There are no supported options yet"); case "unit" when args.Length < 2: Console.WriteLine("usage: unit [power] [type] [province]"); break; case "unit": string unitSpec = input["unit ".Length..]; if (TryParseUnit(unitSpec, out Unit? newUnit)) { World = World.Update(units: World.Units.Append(newUnit)); Console.WriteLine($"Created {newUnit}"); } else if (Strict) { return null; } break; default: // noop on comments that begin with # if (command.StartsWith('#')) break; Console.WriteLine($"Unrecognized command: {command}"); if (Strict) return null; break; } return this; } private bool TryParseUnit(string unitSpec, [NotNullWhen(true)] out Unit? newUnit) { newUnit = null; OrderRegex re = new(World); Regex reUnit = new($"^{re.Power} {OrderRegex.Type} {re.Province}(?:{SlashLocation}|{ParenLocation})?$"); Match match = reUnit.Match(unitSpec); if (!match.Success) { Console.WriteLine($"Could not match unit spec \"{unitSpec}\""); return false; } string power = World.Powers.First(p => p.EqualsAnyCase(match.Groups[1].Value)); string typeName = Enum.GetNames().First(name => name.StartsWithAnyCase(match.Groups[2].Value)); UnitType type = Enum.Parse(typeName); Province province = World.Map.Provinces.First(prov => prov.Name.EqualsAnyCase(match.Groups[3].Value) || prov.Abbreviations.Any(abv => abv.EqualsAnyCase(match.Groups[3].Value))); string locationName = match.Groups[4].Length > 0 ? match.Groups[4].Value : match.Groups[5].Value; Location location = province.Locations.First(loc => loc.Name.StartsWithAnyCase(locationName) || loc.Abbreviation.StartsWithAnyCase(locationName)); newUnit = Unit.Build(location.Key, Season.First, power, type); return true; } }