package de.luca.lager; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.inventory.GuiChest; import net.minecraft.client.renderer.GlStateManager; import net.minecraft.client.renderer.RenderGlobal; import net.minecraft.client.settings.KeyBinding; import net.minecraft.entity.Entity; import net.minecraft.inventory.ContainerChest; import net.minecraft.inventory.IInventory; import net.minecraft.item.ItemStack; import net.minecraft.util.AxisAlignedBB; import net.minecraft.util.BlockPos; import net.minecraft.util.ChatComponentText; import net.minecraft.util.MovingObjectPosition; import net.minecraftforge.client.event.RenderWorldLastEvent; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.event.entity.player.PlayerInteractEvent; import net.minecraftforge.fml.client.registry.ClientRegistry; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.event.FMLInitializationEvent; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.common.gameevent.TickEvent; import org.lwjgl.input.Keyboard; import org.lwjgl.opengl.GL11; import java.io.File; import java.io.PrintWriter; import java.io.BufferedReader; import java.io.FileReader; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; /** * Lager — merkt sich, was in den Truhen liegt, die du selbst geoeffnet hast, * und zeigt dir auf Tastendruck eine Uebersicht ueber das Grundstueck, * auf dem du gerade stehst. * * Es wird ausschliesslich gespeichert, was du mit eigenen Augen gesehen hast. * Die Mod schaut nicht in ungeoeffnete Truhen. */ @Mod(modid = LagerMod.MODID, name = "Lager", version = LagerMod.VERSION) public class LagerMod { public static final String MODID = "lager"; public static final String VERSION = "1.0"; /** Nur Truhen in diesem Umkreis gelten als "dasselbe Grundstueck". */ public static final int UMKREIS = 60; public static LagerMod instanz; /** Bekannte Truhen: Position -> was zuletzt darin lag. */ public final Map truhen = new LinkedHashMap(); /** Gegenstaende, die dauerhaft am Bildschirmrand stehen sollen. */ public final List angeheftet = new ArrayList(); /** Sparziel und Rechner. */ public long sparziel; public long guthaben; public String rechnerItem = ""; public double rechnerPreis; private KeyBinding tasteRechner; private KeyBinding tasteEcke; private KeyBinding tasteSchematik; private KeyBinding tasteFarm; private KeyBinding tasteZiel; /** Merkt sich, wie schnell Geld und Gegenstaende dazukommen. */ public final Farm farm = new Farm(); /** Das Farmziel, das im Widget oben rechts steht. */ public final Ziel ziel = new Ziel(); /** Die laufende Auswahl fuer die Schematic. */ public final Schematik schematik = new Schematik(); private KeyBinding taste; private BlockPos zuletztAngeklickt; private boolean warOffen; private File speicherDatei; /** Was in einer Truhe liegt, samt Zeitpunkt der letzten Sichtung. */ public static class Truhe { public final BlockPos ort; public final Map inhalt = new LinkedHashMap(); public long gesehen; public Truhe(BlockPos ort) { this.ort = ort; } public int gesamt() { int n = 0; for (Integer v : inhalt.values()) { n += v; } return n; } } @Mod.EventHandler public void init(FMLInitializationEvent e) { instanz = this; taste = new KeyBinding("Lager oeffnen", Keyboard.KEY_L, "Lager"); ClientRegistry.registerKeyBinding(taste); tasteRechner = new KeyBinding("Sparziel und Rechner", Keyboard.KEY_K, "Lager"); ClientRegistry.registerKeyBinding(tasteRechner); tasteEcke = new KeyBinding("Schematic: Ecke setzen (mit Schleichen: loeschen)", Keyboard.KEY_N, "Lager"); ClientRegistry.registerKeyBinding(tasteEcke); tasteSchematik = new KeyBinding("Schematic ziehen", Keyboard.KEY_M, "Lager"); ClientRegistry.registerKeyBinding(tasteSchematik); tasteFarm = new KeyBinding("Farm: was kommt wie schnell rein", Keyboard.KEY_F, "Lager"); ClientRegistry.registerKeyBinding(tasteFarm); tasteZiel = new KeyBinding("Farmziel einstellen", Keyboard.KEY_H, "Lager"); ClientRegistry.registerKeyBinding(tasteZiel); MinecraftForge.EVENT_BUS.register(new Haltbarkeit()); MinecraftForge.EVENT_BUS.register(this); net.minecraftforge.fml.common.FMLCommonHandler.instance().bus().register(this); speicherDatei = new File(Minecraft.getMinecraft().mcDataDir, "config/lager.txt"); laden(); } // ================================================================= // Aufzeichnen // ================================================================= /** Merkt sich, welchen Block der Spieler zuletzt angeklickt hat. */ @SubscribeEvent public void beiKlick(PlayerInteractEvent e) { if (e.action == PlayerInteractEvent.Action.RIGHT_CLICK_BLOCK && e.pos != null) { zuletztAngeklickt = e.pos; } } @SubscribeEvent public void beiTick(TickEvent.ClientTickEvent e) { if (e.phase != TickEvent.Phase.END) { return; } Minecraft mc = Minecraft.getMinecraft(); if (mc.thePlayer == null) { return; } // Truhe offen? Dann Inhalt mitschreiben. boolean offen = mc.currentScreen instanceof GuiChest; if (offen) { lesen(mc); } else if (warOffen) { bestandMitschreiben(); sichern(); } warOffen = offen; // Taste gedrueckt -> Uebersicht zeigen if (taste.isPressed() && mc.currentScreen == null) { mc.displayGuiScreen(new GuiLager(this)); } if (tasteRechner.isPressed() && mc.currentScreen == null) { mc.displayGuiScreen(new GuiRechner(this)); } if (tasteEcke.isPressed() && mc.currentScreen == null) { eckeSetzen(mc); } if (tasteSchematik.isPressed() && mc.currentScreen == null) { schematikZiehen(mc); } if (tasteFarm.isPressed() && mc.currentScreen == null) { mc.displayGuiScreen(new GuiFarm(this)); } if (tasteZiel.isPressed() && mc.currentScreen == null) { mc.displayGuiScreen(new GuiZiel(this)); } // Mitzaehlen, was reinkommt einsammelnZaehlen(mc); geldMitschreiben(); // Was aus einem anderen Faden kam, jetzt in den Chat schreiben chatLeeren(mc); // Der Webseite sagen, dass du spielst melden(); } // ================================================================= // Mitzaehlen, was du einsammelst // ================================================================= /** Wie viel vor einem Augenblick von jeder Sorte im Rucksack lag. */ private final Map vorherImRucksack = new HashMap(); /** Bis dahin wird nicht gezaehlt — nach dem Kistenschieben kurz Ruhe. */ private long zaehlpause; private int tickZaehler; private long letzteGeldprobe; /** * Vergleicht den Rucksack mit dem von eben. Was mehr geworden ist, hast * du eingesammelt — egal ob ein Block oder neun auf einmal. * * Waehrend eine Kiste offen ist, zaehle ich nicht mit: sonst wuerde * jedes Herausnehmen als "gefarmt" durchgehen. */ private void einsammelnZaehlen(Minecraft mc) { if (++tickZaehler < 5) { return; } tickZaehler = 0; long jetzt = System.currentTimeMillis(); Map jetztDrin = new HashMap(); ItemStack[] taschen = mc.thePlayer.inventory.mainInventory; for (int i = 0; i < taschen.length; i++) { ItemStack s = taschen[i]; if (s == null) { continue; } String name = s.getDisplayName(); Integer bisher = jetztDrin.get(name); jetztDrin.put(name, (bisher == null ? 0 : bisher) + s.stackSize); } boolean pause = mc.currentScreen != null; if (pause) { zaehlpause = jetzt + 1000; } if (!pause && jetzt >= zaehlpause && !vorherImRucksack.isEmpty()) { for (Map.Entry e : jetztDrin.entrySet()) { Integer vorher = vorherImRucksack.get(e.getKey()); long dazu = e.getValue() - (vorher == null ? 0 : vorher); if (dazu > 0) { farm.einsammeln(e.getKey(), dazu, jetzt); } } } vorherImRucksack.clear(); vorherImRucksack.putAll(jetztDrin); } /** Alle 30 Sekunden den Kontostand mitschreiben, damit $/h stimmt. */ private void geldMitschreiben() { long jetzt = System.currentTimeMillis(); if (jetzt - letzteGeldprobe < 30000) { return; } letzteGeldprobe = jetzt; long[] gb = geldUndBankAusScoreboard(); long summe = (gb[0] > 0 ? gb[0] : 0) + (gb[1] > 0 ? gb[1] : 0); if (summe > 0) { farm.merkenGeld(summe, jetzt); } } /** Nach dem Zumachen einer Truhe: den Lagerstand mitschreiben. */ private void bestandMitschreiben() { long jetzt = System.currentTimeMillis(); for (Map.Entry e : gesamtbestand().entrySet()) { farm.ausTruhe(e.getKey(), e.getValue(), jetzt); } } // ================================================================= // Schematic ziehen // ================================================================= /** Der Block, den du anschaust — schaust du ins Leere, der unter dir. */ private static BlockPos anvisiert(Minecraft mc) { MovingObjectPosition ziel = mc.objectMouseOver; if (ziel != null && ziel.typeOfHit == MovingObjectPosition.MovingObjectType.BLOCK && ziel.getBlockPos() != null) { return ziel.getBlockPos(); } return mc.thePlayer.getPosition().down(); } private void eckeSetzen(Minecraft mc) { if (mc.thePlayer.isSneaking()) { schematik.loeschen(); sagen("§eAuswahl geloescht."); return; } BlockPos p = anvisiert(mc); int welche = schematik.eckeSetzen(p); String wo = p.getX() + " " + p.getY() + " " + p.getZ(); int[] m = schematik.masse(); if (m == null) { sagen("§aEcke " + welche + " §7bei " + wo + " §8— jetzt die andere Ecke"); } else { sagen("§aEcke " + welche + " §7bei " + wo + " §8· §f" + m[0] + "×" + m[1] + "×" + m[2] + " §7(" + schematik.anzahl() + " Bloecke) §8· M zieht sie"); } } private void schematikZiehen(Minecraft mc) { if (!schematik.vollstaendig()) { sagen("§cErst zwei Ecken setzen — Taste N."); return; } try { File ordner = new File(mc.mcDataDir, "schematics"); File datei = schematik.speichern(ordner, null); sagen("§aSchematic gespeichert: §f" + datei.getName()); sagen("§8" + ordner.getAbsolutePath()); if (schematik.nichtGeladen > 0) { sagen("§e" + schematik.nichtGeladen + " Bloecke waren nicht geladen" + " und stehen als Luft drin. Sichtweite hoch, naeher hin," + " nochmal ziehen."); } if (!webSchluessel.isEmpty()) { Schematik.hochladen(datei, webAdresse.replace("/api/setzen", "/api/schematik"), webSchluessel); } } catch (Throwable f) { sagen("§cGing nicht: " + (f.getMessage() == null ? f.toString() : f.getMessage())); } } /** Malt die Auswahl als Kasten in die Welt, damit du siehst was du hast. */ @SubscribeEvent public void beiWeltZeichnen(RenderWorldLastEvent e) { if (!schematik.vollstaendig()) { return; } Minecraft mc = Minecraft.getMinecraft(); Entity auge = mc.getRenderViewEntity(); if (auge == null) { return; } double dx = auge.lastTickPosX + (auge.posX - auge.lastTickPosX) * e.partialTicks; double dy = auge.lastTickPosY + (auge.posY - auge.lastTickPosY) * e.partialTicks; double dz = auge.lastTickPosZ + (auge.posZ - auge.lastTickPosZ) * e.partialTicks; BlockPos a = schematik.ecke1; BlockPos b = schematik.ecke2; AxisAlignedBB kasten = new AxisAlignedBB( Math.min(a.getX(), b.getX()) - dx, Math.min(a.getY(), b.getY()) - dy, Math.min(a.getZ(), b.getZ()) - dz, Math.max(a.getX(), b.getX()) + 1 - dx, Math.max(a.getY(), b.getY()) + 1 - dy, Math.max(a.getZ(), b.getZ()) + 1 - dz); GlStateManager.pushMatrix(); GlStateManager.disableTexture2D(); GlStateManager.disableLighting(); GlStateManager.depthMask(false); GL11.glLineWidth(2.0F); RenderGlobal.drawOutlinedBoundingBox(kasten, 90, 220, 130, 255); GlStateManager.depthMask(true); GlStateManager.enableLighting(); GlStateManager.enableTexture2D(); GlStateManager.popMatrix(); } /** * Zeilen, die noch in den Chat sollen. Das Hochladen laeuft in einem * eigenen Faden, und der darf nicht selbst am Chat herumschreiben — * darum legt er die Zeile hier ab und der naechste Tick holt sie. */ private static final List chatSchlange = java.util.Collections.synchronizedList(new ArrayList()); /** Eine Zeile in deinen Chat — die geht nicht an den Server. */ public static void sagen(String text) { chatSchlange.add(text); } private void chatLeeren(Minecraft mc) { if (chatSchlange.isEmpty() || mc.thePlayer == null) { return; } synchronized (chatSchlange) { for (String zeile : chatSchlange) { mc.thePlayer.addChatMessage(new ChatComponentText("§8[Lager] §r" + zeile)); } chatSchlange.clear(); } } /** * Liest Geld und Bank getrennt aus dem Scoreboard am rechten Rand. * Vorher nahm ich einfach die erste Zeile mit einem $ - auf * GrieferGames ist das die Bank, darum stand auf der Webseite bei * Konto und Bank dieselbe Zahl. * * Rueckgabe: {geld, bank}. -1 heisst "steht nicht im Scoreboard". */ public long[] geldUndBankAusScoreboard() { long geld = -1; long bank = -1; try { Minecraft mc = Minecraft.getMinecraft(); if (mc.theWorld == null) { return new long[] {-1, -1}; } net.minecraft.scoreboard.Scoreboard sb = mc.theWorld.getScoreboard(); net.minecraft.scoreboard.ScoreObjective ziel = sb.getObjectiveInDisplaySlot(1); if (ziel == null) { return new long[] {-1, -1}; } // Manche Server schreiben die Ueberschrift in eine Zeile und den // Betrag in die naechste. Darum merke ich mir, zu welchem Wort // die Zahl noch fehlt. int offen = 0; // 0 = nichts, 1 = Geld, 2 = Bank for (net.minecraft.scoreboard.Score s : sb.getSortedScores(ziel)) { String name = net.minecraft.scoreboard.ScorePlayerTeam.formatPlayerName( sb.getPlayersTeam(s.getPlayerName()), s.getPlayerName()); String rein = ohneFarben(name); String klein = rein.toLowerCase(java.util.Locale.GERMANY); int art = 0; if (klein.indexOf("bank") >= 0) { art = 2; } else if (klein.indexOf("geld") >= 0 || klein.indexOf("konto") >= 0 || klein.indexOf("guthaben") >= 0 || klein.indexOf("cash") >= 0) { art = 1; } long zahl = betragAus(rein); if (zahl < 0) { // Nur eine Ueberschrift - die Zahl kommt gleich. offen = art; continue; } if (art == 0) { // Zahl ohne Wort: gehoert zur Ueberschrift davor. Sonst // ist eine Zeile mit Waehrungszeichen das Bargeld. art = offen; if (art == 0 && (rein.indexOf('$') >= 0 || rein.indexOf('¢') >= 0)) { art = 1; } } offen = 0; if (art == 2 && bank < 0) { bank = zahl; } else if (art == 1 && geld < 0) { geld = zahl; } } } catch (Throwable ignoriert) { // Scoreboard sieht auf jedem Server anders aus - lieber nichts // liefern als abstuerzen. } return new long[] {geld, bank}; } /** Fuer den Rechner: alles zusammen, Bargeld plus Bank. */ public long guthabenAusScoreboard() { long[] gb = geldUndBankAusScoreboard(); long summe = 0; if (gb[0] > 0) { summe += gb[0]; } if (gb[1] > 0) { summe += gb[1]; } return summe; } /** * Holt den Betrag aus einer Scoreboard-Zeile. Punkte trennen die * Tausender, ab dem Komma sind es Cent und die lasse ich weg. Steht * keine Zahl drin, kommt -1 zurueck. */ private static long betragAus(String s) { int i = 0; while (i < s.length() && (s.charAt(i) < '0' || s.charAt(i) > '9')) { i++; } if (i >= s.length()) { return -1; } StringBuilder b = new StringBuilder(); for (; i < s.length(); i++) { char c = s.charAt(i); if (c >= '0' && c <= '9') { b.append(c); } else if (c == '.' || c == ' ') { continue; // Tausendertrenner } else { break; // Komma, Waehrung, Text - fertig } } if (b.length() == 0 || b.length() > 18) { return -1; } try { return Long.parseLong(b.toString()); } catch (Exception e) { return -1; } } /** Damit der Rechner beim Schliessen speichern kann. */ public void sichernOeffentlich() { sichern(); } // ================================================================= // Meldung an die Webseite // ================================================================= /** Adresse und Schluessel der Webseite. Stehen in lager.txt. */ public String webAdresse = "https://lukaba.duckdns.org/griefergames/api/setzen"; public String webSchluessel = ""; private long letzteMeldung; /** * Schickt alle 30 Sekunden Lebenszeichen und Kontostand an die Webseite, * damit dort "online" steht und der Verlauf sich fuellt. * Laeuft in einem eigenen Faden, damit das Spiel nicht stockt. */ private void melden() { if (webSchluessel.isEmpty()) { return; } long jetzt = System.currentTimeMillis(); if (jetzt - letzteMeldung < 30000) { return; } letzteMeldung = jetzt; final long[] gb = geldUndBankAusScoreboard(); final String koerper = "{\"schluessel\":\"" + webSchluessel + "\"" + ",\"aus_dem_spiel\":true" + (gb[0] >= 0 ? (",\"geld\":" + gb[0]) : "") + (gb[1] >= 0 ? (",\"bank\":" + gb[1]) : "") + "}"; new Thread(new Runnable() { public void run() { java.io.OutputStream aus = null; try { java.net.HttpURLConnection v = (java.net.HttpURLConnection) new java.net.URL(webAdresse).openConnection(); v.setRequestMethod("POST"); v.setRequestProperty("Content-Type", "application/json"); v.setConnectTimeout(5000); v.setReadTimeout(5000); v.setDoOutput(true); aus = v.getOutputStream(); aus.write(koerper.getBytes("UTF-8")); aus.flush(); v.getResponseCode(); v.disconnect(); } catch (Throwable ignoriert) { // Kein Netz oder Server aus — die Mod laeuft trotzdem weiter. } finally { if (aus != null) { try { aus.close(); } catch (Exception e2) { } } } } }, "Lager-Meldung").start(); } /** Liest den Inhalt der gerade offenen Truhe. */ private void lesen(Minecraft mc) { if (zuletztAngeklickt == null) { return; } if (!(mc.thePlayer.openContainer instanceof ContainerChest)) { return; } ContainerChest behaelter = (ContainerChest) mc.thePlayer.openContainer; IInventory kiste = behaelter.getLowerChestInventory(); Truhe t = truhen.get(zuletztAngeklickt); if (t == null) { t = new Truhe(zuletztAngeklickt); truhen.put(zuletztAngeklickt, t); } t.inhalt.clear(); for (int i = 0; i < kiste.getSizeInventory(); i++) { ItemStack stapel = kiste.getStackInSlot(i); if (stapel == null) { continue; } String name = stapel.getDisplayName(); Integer bisher = t.inhalt.get(name); t.inhalt.put(name, (bisher == null ? 0 : bisher) + stapel.stackSize); } t.gesehen = System.currentTimeMillis(); } // ================================================================= // Auswerten // ================================================================= /** Alle Truhen in der Naehe des Spielers — also auf demselben Grundstueck. */ public List truhenInDerNaehe() { List liste = new ArrayList(); Minecraft mc = Minecraft.getMinecraft(); if (mc.thePlayer == null) { return liste; } BlockPos ich = mc.thePlayer.getPosition(); for (Truhe t : truhen.values()) { int dx = t.ort.getX() - ich.getX(); int dz = t.ort.getZ() - ich.getZ(); if (dx * dx + dz * dz <= UMKREIS * UMKREIS) { liste.add(t); } } return liste; } /** Alles zusammengezaehlt: welcher Gegenstand liegt wie oft in der Naehe. */ public Map gesamtbestand() { Map summe = new HashMap(); for (Truhe t : truhenInDerNaehe()) { for (Map.Entry e : t.inhalt.entrySet()) { Integer bisher = summe.get(e.getKey()); summe.put(e.getKey(), (bisher == null ? 0 : bisher) + e.getValue()); } } return summe; } // ================================================================= // Dauer-Anzeige am Bildschirmrand // ================================================================= /** Heftet einen Gegenstand an oder loest ihn wieder. */ public void anheftenUmschalten(String name) { if (angeheftet.contains(name)) { angeheftet.remove(name); } else { angeheftet.add(name); } sichern(); } @SubscribeEvent public void beiUeberlagerung(net.minecraftforge.client.event.RenderGameOverlayEvent.Text e) { Minecraft mc = Minecraft.getMinecraft(); if (mc.thePlayer == null || mc.gameSettings.showDebugInfo) { return; } int y = 4; if (!angeheftet.isEmpty()) { Map bestand = gesamtbestand(); for (String name : angeheftet) { Integer menge = bestand.get(name); String kurz = ohneFarben(name); String text = "§7" + kurz + " §e" + (menge == null ? 0 : menge); if (menge != null && menge >= 64) { text += " §8(" + (menge / 64) + "×64)"; } // Wie schnell davon gerade etwas reinkommt Double tempo = farm.gefarmtProStunde(name, System.currentTimeMillis()); if (tempo != null && Math.round(tempo) != 0) { text += " §a+" + Math.round(tempo) + "§8/h"; } mc.fontRendererObj.drawStringWithShadow(text, 4, y, 0xFFFFFF); y += 10; } } zielWidget(mc, e.resolution); // Laeuft gerade eine Auswahl, steht sie mit in der Ecke. if (schematik.ecke1 != null) { int[] m = schematik.masse(); String text = (m == null) ? "§7Schematic: §fEcke 1 steht §8— N setzt Ecke 2" : "§7Schematic: §f" + m[0] + "×" + m[1] + "×" + m[2] + " §8— M zieht sie"; mc.fontRendererObj.drawStringWithShadow(text, 4, y, 0xFFFFFF); } } // ================================================================= // Das Widget mit dem Farmziel, oben rechts // ================================================================= private static final java.text.DecimalFormat WZAHL = new java.text.DecimalFormat("#,##0", new java.text.DecimalFormatSymbols(java.util.Locale.GERMANY)); /** Wie viel von dem Zeug du insgesamt eingesammelt hast. */ public long gefarmtVon(String item) { if (item == null || item.trim().isEmpty()) { return 0; } String suche = ohneFarben(item).trim(); long summe = 0; for (Map.Entry e : farm.gesamt.entrySet()) { if (ohneFarben(e.getKey()).trim().equalsIgnoreCase(suche)) { summe += e.getValue(); } } return summe; } /** Wie viel davon gerade je Stunde reinkommt, oder null. */ public Double tempoVon(String item, long jetzt) { if (item == null || item.trim().isEmpty()) { return null; } String suche = ohneFarben(item).trim(); Double summe = null; for (String name : farm.gefarmt.keySet()) { if (ohneFarben(name).trim().equalsIgnoreCase(suche)) { Double t = farm.gefarmtProStunde(name, jetzt); if (t != null) { summe = (summe == null ? 0 : summe) + t; } } } return summe; } /** Wie viel davon in den Truhen in der Naehe liegt. */ public long imLagerVon(String item) { if (item == null || item.trim().isEmpty()) { return 0; } String suche = ohneFarben(item).trim(); long summe = 0; for (Map.Entry e : gesamtbestand().entrySet()) { if (ohneFarben(e.getKey()).trim().equalsIgnoreCase(suche)) { summe += e.getValue(); } } return summe; } /** * Zeichnet oben rechts, wie weit du beim Ziel bist: was du seit dem * Setzen zusammenhast, wie viel noch fehlt und wann du in dem Tempo * fertig bist. */ private void zielWidget(Minecraft mc, net.minecraft.client.gui.ScaledResolution auf) { int rechts = auf.getScaledWidth() - 4; int y = 4; y = sparzielWidget(mc, rechts, y); farmzielWidget(mc, rechts, y); } /** Das Sparziel in Geld — oben rechts, sobald eines eingetragen ist. */ private int sparzielWidget(Minecraft mc, int rechts, int y) { if (sparziel <= 0) { return y; } long jetzt = System.currentTimeMillis(); long[] gb = geldUndBankAusScoreboard(); long habe = (gb[0] > 0 ? gb[0] : 0) + (gb[1] > 0 ? gb[1] : 0); if (habe <= 0) { habe = guthaben; // was zuletzt von Hand drinstand } long fehlt = Math.max(sparziel - habe, 0); double anteil = Math.min((double) habe / (double) sparziel, 1.0); y = rechtsSchreiben(mc, "§6Sparziel", rechts, y); y = rechtsSchreiben(mc, (fehlt == 0 ? "§a" : "§f") + WZAHL.format(habe) + " §7/ " + WZAHL.format(sparziel) + " §8(" + Math.round(anteil * 100) + "%)", rechts, y); int breite = 110; int x = rechts - breite; net.minecraft.client.gui.Gui.drawRect(x, y, x + breite, y + 4, 0x80000000); net.minecraft.client.gui.Gui.drawRect(x, y, x + (int) Math.round(breite * anteil), y + 4, fehlt == 0 ? 0xFF55FF55 : 0xFFF0C674); y += 8; if (fehlt == 0) { y = rechtsSchreiben(mc, "§aSparziel erreicht.", rechts, y); } else { y = rechtsSchreiben(mc, "§7noch §e" + WZAHL.format(fehlt) + " §7$", rechts, y); Double tempo = farm.geldProStunde(jetzt); if (tempo != null && tempo > 0) { y = rechtsSchreiben(mc, "§a+" + WZAHL.format(Math.round(tempo)) + "§8$/h §7noch " + Farm.dauer(Farm.stundenBis(fehlt, tempo)), rechts, y); } } return y + 4; } private void farmzielWidget(Minecraft mc, int rechts, int y) { if (!ziel.steht()) { return; } long jetzt = System.currentTimeMillis(); long soll = ziel.inStueck(); long habe = ziel.schon + Math.max(gefarmtVon(ziel.item) - ziel.start, 0); long fehlt = Math.max(soll - habe, 0); double anteil = Math.min((double) habe / (double) soll, 1.0); y = rechtsSchreiben(mc, "§6" + ohneFarben(ziel.item) + " §7— " + ziel.beschriftung(), rechts, y); y = rechtsSchreiben(mc, (fehlt == 0 ? "§a" : "§f") + WZAHL.format(habe) + " §7/ " + WZAHL.format(soll) + " §8(" + Math.round(anteil * 100) + "%)", rechts, y); // Balken int breite = 110; int x = rechts - breite; net.minecraft.client.gui.Gui.drawRect(x, y, x + breite, y + 4, 0x80000000); net.minecraft.client.gui.Gui.drawRect(x, y, x + (int) Math.round(breite * anteil), y + 4, fehlt == 0 ? 0xFF55FF55 : 0xFF3B82F6); y += 8; if (fehlt == 0) { y = rechtsSchreiben(mc, "§aZiel geschafft.", rechts, y); } else { y = rechtsSchreiben(mc, "§7noch §e" + WZAHL.format(fehlt) + " §7Stueck §8(" + WZAHL.format((fehlt + Ziel.STAPEL - 1) / Ziel.STAPEL) + " Stapel)", rechts, y); Double tempo = tempoVon(ziel.item, jetzt); if (tempo != null && tempo > 0) { y = rechtsSchreiben(mc, "§a+" + WZAHL.format(Math.round(tempo)) + "§8/h §7noch " + Farm.dauer(Farm.stundenBis(fehlt, tempo)), rechts, y); } else { y = rechtsSchreiben(mc, "§8Tempo wird noch gemessen", rechts, y); } } long lager = imLagerVon(ziel.item); if (lager > 0) { rechtsSchreiben(mc, "§8im Lager: " + WZAHL.format(lager), rechts, y); } } /** Eine Zeile rechtsbuendig schreiben, gibt das naechste y zurueck. */ private static int rechtsSchreiben(Minecraft mc, String text, int rechts, int y) { int b = mc.fontRendererObj.getStringWidth(text); mc.fontRendererObj.drawStringWithShadow(text, rechts - b, y, 0xFFFFFF); return y + 10; } /** Farbcodes entfernen, damit die Anzeige ruhig bleibt. */ public static String ohneFarben(String s) { if (s == null) { return ""; } StringBuilder b = new StringBuilder(); for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); if (c == '§' && i + 1 < s.length()) { i++; } else { b.append(c); } } return b.toString(); } // ================================================================= // Auf Platte sichern, damit es einen Neustart ueberlebt // ================================================================= private void sichern() { try { speicherDatei.getParentFile().mkdirs(); PrintWriter s = new PrintWriter(speicherDatei, "UTF-8"); s.println("ZIEL;" + sparziel + ";" + guthaben + ";" + rechnerPreis + ";" + rechnerItem); s.println("WEB;" + webSchluessel + ";" + webAdresse); if (!ziel.item.isEmpty()) { s.println("ZIEL3;" + ziel.menge + ";" + ziel.einheit + ";" + ziel.jeStufe + ";" + ziel.schon + ";" + ziel.start + ";" + ziel.gesetzt + ";" + ziel.item); } for (Farm.Punkt p : farm.geld) { s.println("FGELD;" + p.zeit + ";" + p.menge); } for (Map.Entry e : farm.gesamt.entrySet()) { s.println("FSUMME;" + e.getValue() + ";" + e.getKey()); } for (Map.Entry> e : farm.gefarmt.entrySet()) { for (Farm.Punkt p : e.getValue()) { s.println("FGEFARMT;" + p.zeit + ";" + p.menge + ";" + e.getKey()); } } for (Map.Entry> e : farm.truhen.entrySet()) { for (Farm.Punkt p : e.getValue()) { s.println("FTRUHE;" + p.zeit + ";" + p.menge + ";" + e.getKey()); } } for (String a : angeheftet) { s.println("ANGEHEFTET;" + a); } for (Truhe t : truhen.values()) { for (Map.Entry e : t.inhalt.entrySet()) { s.println(t.ort.getX() + ";" + t.ort.getY() + ";" + t.ort.getZ() + ";" + t.gesehen + ";" + e.getValue() + ";" + e.getKey()); } } s.close(); } catch (Exception ignoriert) { // Wenn das Speichern nicht klappt, laeuft die Mod trotzdem weiter. } } private void laden() { if (!speicherDatei.exists()) { return; } try { BufferedReader l = new BufferedReader(new FileReader(speicherDatei)); String zeile; while ((zeile = l.readLine()) != null) { if (zeile.startsWith("ANGEHEFTET;")) { angeheftet.add(zeile.substring("ANGEHEFTET;".length())); continue; } if (zeile.startsWith("ZIEL3;")) { String[] w = zeile.split(";", 8); if (w.length >= 8) { try { ziel.menge = Long.parseLong(w[1]); ziel.einheit = w[2]; ziel.jeStufe = Long.parseLong(w[3]); ziel.schon = Long.parseLong(w[4]); ziel.start = Long.parseLong(w[5]); ziel.gesetzt = Long.parseLong(w[6]); ziel.item = w[7]; } catch (Exception ignoriert) { } } continue; } if (zeile.startsWith("ZIEL2;")) { // Alte Schreibweise, noch ohne "schon geschafft" String[] w = zeile.split(";", 7); if (w.length >= 7) { try { ziel.menge = Long.parseLong(w[1]); ziel.einheit = w[2]; ziel.jeStufe = Long.parseLong(w[3]); ziel.start = Long.parseLong(w[4]); ziel.gesetzt = Long.parseLong(w[5]); ziel.item = w[6]; } catch (Exception ignoriert) { } } continue; } if (zeile.startsWith("FGELD;")) { String[] w = zeile.split(";", 3); if (w.length >= 3) { try { farm.geld.add(new Farm.Punkt( Long.parseLong(w[1]), Long.parseLong(w[2]))); } catch (Exception ignoriert) { } } continue; } if (zeile.startsWith("FSUMME;")) { String[] w = zeile.split(";", 3); if (w.length >= 3) { try { farm.gesamt.put(w[2], Long.parseLong(w[1])); } catch (Exception ignoriert) { } } continue; } if (zeile.startsWith("FGEFARMT;") || zeile.startsWith("FTRUHE;")) { boolean istTruhe = zeile.startsWith("FTRUHE;"); String[] w = zeile.split(";", 4); if (w.length >= 4) { try { Map> wo = istTruhe ? farm.truhen : farm.gefarmt; List reihe = wo.get(w[3]); if (reihe == null) { reihe = new ArrayList(); wo.put(w[3], reihe); } reihe.add(new Farm.Punkt(Long.parseLong(w[1]), Long.parseLong(w[2]))); } catch (Exception ignoriert) { } } continue; } if (zeile.startsWith("WEB;")) { String[] w = zeile.split(";", 3); if (w.length >= 2) { webSchluessel = w[1]; } if (w.length >= 3 && !w[2].isEmpty()) { webAdresse = w[2]; } continue; } if (zeile.startsWith("ZIEL;")) { String[] z = zeile.split(";", 5); if (z.length >= 5) { try { sparziel = Long.parseLong(z[1]); guthaben = Long.parseLong(z[2]); rechnerPreis = Double.parseDouble(z[3]); rechnerItem = z[4]; } catch (Exception ignoriert) { } } continue; } String[] teile = zeile.split(";", 6); if (teile.length < 6) { continue; } BlockPos ort = new BlockPos( Integer.parseInt(teile[0]), Integer.parseInt(teile[1]), Integer.parseInt(teile[2])); Truhe t = truhen.get(ort); if (t == null) { t = new Truhe(ort); truhen.put(ort, t); } t.gesehen = Long.parseLong(teile[3]); t.inhalt.put(teile[5], Integer.parseInt(teile[4])); } l.close(); } catch (Exception ignoriert) { // Kaputte Datei -> lieber leer anfangen als abstuerzen. } } }