diff --git a/tools/scaffolding/BridgeVendorTestProbe.cs b/tools/scaffolding/BridgeVendorTestProbe.cs new file mode 100644 index 0000000..08d3dd9 --- /dev/null +++ b/tools/scaffolding/BridgeVendorTestProbe.cs @@ -0,0 +1,98 @@ +using System; +using System.Reflection; + +using Server.Accounting; +using Server.Items; +using Server.Mobiles; + +namespace Server.Custom +{ + /// + /// Spawns a reachable, houseless player vendor next to the `tester` character (wttest) and + /// gives tester gold, so the PlayerVendorSale core patch can be exercised by a real in-game + /// purchase. The buyer must be a non-GM — IsOwner() treats any GameMaster+ as the owner of + /// every player vendor, so an Owner-level character can never buy. + /// + /// Owner is a seed_000 character, so buyer (wttest) and owner (seed_000) are different + /// accounts — a clean cheat-detection example. + /// + /// Test scaffolding. Never deployed. Spawns a mobile and hands out gold; run only on the + /// throwaway seeded world. + /// + public static class BridgeVendorTestProbe + { + private static readonly MethodInfo SetVendorItem = typeof(PlayerVendor).GetMethod( + "SetVendorItem", + BindingFlags.Instance | BindingFlags.NonPublic, + null, + new[] { typeof(Item), typeof(int), typeof(string) }, + null); + + public static void Initialize() + { + if (Config.Get("Bridge.VendorTestOnStart", false)) + EventSink.ServerStarted += () => Timer.DelayCall(TimeSpan.FromSeconds(3.0), Run); + } + + private static void Run() + { + try + { + var ownerAcct = Accounting.Accounts.GetAccount("seed_000") as Account; + var owner = ownerAcct == null ? null : ownerAcct[0]; + + var buyerAcct = Accounting.Accounts.GetAccount("wttest") as Account; + var buyer = buyerAcct == null ? null : buyerAcct[0] as PlayerMobile; + + if (owner == null || buyer == null) + { + Console.WriteLine("[VendorTest] need seed_000 owner and wttest/tester; not found"); + return; + } + + // Remove any prior test vendor (e.g. one orphaned on the Internal map). + if (PlayerVendor.PlayerVendors != null) + { + var doomed = new System.Collections.Generic.List(); + foreach (var v in PlayerVendor.PlayerVendors) + if (v != null && !v.Deleted && v.ShopName == "Bridge Test Shop") + doomed.Add(v); + foreach (var v in doomed) + v.Delete(); + } + + // A confirmed-walkable spot where tester was already standing this session, so the + // vendor is on solid ground and reachable. Then force tester's logout location right + // next to it, so tester logs in beside the vendor regardless of where it was. + var map = Map.Trammel; + var loc = new Point3D(3533, 2546, 20); // vendor + buyer.LogoutMap = map; + buyer.LogoutLocation = new Point3D(3532, 2546, 20); // tester appears here + + var vendor = new PlayerVendor(owner, null) + { + Name = "test vendor", + ShopName = "Bridge Test Shop" + }; + vendor.MoveToWorld(loc, map); + + var blade = new Longsword(); + vendor.Backpack.DropItem(blade); + if (SetVendorItem != null) + SetVendorItem.Invoke(vendor, new object[] { blade, 100, "a test blade" }); + + // Make sure tester can afford it. + if (buyer.Backpack != null) + buyer.Backpack.DropItem(new Gold(1000)); + + Console.WriteLine( + "[VendorTest] spawned '{0}' (owner {1}/seed_000) next to {2}/wttest at {3} on {4}; test blade = 100 gold; gave tester 1000 gold", + vendor.ShopName, owner.Name, buyer.Name, loc, map); + } + catch (Exception ex) + { + Console.WriteLine("[VendorTest] FAILED: " + ex); + } + } + } +}