/* * Engine Alpha ist eine anfängerorientierte 2D-Gaming Engine. * * Copyright (c) 2011 - 2014 Michael Andonie and contributors. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package ea; import ea.internal.collision.Collider; import ea.internal.gui.Fenster; import ea.internal.util.Logger; import java.awt.*; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.util.ArrayList; /** * Zur Darstellung von Texten im Programmbildschirm. * <p/> * TODO: Review der ganzen Klasse (v.a. der Dokumentation) * * @author Michael Andonie */ public class Text extends Raum implements Leuchtend { private static final long serialVersionUID = -2145724725115670955L; /** * Ein Feld aller existenten Fonts, die im Hauptprojektordner gespeichert sind.<br /> Macht das * interne verwenden dieser Fonts moeglich, ohne das Vorhandensein der Fonts in den * Computerressourcen selber zur Voraussetzung zu haben. */ private static Font[] eigene; /** * static-Konstruktor.<br /> * hier werden die externen Fonts geladen. */ static { ArrayList<File> alleFonts = new ArrayList<>(); fontsEinbauen(alleFonts, new File(System.getProperty("user.dir"))); File[] unter = alleFonts.toArray(new File[alleFonts.size()]); eigene = new Font[unter.length]; for (int i = 0; i < unter.length; i++) { try { FileInputStream s = new FileInputStream(unter[i]); eigene[i] = Font.createFont(Font.TRUETYPE_FONT, s); s.close(); } catch (FileNotFoundException e) { Logger.error("Interner Lesefehler. Dies hätte unter keinen Umständen passieren dürfen."); } catch (FontFormatException e) { Logger.error("Das TrueType-Font-Format einer Datei (" + unter[i].getPath() + ") war nicht einlesbar!"); } catch (IOException e) { Logger.error("Lesefehler beim Laden der eigenen Fonts! Zugriffsrechte überprüfen."); } } } /** * Die Schriftgröße des Textes */ protected int groesse; /** * Die Schriftart (<b>fett, kursiv, oder fett & kursiv</b>).<br /> Dies wird dargestellt als * int.Wert:<br /> 0: Normaler Text<br /> 1: Fett<br /> 2: Kursiv<br /> 3: Fett & Kursiv */ protected int schriftart; /** * Der Wert des Textes. */ protected String inhalt; /** * Der Font der Darstellung */ protected Font font; /** * Die Farbe, in der der Text dargestellt wird. */ protected Color farbe; /** * Referenz auf die Farbe, die vor dem leuchten da war (zum wiederherstellen) */ private Color alte; /** * Gibt an, ob dieser Text gerade leuchtet */ private boolean leuchtet = false; /** * Der Zaehler fuer die Leuchtanimation */ private int leuchtzaehler; /** * Textanker: links, mittig oder rechts */ private Anker anker = Anker.LINKS; /** * Ebenefalls ein vereinfachter Konstruktor. Hierbei ist die Farbe "Weiss" und der Text weder * kursiv noch fett; weiterhin ist die Schriftgroesse automatisch 24. * * @param inhalt * Die Zeichenkette, die dargestellt werden soll * @param x * Die X-Koordinate des Anfangs * @param y * Die Y-Koordinate des Anfangs * @param fontName * Der Name des zu verwendenden Fonts.<br /> Wird hierfuer ein Font verwendet, der in dem * Projektordner vorhanden sein soll, <b>und dies ist immer und in jedem Fall zu * empfehlen</b>, muss der Name der Schriftart hier ebenfalls einfach nur eingegeben werden, * <b>nicht der Name der schriftart-Datei!!!!!!!!!!!!!!!!!!!!!!!!</b> */ public Text (String inhalt, float x, float y, String fontName) { this(inhalt, x, y, fontName, 24); } /** * Konstruktor ohne Farb- und sonderartseingabezwang. In diesem Fall ist die Farbe "Weiss" und * der Text weder kursiv noch fett. * * @param inhalt * Die Zeichenkette, die dargestellt werden soll * @param x * Die X-Koordinate des Anfangs * @param y * Die Y-Koordinate des Anfangs * @param fontName * Der Name des zu verwendenden Fonts.<br /> Wird hierfuer ein Font verwendet, der in dem * Projektordner vorhanden sein soll, <b>und dies ist immer und in jedem Fall zu * empfehlen</b>, muss der Name der Schriftart hier ebenfalls einfach nur eingegeben werden. * @param schriftGroesse * Die Groesse, in der die Schrift dargestellt werden soll */ public Text (String inhalt, float x, float y, String fontName, int schriftGroesse) { this(inhalt, x, y, fontName, schriftGroesse, 0, "Weiss"); } /** * Konstruktor für Objekte der Klasse Text<br /> Möglich ist es auch, Fonts zu laden, die im * Projektordner sind. Diese werden zu Anfang einmalig geladen und stehen dauerhaft zur * Verfügung. * * @param inhalt * Die Zeichenkette, die dargestellt werden soll * @param x * Die X-Koordinate des Anfangs * @param y * Die Y-Koordinate des Anfangs * @param fontName * Der Name des zu verwendenden Fonts.<br /> Wird hierfuer ein Font verwendet, der in dem * Projektordner vorhanden sein soll, <b>und dies ist immer und in jedem Fall zu * empfehlen</b>, muss der Name der Schriftart hier ebenfalls einfach nur eingegeben werden, * <b>nicht der Name der schriftart-Datei!</b> * @param schriftGroesse * Die Groesse, in der die Schrift dargestellt werden soll * @param schriftart * Die Schriftart dieses Textes. Folgende Werte entsprechen folgendem:<br /> 0: Normaler * Text<br /> 1: Fett<br /> 2: Kursiv<br /> 3: Fett & Kursiv <br /> <br /> Alles andere sorgt * nur fuer einen normalen Text. * @param farbe * Die Farbe, die für den Text benutzt werden soll. */ public Text (String inhalt, float x, float y, String fontName, int schriftGroesse, int schriftart, String farbe) { this.inhalt = inhalt; this.position = new Punkt(x, y); this.groesse = schriftGroesse; this.farbe = zuFarbeKonvertieren(farbe); if (schriftart >= 0 && schriftart <= 3) { this.schriftart = schriftart; } else { this.schriftart = 0; } setzeFont(fontName); super.leuchterAnmelden(this); } /** * Setzt einen neuen Font fuer den Text * * @param fontName * Der Name des neuen Fonts fuer den Text */ public void setzeFont (String fontName) { Font base = null; for (int i = 0; i < eigene.length; i++) { if (eigene[i].getName().equals(fontName)) { base = eigene[i]; break; } } if (base != null) { this.font = base.deriveFont(schriftart, groesse); } else { if (!Manager.fontExistiert(fontName)) { fontName = "SansSerif"; Logger.error("Achtung! Die gewuenschte Schriftart existiert nicht im Font-Verzeichnis dieses PC! " + "Wurde der Name falsch geschrieben? Oder existiert der Font nicht?"); } this.font = new Font(fontName, schriftart, groesse); } } /** * Einfacherer Konstruktor.<br /> Hierbei wird automatisch die Schriftart auf eine * Standartmaessige gesetzt * * @param inhalt * Die Zeichenkette, die dargestellt werden soll * @param x * Die X-Koordinate des Anfangs * @param y * Die Y-Koordinate des Anfangs * @param schriftGroesse * Die Groesse, in der die Schrift dargestellt werden soll */ public Text (String inhalt, float x, float y, int schriftGroesse) { this(inhalt, x, y, "SansSerif", schriftGroesse, 0, "Weiss"); } /** * Ein vereinfachter Konstruktor.<br /> Hierbei wird eine Standartschriftart, die Farbe weiss * und eine Groesse von 24 gewaehlt. * * @param inhalt * Der Inhalt des Textes * @param x * X-Koordinate * @param y * Y-Koordinate */ public Text (String inhalt, float x, float y) { this(inhalt, x, y, "SansSerif", 24, 0, "Weiss"); } /** * Ein vereinfachter parallerer Konstruktor.<br /> Diesen gibt es inhaltlich genauso bereits, * jedoch sind hier die Argumente vertauscht; dies dient der Praevention undgewollter falscher * Konstruktorenaufrufe. Hierbei wird eine Standartschriftart, die Farbe weiss und eine Groesse * von 24 gewaehlt. * * @param inhalt * Der Inhalt des Textes * @param x * X-Koordinate * @param y * Y-Koordinate */ public Text (float x, float y, String inhalt) { this(inhalt, x, y, "SansSerif", 24, 0, "Weiss"); } /** * Ein vereinfachter parallerer Konstruktor.<br /> Diesen gibt es inhaltlich genauso bereits, * jedoch sind hier die Argumente vertauscht; dies dient der Praevention undgewollter falscher * Konstruktorenaufrufe. Hierbei wird eine Standartschriftart und die Farbe weiss gewaehlt. * * @param inhalt * Der Inhalt des Textes * @param x * X-Koordinate * @param y * Y-Koordinate * @param schriftGroesse * Die Schriftgroesse, die der Text haben soll */ public Text (int x, int y, int schriftGroesse, String inhalt) { this(inhalt, x, y, "SansSerif", schriftGroesse, 0, "Weiss"); } // TODO: Mehr Verwirrung als Hilfe? private static void fontsEinbauen (final ArrayList<File> liste, File akt) { File[] files = akt.listFiles(); if (files != null) { for (int i = 0; i < files.length; i++) { if (files[i].equals(akt)) { Logger.error("Das Sub-Directory war das Directory selbst. Das darf nicht passieren!"); continue; } if (files[i].isDirectory()) { fontsEinbauen(liste, files[i]); } if (files[i].getName().toLowerCase().endsWith(".ttf")) { liste.add(files[i]); } } } } /** * TODO: Dokumentation */ public static Font holeFont (String fontName) { Font base = null; for (int i = 0; i < eigene.length; i++) { if (eigene[i].getName().equals(fontName)) { base = eigene[i]; break; } } if (base != null) { return base; } else { if (!Manager.fontExistiert(fontName)) { fontName = "SansSerif"; Logger.error("Achtung! Die gewuenschte Schriftart existiert weder als geladene Sonderdatei noch im Font-Verzeichnis dieses PC! " + "Wurde der Name falsch geschrieben? Oder existiert der Font nicht?"); } return new Font(fontName, 0, 12); } } /** * Sehr wichtige Methode!<br /> Diese Methode liefert als Protokoll an die Konsole alle Namen, * mit denen die aus dem Projektordner geladenen ".ttf"-Fontdateien gewaehlt werden koennen.<br * /> Diese Namen werden als <code>String</code>-Argument erwartet, wenn die eigens eingebauten * Fontarten verwendet werden sollen.<br /> Der Aufruf dieser Methode wird <b>UMGEHEND</b> * empfohlen, nach dem alle zu verwendenden Arten im Projektordner liegen, denn nur unter dem an * die Konsole projezierten Namen <b>koennen diese ueberhaupt verwendet werden</b>!!<br /> Daher * dient diese Methode der Praevention von Verwirrung, wegen "nicht darstellbarer" Fonts. */ public static void geladeneSchriftartenAusgeben () { Logger.info("Protokoll aller aus dem Projektordner geladener Fontdateien"); if (eigene.length == 0) { Logger.info("Es wurden keine \".ttf\"-Dateien im Projektordner gefunden"); } else { Logger.info("Es wurden " + eigene.length + " \".ttf\"-Dateien im Projektordner gefunden."); Logger.info("Diese sind unter folgenden Namen abrufbar:"); for (Font font : eigene) { Logger.info(font.getName()); } } } /** * Setzt den Inhalt des Textes.<br /> Parallele Methode zu <code>setzeInhalt()</code> * * @param inhalt * Der neue Inhalt des Textes * * @see #setzeInhalt(String) */ public void inhaltSetzen (String inhalt) { setzeInhalt(inhalt); } /** * Setzt den Inhalt des Textes. * * @param inhalt * Der neue Inhalt des Textes */ public void setzeInhalt (String inhalt) { this.inhalt = inhalt; } /** * Setzt die Schriftart. * * @param art * Die Repraesentation der Schriftart als Zahl:<br/> 0: Normaler Text<br /> 1: Fett<br /> 2: * Kursiv<br /> 3: Fett & Kursiv<br /> <br /> Ist die Eingabe nicht eine dieser 4 Zahlen, so * wird nichts geaendert.<br /> Parallele Methode zu <code>setzeSchriftart()</code> * * @see #setzeSchriftart(int) */ public void schriftartSetzen (int art) { setzeSchriftart(art); } /** * Setzt die Schriftart. * * @param art * Die Repraesentation der Schriftart als Zahl:<br/> 0: Normaler Text<br /> 1: Fett<br /> 2: * Kursiv<br /> 3: Fett & Kursiv<br /> <br /> Ist die Eingabe nicht eine dieser 4 Zahlen, so * wird nichts geaendert. */ public void setzeSchriftart (int art) { if (art >= 0 && art <= 3) { schriftart = art; aktualisieren(); } } /** * Klasseninterne Methode zum aktualisieren des Font-Objektes */ private void aktualisieren () { this.font = this.font.deriveFont(schriftart, groesse); } /** * Setzt die Fuellfarbe<br /> Parallele Methode zu <code>setzeFarbe()</code> * * @param farbe * Der Name der neuen Fuellfarbe * * @see #setzeFarbe(String) * @see #farbeSetzen(Farbe) */ public void farbeSetzen (String farbe) { setzeFarbe(farbe); } /** * Setzt die Fuellfarbe * * @param farbe * Der Name der neuen Fuellfarbe */ public void setzeFarbe (String farbe) { this.setzeFarbe(zuFarbeKonvertieren(farbe)); } /** * Setzt die Fuellfarbe * * @param c * Die neue Fuellfarbe */ public void setzeFarbe (Color c) { farbe = c; aktualisieren(); } /** * Setzt die Fuellfarbe * * @param f * Das Farbe-Objekt, das die neue Fuellfarbe beschreibt * * @see #farbeSetzen(String) */ public void farbeSetzen (Farbe f) { setzeFarbe(f.wert()); } /** * Setzt die Schriftgroesse.<br /> Wrappt hierbei die Methode <code>setzeGroesse</code>. * * @param groesse * Die neue Schriftgroesse * * @see #setzeGroesse(int) */ public void groesseSetzen (int groesse) { setzeGroesse(groesse); } /** * Setzt die Schriftgroesse * * @param groesse * Die neue Schriftgroesse */ public void setzeGroesse (int groesse) { this.groesse = groesse; aktualisieren(); } /** * Diese Methode gibt die aktuelle Groesse des Textes aus * * @return Die aktuelle Schriftgroesse des Textes * * @see #groesseSetzen(int) */ public int groesse () { return groesse; } /** * Setzt einen neuen Font fuer den Text.<br /> Parallele Methode zu <code>setzeFont()</code> * * @param name * Der Name des neuen Fonts fuer den Text * * @see #setzeFont(String) */ public void fontSetzen (String name) { setzeFont(name); } /** * Zeichnet das Objekt. * * @param g * Das zeichnende Graphics-Objekt * @param r * Das BoundingRechteck, dass die Kameraperspektive Repraesentiert.<br /> Hierbei soll * zunaechst getestet werden, ob das Objekt innerhalb der Kamera liegt, und erst dann * gezeichnet werden. */ @Override public void zeichnen (Graphics2D g, BoundingRechteck r) { if (!r.schneidetBasic(this.dimension())) { return; } super.beforeRender(g, r); FontMetrics f = Fenster.metrik(font); float x = position.x, y = position.y; if (anker == Anker.MITTE) { x = position.x - f.stringWidth(inhalt) / 2; } else if (anker == Anker.RECHTS) { x = position.x - f.stringWidth(inhalt); } g.setColor(farbe); g.setFont(font); g.drawString(inhalt, (int) (x - r.x), (int) (y - r.y + groesse)); super.afterRender(g, r); } /** * @return Ein BoundingRechteck mit dem minimal noetigen Umfang, um das Objekt <b>voll * einzuschliessen</b>. */ @Override public BoundingRechteck dimension () { FontMetrics f = Fenster.metrik(font); float x = position.x, y = position.y; if (anker == Anker.MITTE) { x = position.x - f.stringWidth(inhalt) / 2; } else if (anker == Anker.RECHTS) { x = position.x - f.stringWidth(inhalt); } return new BoundingRechteck(x, y, f.stringWidth(inhalt), f.getHeight()); } /** * {@inheritDoc} Collider wird direkt aus dem das <code>Raum</code>-Objekt umfassenden * <code>BoundingRechteck</code> erzeugt, dass über die <code>dimension()</code>-Methode * berechnet wird. */ @Override public Collider erzeugeCollider () { return erzeugeLazyCollider(); } /** * Diese Methode loescht alle eventuell vorhandenen Referenzen innerhalb der Engine auf dieses * Objekt, damit es problemlos geloescht werden kann.<br /> <b>Achtung:</b> zwar werden * hierdurch alle Referenzen geloescht, die <b>nur innerhalb</b> der Engine liegen (dies * betrifft vor allem Animationen etc), jedoch nicht die innerhalb eines * <code>Knoten</code>-Objektes!!!!!!!!!<br /> Das heisst, wenn das Objekt an einem Knoten liegt * (was <b>immer der Fall ist, wenn es auch gezeichnet wird (siehe die Wurzel des * Fensters)</b>), muss es trotzdem selbst geloescht werden, <b>dies erledigt diese Methode * nicht!!</b>.<br /> Diese Klasse ueberschreibt die Methode wegen des Leuchtens. */ @Override public void loeschen () { super.leuchterAbmelden(this); super.loeschen(); } /** * Setzt, ob dieses Leuchtend-Objekt leuchten soll.<br /> Ist dies der Fall, so werden immer * wieder schnell dessen Farben geaendert; so entsteht ein Leuchteffekt. * * @param leuchtet * Ob dieses Objekt nun leuchten soll oder nicht (mehr).<br /> <b>Achtung:</b> Die * Leuchtfunktion kann bei bestimmten Klassen sehr psychadelisch und aufreizend wirken! Daher * sollte sie immer mit Bedacht und in Nuancen verwendet werden! */ @Override public void leuchtetSetzen (boolean leuchtet) { if (this.leuchtet == leuchtet) { return; } this.leuchtet = leuchtet; if (leuchtet) { alte = farbe; } else { this.setzeFarbe(alte); } } /** * Fuehrt einen Leuchtschritt aus.<br /> Dies heisst, dass in dieser Methode die Farbe einfach * gewechselt wird. Da diese Methode schnell und oft hintereinander ausgefuehrt wird, soll so * der Leuchteffekt entstehen.<br /> <b>Diese Methode sollte nur innerhalb der Engine * ausgefuehrt werden! Also nicht fuer den Entwickler gedacht.</b> */ @Override public void leuchtSchritt () { leuchtzaehler++; leuchtzaehler %= farbzyklus.length; this.setzeFarbe(farbzyklus[leuchtzaehler]); } /** * Gibt wieder, ob das Leuchtet-Objekt gerade leuchtet oder nicht. * * @return <code>true</code>, wenn das Objekt gerade leuchtet, wenn nicht, dann ist die * Rueckgabe <code>false</code> */ @Override public boolean leuchtet () { return this.leuchtet; } /** * Gibt den aktuellen Textinhalt zurück. * * @return aktueller Textinhalt */ public String gibInhalt () { return inhalt; } /** * Gibt den aktuellen Anker zurück. * * @return aktueller Anker * * @see ea.Text.Anker * @see #setAnker(ea.Text.Anker) */ public Anker getAnker () { return anker; } /** * Setzt den Textanker. Dies beschreibt, wo sich der Text relativ zur x-Koordinate befindet. * Möglich sind: <li>{@code Text.Anker.LINKS},</li> <li>{@code Text.Anker.MITTE},</li> * <li>{@code Text.Anker.RECHTS}.</li> <br> <b>Hinweis</b>: {@code null} wird wie {@code * Anker.LINKS} behandelt! * * @param anker * neuer Anker * * @see ea.Text.Anker * @see #getAnker() */ public void setAnker (Anker anker) { this.anker = anker; } /** * Ein Textanker beschreibt, wo sich der Text relativ zu seiner x-Koordinate befindet. Möglich * sind: <li>{@code Anker.LINKS},</li> <li>{@code Anker.MITTE},</li> <li>{@code * Anker.RECHTS}.</li> * * @see #setAnker(ea.Text.Anker) * @see #getAnker() */ public enum Anker { LINKS, MITTE, RECHTS } }