xabbo-scripts/Scripts/Pet Trainer Auto v84.csx
Administrator 7a548130a3 Move all scripts into Scripts/ subfolder
Keeps the repo root clean - only README.md visible on landing page.

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

2178 lines
71 KiB
C#

// Auto Pet Training Script V84
// FIX: Zeigt echte XP-Menge (+10, +20, +35 etc.)
// FIX: Energy kann > MaxEnergy sein (durch Items)
using System.Linq; // Fuer OrderBy (zufaellige Sortierung)
// ============ CONFIGURATION ============
string petName = ""; // Wird automatisch erkannt beim Anklicken!
int trainDelay = 4000;
int energyThreshold = 40;
int hungerThreshold = 40;
// ============ PET-TYP FOOD FILTER ============
// Essen das NUR für bestimmte Tiere ist und für andere ausgeschlossen werden soll
// HORSE ONLY (Pferde-Essen - Hunde/Katzen etc. essen das NICHT!)
string[] horseOnlyFood = {
"hay", "hay bucket", "heu", "heuballen",
"horse trough", "pferdetrog"
};
// CAT ONLY (Katzen-Essen)
string[] catOnlyFood = {
// Die meisten Fische können alle essen, aber falls nötig hier eintragen
};
// DOG ONLY (Hunde-Essen)
string[] dogOnlyFood = {
// Knochen können die meisten essen
};
// Habbo Pet Types:
// 0=Dog, 1=Cat, 2=Crocodile, 3=Terrier, 4=Bear, 5=Pig, 6=Lion, 7=Horse, 8=Spider, 9=Turtle, 10=Chicken
const int PET_TYPE_HORSE = 7;
// Prüft ob ein Essen für den aktuellen Pet-Typ geeignet ist
bool IsFoodForPetType(string foodName, int currentPetType) {
if (string.IsNullOrEmpty(foodName)) return false;
string lower = foodName.ToLower();
// HORSE-ONLY FOOD: Hay, Hay Bucket, Horse Trough
// Diese dürfen NUR an Pferde (type 7) verfüttert werden!
foreach (var horseFood in horseOnlyFood) {
if (lower.Contains(horseFood)) {
if (currentPetType != PET_TYPE_HORSE) {
Log($"[FOOD-FILTER] '{foodName}' ist Pferde-Essen - Pet ist Typ {currentPetType} (kein Pferd!)");
return false; // Kein Pferd -> kann dieses Essen nicht fressen!
}
return true; // Pferd kann es fressen
}
}
// CAT-ONLY FOOD (falls nötig)
foreach (var catFood in catOnlyFood) {
if (lower.Contains(catFood)) {
if (currentPetType != 1) { // 1 = Cat
return false;
}
return true;
}
}
// DOG-ONLY FOOD (falls nötig)
foreach (var dogFood in dogOnlyFood) {
if (lower.Contains(dogFood)) {
if (currentPetType != 0 && currentPetType != 3) { // 0=Dog, 3=Terrier
return false;
}
return true;
}
}
// Alles andere ist universelles Essen - ALLE Pets können es fressen!
return true;
}
// ============ COMMAND ID MAPPING ============
// Habbo Pet Command IDs -> Command Names
Dictionary<int, string> commandIdToName = new Dictionary<int, string>() {
{ 0, "free" },
{ 1, "sit" },
{ 2, "down" },
{ 3, "here" },
{ 4, "beg" },
{ 5, "play dead" },
{ 6, "stay" },
{ 7, "follow" },
{ 8, "stand" },
{ 9, "jump" },
{ 10, "speak" },
{ 11, "play" },
{ 12, "silent" },
{ 13, "nest" },
{ 14, "drink" },
{ 15, "follow left" },
{ 16, "follow right" },
{ 17, "play football" },
{ 18, "come here" },
{ 24, "move forward" },
{ 25, "turn left" },
{ 26, "turn right" },
{ 43, "eat" },
// Horse/Special Pet Commands
{ 46, "wag tail" }, // Pferde: Wag Tail
{ 47, "count" }, // Pferde: Count (zaehlen)
{ 48, "breed" } // Breed
};
// Commands die NICHT fuer Training verwendet werden sollen
// Diese geben KEINE XP oder sind spezielle Befehle!
string[] excludeFromTraining = {
// Keine XP:
"free", // Laesst Pet frei laufen - KEINE XP!
// Spezielle Befehle:
"eat", // Fuer Care-Modus reserviert
"drink", // Fuer Care-Modus reserviert
"breed", // Zuechten
"wag tail", // Spezial-Animation (Pferd)
"count" // Spezial-Trick (Pferd)
};
// ============ PET FOOD NAMES ============
// NUR echte Pet Food Items aus dem Katalog "Toys and Accessories"!
// KEINE Möbel wie "Delicious Street Food"!
string[] petFoodClassNames = {
"petfood", "petgoodie", "pet_food",
"a0 petfood", "a0 petgoodie"
};
// NUR EXAKTE Namen von echtem Pet Food!
// WICHTIG: "Carrot Bite" ist ein KLEIDUNGSSTUECK, kein Essen!
// KOMPLETTE LISTE aller 33 Pet Food Items aus dem Katalog!
string[] petFoodExactNames = {
// === STANDARD FOOD ===
"Cabbage",
"Carrot",
"Hay",
"Hay Bucket",
// === FLEISCH & FISCH ===
"Meat Bone",
"Doggy Bones",
"T-Bones",
"Salmon",
"Sardines",
"Chicken",
"Shrimps",
// === OBST & GEMUESE ===
"Green Apples",
"Red Apples",
"Ice Apples",
"Kale",
"Grapes",
"Webbed Grapes",
"Banana",
"Red Pepper",
"Yellow Chili",
"Corn",
"Leaves",
// === SUESSIGKEITEN & SPEZIAL ===
"Holiday Cake",
"Popsicles",
"Chocolate Gazelle",
"Choco-Bunny",
"Chocolate Mouse",
"Marzipan Man",
// === SONSTIGES ===
"Pile of Letters",
"Books",
"TV Remote",
"The Fly",
"Grass Duck",
// === DEUTSCHE NAMEN ===
"Kohl",
"Karotte",
"Heu",
"Heuballen",
"Knochen",
"Lachs",
"Sardinen",
"Haehnchen",
"Garnelen",
"Gruene Aepfel",
"Rote Aepfel",
"Eis Aepfel",
"Gruenkohl",
"Trauben",
"Banane",
"Paprika",
"Chili",
"Mais",
"Blaetter",
"Kuchen",
"Eis am Stiel",
"Schokoladen Gazelle",
"Schoko-Hase",
"Schokoladen Maus",
"Marzipan Mann",
"Briefstapel",
"Buecher",
"Fernbedienung",
"Die Fliege",
"Gras Ente"
};
// ============ PET WATER BOWL NAMES (from catalog) ============
string[] petWaterClassNames = {
"waterbowl", "water_bowl", "milkbowl", "pond", "trough",
"grail_water", "grail_nectar", "lm_pond", "pet_waterbottle",
"baby_waterbottle", "babybottle"
};
string[] petWaterDisplayNames = {
// From catalog "Toys and Accessories"
"Milk Bowl", "Water Bowl", "Basic Grey Water Bowl",
"Basic Blue Water Bowl", "Basic Green Water Bowl",
"Basic Pink Water Bowl", "Basic Yellow Water Bowl",
"Basic Red Water Bowl",
"Horse Trough", "Pond", "Water Bottle",
"Grail Water", "Grail Nectar", "Leprechaun Pond",
"Baby Waterbottle", "Baby Water Bottle", "Baby Bottle",
// German names
"Wassernapf", "Wasserschale", "Milchnapf",
"Pferdetrog", "Teich", "Wasserflasche",
"Baby Wasserflasche", "Babyflasche"
};
// ============ SICHERE BASIS-BEFEHLE ============
// Level 1 Pets haben NUR diese 2 Befehle freigeschaltet!
// (eat und drink sind auch freigeschaltet aber nicht fuer Training)
string[] safeCommands = {
"free", "sit"
};
// Befehle die Essen/Wasser benoetigen
string[] careCommands = { "eat", "drink" };
// ============ STATE ============
List<string> unlockedCommands = new List<string>();
string currentCmd = "";
bool waitingForResponse = false;
bool gotXP = false;
int lastXPGained = 0; // Echte XP-Menge aus dem Packet!
bool gotRefusal = false;
int petLevel = 0;
int petXP = 0;
int petMaxXP = 0;
int petEnergy = 100;
int petMaxEnergy = 100; // DYNAMISCH! Wird aus PetInfo gelesen!
int petHunger = 100;
long selectedPetId = -1;
int petType = -1; // Pet-Typ: 0=Dog, 1=Cat, 2=Croc, 3=Terrier, 7=Horse, etc.
bool petSwitchRequested = false; // Flag: Neues Pet wurde angeklickt!
long newPetId = -1; // ID des neuen Pets
// PetCommands state - WICHTIG: vor dem Handler deklarieren!
bool petCommandsReceived = false;
List<int> petCommandIds = new List<int>();
int cmdIndex = 0; // GLOBAL damit PetCommands Handler es resetten kann
// Pet movement tracking
bool petIsMoving = false;
bool petIsBusy = false;
DateTime lastPetUpdate = DateTime.Now;
// ============ AUTO-SWITCH: Regelmäßig zum niedrigsten Level wechseln! ============
DateTime lastLevelScan = DateTime.MinValue;
int levelScanIntervalSeconds = 30; // Alle 30 Sekunden scannen!
string targetPetName = ""; // Name der Pets die trainiert werden sollen
// Temporäre Variablen für Level-Scan
int scanTempLevel = -1;
bool scanGotResponse = false;
// Care mode state - DYNAMISCH basierend auf petMaxEnergy!
bool inCareMode = false;
int careModeThresholdPercent = 20; // Unter 20% = Care-Modus STARTEN
int careModeExitPercent = 95; // Bei 95%+ = Care-Modus BEENDEN
int careModeCmdCount = 0; // Zaehler fuer Care-Modus Befehle
int careModeCmdLimit = 50; // Nach 50 eat/drink -> kurze Pause
// Room data
string roomHeightMap = "";
List<(int x, int y)> occupiedTiles = new List<(int x, int y)>();
// ============ BOT MESSAGE (Bubble 31) ============
void BotMessage(string msg) {
Send(In["Chat"], 0, $"[PET TRAINER] {msg}", 0, 31, 0, -1);
Log($"[BOT] {msg}");
}
// ============ PACKET HANDLERS ============
// Capture FloorHeightMap
OnIntercept(In["FloorHeightMap"], e => {
try {
e.Packet.ReadBool();
e.Packet.ReadInt();
roomHeightMap = e.Packet.ReadString();
} catch { }
});
// Chat responses
OnIntercept(In["Chat"], e => {
if (!waitingForResponse) return;
int idx = e.Packet.ReadInt();
string msg = e.Packet.ReadString();
if (msg.Contains("I'd rather not") ||
msg.Contains("Yes, yes, I'll do it later") ||
msg.Contains("Well, I'm not going to do it") ||
msg.Contains("Maybe I'll do it later") ||
msg.Contains("Maybe later") ||
msg.Contains("Not now") ||
msg.Contains("I don't want to") ||
msg.Contains("Food, please") ||
msg.Contains("My stomach is growling")) {
gotRefusal = true;
}
});
// XP gain - lese echte XP-Menge aus dem Packet!
// PetExperience Packet Format: {i:PetId}{i:PetRoomIndex}{i:XPGained}
OnIntercept(In["PetExperience"], e => {
try {
int petId = e.Packet.ReadInt(); // Pet ID
int roomIndex = e.Packet.ReadInt(); // Pet Room Index
int xpGained = e.Packet.ReadInt(); // XP gewonnen!
lastXPGained = xpGained;
if (waitingForResponse) gotXP = true;
Log($"[XP] +{xpGained} XP erhalten!");
} catch {
// Fallback wenn Packet-Format anders ist
lastXPGained = 10; // Annahme: 10 XP
if (waitingForResponse) gotXP = true;
}
});
// Pet info - KORRIGIERTES Paket-Format!
// {i:ID}{s:Name}{i:Level}{i:MaxLevel}{i:XP}{i:MaxXP}{i:ENERGY}{i:MaxEnergy}{i:Hunger}{i:MaxHunger}...
string lastPetInfoName = ""; // Speichere Pet-Name aus PetInfo
int lastPetInfoId = -1; // Speichere Pet-ID aus PetInfo
OnIntercept(In["PetInfo"], e => {
try {
int id = e.Packet.ReadInt(); // 0: Pet ID
string name = e.Packet.ReadString(); // 1: Name
lastPetInfoName = name; // Speichern für Fallback!
lastPetInfoId = id; // ID auch speichern
int receivedLevel = e.Packet.ReadInt(); // 2: Level
petLevel = receivedLevel;
// FÜR LEVEL-SCAN: Speichere Level in temporärer Variable!
scanTempLevel = receivedLevel;
scanGotResponse = true;
int maxLevel = e.Packet.ReadInt(); // 3: Max Level (20)
petXP = e.Packet.ReadInt(); // 4: XP
petMaxXP = e.Packet.ReadInt(); // 5: Max XP
// Energy und MaxEnergy - NICHT TAUSCHEN!
// In Habbo kann Energy HÖHER als MaxEnergy sein (durch Items/Buffs)!
// Das Packet-Format ist IMMER: Energy zuerst, dann MaxEnergy
petEnergy = e.Packet.ReadInt(); // 6: Energy (aktuell)
petMaxEnergy = e.Packet.ReadInt(); // 7: MaxEnergy (Limit)
petHunger = e.Packet.ReadInt(); // 8: Hunger
int maxHunger = e.Packet.ReadInt(); // 9: Max Hunger (100)
// PetInfo Format (nach Hunger):
// 10: Respect (int)
// 11: OwnerId (int)
// 12: Age (int)
// 13: OwnerName (string)
// 14: PetType (int) <-- DAS BRAUCHEN WIR!
int respect = e.Packet.ReadInt(); // 10: Respect
int ownerId = e.Packet.ReadInt(); // 11: Owner ID
int age = e.Packet.ReadInt(); // 12: Age (Tage)
string ownerName = e.Packet.ReadString(); // 13: Owner Name
petType = e.Packet.ReadInt(); // 14: PET TYPE!
// Berechne Prozent
int energyPercent = petMaxEnergy > 0 ? (petEnergy * 100 / petMaxEnergy) : 0;
// Pet-Typ Namen fuer Debug
string[] petTypeNames = { "Dog", "Cat", "Crocodile", "Terrier", "Bear", "Pig", "Lion", "Horse", "Spider", "Turtle", "Chicken" };
string petTypeName = petType >= 0 && petType < petTypeNames.Length ? petTypeNames[petType] : $"Unknown({petType})";
Log($"[PetInfo] {name} Lv{petLevel} | Type: {petTypeName} | Energy: {petEnergy}/{petMaxEnergy} ({energyPercent}%)");
// Wenn petName noch leer ist, setze ihn hier!
if (string.IsNullOrEmpty(petName)) {
petName = name;
Log($"[PetInfo] Pet-Name gesetzt: {petName}");
}
// SOFORT Care-Modus pruefen wenn Energie kritisch niedrig!
if (energyPercent <= 5 && !inCareMode) {
Log($"[PetInfo] !!! KRITISCH NIEDRIGE ENERGIE: {energyPercent}% !!!");
}
} catch (Exception ex) {
Log($"[PetInfo] FEHLER: {ex.Message}");
}
});
// Pet selection - Pet wurde angeklickt
OnIntercept(Out["GetPetInfo"], e => {
int clickedPetId = e.Packet.ReadInt();
Log($"[SELECT] Pet angeklickt mit ID: {clickedPetId}");
// Wenn bereits ein Pet trainiert wird und ein ANDERES angeklickt wurde
if (selectedPetId != -1 && clickedPetId != selectedPetId) {
Log($"[SWITCH] Neues Pet angeklickt! Wechsel von ID {selectedPetId} zu {clickedPetId}");
petSwitchRequested = true;
newPetId = clickedPetId;
} else {
selectedPetId = clickedPetId;
}
// PetCommands Packet sollte kurz danach kommen!
petCommandsReceived = false; // Reset fuer neues Pet
});
// ============ PET COMMANDS PACKET ============
// Laedt die freigeschalteten Commands vom Server
// PACKET FORMAT:
// {i:PetID}
// {i:AnzahlSichtbareCommands}{i:cmd1}{i:cmd2}... <- IGNORIEREN (alle sichtbaren)
// {i:AnzahlGelernteCommands}{i:cmd1}{i:cmd2}... <- NUR DIESE LESEN!
OnIntercept(In["PetCommands"], e => {
try {
int petId = e.Packet.ReadInt();
Log($"[PetCommands] Empfangen fuer Pet ID: {petId}");
petCommandIds.Clear();
unlockedCommands.Clear();
// ERSTE LISTE = Alle sichtbaren Commands (IGNORIEREN!)
int allCommandsCount = e.Packet.ReadInt();
Log($"[PetCommands] Sichtbare Befehle: {allCommandsCount} (ueberspringe...)");
// Ueberspringe die erste Liste komplett
for (int i = 0; i < allCommandsCount; i++) {
e.Packet.ReadInt(); // Lesen und wegwerfen
}
// ZWEITE LISTE = Gelernte Commands (NUR DIESE NUTZEN!)
int learnedCount = e.Packet.ReadInt();
Log($"[PetCommands] GELERNTE Befehle: {learnedCount}");
// Lese die gelernten Commands
for (int i = 0; i < learnedCount; i++) {
int cmdId = e.Packet.ReadInt();
if (commandIdToName.ContainsKey(cmdId) && !petCommandIds.Contains(cmdId)) {
petCommandIds.Add(cmdId);
string cmdName = commandIdToName[cmdId];
// Nur Training-Commands hinzufuegen (nicht eat/drink/nest/silent)
bool excluded = false;
foreach (var ex in excludeFromTraining) {
if (cmdName == ex) {
excluded = true;
break;
}
}
if (!excluded) {
unlockedCommands.Add(cmdName);
}
Log($"[PetCommands] + {cmdId} = {cmdName}" + (excluded ? " (excluded)" : ""));
} else if (!commandIdToName.ContainsKey(cmdId)) {
Log($"[PetCommands] ? {cmdId} = UNBEKANNT (ignoriert)");
}
}
Log($"[PetCommands] {unlockedCommands.Count} Training-Commands geladen!");
Log($"[PetCommands] Befehle: {string.Join(", ", unlockedCommands)}");
petCommandsReceived = true;
cmdIndex = 0;
BotMessage($"Neue Commands geladen! {unlockedCommands.Count} Befehle");
} catch (Exception ex) {
Log($"[PetCommands] FEHLER: {ex.Message}");
}
});
// Track pet movement
OnIntercept(In["UserUpdate"], e => {
try {
string data = e.Packet.ToString();
if (data.Contains("mv ") || data.Contains("/mv ")) {
petIsMoving = true;
petIsBusy = true;
lastPetUpdate = DateTime.Now;
} else if (data.Contains("//eat") || data.Contains("//scr") ||
data.Contains("//snf") || data.Contains("//lay") ||
data.Contains("//sit") || data.Contains("//beg") ||
data.Contains("//ded") || data.Contains("//gst")) {
petIsMoving = false;
petIsBusy = true;
lastPetUpdate = DateTime.Now;
} else if (data.Contains("\"//\"")) {
petIsMoving = false;
petIsBusy = false;
lastPetUpdate = DateTime.Now;
}
} catch { }
});
// ============ HELPERS ============
void SendCmd(string cmd) {
Send(Out["Chat"], $"{petName} {cmd}", 0, -1);
}
void ResetFlags() {
gotXP = false;
gotRefusal = false;
}
void GetPetInfo() {
// DIREKT selectedPetId benutzen - nicht nach Name suchen!
// WICHTIG: Send erwartet int, also casten!
if (selectedPetId > 0) {
Send(Out["GetPetInfo"], (int)selectedPetId);
Delay(300); // Kurz warten auf Antwort
}
}
void WaitForPetFinish() {
Delay(2500);
}
// ============ ZUM PET HINLAUFEN ============
// WICHTIG: Bei mehreren Pets mit gleichem Namen reagiert das NÄCHSTE Pet!
// Diese Funktion läuft zum Pet damit es das nächste ist.
void WalkToPet(long petId) {
foreach (var pet in Pets) {
if (pet.Id == petId) {
int petX = pet.Location.X;
int petY = pet.Location.Y;
int myX = Self.Location.X;
int myY = Self.Location.Y;
int dist = Math.Abs(petX - myX) + Math.Abs(petY - myY);
if (dist <= 1) {
Log($"[WALK] Bin schon neben {pet.Name} (Distanz: {dist})");
return;
}
// Finde Tile neben dem Pet
int targetX = petX;
int targetY = petY;
if (myX < petX) targetX = petX - 1;
else if (myX > petX) targetX = petX + 1;
else if (myY < petY) targetY = petY - 1;
else if (myY > petY) targetY = petY + 1;
Log($"[WALK] Laufe zu {pet.Name} auf ({targetX}, {targetY})...");
Log($"[WALK] Pet: ({petX},{petY}), Ich: ({myX},{myY})");
try {
Send(Out["Walk"], targetX, targetY);
} catch {
try {
Send(Out["MoveAvatar"], targetX, targetY);
} catch {
Log("[WALK] Walk-Packet fehlgeschlagen!");
return;
}
}
// Warte bis angekommen (max 4 Sek)
for (int i = 0; i < 40 && Run; i++) {
Delay(100);
int newDist = Math.Abs(petX - Self.Location.X) + Math.Abs(petY - Self.Location.Y);
if (newDist <= 1) {
Log($"[WALK] Angekommen neben {pet.Name}!");
return;
}
}
Log("[WALK] Timeout - hoffentlich nah genug!");
return;
}
}
Log($"[WALK] Pet mit ID {petId} nicht gefunden!");
}
// EINFACHE, ZUVERLÄSSIGE Wartezeit nach eat/drink
// Das Pet braucht ca. 6-8 Sekunden um zum Essen zu laufen und zu essen
void WaitForPetToEat() {
Log("[WAIT] Warte 8 Sekunden bis Pet fertig...");
// Feste Wartezeit - das ist ZUVERLÄSSIGER als komplizierte Logik!
for (int i = 0; i < 8 && Run; i++) {
Delay(1000);
}
Log("[WAIT] Pet sollte jetzt fertig sein!");
}
// Tile scanning - KOMPLETT NEU!
// Sammle ALLE belegten Tiles (Items + Users)
void ScanOccupiedTiles() {
occupiedTiles.Clear();
foreach (var item in FloorItems) {
var pos = (item.Location.X, item.Location.Y);
if (!occupiedTiles.Contains(pos)) occupiedTiles.Add(pos);
}
foreach (var user in Users) {
var pos = (user.Location.X, user.Location.Y);
if (!occupiedTiles.Contains(pos)) occupiedTiles.Add(pos);
}
Log($"[TILES] {occupiedTiles.Count} belegte Tiles gescannt");
}
// NEUE METHODE: Finde ALLE bekannten Tiles im Raum
// Basierend auf FloorItems, Users, und der Heightmap
List<(int x, int y)> GetAllKnownTiles() {
var knownTiles = new HashSet<(int x, int y)>();
// 1. Alle FloorItem-Positionen sind gueltige Tiles
foreach (var item in FloorItems) {
knownTiles.Add((item.Location.X, item.Location.Y));
}
// 2. Alle User-Positionen sind gueltige Tiles
foreach (var user in Users) {
knownTiles.Add((user.Location.X, user.Location.Y));
}
// 3. Self Position
knownTiles.Add((Self.Location.X, Self.Location.Y));
Log($"[TILES] {knownTiles.Count} bekannte Tiles aus FloorItems/Users");
// 4. Erweitere um Nachbar-Tiles (1 Tile Radius um bekannte Tiles)
// Diese sind sehr wahrscheinlich auch gueltig!
var expandedTiles = new HashSet<(int x, int y)>(knownTiles);
foreach (var tile in knownTiles) {
for (int dx = -1; dx <= 1; dx++) {
for (int dy = -1; dy <= 1; dy++) {
int nx = tile.x + dx;
int ny = tile.y + dy;
if (nx >= 0 && ny >= 0) {
expandedTiles.Add((nx, ny));
}
}
}
}
Log($"[TILES] {expandedTiles.Count} Tiles nach Expansion (1 Tile Radius)");
// 5. Filter mit Heightmap wenn vorhanden
if (!string.IsNullOrEmpty(roomHeightMap)) {
var validTiles = new List<(int x, int y)>();
var rows = roomHeightMap.Split('\r');
foreach (var tile in expandedTiles) {
if (tile.y < rows.Length && tile.x < rows[tile.y].Length) {
char c = rows[tile.y][tile.x];
// Gueltig: 0-9 (Hoehen), a-z (hohe Hoehen), NICHT 'x' (blockiert)
if (c != 'x' && c != 'X') {
validTiles.Add(tile);
}
}
}
Log($"[TILES] {validTiles.Count} gueltige Tiles nach Heightmap-Filter");
return validTiles;
}
return expandedTiles.ToList();
}
// Finde freie Tiles (bekannte Tiles minus belegte)
List<(int x, int y)> FindAllFreeTiles() {
ScanOccupiedTiles(); // Update occupiedTiles
var allTiles = GetAllKnownTiles();
var freeTiles = new List<(int x, int y)>();
foreach (var tile in allTiles) {
if (!occupiedTiles.Contains(tile)) {
freeTiles.Add(tile);
}
}
Log($"[TILES] {freeTiles.Count} freie Tiles gefunden (von {allTiles.Count} bekannten)");
return freeTiles;
}
// ============ STACK MAGIC TILE FUNKTIONEN ============
// Stack Magic Tile Identifier (BC Item)
string[] stackMagicIdentifiers = { "bc_tile", "stack_magic", "tile_stackmagic", "stackmagic" };
string[] stackMagicNames = { "Stack Magic Tile", "BC Tile", "Magic Tile", "Builders Club Tile" };
// Finde Stack Magic Tile im Inventar
(long id, string name, bool found) FindStackMagicInInventory() {
EnsureInventory();
foreach (var item in Inventory) {
string name = item.GetName();
string identifier = "";
try { identifier = item.GetInfo().Identifier; } catch { }
string lowerName = name.ToLower();
string lowerIdent = identifier.ToLower();
// Check identifier
foreach (var id in stackMagicIdentifiers) {
if (lowerIdent.Contains(id)) {
Log($"[STACK] Gefunden im Inventar: '{name}' [ID: {identifier}]");
return (item.Id, name, true);
}
}
// Check name
foreach (var stackName in stackMagicNames) {
if (lowerName.Contains(stackName.ToLower())) {
Log($"[STACK] Gefunden im Inventar: '{name}'");
return (item.Id, name, true);
}
}
}
return (0, "", false);
}
// Finde Stack Magic Tile im Raum (um es spaeter aufzuheben)
(long id, int x, int y, bool found) FindStackMagicInRoom() {
foreach (var item in FloorItems) {
string name = item.GetName();
string identifier = "";
try { identifier = item.GetInfo().Identifier; } catch { }
string lowerName = name.ToLower();
string lowerIdent = identifier.ToLower();
// Check identifier
foreach (var id in stackMagicIdentifiers) {
if (lowerIdent.Contains(id)) {
return (item.Id, item.Location.X, item.Location.Y, true);
}
}
// Check name
foreach (var stackName in stackMagicNames) {
if (lowerName.Contains(stackName.ToLower())) {
return (item.Id, item.Location.X, item.Location.Y, true);
}
}
}
return (0, 0, 0, false);
}
// Platziere Stack Magic Tile aus Inventar
bool PlaceStackMagicTile(int x, int y) {
var stackMagic = FindStackMagicInInventory();
if (!stackMagic.found) {
Log("[STACK] Kein Stack Magic Tile im Inventar!");
BotMessage("Kein Stack Magic Tile im Inventar!");
// TODO: Aus BC Katalog kaufen
return false;
}
Log($"[STACK] Platziere '{stackMagic.name}' auf ({x}, {y})...");
Send(Out["PlaceObject"], $"-{stackMagic.id} {x} {y} 0");
Delay(800);
// Verifiziere
var placed = FindStackMagicInRoom();
if (placed.found && placed.x == x && placed.y == y) {
Log($"[STACK] Stack Magic Tile erfolgreich platziert auf ({x}, {y})!");
return true;
}
// Vielleicht woanders platziert?
if (placed.found) {
Log($"[STACK] Stack Magic Tile platziert auf ({placed.x}, {placed.y}) (nicht exakt)");
return true;
}
Log("[STACK] FEHLER: Stack Magic Tile konnte nicht platziert werden!");
return false;
}
// Nimm Stack Magic Tile wieder auf (Pickup)
bool PickupStackMagicTile() {
var stackMagic = FindStackMagicInRoom();
if (!stackMagic.found) {
Log("[STACK] Kein Stack Magic Tile im Raum zum Aufnehmen!");
return false;
}
Log($"[STACK] Nehme Stack Magic Tile auf (ID: {stackMagic.id})...");
Send(Out["PickupObject"], 2, (int)stackMagic.id); // 2 = Floor item
Delay(500);
// Verifiziere
var stillThere = FindStackMagicInRoom();
if (!stillThere.found) {
Log("[STACK] Stack Magic Tile erfolgreich aufgenommen!");
return true;
}
Log("[STACK] WARNUNG: Stack Magic Tile ist noch im Raum!");
return false;
}
// Food detection - Sucht nach bekannten Pet Food Namen UND Class Names!
// WICHTIG: Katalog zeigt class names wie "petfood5_dt", "A0 petfood1_dt"
bool IsPetFood(string itemName) {
if (string.IsNullOrEmpty(itemName)) return false;
// Normalisiere den Namen: lowercase, trim, remove extra spaces
string lower = itemName.ToLower().Trim();
// AUSSCHLUSS ZUERST: Moebel und Kleidung die KEIN Pet Food sind!
if (lower.Contains("street food") || lower.Contains("streetfood") ||
lower.Contains("bite") || // "Carrot Bite" ist Kleidung!
lower.Contains("costume") ||
lower.Contains("outfit") ||
lower.Contains("clothing") ||
lower.Contains("cabinet")) { // "Cabinet" matched sonst wegen "cab"
return false;
}
// CHECK 1: Class name patterns aus dem Katalog!
// Diese sind ZUVERLAESSIG weil sie direkt vom Server kommen
if (lower.Contains("petfood") || lower.Contains("pet_food") ||
lower.Contains("petgoodie") || lower.Contains("pet_goodie")) {
return true;
}
// CHECK 2: EXAKTER Match fuer bekannte Food-Namen
// KOMPLETTE LISTE aller 33 Pet Food Items!
foreach (var foodName in petFoodExactNames) {
if (lower == foodName.ToLower()) {
return true;
}
}
// CHECK 3: Class names (partial match) - fuer Identifier
foreach (var className in petFoodClassNames) {
if (lower.Contains(className)) {
return true;
}
}
return false;
}
// Prueft ob ein Identifier Pet Food ist
bool IsPetFoodByIdentifier(string identifier) {
if (string.IsNullOrEmpty(identifier)) return false;
string lower = identifier.ToLower();
return lower.Contains("petfood") || lower.Contains("pet_food") ||
lower.Contains("petgoodie") || lower.Contains("pet_goodie");
}
// Hilfsfunktion: Prueft Identifier und Name fuer Inventory Items
bool CheckInventoryItemIsFood(string identifier, string name) {
if (!string.IsNullOrEmpty(identifier)) {
if (IsPetFoodByIdentifier(identifier)) {
return true;
}
}
return IsPetFood(name);
}
// Water detection - uses water arrays from catalog
bool IsPetWater(string itemName) {
string lower = itemName.ToLower();
// Check display names (partial match)
foreach (var waterName in petWaterDisplayNames) {
if (lower.Contains(waterName.ToLower())) {
return true;
}
}
// Check class names
foreach (var className in petWaterClassNames) {
if (lower.Contains(className)) {
return true;
}
}
return false;
}
// Find water bowl using the IsPetWater function
(long id, bool empty, bool found) FindWaterBowl() {
foreach (var item in FloorItems) {
if (IsPetWater(item.GetName())) {
return (item.Id, item.State == 0, true);
}
}
return (0, false, false);
}
// Find ALL water bowls in the room
List<(long id, string name, bool empty, int state)> FindAllWaterBowls() {
var bowls = new List<(long id, string name, bool empty, int state)>();
foreach (var item in FloorItems) {
if (IsPetWater(item.GetName())) {
// DEBUG: Zeige den State-Wert
Log($"[WATER-DEBUG] '{item.GetName()}' ID:{item.Id} State:{item.State}");
bowls.Add((item.Id, item.GetName(), item.State == 0, item.State));
}
}
return bowls;
}
// Refill a single water bowl by ID
// WICHTIG: Habbo muss zum Napf LAUFEN - das dauert!
void RefillWaterBowlById(long id, string name) {
Log($"[WATER] Fuelle '{name}' nach (ID: {id})...");
Log("[WATER] Warte bis Habbo zum Napf gelaufen ist...");
Send(Out["UseFurniture"], (int)id, 0);
// LANGE WARTEN! Habbo muss hinlaufen + Animation abspielen
// Bei weit entfernten Näpfen kann das 5-8 Sekunden dauern!
for (int i = 0; i < 6 && Run; i++) {
Delay(1000); // 6 Sekunden total pro Napf
}
Log($"[WATER] '{name}' sollte jetzt voll sein!");
}
// Refill ALL water bowls in the room
// WICHTIG: Füllt ALLE Näpfe auf weil State-Erkennung unzuverlässig ist!
int RefillAllEmptyWaterBowls() {
var bowls = FindAllWaterBowls();
int refilled = 0;
// DEBUG: Zeige alle gefundenen Näpfe
Log($"[WATER] === WASSERNAPF-SCAN ===");
foreach (var bowl in bowls) {
Log($"[WATER] Napf: '{bowl.name}' | State: {bowl.state}");
}
Log($"[WATER] Total: {bowls.Count} Näpfe - ALLE werden aufgefüllt!");
Log($"[WATER] =======================");
if (bowls.Count == 0) {
Log("[WATER] Keine Wassernaepfe gefunden!");
return 0;
}
BotMessage($"Fuelle ALLE {bowls.Count} Wassernapf/e auf...");
// ALLE Näpfe auffüllen, nicht nur die mit State 0!
foreach (var bowl in bowls) {
if (!Run) break;
refilled++;
Log($"[WATER] === Napf {refilled}/{bowls.Count}: {bowl.name} ===");
// Sende Befehl zum Auffüllen
Send(Out["UseFurniture"], (int)bowl.id, 0);
// WARTE bis Habbo hingelaufen ist und aufgefüllt hat!
Log("[WATER] Warte 4 Sekunden...");
for (int i = 0; i < 4 && Run; i++) {
Delay(1000);
}
Log($"[WATER] Napf {refilled}/{bowls.Count} erledigt!");
// Kurze Extra-Pause zwischen den Näpfen
if (refilled < bowls.Count) {
Delay(1000);
}
}
if (refilled > 0) {
Log($"[WATER] === ALLE {refilled} NAEPFE AUFGEFUELLT! ===");
BotMessage($"Fertig! {refilled} Wassernapf/e aufgefuellt!");
// Habbo soll weggehen vom Wassernapf - finde freies Tile
var freeTiles = FindAllFreeTiles();
Log($"[WATER] Freie Tiles gefunden: {freeTiles.Count}");
if (freeTiles.Count > 0) {
// Nimm ein zufälliges freies Tile
var random = new Random();
var targetTile = freeTiles[random.Next(freeTiles.Count)];
Log($"[WATER] Gehe weg zu freiem Tile ({targetTile.x}, {targetTile.y})...");
// xabbo: Walk Packet senden
try {
Send(Out["Walk"], targetTile.x, targetTile.y);
} catch {
// Fallback: MoveAvatar probieren
try {
Send(Out["MoveAvatar"], targetTile.x, targetTile.y);
} catch {
Log("[WATER] WARNUNG: Konnte nicht weggehen!");
}
}
Delay(2000); // Warte bis Habbo angekommen ist
} else {
Log("[WATER] Keine freien Tiles gefunden!");
}
Log("[WATER] Fertig - weiter geht's!");
}
return refilled;
}
// Check if any water bowl is empty
bool HasEmptyWaterBowl() {
var bowls = FindAllWaterBowls();
foreach (var bowl in bowls) {
if (bowl.empty) return true;
}
return false;
}
void RefillWaterBowl() {
// Legacy function - now uses RefillAllEmptyWaterBowls
RefillAllEmptyWaterBowls();
}
// List all pet food in room
void ListFoodInRoom() {
Log("");
Log("=== PET FOOD IM RAUM ===");
int count = 0;
foreach (var item in FloorItems) {
string identifier = "";
try { identifier = item.GetInfo().Identifier; } catch { }
if (CheckFloorItemIsFood(identifier, item.GetName())) {
count++;
Log($" [{count}] {item.GetName()} [ID: {identifier}] (Position: {item.Location.X},{item.Location.Y})");
}
}
if (count == 0) {
Log(" Kein Pet Food im Raum gefunden!");
} else {
Log($" TOTAL: {count} Pet Food Items");
}
Log("========================");
Log("");
}
// List all pet food in inventory
void ListFoodInInventory() {
EnsureInventory();
Log("");
Log("=== PET FOOD IM INVENTAR ===");
int count = 0;
foreach (var item in Inventory) {
string identifier = "";
try { identifier = item.GetInfo().Identifier; } catch { }
if (CheckInventoryItemIsFood(identifier, item.GetName())) {
count++;
Log($" [{count}] {item.GetName()} [ID: {identifier}]");
}
}
if (count == 0) {
Log(" Kein Pet Food im Inventar gefunden!");
} else {
Log($" TOTAL: {count} Pet Food Items");
}
Log("============================");
Log("");
}
// DEBUG: Liste ALLE Inventar-Items (erste 50)
void DebugListAllInventoryItems() {
EnsureInventory();
Delay(1500); // Extra warten!
Log("");
Log("###############################################");
Log("### DEBUG: ALLE INVENTAR-ITEMS (erste 50) ###");
Log("###############################################");
int count = 0;
int foodCount = 0;
foreach (var item in Inventory) {
count++;
string name = item.GetName();
string identifier = "";
try { identifier = item.GetInfo().Identifier; } catch { identifier = "?"; }
bool isFood = CheckInventoryItemIsFood(identifier, name);
if (isFood) {
foodCount++;
Log($" [{count}] >>> {name} <<< [ID: {identifier}] [FOOD!]");
} else if (count <= 50) {
Log($" [{count}] {name} [ID: {identifier}]");
}
if (count == 50 && !isFood) {
Log(" ... (mehr Items vorhanden, zeige nur erste 50)");
}
}
Log("");
Log($" TOTAL: {count} Items im Inventar");
Log($" DAVON FOOD: {foodCount} Items");
Log("###############################################");
Log("");
}
// List all pet water in room
void ListWaterInRoom() {
Log("");
Log("=== PET WATER IM RAUM ===");
int count = 0;
foreach (var item in FloorItems) {
if (IsPetWater(item.GetName())) {
count++;
string state = item.State == 0 ? " [LEER!]" : " [voll]";
Log($" [{count}] {item.GetName()} (Position: {item.Location.X},{item.Location.Y}){state}");
}
}
if (count == 0) {
Log(" Kein Water Bowl im Raum gefunden!");
} else {
Log($" TOTAL: {count} Water Items");
}
Log("=========================");
Log("");
}
// List all pet water in inventory
void ListWaterInInventory() {
EnsureInventory();
Log("");
Log("=== PET WATER IM INVENTAR ===");
int count = 0;
foreach (var item in Inventory) {
if (IsPetWater(item.GetName())) {
count++;
Log($" [{count}] {item.GetName()}");
}
}
if (count == 0) {
Log(" Kein Water Bowl im Inventar gefunden!");
} else {
Log($" TOTAL: {count} Water Items");
}
Log("=============================");
Log("");
}
// List all pet food (room + inventory)
void ListAllFood() {
ListFoodInRoom();
ListFoodInInventory();
}
// List all pet water (room + inventory)
void ListAllWater() {
ListWaterInRoom();
ListWaterInInventory();
}
// List EVERYTHING (food + water, room + inventory)
void ListAllPetItems() {
Log("");
Log("###############################################");
Log("### PET ITEMS UEBERSICHT ###");
Log("###############################################");
ListFoodInRoom();
ListWaterInRoom();
ListFoodInInventory();
ListWaterInInventory();
Log("###############################################");
Log("");
}
// Prueft ob ein FloorItem Pet Food ist - mit Identifier!
bool CheckFloorItemIsFood(string identifier, string name) {
// Pruefe zuerst Identifier
if (!string.IsNullOrEmpty(identifier)) {
if (IsPetFoodByIdentifier(identifier)) {
return true;
}
}
// Fallback: Name pruefen
return IsPetFood(name);
}
bool HasFoodInRoom() {
foreach (var item in FloorItems) {
string identifier = "";
string name = item.GetName();
try { identifier = item.GetInfo().Identifier; } catch { }
if (CheckFloorItemIsFood(identifier, name)) {
// DEBUG: Zeige gefundenes Essen
Log($"[FOOD-CHECK] Gefunden: '{name}' [ID: {identifier}]");
return true;
}
}
return false;
}
// Zaehle Essen im Raum (fuer Debug)
int CountFoodInRoom() {
int count = 0;
foreach (var item in FloorItems) {
string identifier = "";
try { identifier = item.GetInfo().Identifier; } catch { }
if (CheckFloorItemIsFood(identifier, item.GetName())) {
count++;
}
}
return count;
}
// Zaehle Essen im Inventar (fuer Debug)
int CountFoodInInventory() {
EnsureInventory();
int count = 0;
foreach (var item in Inventory) {
string identifier = "";
try { identifier = item.GetInfo().Identifier; } catch { }
if (CheckInventoryItemIsFood(identifier, item.GetName())) {
count++;
}
}
return count;
}
bool PlaceFoodFromInventory() {
Log("");
Log("###############################################");
Log("### STACK MAGIC TILE - ALLE ESSEN PLATZIEREN ###");
Log("###############################################");
// Erst Inventar laden
Log("[FOOD] Lade Inventar...");
EnsureInventory();
Delay(2000);
// 1. Sammle ALLE Food-Items aus dem Inventar
var foodItems = new List<(long id, string name)>();
foreach (var item in Inventory) {
string itemName = item.GetName();
string lowerName = itemName.ToLower();
// AUSSCHLUSS: Kleidungsstuecke und Stack Magic Tile!
if (lowerName.Contains("bite") ||
lowerName.Contains("costume") ||
lowerName.Contains("outfit") ||
lowerName.Contains("clothing") ||
lowerName.Contains("cabinet") ||
lowerName.Contains("stack magic") ||
lowerName.Contains("bc tile")) {
continue;
}
// Pruefe ob es Food ist
string identifier = "";
try { identifier = item.GetInfo().Identifier; } catch { }
string lowerIdent = identifier.ToLower();
// Skip Stack Magic Tile by identifier
bool isStackMagic = false;
foreach (var stackId in stackMagicIdentifiers) {
if (lowerIdent.Contains(stackId)) {
isStackMagic = true;
break;
}
}
if (isStackMagic) continue;
bool isFood = false;
if (!string.IsNullOrEmpty(identifier)) {
if (lowerIdent.Contains("petfood") || lowerIdent.Contains("petgoodie")) {
isFood = true;
}
}
if (!isFood) {
foreach (var foodName in petFoodExactNames) {
if (lowerName == foodName.ToLower()) {
isFood = true;
break;
}
}
}
// V74: Filter nach Pet-Typ!
// Überspringe Essen das nicht für dieses Pet geeignet ist
if (isFood && petType >= 0) {
if (!IsFoodForPetType(itemName, petType)) {
Log($"[FOOD] SKIP: '{itemName}' nicht für Pet-Typ {petType} geeignet!");
isFood = false;
}
}
if (isFood) {
foodItems.Add((item.Id, itemName));
}
}
Log($"[FOOD] {foodItems.Count} Food-Items im Inventar gefunden (gefiltert für Pet-Typ {petType})!");
if (foodItems.Count == 0) {
Log("[FOOD] KEIN Pet Food im Inventar!");
BotMessage("KEIN ESSEN IM INVENTAR! Bitte im Katalog kaufen!");
return false;
}
// 2. Finde ein freies, zugaengliches Tile
var freeTiles = FindAllFreeTiles();
if (freeTiles.Count == 0) {
Log("[FOOD] FEHLER: Keine freien Tiles im Raum!");
BotMessage("Keine freien Tiles im Raum!");
return false;
}
// Waehle ein zufaelliges freies Tile (nah am Spieler bevorzugt)
int playerX = Self.Location.X;
int playerY = Self.Location.Y;
// Sortiere nach Distanz zum Spieler
freeTiles = freeTiles.OrderBy(t => Math.Abs(t.x - playerX) + Math.Abs(t.y - playerY)).ToList();
// Nimm eins der naechsten 5 Tiles
var random = new Random();
int tileChoice = Math.Min(random.Next(5), freeTiles.Count - 1);
var targetTile = freeTiles[tileChoice];
Log($"[FOOD] Ziel-Tile: ({targetTile.x}, {targetTile.y}) - nah am Spieler");
// 3. Pruefe ob Stack Magic Tile bereits im Raum ist
var existingStackMagic = FindStackMagicInRoom();
bool needToPlaceStackMagic = !existingStackMagic.found;
bool needToPickupStackMagic = false;
if (existingStackMagic.found) {
Log($"[STACK] Stack Magic Tile bereits im Raum auf ({existingStackMagic.x}, {existingStackMagic.y})");
targetTile = (existingStackMagic.x, existingStackMagic.y);
} else {
// 4. Platziere Stack Magic Tile
Log("[STACK] Platziere Stack Magic Tile...");
var stackMagic = FindStackMagicInInventory();
if (!stackMagic.found) {
Log("[STACK] KEIN Stack Magic Tile im Inventar!");
BotMessage("Kein Stack Magic Tile! Bitte aus BC Katalog holen!");
// Fallback: Normale Platzierung ohne Stack Magic
Log("[FOOD] Fallback: Platziere ohne Stack Magic Tile...");
return PlaceFoodWithoutStackMagic(foodItems);
}
// Platziere Stack Magic Tile
Log($"[STACK] Platziere '{stackMagic.name}' auf ({targetTile.x}, {targetTile.y})...");
Send(Out["PlaceObject"], $"-{stackMagic.id} {targetTile.x} {targetTile.y} 0");
Delay(800);
// Verifiziere
var placedStack = FindStackMagicInRoom();
if (!placedStack.found) {
Log("[STACK] FEHLER: Stack Magic Tile konnte nicht platziert werden!");
return PlaceFoodWithoutStackMagic(foodItems);
}
// Update target tile to where the stack magic actually was placed
targetTile = (placedStack.x, placedStack.y);
needToPickupStackMagic = true;
Log($"[STACK] Stack Magic Tile platziert auf ({targetTile.x}, {targetTile.y})!");
}
// 5. Platziere ALLE Food-Items auf das gleiche Tile!
Log("");
Log($"[FOOD] === PLATZIERE {foodItems.Count} ESSEN AUF ({targetTile.x}, {targetTile.y}) ===");
int placed = 0;
foreach (var foodItem in foodItems) {
if (!Run) break;
Log($"[FOOD] {placed + 1}/{foodItems.Count}: '{foodItem.name}'");
Send(Out["PlaceObject"], $"-{foodItem.id} {targetTile.x} {targetTile.y} 0");
Delay(300); // Schnell da Stack Magic Tile alles erlaubt!
placed++;
}
Log($"[FOOD] === {placed} ESSEN PLATZIERT! ===");
BotMessage($"{placed}x Essen auf einem Tile gestapelt!");
// 6. Stack Magic Tile wieder aufnehmen
if (needToPickupStackMagic) {
Log("[STACK] Nehme Stack Magic Tile wieder auf...");
Delay(500);
var stackToPickup = FindStackMagicInRoom();
if (stackToPickup.found) {
Send(Out["PickupObject"], 2, (int)stackToPickup.id);
Delay(500);
var stillThere = FindStackMagicInRoom();
if (!stillThere.found) {
Log("[STACK] Stack Magic Tile aufgenommen!");
} else {
Log("[STACK] WARNUNG: Stack Magic Tile konnte nicht aufgenommen werden!");
}
}
}
Log("###############################################");
Log("");
return placed > 0;
}
// Fallback-Funktion ohne Stack Magic Tile (alte Methode)
bool PlaceFoodWithoutStackMagic(List<(long id, string name)> foodItems) {
Log("[FOOD] Fallback: Platziere auf verschiedenen Tiles...");
int targetAmount = Math.Min(20, foodItems.Count);
var freeTiles = FindAllFreeTiles();
var random = new Random();
int placed = 0;
int tileIndex = 0;
freeTiles = freeTiles.OrderBy(x => random.Next()).ToList();
foreach (var foodItem in foodItems) {
if (placed >= targetAmount) break;
if (tileIndex >= freeTiles.Count) break;
var tile = freeTiles[tileIndex];
tileIndex++;
Send(Out["PlaceObject"], $"-{foodItem.id} {tile.x} {tile.y} 0");
Delay(400);
placed++;
}
if (placed > 0) {
BotMessage($"{placed}x Essen platziert (verteilt)!");
return true;
}
return false;
}
// Quick scan - pruefen ob Essen/Wasser vorhanden und fixen
void QuickScan() {
// DEAKTIVIERT: Water refill
// if (HasEmptyWaterBowl()) {
// Log("[SCAN] Leerer Wassernapf gefunden!");
// RefillAllEmptyWaterBowls();
// }
// Check food - WICHTIG!
bool hasFood = HasFoodInRoom();
if (!hasFood) {
Log("[SCAN] KEIN ESSEN IM RAUM! Platziere aus Inventar...");
BotMessage("Kein Essen im Raum - platziere...");
bool placed = PlaceFoodFromInventory();
if (!placed) {
Log("[SCAN] WARNUNG: Konnte kein Essen platzieren!");
}
}
}
// Full room scan at startup - fix everything
void StartupRoomScan() {
Log("");
Log("###############################################");
Log("### RAUM-SCAN BEIM START ###");
Log("###############################################");
// WICHTIG: Kurz warten damit FloorItems geladen sind!
Log("[STARTUP] Warte auf Raum-Daten...");
Delay(1500);
// DEAKTIVIERT: Water bowl check
// Log("[STARTUP] Pruefe Wassernaepfe...");
// var bowls = FindAllWaterBowls();
// ...
// 2. Check food
Log("[STARTUP] Pruefe Essen...");
int foodInRoom = CountFoodInRoom();
int foodInInventory = CountFoodInInventory();
Log($"[STARTUP] Essen im Raum: {foodInRoom}");
Log($"[STARTUP] Essen im Inventar: {foodInInventory}");
if (foodInRoom == 0) {
Log("[STARTUP] KEIN ESSEN IM RAUM - PLATZIERE JETZT!");
BotMessage("Platziere Essen...");
if (foodInInventory > 0) {
bool success = PlaceFoodFromInventory();
if (success) {
Log("[STARTUP] Essen erfolgreich platziert!");
BotMessage("Essen platziert!");
} else {
Log("[STARTUP] FEHLER: Konnte Essen nicht platzieren!");
BotMessage("FEHLER: Konnte Essen nicht platzieren!");
}
} else {
Log("[STARTUP] AUCH KEIN ESSEN IM INVENTAR!");
BotMessage("KEIN ESSEN! Bitte im Katalog kaufen!");
}
Delay(1000);
} else {
Log($"[STARTUP] OK - {foodInRoom} Essen im Raum vorhanden");
}
Log("###############################################");
Log("[STARTUP] Raum-Scan abgeschlossen!");
Log("###############################################");
Log("");
// Kurze Pause bevor Training beginnt
Delay(2000);
}
// Care pet - used during training
void CarePet() {
GetPetInfo();
Delay(300);
// DEAKTIVIERT: Water refill
// if (HasEmptyWaterBowl()) {
// BotMessage("Leere Wassernaepfe gefunden!");
// RefillAllEmptyWaterBowls();
// }
// Check food
if (!HasFoodInRoom()) {
BotMessage("Kein Essen im Raum!");
PlaceFoodFromInventory();
}
}
// Care mode - feed and water pet until energy is restored
void EnterCareMode() {
if (inCareMode) return;
inCareMode = true;
careModeCmdCount = 0; // Reset counter
int energyPercent = petMaxEnergy > 0 ? (petEnergy * 100 / petMaxEnergy) : 0;
Log("");
Log("!!! CARE-MODUS AKTIVIERT !!!");
Log($"!!! Energie: {petEnergy}/{petMaxEnergy} ({energyPercent}%) - Ziel: {careModeExitPercent}% !!!");
BotMessage($"CARE-MODUS! Energie: {energyPercent}%");
// DEAKTIVIERT: Water refill
// Log("[CARE] Pruefe und fuelle ALLE Wassernaepfe...");
// RefillAllEmptyWaterBowls();
// Dann Essen checken
QuickScan();
}
// ============ LEVEL-SCAN: Finde Pet mit niedrigstem Level ============
// Scannt alle Pets mit dem gleichen Namen und wechselt zum niedrigsten Level!
long ScanAndSwitchToLowestLevel() {
if (string.IsNullOrEmpty(targetPetName)) {
Log("[LEVEL-SCAN] Kein Target-Name gesetzt!");
return -1;
}
Log("");
Log("###############################################");
Log($"### LEVEL-SCAN: Suche alle '{targetPetName}' ###");
Log("###############################################");
// Sammle alle Pet IDs mit dem gleichen Namen
List<long> petIds = new List<long>();
foreach (var pet in Pets) {
if (pet.Name == targetPetName) {
petIds.Add(pet.Id);
Log($"[LEVEL-SCAN] Gefunden: {pet.Name} (ID: {pet.Id})");
}
}
if (petIds.Count == 0) {
Log($"[LEVEL-SCAN] Keine Pets mit Namen '{targetPetName}' gefunden!");
return -1L;
}
if (petIds.Count == 1) {
Log($"[LEVEL-SCAN] Nur 1 Pet gefunden - kein Wechsel nötig");
return selectedPetId;
}
Log($"[LEVEL-SCAN] {petIds.Count} Pets gefunden - scanne Level...");
Log("");
// Scanne jedes Pet und speichere Level
List<int> levels = new List<int>();
for (int i = 0; i < petIds.Count; i++) {
long petId = petIds[i];
// Request PetInfo
scanGotResponse = false;
scanTempLevel = -1;
Send(Out["GetPetInfo"], (int)petId);
// Warte auf Antwort - LÄNGER WARTEN! (max 2 Sekunden)
int waitCount = 0;
while (!scanGotResponse && waitCount < 20 && Run) {
Delay(100);
waitCount++;
}
if (scanGotResponse && scanTempLevel > 0) {
levels.Add(scanTempLevel);
Log($"[LEVEL-SCAN] Pet {i+1}/{petIds.Count}: ID {petId} = Level {scanTempLevel}");
} else {
levels.Add(99);
Log($"[LEVEL-SCAN] Pet {i+1}/{petIds.Count}: ID {petId} = Level UNBEKANNT (timeout)");
}
Delay(300); // Längere Pause zwischen Anfragen
}
// ZEIGE ALLE LEVELS
Log("");
Log("[LEVEL-SCAN] === ALLE LEVELS ===");
for (int i = 0; i < petIds.Count; i++) {
string marker = "";
if (petIds[i] == selectedPetId) marker = " <-- AKTUELL";
Log($" Pet ID {petIds[i]}: Level {levels[i]}{marker}");
}
// Finde das Pet mit dem niedrigsten Level
int lowestLevel = 99;
long lowestLevelPetId = -1;
for (int i = 0; i < petIds.Count; i++) {
if (levels[i] < lowestLevel) {
lowestLevel = levels[i];
lowestLevelPetId = petIds[i];
}
}
Log("");
Log($"[LEVEL-SCAN] === ERGEBNIS ===");
Log($"[LEVEL-SCAN] Niedrigstes Level: {lowestLevel} (Pet ID: {lowestLevelPetId})");
Log($"[LEVEL-SCAN] Aktuelles Pet: ID {selectedPetId}, Level {petLevel}");
// Wechsle nur wenn ein anderes Pet niedriger ist!
if (lowestLevelPetId != selectedPetId && lowestLevelPetId > 0) {
Log($"[LEVEL-SCAN] >>> WECHSEL ZU PET MIT LEVEL {lowestLevel}! <<<");
BotMessage($"Wechsel zu Level {lowestLevel} Pet!");
// Setze Wechsel-Flag
petSwitchRequested = true;
newPetId = lowestLevelPetId; // KEINE CAST! Beide sind long!
return lowestLevelPetId;
} else {
Log($"[LEVEL-SCAN] Kein Wechsel nötig - aktuelles Pet ist bereits das niedrigste");
}
Log("###############################################");
Log("");
return selectedPetId;
}
// ============ MAIN ============
Log("===========================================");
Log("=== Auto Pet Training V82 ===");
Log("=== SIMPEL: Ein Pet anklicken + trainieren ===");
Log("===========================================");
Log($"Care-Modus startet bei: {careModeThresholdPercent}% Energie");
Log($"Care-Modus endet bei: {careModeExitPercent}% Energie");
Log("");
Log("Klicke auf ein Pet um zu starten!");
while (selectedPetId == -1 && Run) {
Delay(100);
}
// Pet-Name kommt aus dem PetInfo Packet (wird automatisch empfangen beim Klicken)
// Warte kurz bis PetInfo angekommen ist
Log("[INIT] Warte auf PetInfo Packet...");
Delay(500);
// Der Name wurde bereits im PetInfo Handler gesetzt
if (string.IsNullOrEmpty(petName) && !string.IsNullOrEmpty(lastPetInfoName)) {
petName = lastPetInfoName;
}
// Falls immer noch leer, warte nochmal
if (string.IsNullOrEmpty(petName)) {
Delay(500);
if (!string.IsNullOrEmpty(lastPetInfoName)) {
petName = lastPetInfoName;
}
}
if (string.IsNullOrEmpty(petName)) {
BotMessage("FEHLER: Pet-Name nicht erkannt!");
BotMessage("Bitte Script neu starten und Pet anklicken.");
}
Log($">>> Pet ausgewaehlt: {petName} (ID: {selectedPetId}) <<<");
// WICHTIG: Setze targetPetName für Level-Scan!
targetPetName = petName;
Log($"[INIT] Target-Name für Level-Scan: '{targetPetName}'");
// Falls petLevel noch 0 ist, nochmal anfragen
if (petLevel == 0) {
Log("[INIT] Lade Pet-Informationen...");
GetPetInfo();
// Warte bis petLevel > 0 (max 3 Sekunden)
int waitCount = 0;
while (petLevel == 0 && waitCount < 30 && Run) {
Delay(100);
waitCount++;
}
}
if (petLevel == 0) {
Log("[INIT] WARNUNG: Pet Level konnte nicht geladen werden!");
Log("[INIT] Setze Level auf 1 als Fallback...");
petLevel = 1; // Fallback
}
Log($"[INIT] Pet Level: {petLevel}");
Log($"[INIT] Pet Energie: {petEnergy}/{petMaxEnergy} ({(petMaxEnergy > 0 ? petEnergy * 100 / petMaxEnergy : 0)}%)");
Log($"[INIT] Pet Hunger: {petHunger}/100");
// Erster Level-Scan wird nach dem Start durchgeführt
Log("");
Log("[INIT] Level-Scan startet automatisch nach dem Start!");
Log("");
// ============ QUICK INVENTORY CHECK ============
Log("");
Log("=== INVENTAR-CHECK ===");
EnsureInventory();
Delay(2000);
int totalInv = 0;
int foodInv = 0;
foreach (var item in Inventory) {
totalInv++;
string name = item.GetName();
string lowerName = name.ToLower();
// AUSSCHLUSS: Kleidungsstuecke!
if (lowerName.Contains("bite") || lowerName.Contains("costume") || lowerName.Contains("outfit")) {
continue; // Kein Essen!
}
// Zaehle Food - NUR echtes Pet Food!
if (lowerName == "cabbage" || lowerName == "carrot" || lowerName == "hay" ||
lowerName == "bone" || lowerName == "dog food" || lowerName == "cat food" ||
lowerName == "apple" || lowerName == "piggy pudding" || lowerName == "t-bone") {
foodInv++;
if (foodInv <= 5) { // Zeige nur erste 5 Food Items
Log($"[INV] FOOD: '{name}'");
}
}
}
Log($"[INV] Total: {totalInv} Items, davon {foodInv} Food Items");
Log("======================");
Log("");
// Skip detailed pet items listing - script is working now!
// ListAllPetItems();
// DebugListAllInventoryItems();
// ============ STARTUP ROOM SCAN - FIX EVERYTHING ============
StartupRoomScan();
// ============ FORDERE PET COMMANDS AN ============
Log("");
Log("===========================================");
Log("Fordere Pet-Commands vom Server an...");
Log("===========================================");
// WICHTIG: Wir SENDEN GetPetCommands um die verfuegbaren Befehle zu erhalten!
// Das Packet wird NICHT automatisch gesendet - wir muessen es anfordern!
Log($"[CMD] Sende GetPetCommands fuer Pet ID: {selectedPetId}");
Send(Out["GetPetCommands"], (int)selectedPetId);
// Warte bis PetCommands empfangen wurde (max 3 Sekunden)
int cmdWaitCount = 0;
while (!petCommandsReceived && cmdWaitCount < 30 && Run) {
Delay(100);
cmdWaitCount++;
}
if (!petCommandsReceived) {
Log("");
Log("[WARNUNG] =============================================");
Log("[WARNUNG] PetCommands Packet nicht empfangen!");
Log("[WARNUNG] Nutze Fallback mit Basis-Befehlen.");
Log("[WARNUNG] =============================================");
Log("");
// Fallback: Nutze sichere Basis-Befehle
unlockedCommands.Clear();
unlockedCommands.AddRange(safeCommands);
// Fallback: Assume eat/drink sind verfuegbar
petCommandIds.Add(43); // eat
petCommandIds.Add(14); // drink
} else {
Log("[OK] PetCommands erfolgreich geladen!");
}
// Zeige geladene Commands
Log("");
Log($"=== {unlockedCommands.Count} TRAINING-BEFEHLE GELADEN ===");
foreach (var cmd in unlockedCommands) {
Log($" + {cmd}");
}
Log("===========================================");
// Check ob eat/drink verfuegbar sind (fuer Care-Modus)
// WICHTIG: Ausserhalb des Loops damit Pet-Wechsel sie aktualisieren kann!
bool hasEat = petCommandIds.Contains(43); // 43 = eat
bool hasDrink = petCommandIds.Contains(14); // 14 = drink
Log($"[INFO] eat verfuegbar: {hasEat}");
Log($"[INFO] drink verfuegbar: {hasDrink}");
// 8 Sekunden Wartezeit NUR für eat/drink!
// Andere Befehle brauchen nur kurze Wartezeit
// ============ START TRAINING IMMEDIATELY ============
Log("");
Log("Starte Training SOFORT!");
BotMessage($"Training gestartet! Level {petLevel}, {unlockedCommands.Count} Befehle");
// DEAKTIVIERT: Level-Scan - trainiere nur das angeklickte Pet!
// Log("");
// Log("=== INITIALER LEVEL-SCAN ===");
// lastLevelScan = DateTime.Now;
// long initialScanResult = ScanAndSwitchToLowestLevel();
// Log("=== LEVEL-SCAN ABGESCHLOSSEN ===");
// Log("");
// Log("=== LAUFE ZUM PET ===");
// WalkToPet(selectedPetId);
// Log("=== BEREIT ZUM TRAINING ===");
// Log("");
// cmdIndex ist jetzt GLOBAL definiert (oben) - damit PetCommands es resetten kann
int totalCmds = 0;
int totalXP = 0;
DateTime lastCareCheck = DateTime.MinValue;
DateTime lastRoomScan = DateTime.Now; // Fuer 1-Minuten Scan
while (Run) {
// ============ PET WECHSEL CHECK ============
// Wenn ein anderes Pet angeklickt wurde, wechsle zu diesem!
if (petSwitchRequested) {
Log("");
Log("###############################################");
Log("### PET WECHSEL! ###");
Log("###############################################");
// Übernehme neue Pet ID
selectedPetId = newPetId;
petSwitchRequested = false;
newPetId = -1;
// Reset States
petName = ""; // Wird aus PetInfo neu geladen
petType = -1;
inCareMode = false;
careModeCmdCount = 0;
cmdIndex = 0;
// Warte auf PetInfo (wurde schon beim Klicken angefordert)
Log("[SWITCH] Warte auf Pet-Daten...");
Delay(800);
// Pet-Name aus lastPetInfoName übernehmen
if (!string.IsNullOrEmpty(lastPetInfoName)) {
petName = lastPetInfoName;
}
// Falls Name noch leer, nochmal PetInfo anfragen
if (string.IsNullOrEmpty(petName)) {
GetPetInfo();
Delay(500);
if (!string.IsNullOrEmpty(lastPetInfoName)) {
petName = lastPetInfoName;
}
}
Log($"[SWITCH] Neues Pet: {petName} (ID: {selectedPetId}, Typ: {petType})");
// Lade Commands für neues Pet
Log("[SWITCH] Lade Commands...");
Send(Out["GetPetCommands"], (int)selectedPetId);
// Warte auf PetCommands
int switchWait = 0;
while (!petCommandsReceived && switchWait < 30 && Run) {
Delay(100);
switchWait++;
}
if (petCommandsReceived) {
Log($"[SWITCH] {unlockedCommands.Count} Commands geladen!");
} else {
Log("[SWITCH] WARNUNG: Commands nicht empfangen - nutze Fallback");
unlockedCommands.Clear();
unlockedCommands.AddRange(safeCommands);
}
// Update hasEat/hasDrink
hasEat = petCommandIds.Contains(43);
hasDrink = petCommandIds.Contains(14);
// WalkToPet entfernt - nicht nötig für manuelles Switching
BotMessage($"Wechsel zu {petName}! Level {petLevel}, {unlockedCommands.Count} Befehle");
Log("###############################################");
Log("");
// Starte Training mit neuem Pet
continue;
}
// WICHTIG: Pet Info bei JEDER Iteration aktualisieren!
GetPetInfo();
// Berechne Energie-Prozent fuer Anzeige
int currentEnergyPercent = petMaxEnergy > 0 ? (petEnergy * 100 / petMaxEnergy) : 0;
// Status-Anzeige alle 10 Sekunden
if ((DateTime.Now - lastCareCheck).TotalSeconds > 10) {
lastCareCheck = DateTime.Now;
Log($"[Status] Level: {petLevel} | Energy: {petEnergy}/{petMaxEnergy} ({currentEnergyPercent}%) | Hunger: {petHunger}");
}
// RAUM-SCAN alle 15 SEKUNDEN (haeufiger um Essen zu checken!)
if ((DateTime.Now - lastRoomScan).TotalSeconds > 15) {
lastRoomScan = DateTime.Now;
// DEBUG: Zeige aktuellen Food-Status
int foodCount = CountFoodInRoom();
Log($"[SCAN] Essen im Raum: {foodCount}");
// DEAKTIVIERT: Wasser pruefen und nachfuellen
// if (HasEmptyWaterBowl()) {
// Log("[SCAN] Leere Wassernaepfe gefunden - fuelle nach!");
// RefillAllEmptyWaterBowls();
// }
// Essen pruefen und nachfuellen
if (foodCount == 0) {
Log("[SCAN] KEIN ESSEN IM RAUM - platziere!");
BotMessage("Kein Essen - platziere...");
PlaceFoodFromInventory();
}
Log("[SCAN] Scan abgeschlossen.");
}
// DEAKTIVIERT: Level-Scan - trainiere nur das angeklickte Pet!
// if ((DateTime.Now - lastLevelScan).TotalSeconds > levelScanIntervalSeconds) {
// lastLevelScan = DateTime.Now;
// Log($"[LEVEL-SCAN] Zeit für Level-Scan! (alle {levelScanIntervalSeconds} Sek.)");
// long result = ScanAndSwitchToLowestLevel();
// if (petSwitchRequested) {
// Log("[LEVEL-SCAN] Wechsel wird durchgeführt...");
// continue;
// }
// }
// ============ DYNAMISCHE CARE-MODUS LOGIK ============
// Berechne aktuelle Energie in Prozent!
// WICHTIG: Energy kann > MaxEnergy sein (durch Items) - dann ist Prozent > 100!
int energyPercent = petMaxEnergy > 0 ? (petEnergy * 100 / petMaxEnergy) : 100;
// Wenn Energy >= MaxEnergy, brauchen wir KEINEN Care-Modus!
if (petEnergy >= petMaxEnergy) {
energyPercent = 100; // Überschreibe auf 100% wenn voll oder übervoll
}
int careModeThreshold = (petMaxEnergy * careModeThresholdPercent) / 100; // z.B. 20% von 280 = 56
int careModeExitThreshold = (petMaxEnergy * careModeExitPercent) / 100; // z.B. 95% von 280 = 266
// Check if we need to enter or exit care mode
// WICHTIG: Nicht starten wenn Energy >= MaxEnergy!
if (energyPercent <= careModeThresholdPercent && !inCareMode && petEnergy < petMaxEnergy) {
Log($"[CARE] Energie {petEnergy}/{petMaxEnergy} ({energyPercent}%) unter {careModeThresholdPercent}%! STARTE CARE-MODUS!");
EnterCareMode();
} else if (inCareMode) {
// Exit care mode if: energy restored OR too many commands sent
if (energyPercent >= careModeExitPercent) {
inCareMode = false;
Log($"[CARE] Care-Modus beendet - Energie {petEnergy}/{petMaxEnergy} ({energyPercent}%) >= {careModeExitPercent}%!");
BotMessage($"Care-Modus beendet! Energie: {energyPercent}%");
} else if (careModeCmdCount >= careModeCmdLimit) {
// Nur kurze Pause, NICHT komplett beenden wenn Energie noch niedrig!
if (energyPercent < 50) {
Log($"[CARE] {careModeCmdLimit} Befehle aber Energie noch {energyPercent}% - weitermachen!");
careModeCmdCount = 0; // Reset und weitermachen
} else {
inCareMode = false;
Log($"[CARE] Care-Modus Pause nach {careModeCmdLimit} Befehlen - Energie: {energyPercent}%");
BotMessage($"Care-Pause - Energie: {energyPercent}%");
}
}
}
// DEBUG: Zeige Care-Modus Status
if (inCareMode) {
Log($"[CARE] AKTIV! Energy={petEnergy}/{petMaxEnergy} ({energyPercent}%), Ziel={careModeExitPercent}%, Befehle={careModeCmdCount}/{careModeCmdLimit}");
}
// Select command based on pet needs and care mode
string cmd;
bool skipCommand = false;
if (inCareMode) {
careModeCmdCount++; // Zaehle Care-Modus Befehle
Log($"[CARE] Befehl {careModeCmdCount}/{careModeCmdLimit}");
// In care mode: alternate between eat and drink
// PRE-CHECK passiert spaeter - hier nur Befehl waehlen!
if (hasDrink && totalCmds % 3 == 0) {
cmd = "drink";
} else if (hasEat) {
cmd = "eat";
} else {
cmd = "drink";
}
Status($"[CARE-MODUS] Energy: {energyPercent}% -> Ziel: {careModeExitPercent}%");
} else {
// NORMALES TRAINING - keine separaten eat-Checks mehr!
cmd = unlockedCommands[cmdIndex];
cmdIndex = (cmdIndex + 1) % unlockedCommands.Count;
Log($"[TRAIN] Befehl: {cmd} (Index: {cmdIndex-1})");
Status($"[{totalCmds}] {cmd} | +{totalXP} XP");
}
if (skipCommand) {
continue;
}
// ============ WICHTIG: VOR eat/drink IMMER pruefen! ============
if (cmd == "eat") {
// IMMER pruefen ob Essen im Raum ist BEVOR der Befehl gesendet wird!
if (!HasFoodInRoom()) {
Log("[PRE-CHECK] KEIN ESSEN IM RAUM - platziere zuerst!");
bool placed = PlaceFoodFromInventory();
if (!placed) {
Log("[PRE-CHECK] Konnte kein Essen platzieren - ueberspringe eat!");
continue; // Skip this command entirely
}
Delay(1000); // Kurz warten nach Platzierung
}
} else if (cmd == "drink") {
// DEAKTIVIERT: Water refill
// if (HasEmptyWaterBowl()) {
// Log("[PRE-CHECK] Wassernapf leer - fuelle nach!");
// RefillAllEmptyWaterBowls();
// }
}
// ============ BEFEHL SENDEN MIT RETRY ============
int maxRetries = 5; // Maximal 5 Versuche bei Verweigerung
int retryCount = 0;
bool commandSucceeded = false;
while (retryCount < maxRetries && !commandSucceeded && Run) {
ResetFlags();
waitingForResponse = true;
if (retryCount > 0) {
Log($"[RETRY] Versuch {retryCount + 1}/{maxRetries}: {petName} {cmd}");
} else {
Log($"[CMD] Sende: {petName} {cmd}");
}
SendCmd(cmd);
if (retryCount == 0) totalCmds++;
int waited = 0;
while (waited < trainDelay && !gotXP && !gotRefusal && Run) {
Delay(100);
waited += 100;
}
waitingForResponse = false;
if (gotXP) {
totalXP += lastXPGained; // Echte XP addieren!
commandSucceeded = true;
Log($"[OK] {cmd} erfolgreich! (+{lastXPGained} XP)");
} else if (gotRefusal) {
retryCount++;
Log($"[!] Pet verweigert '{cmd}' - Retry {retryCount}/{maxRetries}");
// Bei Verweigerung: Problem beheben und SOFORT wieder versuchen!
if (cmd == "eat") {
Log("[RETRY] Pruefe/platziere Essen...");
if (!HasFoodInRoom()) {
PlaceFoodFromInventory();
}
Delay(1500);
} else if (cmd == "drink") {
Log("[RETRY] Drink retry - skip water refill");
// DEAKTIVIERT: RefillAllEmptyWaterBowls();
Delay(1000);
} else {
// Andere Befehle: kurz warten und nochmal
Delay(1000);
}
} else {
// Keine Antwort - wahrscheinlich OK
commandSucceeded = true;
}
}
if (!commandSucceeded && retryCount >= maxRetries) {
Log($"[FAIL] {cmd} nach {maxRetries} Versuchen fehlgeschlagen - ueberspringe");
BotMessage($"{cmd} fehlgeschlagen nach {maxRetries} Versuchen!");
}
// Wartezeit nach Befehl:
// - eat/drink: 8 Sekunden (Pet muss laufen + essen)
// - Alle anderen: Nur 2 Sekunden (kurze Animation)
if (cmd == "eat" || cmd == "drink") {
Log($"[WAIT] Warte 8 Sek. bis Pet fertig mit {cmd}...");
WaitForPetToEat(); // 8 Sekunden
GetPetInfo(); // Update energy nach dem Essen
int newEnergyPercent = petMaxEnergy > 0 ? (petEnergy * 100 / petMaxEnergy) : 0;
Log($"[WAIT] Fertig! Energie jetzt: {petEnergy}/{petMaxEnergy} ({newEnergyPercent}%)");
} else {
// Normale Befehle: Nur kurz warten (2 Sekunden)
Delay(2000);
}
}
Log($"Training beendet! {totalCmds} Befehle, +{totalXP} XP");