Keeps the repo root clean - only README.md visible on landing page. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
134 lines
3.8 KiB
C#
134 lines
3.8 KiB
C#
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");
|