Habbo Hotel automation scripts including: - Game solvers (Snake, Color Puzzle, Tetris, Flappy Bird, Flood-IT) - Room utilities (Autogate, One-Way Door, Furni Scanner) - Bot tools (Heal Bot, Pet Trainer, User Collector) - Trading & economy (Furni-Matic, Seed Trade, Trade Spam) Cleaned up: removed 5 duplicates and 2 broken scripts, renamed 37 gibberish filenames to descriptive names.
2178 lines
71 KiB
C#
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");
|