using System; using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; Log("started"); const string FURNI_NAME_CONTAINS_TEXT = "One Way Gate"; Regex mvRegex = new Regex(@"/mv (\d+),(\d+),([\d\.]+)"); // Cache: trigger-tile -> (gateId, gateX, gateY, direction) Dictionary<(int x, int y), (long id, int gx, int gy, int dir)> triggerMap = new(); (int dx, int dy) GetTriggerOffset(int dir) => dir switch { 0 => (0, -1), 2 => (1, 0), 4 => (0, 1), 6 => (-1, 0), _ => (0, 0) }; void RebuildGateCache() { triggerMap.Clear(); if (FloorItems == null) return; foreach (var item in FloorItems) { if (item?.Location == null) continue; string name = null; try { name = item.GetName(); } catch { continue; } if (name == null || !name.Contains(FURNI_NAME_CONTAINS_TEXT)) continue; var (dx, dy) = GetTriggerOffset(item.Direction); var trigger = (item.Location.X + dx, item.Location.Y + dy); triggerMap[trigger] = (item.Id, item.Location.X, item.Location.Y, item.Direction); } Log($"Gate cache built: {triggerMap.Count} gates indexed"); } void TryEnterGate(int userX, int userY) { if (triggerMap.TryGetValue((userX, userY), out var gate)) { Log($"Match at ({userX},{userY}) for Gate ID {gate.id} at ({gate.gx},{gate.gy} Dir:{gate.dir}). Sending packet."); Send(Out.EnterOneWayDoor, gate.id); } } 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) { int checkX = currentX, checkY = currentY; Match match = mvRegex.Match(action); if (match.Success) { int.TryParse(match.Groups[1].Value, out checkX); int.TryParse(match.Groups[2].Value, out checkY); } TryEnterGate(checkX, checkY); } } } void HandleObjectUpdate(dynamic e) { if (Self?.Location == null) return; var packet = e.Packet; int furniId = packet.ReadInt(); packet.ReadInt(); int itemX = packet.ReadInt(); int itemY = packet.ReadInt(); int newDir = packet.ReadInt(); // Update cache for this specific gate var item = FloorItems?.FirstOrDefault(f => f != null && f.Id == furniId); if (item != null) { string name = null; try { name = item.GetName(); } catch { return; } if (name != null && name.Contains(FURNI_NAME_CONTAINS_TEXT)) { // Remove old trigger entry for this gate var toRemove = triggerMap.Where(kv => kv.Value.id == furniId).Select(kv => kv.Key).ToList(); foreach (var key in toRemove) triggerMap.Remove(key); // Add new trigger position var (dx, dy) = GetTriggerOffset(newDir); triggerMap[(itemX + dx, itemY + dy)] = (furniId, itemX, itemY, newDir); // Check if user is on new trigger tile TryEnterGate(Self.Location.X, Self.Location.Y); } } } void OnRoomReady(dynamic e) { RebuildGateCache(); if (Self?.Location != null) TryEnterGate(Self.Location.X, Self.Location.Y); } OnIntercept(In["UserUpdate"], e => HandleUserUpdate(e)); OnIntercept(In["ObjectUpdate"], e => HandleObjectUpdate(e)); OnEnteredRoom(e => OnRoomReady(e)); // Sofort beim Start Cache bauen und prüfen (schon im Raum) RebuildGateCache(); if (Self?.Location != null) TryEnterGate(Self.Location.X, Self.Location.Y); while (Run) { Delay(30); } Log("closed");