xabbo-scripts/FollowFurni.csx
Administrator b6c31a7feb Add 165 scripts from desktop collection + update README
Added scripts from C:\Users\ploet\Desktop\Habbo\Xabbo Scripte:
- 39 KI/Chatbot scripts (ChatGPT, Gemini, Grok, DeepSeek, Ollama)
- Game solvers (Domino, Dodgeball, Obsidian Maze, IceBall)
- Collision avoidance bots (5 versions)
- Plant/breeding automation (12 scripts)
- Trading tools, packet debuggers, room utilities
- Navigation & teleport helpers

Removed: 9 files (3 empty, 2 trivial, 4 cross-duplicates)
Updated README with full categorized index of all 230+ scripts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 09:47:56 +01:00

272 lines
11 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Globalization;
using System.Threading;
using System.Text.RegularExpressions;
public struct Point : IEquatable<Point>
{
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})";
}
public class Tile
{
public int X { get; set; }
public int Y { get; set; }
public double Z { get; set; }
public Point XY => new Point(X, Y);
public Tile(int x, int y, double z = 0.0) { X = x; Y = y; Z = z; }
public Tile(Point p, double z = 0.0) : this(p.X, p.Y, z) { }
}
public class TrackedFurni
{
public long Id { get; set; }
public string Name { get; set; }
public Tile Location { get; set; }
}
public class WiredMovement
{
public int FromX { get; set; }
public int FromY { get; set; }
public int ToX { get; set; }
public int ToY { get; set; }
public string FromHeight { get; set; }
public string ToHeight { get; set; }
public int Id { get; set; }
}
Log("started");
long targetFurniIdToFollow = 2147418121;
HashSet<Point> walkableTiles = null;
int roomWidth = 0;
int roomLength = 0;
bool floorPlanParsedSuccessfully = false;
DateTime lastFloorPlanParseAttempt = DateTime.MinValue;
Dictionary<long, Dictionary<long, TrackedFurni>> AllTrackedFurnisGlobal = new Dictionary<long, Dictionary<long, TrackedFurni>>();
Tile _myAvatarActualTargetTile = null;
DateTime _lastMoveCommandSentTime = DateTime.MinValue;
Point _lastMoveCommandSentToXY = default(Point);
TimeSpan _clientSideAnticipationWindow = TimeSpan.FromMilliseconds(250);
Point CurrentAnticipatedBotPosition
{
get
{
if (Self == null) return default(Point);
if (_myAvatarActualTargetTile != null) return _myAvatarActualTargetTile.XY;
if (!_lastMoveCommandSentToXY.Equals(default(Point)) && (DateTime.UtcNow - _lastMoveCommandSentTime) < _clientSideAnticipationWindow)
return _lastMoveCommandSentToXY;
if (Self.Location != null) return new Point(Self.Location.X, Self.Location.Y);
return default(Point);
}
}
void ExecuteMove(int x, int y)
{
Move(x,y);
_lastMoveCommandSentToXY = new Point(x,y);
_lastMoveCommandSentTime = DateTime.UtcNow;
_myAvatarActualTargetTile = null;
}
void TryParseFloorPlan()
{
if ((DateTime.UtcNow - lastFloorPlanParseAttempt).TotalSeconds < 10 && floorPlanParsedSuccessfully) return;
lastFloorPlanParseAttempt = DateTime.UtcNow;
bool currentParseSuccess = false;
dynamic currentFloorPlan = null;
int tempRoomWidth = 0;
int tempRoomLength = 0;
HashSet<Point> tempWalkableTiles = null;
try { currentFloorPlan = FloorPlan; }
catch (Exception ex) { Log($"Error accessing FloorPlan: {ex.Message}"); floorPlanParsedSuccessfully = false; return; }
if (currentFloorPlan == null) { Log("FloorPlan is null."); floorPlanParsedSuccessfully = false; return; }
try
{
tempRoomWidth = currentFloorPlan.Width;
tempRoomLength = currentFloorPlan.Length;
if (tempRoomWidth <= 0 || tempRoomLength <= 0) { Log($"Invalid dimensions: W={tempRoomWidth}, L={tempRoomLength}"); floorPlanParsedSuccessfully = false; return; }
tempWalkableTiles = new HashSet<Point>();
IReadOnlyList<int> tilesData = null; string heightmapString = null;
object tilesProperty = null; try { tilesProperty = currentFloorPlan.Tiles; } catch { }
object heightmapProperty = null; try { heightmapProperty = currentFloorPlan.Heightmap; } catch { }
if (tilesProperty is IReadOnlyList<int> intTiles) tilesData = intTiles;
else if (heightmapProperty is string hmString) {
heightmapString = hmString.Replace("\r", "").Replace("\n", "");
if (heightmapString.Length != tempRoomWidth * tempRoomLength) { Log("Heightmap length mismatch."); floorPlanParsedSuccessfully = false; return; }
} else { Log("No recognizable Tiles/Heightmap."); floorPlanParsedSuccessfully = false; return; }
for (int y = 0; y < tempRoomLength; y++) {
for (int x = 0; x < tempRoomWidth; x++) {
bool isTileWalkable = false;
if (tilesData != null) {
int tileIndex = y * tempRoomWidth + x;
if (tileIndex < tilesData.Count) isTileWalkable = tilesData[tileIndex] >= 0 && tilesData[tileIndex] < 250;
} else if (heightmapString != null) {
isTileWalkable = heightmapString[y * tempRoomWidth + x] != 'x';
}
if(isTileWalkable)
{
tempWalkableTiles.Add(new Point(x,y));
}
}
}
currentParseSuccess = true;
}
catch (Exception ex) { Log($"Error parsing FloorPlan: {ex.Message}"); currentParseSuccess = false; }
if(currentParseSuccess) {
walkableTiles = tempWalkableTiles;
roomWidth = tempRoomWidth; roomLength = tempRoomLength;
floorPlanParsedSuccessfully = true; Log($"FloorPlan parsed: {walkableTiles.Count} walkable tiles in a {roomWidth}x{roomLength} area.");
} else {
walkableTiles = null; floorPlanParsedSuccessfully = false;
}
}
void OnBotEnteredNewRoom()
{
Log("Entered new room. Wiping memory.");
_myAvatarActualTargetTile = null;
_lastMoveCommandSentToXY = default(Point);
long currentRoomId = RoomId;
if (!AllTrackedFurnisGlobal.ContainsKey(currentRoomId)) AllTrackedFurnisGlobal[currentRoomId] = new Dictionary<long, TrackedFurni>();
AllTrackedFurnisGlobal[currentRoomId].Clear();
if (FloorItems != null) {
foreach (var item in FloorItems) {
if (item == null || item.Location == null) continue;
try {
if (!AllTrackedFurnisGlobal[currentRoomId].ContainsKey(item.Id)) {
AllTrackedFurnisGlobal[currentRoomId].Add(item.Id, new TrackedFurni {
Id = item.Id, Name = item.GetName(), Location = new Tile(item.Location.X, item.Location.Y, item.Location.Z)
});
}
} catch {}
}
}
TryParseFloorPlan();
}
void InterceptWiredMovements(dynamic e)
{
long currentRoomId = RoomId;
if (!AllTrackedFurnisGlobal.ContainsKey(currentRoomId)) AllTrackedFurnisGlobal[currentRoomId] = new Dictionary<long, TrackedFurni>();
var packet = e.Packet;
int count = packet.ReadInt();
for (int i = 0; i < count; i++) {
packet.ReadInt();
var movement = new WiredMovement { FromX = packet.ReadInt(), FromY = packet.ReadInt(), ToX = packet.ReadInt(), ToY = packet.ReadInt(), FromHeight = packet.ReadString(), ToHeight = packet.ReadString(), Id = packet.ReadInt() };
packet.ReadInt(); packet.ReadInt();
long furniLongId = movement.Id;
if (double.TryParse(movement.ToHeight, NumberStyles.Any, CultureInfo.InvariantCulture, out double z)) {
var newLocation = new Tile(movement.ToX, movement.ToY, z);
if (AllTrackedFurnisGlobal[currentRoomId].TryGetValue(furniLongId, out TrackedFurni trackedFurni)) {
trackedFurni.Location = newLocation;
} else {
AllTrackedFurnisGlobal[currentRoomId][furniLongId] = new TrackedFurni { Id = furniLongId, Location = newLocation };
}
}
}
}
Regex mvRegex = new Regex(@"/mv (\d+),(\d+),([\d\.]+)/");
void InterceptUserUpdate(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 x = packet.ReadInt(); int y = packet.ReadInt(); string zStr = packet.ReadString();
int headRot = packet.ReadInt(); int bodyRot = packet.ReadInt(); string action = packet.ReadString();
if (entityIndex == Self.Index) {
Match match = mvRegex.Match(action);
if (match.Success) {
_myAvatarActualTargetTile = new Tile(int.Parse(match.Groups[1].Value), int.Parse(match.Groups[2].Value), double.Parse(match.Groups[3].Value, CultureInfo.InvariantCulture));
_lastMoveCommandSentToXY = default(Point);
} else if (action.EndsWith("//") && !action.Contains("/mv")) {
_myAvatarActualTargetTile = null;
}
}
}
}
OnEnteredRoom(e => OnBotEnteredNewRoom());
OnIntercept(In["WiredMovements"], e => InterceptWiredMovements(e));
OnIntercept(In["UserUpdate"], e => InterceptUserUpdate(e));
while(Run)
{
try
{
if (!Run) break;
Point currentSelfLocationXY = CurrentAnticipatedBotPosition;
if (currentSelfLocationXY.Equals(default(Point))) { Delay(30); continue; }
if (!floorPlanParsedSuccessfully) TryParseFloorPlan();
if (!floorPlanParsedSuccessfully) { Delay(50); continue; }
long currentRoomId = RoomId;
TrackedFurni targetFurni = null;
if (AllTrackedFurnisGlobal.TryGetValue(currentRoomId, out var currentRoomTrackedItems))
{
currentRoomTrackedItems.TryGetValue(targetFurniIdToFollow, out targetFurni);
}
if (targetFurni == null && FloorItems != null)
{
var itemFromFloor = FloorItems.FirstOrDefault(f => f != null && f.Id == targetFurniIdToFollow);
if (itemFromFloor != null && itemFromFloor.Location != null)
{
string itemName = null;
try { itemName = itemFromFloor.GetName(); } catch { }
targetFurni = new TrackedFurni {
Id = itemFromFloor.Id,
Name = itemName,
Location = new Tile(itemFromFloor.Location.X, itemFromFloor.Location.Y, itemFromFloor.Location.Z)
};
if (!AllTrackedFurnisGlobal.ContainsKey(currentRoomId))
{
AllTrackedFurnisGlobal[currentRoomId] = new Dictionary<long, TrackedFurni>();
}
AllTrackedFurnisGlobal[currentRoomId][targetFurni.Id] = targetFurni;
}
}
if (targetFurni != null && targetFurni.Location != null)
{
Point targetPoint = targetFurni.Location.XY;
if (!currentSelfLocationXY.Equals(targetPoint))
{
ExecuteMove(targetPoint.X, targetPoint.Y);
}
}
}
catch (Exception ex) { Log($"LOOP ERROR: {ex.GetType().Name} - {ex.Message}"); }
if (!Run) break;
Delay(30);
}
Log("closed");