From 39066900773e7857faf2d02e7ed51b71d219d97d Mon Sep 17 00:00:00 2001 From: godosa Date: Tue, 6 Oct 2026 23:39:36 +0200 Subject: godosa-engine: initial public history --- src/Godosa.Core/Runs/Scripts/InputPlans.cs | 111 +++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 src/Godosa.Core/Runs/Scripts/InputPlans.cs (limited to 'src/Godosa.Core/Runs/Scripts/InputPlans.cs') diff --git a/src/Godosa.Core/Runs/Scripts/InputPlans.cs b/src/Godosa.Core/Runs/Scripts/InputPlans.cs new file mode 100644 index 0000000..9c150b3 --- /dev/null +++ b/src/Godosa.Core/Runs/Scripts/InputPlans.cs @@ -0,0 +1,111 @@ +// Provenance: own design — shared run scripts (spec §4.1) +using System.Globalization; + +namespace Godosa.Core.Runs.Scripts; + +public static class Positions +{ + /// x,y or a locator @name(args) (args comma separated). + public static (int X, int Y) Resolve(string token, IRunGame game) + { + if (token.StartsWith('@')) + { + var paren = token.IndexOf('('); + var name = paren > 0 ? token[1..paren] : token[1..]; + var args = paren > 0 && token.EndsWith(')') + ? token[(paren + 1)..^1].Split(',', StringSplitOptions.TrimEntries).Where(a => a.Length > 0).ToArray() + : []; + if (!game.Locators.TryGetValue(name, out var locator)) + throw new ScriptFault($"unknown locator @{name}"); + var r = locator.Locate(args); + return r.IsOk ? (r.X, r.Y) : throw new ScriptFault($"locator {token}: {r.Error}"); + } + var parts = token.Split(','); + if (parts.Length == 2 && int.TryParse(parts[0], NumberStyles.AllowLeadingSign, CultureInfo.InvariantCulture, out var x) + && int.TryParse(parts[1], NumberStyles.AllowLeadingSign, CultureInfo.InvariantCulture, out var y)) + return (x, y); + throw new ScriptFault($"bad position '{token}'"); + } +} + +public static class InputPlans +{ + public static bool IsInputVerb(string verb) => verb is "click" or "rclick" or "dclick" or "drag" or "move" or "wheel" + or "key" or "hold" or "chord" or "type"; + + public static InputPlan For(string verb, IReadOnlyList args, Func position) + { + List e = []; + void Add(int frame, InputEvent ev) => e.Add(new PlannedInput(frame, ev)); + switch (verb) + { + case "click" or "rclick" or "dclick": + { + var (x, y) = position(args[0].Text); + var button = verb == "rclick" ? MouseButton.Right : MouseButton.Left; + Add(0, new InputEvent(InputKind.Move, x, y)); + Add(0, new InputEvent(InputKind.Down, x, y, button)); + Add(1, new InputEvent(InputKind.Up, x, y, button)); + if (verb == "dclick") + { + Add(2, new InputEvent(InputKind.Down, x, y, button)); + Add(3, new InputEvent(InputKind.Up, x, y, button)); + } + break; + } + case "drag": + { + var (ax, ay) = position(args[0].Text); + var (bx, by) = position(args[1].Text); + Add(0, new InputEvent(InputKind.Move, ax, ay)); + Add(0, new InputEvent(InputKind.Down, ax, ay)); + for (var step = 1; step <= 3; step++) + Add(step, new InputEvent(InputKind.Move, ax + (bx - ax) * step / 3, ay + (by - ay) * step / 3)); + Add(4, new InputEvent(InputKind.Up, bx, by)); + break; + } + case "move": + { + var (x, y) = position(args[0].Text); + Add(0, new InputEvent(InputKind.Move, x, y)); + break; + } + case "wheel": + { + var delta = int.TryParse(args[0].Text, NumberStyles.AllowLeadingSign, CultureInfo.InvariantCulture, out var d) + ? d : throw new ScriptFault($"bad wheel delta '{args[0].Text}'"); + var (x, y) = args.Count > 1 ? position(args[1].Text) : (0, 0); + if (args.Count > 1) + Add(0, new InputEvent(InputKind.Move, x, y)); + Add(0, new InputEvent(InputKind.Wheel, x, y, Delta: delta)); + break; + } + case "key": + Add(0, new InputEvent(InputKind.KeyDown, Key: args[0].Text)); + Add(1, new InputEvent(InputKind.KeyUp, Key: args[0].Text)); + break; + case "hold": + { + var frames = Durations.Frames(args[1].Text) is { } f and > 0 ? f : throw new ScriptFault($"bad hold frames '{args[1].Text}'"); + Add(0, new InputEvent(InputKind.KeyDown, Key: args[0].Text)); + Add(frames, new InputEvent(InputKind.KeyUp, Key: args[0].Text)); + break; + } + case "chord": + { + var keys = args[0].Text.Split('+', StringSplitOptions.RemoveEmptyEntries); + foreach (var k in keys) + Add(0, new InputEvent(InputKind.KeyDown, Key: k)); + foreach (var k in keys.Reverse()) + Add(1, new InputEvent(InputKind.KeyUp, Key: k)); + break; + } + case "type": + Add(0, new InputEvent(InputKind.Text, Text: args[0].Text)); + break; + default: + throw new ScriptFault($"not an input verb: {verb}"); + } + return new InputPlan(e); + } +} -- cgit