using System; using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; public struct Point : IEquatable { public int X { get; } public int Y { get; } public Point(int x, int y) { X = x; Y = y; } public static implicit operator Point((int x, int y) tuple) => new Point(tuple.x, tuple.y); public bool Equals(Point other) => X == other.X && Y == other.Y; public override bool Equals(object obj) => obj is Point other && Equals(other); public override int GetHashCode() => HashCode.Combine(X, Y); public static bool operator ==(Point left, Point right) => left.Equals(right); public static bool operator !=(Point left, Point right) => !(left == right); public override string ToString() => $"({X},{Y})"; } Log("started"); const string FURNI_NAME_CONTAINS_TEXT = "One Way Gate"; Regex mvRegex = new Regex(@"/mv (\d+),(\d+),([\d\.]+)/"); void CheckAndEnterGateGeneral(int userTileX, int userTileY) { if (FloorItems == null) return; foreach (var item in FloorItems) { if (item == null || item.Location == null) continue; string itemName = null; try { itemName = item.GetName(); } catch { continue; } if (itemName != null && itemName.Contains(FURNI_NAME_CONTAINS_TEXT)) { int gateX = item.Location.X; int gateY = item.Location.Y; int gateDir = item.Direction; long gateId = item.Id; bool shouldEnter = false; if (gateDir == 0) { if (userTileX == gateX && userTileY == gateY - 1) shouldEnter = true; } else if (gateDir == 2) { if (userTileX == gateX + 1 && userTileY == gateY) shouldEnter = true; } else if (gateDir == 4) { if (userTileX == gateX && userTileY == gateY + 1) shouldEnter = true; } else if (gateDir == 6) { if (userTileX == gateX - 1 && userTileY == gateY) shouldEnter = true; } if (shouldEnter) { Log($"User tile ({userTileX},{userTileY}) matches criteria for Gate (ID {gateId}, Name: '{itemName}') at ({gateX},{gateY} Dir:{gateDir}) via GeneralCheck. Sending packet."); Send(Out.EnterOneWayDoor, gateId); return; } } } } void HandleUserUpdate(dynamic e) { if (Self == null) return; var packet = e.Packet; int numUpdates = packet.ReadInt(); for (int i = 0; i < numUpdates; i++) { int entityIndex = packet.ReadInt(); int currentX = packet.ReadInt(); int currentY = packet.ReadInt(); packet.ReadString(); packet.ReadInt(); packet.ReadInt(); string action = packet.ReadString(); if (entityIndex == Self.Index) { Point tileToConsiderForGate; Match match = mvRegex.Match(action); if (match.Success) { try { int targetX = int.Parse(match.Groups[1].Value); int targetY = int.Parse(match.Groups[2].Value); tileToConsiderForGate = new Point(targetX, targetY); } catch { tileToConsiderForGate = new Point(currentX, currentY); } } else { tileToConsiderForGate = new Point(currentX, currentY); } CheckAndEnterGateGeneral(tileToConsiderForGate.X, tileToConsiderForGate.Y); } } } void HandleObjectUpdate(dynamic e) { if (Self == null || Self.Location == null) return; var packet = e.Packet; int furniIdFromPacket = packet.ReadInt(); packet.ReadInt(); int itemXFromPacket = packet.ReadInt(); int itemYFromPacket = packet.ReadInt(); int itemNewDirectionFromPacket = packet.ReadInt(); var itemInstance = FloorItems?.FirstOrDefault(f => f != null && f.Id == furniIdFromPacket); if (itemInstance != null) { string itemName = null; try { itemName = itemInstance.GetName(); } catch { return; } if (itemName != null && itemName.Contains(FURNI_NAME_CONTAINS_TEXT)) { bool shouldEnter = false; int userX = Self.Location.X; int userY = Self.Location.Y; if (itemNewDirectionFromPacket == 0) { if (userX == itemXFromPacket && userY == itemYFromPacket - 1) shouldEnter = true; } else if (itemNewDirectionFromPacket == 2) { if (userX == itemXFromPacket + 1 && userY == itemYFromPacket) shouldEnter = true; } else if (itemNewDirectionFromPacket == 4) { if (userX == itemXFromPacket && userY == itemYFromPacket + 1) shouldEnter = true; } else if (itemNewDirectionFromPacket == 6) { if (userX == itemXFromPacket - 1 && userY == itemYFromPacket) shouldEnter = true; } if (shouldEnter) { Log($"User at ({userX},{userY}) matches criteria for Gate ID {furniIdFromPacket} ({itemXFromPacket},{itemYFromPacket}) with NewDir:{itemNewDirectionFromPacket} from ObjectUpdate. Sending packet."); Send(Out.EnterOneWayDoor, furniIdFromPacket); } } } } void InitializeStateAndCheckGates(dynamic eventArgs) { if (Self != null && Self.Location != null) { Point initialPosition = new Point(Self.Location.X, Self.Location.Y); CheckAndEnterGateGeneral(initialPosition.X, initialPosition.Y); } } OnIntercept(In["UserUpdate"], e => HandleUserUpdate(e)); OnIntercept(In["ObjectUpdate"], e => HandleObjectUpdate(e)); OnEnteredRoom(e => InitializeStateAndCheckGates(e)); if (Self != null && Self.Location != null) { InitializeStateAndCheckGates(null); } while(Run) { try { } catch (Exception ex) { Log($"MAIN LOOP ERROR: {ex.GetType().Name} - {ex.Message}"); } if (!Run) break; Delay(30); } Log("closed");