From 7fa5f7040f84cc2c8a468476642235289fd68ab0 Mon Sep 17 00:00:00 2001 From: Diogo Strube de Lima Date: Thu, 4 Feb 2021 22:03:11 -0500 Subject: [PATCH] draft of implementation approach for review --- external/FNA | 2 +- src/Game/Scripting/Commands.cs | 181 +++++++++++++++++------------- src/Game/Scripting/Interpreter.cs | 168 +++++++++++++++++++-------- src/Utility/HuesHelper.cs | 70 ++++++++++-- 4 files changed, 285 insertions(+), 136 deletions(-) diff --git a/external/FNA b/external/FNA index 49fcbdac24..115ac5dd45 160000 --- a/external/FNA +++ b/external/FNA @@ -1 +1 @@ -Subproject commit 49fcbdac24dcef83cc35989eb7eeee9e803a224d +Subproject commit 115ac5dd452589f971b1572a46744d7a23fd5d22 diff --git a/src/Game/Scripting/Commands.cs b/src/Game/Scripting/Commands.cs index 8c05ae246e..ae68510bc0 100644 --- a/src/Game/Scripting/Commands.cs +++ b/src/Game/Scripting/Commands.cs @@ -66,6 +66,9 @@ public static class Commands public static void Register() { + Interpreter.RegisterCommandHandler("findobject", CmdFindObject); + Interpreter.RegisterExpressionHandler("findobject", ExpFindObject); + #region Deprecated Interpreter.RegisterCommandHandler("autoloot", Deprecated); Interpreter.RegisterCommandHandler("toggleautoloot", Deprecated); @@ -194,18 +197,40 @@ public static void Register() Interpreter.RegisterCommandHandler("getfriend", UnimplementedCommand); } - private static bool UnimplementedCommand(string command, Argument[] args, bool quiet, bool force) + private static bool UnimplementedCommand(string command, Arguments args, bool quiet, bool force) { GameActions.Print($"Unimplemented command: '{command}'", type: MessageType.System); return true; } - private static bool Deprecated(string command, Argument[] args, bool quiet, bool force) + private static bool Deprecated(string command, Arguments args, bool quiet, bool force) { GameActions.Print($"Deprecated command: '{command}'", type: MessageType.System); return true; } + private static bool CmdFindObject(string command, Arguments args, bool quiet, bool force) + { + // UOStream - findobject (serial) [color] [source] [amount] [range] + var serial = args.NextAsSerial(Arguments.ArgumentType.Mandatory); + var color = args.NextAsColor(); + var source = "backpack"; //args.NextAsSource(); + var amount = 1; // args.NextAsAmount(); + var range = 5; // args.NextAsRange(); + + // Check if serial exist + var graphic = World.GetOrCreateItem(serial).Graphic; + var item = (source == "backpack") ? World.Player.FindItem(graphic) : World.Player.FindItemByTypeOnGroundWithHueInRange(graphic, color, range); + return item != null && item.Amount > amount; + + } + private static bool ExpFindObject(string expression, Arguments args, bool quiet) + { + return CmdFindObject(expression, args, quiet, false); + } + + + //private static bool UseItem(Item cont, ushort find) //{ // for (int i = 0; i < cont.Contains.Count; i++) @@ -227,13 +252,13 @@ private static bool Deprecated(string command, Argument[] args, bool quiet, bool // return false; //} - private static bool SetAbility(string command, Argument[] args, bool quiet, bool force) + private static bool SetAbility(string command, Arguments args, bool quiet, bool force) { var ability = args[0].AsString(); if (args.Length < 1 || !abilities.Contains(ability)) { - throw new RunTimeError(null, "Usage: setability ('primary'/'secondary'/'stun'/'disarm') ['on'/'off']"); + throw new ScriptRunTimeError(null, "Usage: setability ('primary'/'secondary'/'stun'/'disarm') ['on'/'off']"); } if (args.Length == 2 && args[1].AsString() == "on" || args.Length == 1) @@ -264,20 +289,20 @@ private static bool SetAbility(string command, Argument[] args, bool quiet, bool return true; } - private static bool Attack(string command, Argument[] args, bool quiet, bool force) + private static bool Attack(string command, Arguments args, bool quiet, bool force) { if (args.Length < 1) - throw new RunTimeError(null, "Usage: attack (serial)"); + throw new ScriptRunTimeError(null, "Usage: attack (serial)"); GameActions.Attack(args[0].AsSerial()); return true; } - private static bool ClearHands(string command, Argument[] args, bool quiet, bool force) + private static bool ClearHands(string command, Arguments args, bool quiet, bool force) { if (args.Length == 0 || !hands.Contains(args[0].AsString())) - throw new RunTimeError(null, "Usage: clearhands ('left'/'right'/'both')"); + throw new ScriptRunTimeError(null, "Usage: clearhands ('left'/'right'/'both')"); switch (args[0].AsString().ToLower()) { @@ -297,10 +322,10 @@ private static bool ClearHands(string command, Argument[] args, bool quiet, bool return true; } - private static bool ClickObject(string command, Argument[] args, bool quiet, bool force) + private static bool ClickObject(string command, Arguments args, bool quiet, bool force) { if (args.Length == 0) - throw new RunTimeError(null, "Usage: clickobject (serial)"); + throw new ScriptRunTimeError(null, "Usage: clickobject (serial)"); uint serial = args[0].AsSerial(); @@ -309,17 +334,17 @@ private static bool ClickObject(string command, Argument[] args, bool quiet, boo return true; } - private static bool BandageSelf(string command, Argument[] args, bool quiet, bool force) + private static bool BandageSelf(string command, Arguments args, bool quiet, bool force) { GameActions.BandageSelf(); return true; } - private static bool UseType(string command, Argument[] args, bool quiet, bool force) + private static bool UseType(string command, Arguments args, bool quiet, bool force) { if (args.Length < 1) { - throw new RunTimeError(null, "Usage: usetype (graphic) [color] [source] [range or search level]"); + throw new ScriptRunTimeError(null, "Usage: usetype (graphic) [color] [source] [range or search level]"); } var searchGround = false; @@ -375,11 +400,11 @@ private static bool UseType(string command, Argument[] args, bool quiet, bool fo return true; } - private static bool UseObject(string command, Argument[] args, bool quiet, bool force) + private static bool UseObject(string command, Arguments args, bool quiet, bool force) { if (args.Length == 0) { - throw new RunTimeError(null, "Usage: useobject (serial)"); + throw new ScriptRunTimeError(null, "Usage: useobject (serial)"); } var serial = args[0].AsSerial(); @@ -396,11 +421,11 @@ private static bool UseObject(string command, Argument[] args, bool quiet, bool return true; } - private static bool MoveItem(string command, Argument[] args, bool quiet, bool force) + private static bool MoveItem(string command, Arguments args, bool quiet, bool force) { if (args.Length < 2) { - throw new RunTimeError(null, "Usage: moveitem (serial) (destination) [(x, y, z)] [amount]"); + throw new ScriptRunTimeError(null, "Usage: moveitem (serial) (destination) [(x, y, z)] [amount]"); } uint serial = args[0].AsSerial(); @@ -435,16 +460,16 @@ private static bool MoveItem(string command, Argument[] args, bool quiet, bool f return true; } - private static bool Walk(string command, Argument[] args, bool quiet, bool force) + private static bool Walk(string command, Arguments args, bool quiet, bool force) { if (args.Length != 1) { - throw new RunTimeError(null, "Usage: walk ('direction name')"); + throw new ScriptRunTimeError(null, "Usage: walk ('direction name')"); } if (!directions.TryGetValue(args[0].AsString().ToLower(), out var dir)) { - throw new RunTimeError(null, "Usage: walk ('direction name')"); + throw new ScriptRunTimeError(null, "Usage: walk ('direction name')"); } if (DateTime.UtcNow < movementCooldown) @@ -482,16 +507,16 @@ private static bool Walk(string command, Argument[] args, bool quiet, bool force return World.Player.Walk(dir, ProfileManager.CurrentProfile.AlwaysRun); } - private static bool Turn(string command, Argument[] args, bool quiet, bool force) + private static bool Turn(string command, Arguments args, bool quiet, bool force) { if (args.Length != 1) { - throw new RunTimeError(null, "Usage: turn ('direction name')"); + throw new ScriptRunTimeError(null, "Usage: turn ('direction name')"); } if (!directions.TryGetValue(args[0].AsString().ToLower(), out var dir)) { - throw new RunTimeError(null, "Usage: turn ('direction name')"); + throw new ScriptRunTimeError(null, "Usage: turn ('direction name')"); } if (DateTime.UtcNow < movementCooldown) @@ -508,16 +533,16 @@ private static bool Turn(string command, Argument[] args, bool quiet, bool force return true; } - private static bool Run(string command, Argument[] args, bool quiet, bool force) + private static bool Run(string command, Arguments args, bool quiet, bool force) { if (args.Length != 1) { - throw new RunTimeError(null, "Usage: run ('direction name')"); + throw new ScriptRunTimeError(null, "Usage: run ('direction name')"); } if (!directions.TryGetValue(args[0].AsString().ToLower(), out var dir)) { - throw new RunTimeError(null, "Usage: run ('direction name')"); + throw new ScriptRunTimeError(null, "Usage: run ('direction name')"); } if (DateTime.UtcNow < movementCooldown) @@ -548,10 +573,10 @@ private static bool Run(string command, Argument[] args, bool quiet, bool force) return World.Player.Walk(dir, true); } - private static bool UseSkill(string command, Argument[] args, bool quiet, bool force) + private static bool UseSkill(string command, Arguments args, bool quiet, bool force) { if (args.Length == 0) - throw new RunTimeError(null, "Usage: useskill ('skill name'/'last')"); + throw new ScriptRunTimeError(null, "Usage: useskill ('skill name'/'last')"); if (args[0].AsString() == "last") { @@ -563,22 +588,22 @@ private static bool UseSkill(string command, Argument[] args, bool quiet, bool f if (!GameActions.UseSkill(skillName)) { - throw new RunTimeError(null, "That skill is not usable"); + throw new ScriptRunTimeError(null, "That skill is not usable"); } return true; } - //private static bool Feed(string command, Argument[] args, bool quiet, bool force) + //private static bool Feed(string command, Arguments args, bool quiet, bool force) //{ // return true; //} - private static bool Rename(string command, Argument[] args, bool quiet, bool force) + private static bool Rename(string command, Arguments args, bool quiet, bool force) { if (args.Length != 2) { - throw new RunTimeError(null, "Usage: rename (serial) ('name')"); + throw new ScriptRunTimeError(null, "Usage: rename (serial) ('name')"); } var target = args[0].AsSerial(); @@ -589,17 +614,17 @@ private static bool Rename(string command, Argument[] args, bool quiet, bool for return true; } - private static bool SetAlias(string command, Argument[] args, bool quiet, bool force) + private static bool SetAlias(string command, Arguments args, bool quiet, bool force) { if (args.Length != 2) - throw new RunTimeError(null, "Usage: setalias ('name') [serial]"); + throw new ScriptRunTimeError(null, "Usage: setalias ('name') [serial]"); Interpreter.SetAlias(args[0].AsString(), args[1].AsSerial()); return true; } - //private static bool PromptAlias(string command, Argument[] args, bool quiet, bool force) + //private static bool PromptAlias(string command, Arguments args, bool quiet, bool force) //{ // Interpreter.Pause(60000); @@ -618,7 +643,7 @@ private static bool SetAlias(string command, Argument[] args, bool quiet, bool f // return true; //} - //private static bool WaitForGump(string command, Argument[] args, bool quiet, bool force) + //private static bool WaitForGump(string command, Arguments args, bool quiet, bool force) //{ // if (args.Length < 2) // throw new RunTimeError(null, "Usage: waitforgump (gump id/'any') (timeout)"); @@ -642,14 +667,14 @@ private static bool SetAlias(string command, Argument[] args, bool quiet, bool f // return false; //} - //private static bool ClearJournal(string command, Argument[] args, bool quiet, bool force) + //private static bool ClearJournal(string command, Arguments args, bool quiet, bool force) //{ // Journal.Clear(); // return true; //} - //private static bool WaitForJournal(string command, Argument[] args, bool quiet, bool force) + //private static bool WaitForJournal(string command, Arguments args, bool quiet, bool force) //{ // if (args.Length < 2) // throw new RunTimeError(null, "Usage: waitforjournal ('text') (timeout) ['author'/'system']"); @@ -663,7 +688,7 @@ private static bool SetAlias(string command, Argument[] args, bool quiet, bool f // return true; //} - //private static bool PopList(string command, Argument[] args, bool quiet, bool force) + //private static bool PopList(string command, Arguments args, bool quiet, bool force) //{ // if (args.Length != 2) // throw new RunTimeError(null, "Usage: poplist ('list name') ('element value'/'front'/'back')"); @@ -693,7 +718,7 @@ private static bool SetAlias(string command, Argument[] args, bool quiet, bool f // return true; //} - //private static bool PushList(string command, Argument[] args, bool quiet, bool force) + //private static bool PushList(string command, Arguments args, bool quiet, bool force) //{ // if (args.Length < 2 || args.Length > 3) // throw new RunTimeError(null, "Usage: pushlist ('list name') ('element value') ['front'/'back']"); @@ -710,7 +735,7 @@ private static bool SetAlias(string command, Argument[] args, bool quiet, bool f // return true; //} - //private static bool RemoveList(string command, Argument[] args, bool quiet, bool force) + //private static bool RemoveList(string command, Arguments args, bool quiet, bool force) //{ // if (args.Length != 1) // throw new RunTimeError(null, "Usage: removelist ('list name')"); @@ -720,7 +745,7 @@ private static bool SetAlias(string command, Argument[] args, bool quiet, bool f // return true; //} - //private static bool CreateList(string command, Argument[] args, bool quiet, bool force) + //private static bool CreateList(string command, Arguments args, bool quiet, bool force) //{ // if (args.Length != 1) // throw new RunTimeError(null, "Usage: createlist ('list name')"); @@ -730,7 +755,7 @@ private static bool SetAlias(string command, Argument[] args, bool quiet, bool f // return true; //} - //private static bool ClearList(string command, Argument[] args, bool quiet, bool force) + //private static bool ClearList(string command, Arguments args, bool quiet, bool force) //{ // if (args.Length != 1) // throw new RunTimeError(null, "Usage: clearlist ('list name')"); @@ -740,20 +765,20 @@ private static bool SetAlias(string command, Argument[] args, bool quiet, bool f // return true; //} - private static bool UnsetAlias(string command, Argument[] args, bool quiet, bool force) + private static bool UnsetAlias(string command, Arguments args, bool quiet, bool force) { if (args.Length == 0) - throw new RunTimeError(null, "Usage: unsetalias (string)"); + throw new ScriptRunTimeError(null, "Usage: unsetalias (string)"); Interpreter.SetAlias(args[0].AsString(), 0); return true; } - private static bool ShowNames(string command, Argument[] args, bool quiet, bool force) + private static bool ShowNames(string command, Arguments args, bool quiet, bool force) { if (args.Length == 0) - throw new RunTimeError(null, "Usage: shownames ['mobiles'/'corpses']"); + throw new ScriptRunTimeError(null, "Usage: shownames ['mobiles'/'corpses']"); if (args[0].AsString() == "mobiles") { @@ -766,11 +791,11 @@ private static bool ShowNames(string command, Argument[] args, bool quiet, bool return true; } - public static bool ToggleHands(string command, Argument[] args, bool quiet, bool force) + public static bool ToggleHands(string command, Arguments args, bool quiet, bool force) { if (args.Length == 0) { - throw new RunTimeError(null, "Usage: togglehands ('left'/'right')"); + throw new ScriptRunTimeError(null, "Usage: togglehands ('left'/'right')"); } switch (args[0].AsString().ToLower()) @@ -782,17 +807,17 @@ public static bool ToggleHands(string command, Argument[] args, bool quiet, bool GameActions.ToggleEquip(IO.ItemExt_PaperdollAppearance.Right); break; default: - throw new RunTimeError(null, "Usage: togglehands ('left'/'right')"); + throw new ScriptRunTimeError(null, "Usage: togglehands ('left'/'right')"); } return true; } - public static bool EquipItem(string command, Argument[] args, bool quiet, bool force) + public static bool EquipItem(string command, Arguments args, bool quiet, bool force) { if (args.Length < 1) { - throw new RunTimeError(null, "Usage: equipitem (serial)"); + throw new ScriptRunTimeError(null, "Usage: equipitem (serial)"); } var item = (Item)World.Get(args[0].AsSerial()); @@ -805,37 +830,37 @@ public static bool EquipItem(string command, Argument[] args, bool quiet, bool f return true; } - //public static bool ToggleScavenger(string command, Argument[] args, bool quiet, bool force) + //public static bool ToggleScavenger(string command, Arguments args, bool quiet, bool force) //{ // ScavengerAgent.Instance.ToggleEnabled(); // return true; //} - private static bool Pause(string command, Argument[] args, bool quiet, bool force) + private static bool Pause(string command, Arguments args, bool quiet, bool force) { if (args.Length == 0) - throw new RunTimeError(null, "Usage: pause (timeout)"); + throw new ScriptRunTimeError(null, "Usage: pause (timeout)"); Interpreter.Pause(args[0].AsUInt()); return true; } - //private static bool Ping(string command, Argument[] args, bool quiet, bool force) + //private static bool Ping(string command, Arguments args, bool quiet, bool force) //{ // Assistant.Ping.StartPing(5); // return true; //} - //private static bool Resync(string command, Argument[] args, bool quiet, bool force) + //private static bool Resync(string command, Arguments args, bool quiet, bool force) //{ // Client.Instance.SendToServer(new ResyncReq()); // return true; //} - //private static bool MessageBox(string command, Argument[] args, bool quiet, bool force) + //private static bool MessageBox(string command, Arguments args, bool quiet, bool force) //{ // if (args.Length != 2) // throw new RunTimeError(null, "Usage: messagebox ('title') ('body')"); @@ -845,7 +870,7 @@ private static bool Pause(string command, Argument[] args, bool quiet, bool forc // return true; //} - public static bool Msg(string command, Argument[] args, bool quiet, bool force) + public static bool Msg(string command, Arguments args, bool quiet, bool force) { switch (args.Length) { @@ -856,13 +881,13 @@ public static bool Msg(string command, Argument[] args, bool quiet, bool force) GameActions.Say(args[0].AsString(), hue: args[1].AsUShort()); break; default: - throw new RunTimeError(null, "Usage: msg ('text') [color]"); + throw new ScriptRunTimeError(null, "Usage: msg ('text') [color]"); } return true; } - //private static bool Paperdoll(string command, Argument[] args, bool quiet, bool force) + //private static bool Paperdoll(string command, Arguments args, bool quiet, bool force) //{ // if (args.Length > 1) // throw new RunTimeError(null, "Usage: paperdoll [serial]"); @@ -873,7 +898,7 @@ public static bool Msg(string command, Argument[] args, bool quiet, bool force) // return true; //} - //public static bool Cast(string command, Argument[] args, bool quiet, bool force) + //public static bool Cast(string command, Arguments args, bool quiet, bool force) //{ // if (args.Length == 0) // throw new RunTimeError(null, "Usage: cast 'spell' [serial]"); @@ -905,7 +930,7 @@ public static bool Msg(string command, Argument[] args, bool quiet, bool force) // return true; //} - //private static bool WaitForTarget(string command, Argument[] args, bool quiet, bool force) + //private static bool WaitForTarget(string command, Arguments args, bool quiet, bool force) //{ // if (args.Length != 1) // throw new RunTimeError(null, "Usage: waitfortarget (timeout)"); @@ -917,7 +942,7 @@ public static bool Msg(string command, Argument[] args, bool quiet, bool force) // return false; //} - //private static bool CancelTarget(string command, Argument[] args, bool quiet, bool force) + //private static bool CancelTarget(string command, Arguments args, bool quiet, bool force) //{ // if (args.Length != 0) // throw new RunTimeError(null, "Usage: canceltarget"); @@ -928,7 +953,7 @@ public static bool Msg(string command, Argument[] args, bool quiet, bool force) // return true; //} - //private static bool Target(string command, Argument[] args, bool quiet, bool force) + //private static bool Target(string command, Arguments args, bool quiet, bool force) //{ // if (args.Length != 1) // throw new RunTimeError(null, "Usage: target (serial)"); @@ -941,7 +966,7 @@ public static bool Msg(string command, Argument[] args, bool quiet, bool force) // return true; //} - //private static bool TargetType(string command, Argument[] args, bool quiet, bool force) + //private static bool TargetType(string command, Arguments args, bool quiet, bool force) //{ // if (args.Length < 1 || args.Length > 3) // throw new RunTimeError(null, "Usage: targettype (graphic) [color] [range]"); @@ -1001,7 +1026,7 @@ public static bool Msg(string command, Argument[] args, bool quiet, bool force) // return true; //} - //private static bool TargetGround(string command, Argument[] args, bool quiet, bool force) + //private static bool TargetGround(string command, Arguments args, bool quiet, bool force) //{ // if (args.Length < 1 || args.Length > 3) // throw new RunTimeError(null, "Usage: targetground (graphic) [color] [range]"); @@ -1009,7 +1034,7 @@ public static bool Msg(string command, Argument[] args, bool quiet, bool force) // throw new RunTimeError(null, $"Unimplemented command {command}"); //} - //private static bool TargetTile(string command, Argument[] args, bool quiet, bool force) + //private static bool TargetTile(string command, Arguments args, bool quiet, bool force) //{ // if (!(args.Length == 1 || args.Length == 3)) // throw new RunTimeError(null, "Usage: targettile ('last'/'current'/(x y z))"); @@ -1052,7 +1077,7 @@ public static bool Msg(string command, Argument[] args, bool quiet, bool force) // return true; //} - //private static bool TargetTileOffset(string command, Argument[] args, bool quiet, bool force) + //private static bool TargetTileOffset(string command, Arguments args, bool quiet, bool force) //{ // if (args.Length != 3) // throw new RunTimeError(null, "Usage: targettileoffset (x y z)"); @@ -1073,7 +1098,7 @@ public static bool Msg(string command, Argument[] args, bool quiet, bool force) // return true; //} - //private static bool TargetTileRelative(string command, Argument[] args, bool quiet, bool force) + //private static bool TargetTileRelative(string command, Arguments args, bool quiet, bool force) //{ // if (args.Length != 2) // throw new RunTimeError(null, "Usage: targettilerelative (serial) (range). Range may be negative."); @@ -1135,7 +1160,7 @@ public static bool Msg(string command, Argument[] args, bool quiet, bool force) // return true; //} - //public static bool HeadMsg(string command, Argument[] args, bool quiet, bool force) + //public static bool HeadMsg(string command, Arguments args, bool quiet, bool force) //{ // switch (args.Length) // { @@ -1158,7 +1183,7 @@ public static bool Msg(string command, Argument[] args, bool quiet, bool force) // return true; //} - //public static bool SysMsg(string command, Argument[] args, bool quiet, bool force) + //public static bool SysMsg(string command, Arguments args, bool quiet, bool force) //{ // switch (args.Length) // { @@ -1175,7 +1200,7 @@ public static bool Msg(string command, Argument[] args, bool quiet, bool force) // return true; //} - //public static bool DressCommand(string command, Argument[] args, bool quiet, bool force) + //public static bool DressCommand(string command, Arguments args, bool quiet, bool force) //{ // //we're using a named dresslist or a temporary dresslist? // if (args.Length == 0) @@ -1197,7 +1222,7 @@ public static bool Msg(string command, Argument[] args, bool quiet, bool force) // return true; //} - //public static bool UnDressCommand(string command, Argument[] args, bool quiet, bool force) + //public static bool UnDressCommand(string command, Arguments args, bool quiet, bool force) //{ // //we're using a named dresslist or a temporary dresslist? // if (args.Length == 0) @@ -1219,7 +1244,7 @@ public static bool Msg(string command, Argument[] args, bool quiet, bool force) // return true; //} - //public static bool DressConfig(string command, Argument[] args, bool quiet, bool force) + //public static bool DressConfig(string command, Arguments args, bool quiet, bool force) //{ // if (DressList._Temporary == null) // DressList._Temporary = new DressList("dressconfig"); @@ -1236,7 +1261,7 @@ public static bool Msg(string command, Argument[] args, bool quiet, bool force) // return true; //} - //private static bool SetTimer(string command, Argument[] args, bool quiet, bool force) + //private static bool SetTimer(string command, Arguments args, bool quiet, bool force) //{ // if (args.Length != 2) // throw new RunTimeError(null, "Usage: settimer (timer name) (value)"); @@ -1246,7 +1271,7 @@ public static bool Msg(string command, Argument[] args, bool quiet, bool force) // return true; //} - //private static bool RemoveTimer(string command, Argument[] args, bool quiet, bool force) + //private static bool RemoveTimer(string command, Arguments args, bool quiet, bool force) //{ // if (args.Length != 1) // throw new RunTimeError(null, "Usage: removetimer (timer name)"); @@ -1255,7 +1280,7 @@ public static bool Msg(string command, Argument[] args, bool quiet, bool force) // return true; //} - //private static bool CreateTimer(string command, Argument[] args, bool quiet, bool force) + //private static bool CreateTimer(string command, Arguments args, bool quiet, bool force) //{ // if (args.Length != 1) // throw new RunTimeError(null, "Usage: createtimer (timer name)"); diff --git a/src/Game/Scripting/Interpreter.cs b/src/Game/Scripting/Interpreter.cs index 032600fa8e..5371197334 100644 --- a/src/Game/Scripting/Interpreter.cs +++ b/src/Game/Scripting/Interpreter.cs @@ -19,17 +19,29 @@ // along with this program. If not, see . #endregion +using Microsoft.Xna.Framework; using System; using System.Collections.Generic; using System.Globalization; +using ClassicUO.Utility; namespace ClassicUO.Game.Scripting { - public class RunTimeError : Exception + public class ScriptRunTimeError : Exception { public ASTNode Node; - public RunTimeError(ASTNode node, string error) : base(error) + public ScriptRunTimeError(ASTNode node, string error) : base(error) + { + Node = node; + } + } + + public class ScriptTypeConversionError : Exception + { + public ASTNode Node; + + public ScriptTypeConversionError(ASTNode node, string error) : base(error) { Node = node; } @@ -49,7 +61,7 @@ public static int ToInt(string token) else if (int.TryParse(token, out val)) return val; - throw new RunTimeError(null, "Cannot convert argument to int"); + throw new ScriptTypeConversionError(null, "Cannot convert argument to int"); } public static uint ToUInt(string token) @@ -64,7 +76,7 @@ public static uint ToUInt(string token) else if (uint.TryParse(token, out val)) return val; - throw new RunTimeError(null, "Cannot convert argument to uint"); + throw new ScriptTypeConversionError(null, "Cannot convert argument to uint"); } public static ushort ToUShort(string token) @@ -79,7 +91,7 @@ public static ushort ToUShort(string token) else if (ushort.TryParse(token, out val)) return val; - throw new RunTimeError(null, "Cannot convert argument to ushort"); + throw new ScriptTypeConversionError(null, "Cannot convert argument to ushort"); } public static double ToDouble(string token) @@ -89,7 +101,7 @@ public static double ToDouble(string token) if (double.TryParse(token, out val)) return val; - throw new RunTimeError(null, "Cannot convert argument to double"); + throw new ScriptTypeConversionError(null, "Cannot convert argument to double"); } public static bool ToBool(string token) @@ -99,7 +111,7 @@ public static bool ToBool(string token) if (bool.TryParse(token, out val)) return val; - throw new RunTimeError(null, "Cannot convert argument to bool"); + throw new ScriptTypeConversionError(null, "Cannot convert argument to bool"); } } @@ -152,7 +164,7 @@ public Argument(ScriptExecutionState script, ASTNode node) public int AsInt() { if (_node.Lexeme == null) - throw new RunTimeError(_node, "Cannot convert argument to int"); + throw new ScriptRunTimeError(_node, "Cannot convert argument to int"); // Try to resolve it as a scoped variable first var arg = _script.Lookup(_node.Lexeme); @@ -166,7 +178,7 @@ public int AsInt() public uint AsUInt() { if (_node.Lexeme == null) - throw new RunTimeError(_node, "Cannot convert argument to uint"); + throw new ScriptRunTimeError(_node, "Cannot convert argument to uint"); // Try to resolve it as a scoped variable first var arg = _script.Lookup(_node.Lexeme); @@ -179,7 +191,7 @@ public uint AsUInt() public ushort AsUShort() { if (_node.Lexeme == null) - throw new RunTimeError(_node, "Cannot convert argument to ushort"); + throw new ScriptRunTimeError(_node, "Cannot convert argument to ushort"); // Try to resolve it as a scoped variable first var arg = _script.Lookup(_node.Lexeme); @@ -194,7 +206,7 @@ public ushort AsUShort() public uint AsSerial() { if (_node.Lexeme == null) - throw new RunTimeError(_node, "Cannot convert argument to serial"); + throw new ScriptRunTimeError(_node, "Cannot convert argument to serial"); // Try to resolve it as a scoped variable first var arg = _script.Lookup(_node.Lexeme); @@ -213,7 +225,7 @@ public uint AsSerial() public string AsString() { if (_node.Lexeme == null) - throw new RunTimeError(_node, "Cannot convert argument to string"); + throw new ScriptRunTimeError(_node, "Cannot convert argument to string"); // Try to resolve it as a scoped variable first var arg = _script.Lookup(_node.Lexeme); @@ -226,7 +238,7 @@ public string AsString() public bool AsBool() { if (_node.Lexeme == null) - throw new RunTimeError(_node, "Cannot convert argument to bool"); + throw new ScriptRunTimeError(_node, "Cannot convert argument to bool"); return TypeConverter.ToBool(_node.Lexeme); } @@ -253,6 +265,70 @@ public bool Equals(Argument other) } } + public class Arguments + { + public enum ArgumentType + { + Mandatory, + Optional, + } + public uint DefaultSerial = 0; + public ushort DefaultColor = 65535; + + private Argument[] _args; + private int _index; + + public Arguments(Argument[] args) + { + _args = args; + _index = -1; + } + + public Argument this[int i] + { + + get { _index = i; return _args[_index]; } + set { _index = i; _args[_index] = value; } + } + + public int Length + { + get { return _args.Length; } + } + + public uint NextAsSerial(ArgumentType type = ArgumentType.Optional) + { + _index++; + if (_args.Length > _index) + return _args[_index].AsSerial(); + else if(type == ArgumentType.Optional) + return DefaultSerial; + else throw new ScriptRunTimeError(null, "Serial argument does not exist at " + _index); + } + + public ushort NextAsColor(ArgumentType type = ArgumentType.Optional) + { + _index++; + if (_args.Length > _index) + { + try + { + var color = _args[_index].AsUShort(); + return color; + } + catch (ScriptTypeConversionError ex) + { + // a color can also be the string "any", "grey", "red", etc.. + var colorName = _args[_index].AsString(); + return HuesHelper.ColorNameToHue(colorName); + } + } + else if (type == ArgumentType.Optional) + return DefaultColor; + else throw new ScriptRunTimeError(null, "Serial argument does not exist at " + _index); + } + } + public class ScriptExecutionState { private ASTNode _statement; @@ -286,7 +362,7 @@ private void PopScope() _scope = _scope.Parent; } - private Argument[] ConstructArguments(ref ASTNode node) + private Arguments ConstructArguments(ref ASTNode node) { List args = new List(); @@ -304,7 +380,7 @@ private Argument[] ConstructArguments(ref ASTNode node) case ASTNodeType.LESS_THAN_OR_EQUAL: case ASTNodeType.GREATER_THAN: case ASTNodeType.GREATER_THAN_OR_EQUAL: - return args.ToArray(); + return new Arguments(args.ToArray()); } args.Add(new Argument(this, node)); @@ -312,7 +388,7 @@ private Argument[] ConstructArguments(ref ASTNode node) node = node.Next(); } - return args.ToArray(); + return new Arguments(args.ToArray()); } // For now, the scripts execute directly from the @@ -336,12 +412,12 @@ public bool ExecuteNext() return false; if (_statement.Type != ASTNodeType.STATEMENT) - throw new RunTimeError(_statement, "Invalid script"); + throw new ScriptRunTimeError(_statement, "Invalid script"); var node = _statement.FirstChild(); if (node == null) - throw new RunTimeError(_statement, "Invalid statement"); + throw new ScriptRunTimeError(_statement, "Invalid statement"); int depth = 0; @@ -410,7 +486,7 @@ public bool ExecuteNext() } if (_statement == null) - throw new RunTimeError(node, "If with no matching endif"); + throw new ScriptRunTimeError(node, "If with no matching endif"); break; } @@ -439,7 +515,7 @@ public bool ExecuteNext() } if (_statement == null) - throw new RunTimeError(node, "If with no matching endif"); + throw new ScriptRunTimeError(node, "If with no matching endif"); break; case ASTNodeType.ENDIF: @@ -471,7 +547,7 @@ public bool ExecuteNext() } if (_statement == null) - throw new RunTimeError(node, "If with no matching endif"); + throw new ScriptRunTimeError(node, "If with no matching endif"); break; case ASTNodeType.WHILE: @@ -546,7 +622,7 @@ public bool ExecuteNext() } if (_statement == null) - throw new RunTimeError(node, "Unexpected endwhile"); + throw new ScriptRunTimeError(node, "Unexpected endwhile"); break; case ASTNodeType.FOR: @@ -563,7 +639,7 @@ public bool ExecuteNext() var max = node.FirstChild(); if (max.Type != ASTNodeType.INTEGER) - throw new RunTimeError(max, "Invalid for loop syntax"); + throw new ScriptRunTimeError(max, "Invalid for loop syntax"); // Create a dummy argument that acts as our loop variable var iter = new ASTNode(ASTNodeType.INTEGER, "0", node, 0); @@ -727,7 +803,7 @@ public bool ExecuteNext() } if (_statement == null) - throw new RunTimeError(node, "Unexpected endfor"); + throw new ScriptRunTimeError(node, "Unexpected endfor"); break; case ASTNodeType.BREAK: @@ -795,7 +871,7 @@ public bool ExecuteNext() } if (_statement == null) - throw new RunTimeError(node, "Unexpected continue"); + throw new ScriptRunTimeError(node, "Unexpected continue"); break; case ASTNodeType.STOP: _statement = null; @@ -855,12 +931,12 @@ private bool ExecuteCommand(ASTNode node) var handler = Interpreter.GetCommandHandler(node.Lexeme); if (handler == null) - throw new RunTimeError(node, "Unknown command"); + throw new ScriptRunTimeError(node, "Unknown command"); var cont = handler(node.Lexeme, ConstructArguments(ref node), quiet, force); if (node != null) - throw new RunTimeError(node, "Command did not consume all available arguments"); + throw new ScriptRunTimeError(node, "Command did not consume all available arguments"); return cont; } @@ -868,12 +944,12 @@ private bool ExecuteCommand(ASTNode node) private bool EvaluateExpression(ref ASTNode expr) { if (expr == null || (expr.Type != ASTNodeType.UNARY_EXPRESSION && expr.Type != ASTNodeType.BINARY_EXPRESSION && expr.Type != ASTNodeType.LOGICAL_EXPRESSION)) - throw new RunTimeError(expr, "No expression following control statement"); + throw new ScriptRunTimeError(expr, "No expression following control statement"); var node = expr.FirstChild(); if (node == null) - throw new RunTimeError(expr, "Empty expression following control statement"); + throw new ScriptRunTimeError(expr, "Empty expression following control statement"); switch (expr.Type) { @@ -894,7 +970,7 @@ private bool EvaluateExpression(ref ASTNode expr) node = node.Next(); if (node == null) - throw new RunTimeError(node, "Invalid logical expression"); + throw new ScriptRunTimeError(node, "Invalid logical expression"); bool rhs; @@ -909,7 +985,7 @@ private bool EvaluateExpression(ref ASTNode expr) rhs = EvaluateBinaryExpression(ref e); break; default: - throw new RunTimeError(node, "Nested logical expressions are not possible"); + throw new ScriptRunTimeError(node, "Nested logical expressions are not possible"); } switch (op) @@ -921,7 +997,7 @@ private bool EvaluateExpression(ref ASTNode expr) lhs = lhs || rhs; break; default: - throw new RunTimeError(node, "Invalid logical operator"); + throw new ScriptRunTimeError(node, "Invalid logical operator"); } node = node.Next(); @@ -975,10 +1051,10 @@ private bool CompareOperands(ASTNodeType op, IComparable lhs, IComparable rhs) } catch (ArgumentException e) { - throw new RunTimeError(null, e.Message); + throw new ScriptRunTimeError(null, e.Message); } - throw new RunTimeError(null, "Unknown operator in expression"); + throw new ScriptRunTimeError(null, "Unknown operator in expression"); } @@ -989,7 +1065,7 @@ private bool EvaluateUnaryExpression(ref ASTNode node) var handler = Interpreter.GetExpressionHandler(node.Lexeme); if (handler == null) - throw new RunTimeError(node, "Unknown expression"); + throw new ScriptRunTimeError(node, "Unknown expression"); var result = handler(node.Lexeme, ConstructArguments(ref node), quiet); @@ -1050,7 +1126,7 @@ private IComparable EvaluateBinaryOperand(ref ASTNode node) break; } default: - throw new RunTimeError(node, "Invalid type found in expression"); + throw new ScriptRunTimeError(node, "Invalid type found in expression"); } return val; @@ -1069,12 +1145,12 @@ public static class Interpreter private static Dictionary _timers = new Dictionary(); // Expressions - public delegate IComparable ExpressionHandler(string expression, Argument[] args, bool quiet); - public delegate T ExpressionHandler(string expression, Argument[] args, bool quiet) where T : IComparable; + public delegate IComparable ExpressionHandler(string expression, Arguments args, bool quiet); + public delegate T ExpressionHandler(string expression, Arguments args, bool quiet) where T : IComparable; private static Dictionary _exprHandlers = new Dictionary(); - public delegate bool CommandHandler(string command, Argument[] args, bool quiet, bool force); + public delegate bool CommandHandler(string command, Arguments args, bool quiet, bool force); private static Dictionary _commandHandlers = new Dictionary(); @@ -1187,7 +1263,7 @@ public static bool ListExists(string name) public static bool ListContains(string name, Argument arg) { if (!_lists.ContainsKey(name)) - throw new RunTimeError(null, "List does not exist"); + throw new ScriptRunTimeError(null, "List does not exist"); return _lists[name].Contains(arg); } @@ -1195,7 +1271,7 @@ public static bool ListContains(string name, Argument arg) public static int ListLength(string name) { if (!_lists.ContainsKey(name)) - throw new RunTimeError(null, "List does not exist"); + throw new ScriptRunTimeError(null, "List does not exist"); return _lists[name].Count; } @@ -1203,7 +1279,7 @@ public static int ListLength(string name) public static void PushList(string name, Argument arg, bool front, bool unique) { if (!_lists.ContainsKey(name)) - throw new RunTimeError(null, "List does not exist"); + throw new ScriptRunTimeError(null, "List does not exist"); if (unique && _lists[name].Contains(arg)) return; @@ -1217,7 +1293,7 @@ public static void PushList(string name, Argument arg, bool front, bool unique) public static bool PopList(string name, Argument arg) { if (!_lists.ContainsKey(name)) - throw new RunTimeError(null, "List does not exist"); + throw new ScriptRunTimeError(null, "List does not exist"); return _lists[name].Remove(arg); } @@ -1225,7 +1301,7 @@ public static bool PopList(string name, Argument arg) public static bool PopList(string name, bool front) { if (!_lists.ContainsKey(name)) - throw new RunTimeError(null, "List does not exist"); + throw new ScriptRunTimeError(null, "List does not exist"); var idx = front ? 0 : _lists[name].Count - 1; @@ -1237,7 +1313,7 @@ public static bool PopList(string name, bool front) public static Argument GetListValue(string name, int idx) { if (!_lists.ContainsKey(name)) - throw new RunTimeError(null, "List does not exist"); + throw new ScriptRunTimeError(null, "List does not exist"); var list = _lists[name]; @@ -1255,7 +1331,7 @@ public static void CreateTimer(string name) public static TimeSpan GetTimer(string name) { if (!_timers.TryGetValue(name, out DateTime timestamp)) - throw new RunTimeError(null, "Timer does not exist"); + throw new ScriptRunTimeError(null, "Timer does not exist"); TimeSpan elapsed = DateTime.UtcNow - timestamp; diff --git a/src/Utility/HuesHelper.cs b/src/Utility/HuesHelper.cs index ca2078bd2e..e0d7d741ab 100644 --- a/src/Utility/HuesHelper.cs +++ b/src/Utility/HuesHelper.cs @@ -46,10 +46,10 @@ internal static class HuesHelper [MethodImpl(MethodImplOptions.AggressiveInlining)] public static (byte, byte, byte, byte) GetBGRA(uint cl) { - return ((byte) (cl & 0xFF), // B - (byte) ((cl >> 8) & 0xFF), // G - (byte) ((cl >> 16) & 0xFF), // R - (byte) ((cl >> 24) & 0xFF) // A + return ((byte)(cl & 0xFF), // B + (byte)((cl >> 8) & 0xFF), // G + (byte)((cl >> 16) & 0xFF), // R + (byte)((cl >> 24) & 0xFF) // A ); } @@ -62,19 +62,19 @@ public static uint RgbaToArgb(uint rgba) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static uint Color16To32(ushort c) { - return (uint) (_table[(c >> 10) & 0x1F] | (_table[(c >> 5) & 0x1F] << 8) | (_table[c & 0x1F] << 16)); + return (uint)(_table[(c >> 10) & 0x1F] | (_table[(c >> 5) & 0x1F] << 8) | (_table[c & 0x1F] << 16)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ushort Color32To16(uint c) { - return (ushort) ((((c & 0xFF) << 5) >> 8) | (((((c >> 16) & 0xFF) << 5) >> 8) << 10) | (((((c >> 8) & 0xFF) << 5) >> 8) << 5)); + return (ushort)((((c & 0xFF) << 5) >> 8) | (((((c >> 16) & 0xFF) << 5) >> 8) << 10) | (((((c >> 8) & 0xFF) << 5) >> 8) << 5)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ushort ConvertToGray(ushort c) { - return (ushort) (((c & 0x1F) * 299 + ((c >> 5) & 0x1F) * 587 + ((c >> 10) & 0x1F) * 114) / 1000); + return (ushort)(((c & 0x1F) * 299 + ((c >> 5) & 0x1F) * 587 + ((c >> 10) & 0x1F) * 114) / 1000); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -84,30 +84,78 @@ public static ushort ColorToHue(Color c) ushort origgreen = c.G; ushort origblue = c.B; const double scale = 31.0 / 255; - ushort newred = (ushort) (origred * scale); + ushort newred = (ushort)(origred * scale); if (newred == 0 && origred != 0) { newred = 1; } - ushort newgreen = (ushort) (origgreen * scale); + ushort newgreen = (ushort)(origgreen * scale); if (newgreen == 0 && origgreen != 0) { newgreen = 1; } - ushort newblue = (ushort) (origblue * scale); + ushort newblue = (ushort)(origblue * scale); if (newblue == 0 && origblue != 0) { newblue = 1; } - ushort v = (ushort) ((newred << 10) | (newgreen << 5) | newblue); + ushort v = (ushort)((newred << 10) | (newgreen << 5) | newblue); return v; } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ushort ColorNameToHue(string c) + { + switch (c) + { + case "red": + return Color32To16(0x0000FFFF); + case "cyan": + return Color32To16(0xFFFF00FF); + case "blue": + return Color32To16(0xFF0000FF); + case "darkblue": + return Color32To16(0xA00000FF); + case "lightblue": + return Color32To16(0xE6D8ADFF); + case "purple": + return Color32To16(0x800080FF); + case "yellow": + return Color32To16(0x00FFFFFF); + case "lime": + return Color32To16(0x00FF00FF); + case "magenta": + return Color32To16(0xFF00FFFF); + case "white": + return Color32To16(0xFFFEFEFF); + case "silver": + return Color32To16(0xC0C0C0FF); + case "gray": + return Color32To16(0x808080FF); + case "grey": + return Color32To16(0x808080FF); + case "black": + return Color32To16(0x010101FF); + case "orange": + return Color32To16(0x00A5FFFF); + case "brown": + return Color32To16(0x2A2AA5FF); + case "maroon": + return Color32To16(0x000080FF); + case "green": + return Color32To16(0x008000FF); + case "olive": + return Color32To16(0x008080FF); + default: + return 65535; + } + } } } \ No newline at end of file