// 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 commandIdToName = new Dictionary() { { 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 unlockedCommands = new List(); 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 petCommandIds = new List(); 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 petIds = new List(); 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 levels = new List(); 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");