/*
* 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.gui.*;
import ea.internal.phy.Physik;
import ea.internal.util.Logger;
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.filechooser.FileFilter;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Random;
/**
* Diese Klasse ist für die sofortige, einfache Verwendung der Engine verantwortlich.<br /> Aus ihr
* sollte die Klasse abgleitet werden, die die Spielorganisation beinhaltet.
*
* @author Michael Andonie
*/
public abstract class Game implements TastenReagierbar {
static {
System.setProperty("sun.java2d.opengl", "true"); // ok
System.setProperty("sun.java2d.d3d", "false"); // ok
System.setProperty("sun.java2d.noddraw", "true"); // set false if possible, linux
System.setProperty("sun.java2d.pmoffscreen", "false"); // set true if possible, linux
System.setProperty("sun.java2d.ddoffscreen", "true"); // ok, windows
System.setProperty("sun.java2d.ddscale", "true"); // ok, hardware accelerated image scaling on windows
}
/**
* Der Tickermanager. An ihm werden die Ticker angemeldet.
*/
public final Manager manager = new Manager();
/**
* Der Animations-Manager. Er kann Animationen organisieren.
*/
public final AnimationsManager animationsManager = AnimationsManager.getAnimationsManager();
/**
* Der Wurzel-Knoten. An ihm muessen direkt oder indirekt (ueber weitere Knoten) alle
* <code>Raum</code>-Objekte angemeldet werden, die auch (normal) gezeichnet werden sollen.
*/
public final Knoten wurzel;
/**
* Die statische Wurzel.<br /> Objekte, die an diesem Knoten angemeldet werden, werden ebenfalls
* gezeichnet, jedoch mit einem essentiellen Unterschied bei Verschiebung der
* <code>Kamera</code> werden diese nicht verschoben gezeichnet, sondern bleiben weiter
* (<b>statisch</b>) auf ihrer festen Position. Dies bietet sich zum Beispiel fuer eine
* Punkte-Anzeige etc an.
*/
public final Knoten statischeWurzel;
/**
* Die <code>Kamera</code> des Spiels.<br /> Dieser kann ueber <code>setzeFokus(Raum m)</code>
* ein bestimmtes Raum-Objekt immer im Zentrum zeigen. Es gibt auch weitere interessante
* Methoden dieser Klasse.<br /> Hierzu siehe <b>Handbuch oder Doku</b>.
*/
public final Kamera cam;
/**
* Dieser String ist <b>immer das korrekte, Systemabhaengige Pfadtrenner</b>-Literal, das ganz
* einfach bei Pfadangaben verwendet werden kann:<br /> <br /> <code> String verzeichnis =
* "meinOrdner" + "meinDarinLiegenderUnterordner" + "meineDatei.eaf"; </code>
*/
public final String pfadtrenner = DateiManager.sep;
/**
* Das Spielfenster
*/
private final Fenster fenster;
/**
* An diesem Knoten angelegte Objekte werden immer im Vordergrund sein.<br /> Dies wird zB fuer
* einen Abblendbildschirm verwendet.
*/
@SuppressWarnings ( "unused" )
private final Knoten superWurzel;
/**
* Gibt an, ob bei Escape-Druck das Spiel beendet werden soll.
*/
private final boolean exitOnEsc;
/**
* Der Zufallsgenerator des Spiels. Basiert nicht auf echtem Zufall.
*/
private final Random zufall = new Random();
/**
* Die eventuell aktive Blende; diese ueberblendet das Spielgeschehen.<br /> Dieses Rechteck
* sollte nicht in seinen Massen veraendert werden. Ansonsten koennte es die Wirkung als Blende
* verfehlen.
*/
private final Rechteck blende;
/**
* Der Font für die Fenstertexte
*/
private Font font;
/**
* Erstellt ein Spiel, bei dem automatisch beim Drücken von ESC alles beendet wird und ohne
* Titel.
*
* @param x
* Die Breite des Fensters
* @param y
* Die Hoehe des Fensters
* @param vollbild
* Ob das Fenster ein echtes Vollbild sein soll.<br /> Manche Computer unterstuetzen kein
* Vollbild, in diesem Fall wird ein moeglichst grosses Fenster erzeugt.<br /> Im
* Vollbildmodus nimmt das Fenster den gesamten Bildschirm ein, die Eingabeparameter
* <code>x</code> und <code>y</code> beschreiben dann die Masse dieses Fensters, <b>der
* Bildschirm wird also an die gewuenschte X-Y-Groesse angepasst!!!</b>
*/
public Game (int x, int y, boolean vollbild) {
this(x, y, "", vollbild, true);
}
/**
* Konstruktor fuer Objekte der Klasse Game. Erstellt unter anderem ein Fenster, das harmonisch
* im Spielbildschirm liegt.
*
* @param x
* Die Breite des Fensters
* @param y
* Die Hoehe des Fensters
* @param titel
* Der Titel des Spielfensters
* @param vollbild
* Ob das Fenster ein echtes Vollbild sein soll.<br /> Manche Computer unterstuetzen kein
* Vollbild, in diesem Fall wird ein moeglichst grosses Fenster erzeugt.<br /> Im
* Vollbildmodus nimmt das Fenster den gesamten Bildschirm ein, die Eingabeparameter
* <code>x</code> und <code>y</code> beschreiben dann die Masse dieses Fensters, <b>der
* Bildschirm wird also an die gewuenschte X-Y-Groesse angepasst!!!</b>
* @param exitOnEsc
* Ist dieser Wert <code>true</code>, so wird das Spiel automatisch beendet, wenn die
* "Escape"-Taste gedrueckt wurde. Dies bietet sich vor allem an, wenn das Spiel ein Vollbild
* ist oder die Maus aufgrund der Verwendung einer Maus im Spiel nicht auf das "X"-Symbol des
* Fensters geklickt werden kann, wodurch der Benutzer im Spiel "gefangen" waere und <b>dies
* ist etwas unbeschreiblich aergerliches fuer den Spielbenutzer!!!!!!!!!!!</b>
*/
public Game (int x, int y, String titel, boolean vollbild, boolean exitOnEsc) {
this(x, y, titel, vollbild, exitOnEsc, -1, -1);
}
/**
* Groesster Konstruktor fuer Objekte der Klasse Game.
*
* @param x
* Die Breite des Fensters
* @param y
* Die Hoehe des Fensters
* @param titel
* Der Titel des Spielfensters
* @param vollbild
* Ob das Fenster ein echtes Vollbild sein soll.<br /> Manche Computer unterstuetzen kein
* Vollbild, in diesem Fall wird ein moeglichst grosses Fenster erzeugt.<br /> Im
* Vollbildmodus nimmt das Fenster den gesamten Bildschirm ein, die Eingabeparameter
* <code>x</code> und <code>y</code> beschreiben dann die Masse dieses Fensters, <b>der
* Bildschirm wird also an die gewuenschte X-Y-Groesse angepasst!!!</b>
* @param exitOnEsc
* Ist dieser Wert <code>true</code>, so wird das Spiel automatisch beendet, wenn die
* "Escape"-Taste gedrueckt wurde. Dies bietet sich vor allem an, wenn das Spiel ein Vollbild
* ist oder die Maus aufgrund der Verwendung einer Maus im Spiel nicht auf das "X"-Symbol des
* Fensters geklickt werden kann, wodurch der Benutzer im Spiel "gefangen" waere und <b>dies
* ist etwas unbeschreiblich aergerliches fuer den Spielbenutzer!!!!!!!!!!!</b>
* @param fensterX
* Die X-Koordinate der linken oberen Ecke des Fensters auf dem Computerbildschirm
* @param fensterY
* Die Y-Koordinate der linken oberen Ecke des Fensters auf dem Computerbildschirm
*/
public Game (int x, int y, String titel, boolean vollbild, boolean exitOnEsc, int fensterX, int fensterY) {
fenster = new Fenster(x, y, titel, vollbild, fensterX, fensterY);
this.exitOnEsc = exitOnEsc;
cam = fenster.getCam();
cam.wurzel().add(wurzel = new Knoten(), superWurzel = new Knoten());
blende = fenster.fenstermasse().ausDiesem();
blende.farbeSetzen(new Farbe(255, 255, 255, 190));
blende.sichtbarSetzen(false);
statischeWurzel = fenster.getStatNode();
statischeWurzel.add(blende);
fenster.anmelden(this);
try {
fenster.setIconImage(ImageIO.read(getClass().getResourceAsStream("/assets/favicon.png")));
} catch (Exception e) {
Logger.warning("Standard-Icon konnte nicht geladen werden.");
}
}
/**
* Vereinfachter Konstruktor.<br /> Hierbei gilt automatisch, dass das Fenster <b>beim
* Tastendruck auf "Escape" beendet wird</b>, sowie dass kein Vollbild aktiviert wird.<br /> Ist
* dies nicht gewuenscht, so muss der komplexere Konstruktor aufgerufen werden. Es wird kein
* Fenstertitel erstellt.
*
* @param x
* Die Breite des Fensters
* @param y
* Die Hoehe des Fenstsers
*/
public Game (int x, int y) {
this(x, y, "");
}
/**
* Vereinfachter Konstruktor.<br /> Hierbei gilt automatisch, dass das Fenster <b>beim
* Tastendruck auf "Escape" beendet wird</b>, sowie dass kein Vollbild aktiviert wird.<br /> Ist
* dies nicht gewuenscht, so muss der komplexere Konstruktor aufgerufen werden.
*
* @param x
* Die Breite des Fensters
* @param y
* Die Hoehe des Fenstsers
* @param titel
* Der Titel des Spielfensters
*/
public Game (int x, int y, String titel) {
this(x, y, titel, false, true);
}
/**
* Parameterloser Alternativkonstruktor.<br /> Hierbei wird automatisch ein Spielfenster der
* Groesse 500 auf 500 (kein Vollbild) erstellt, das bei Tastendruck auf "Escape" beendet wird.
*/
public Game () {
this(500, 500, "", false);
}
/**
* Etwas vereinfachter Konstruktor.<br /> Hierbei gilt automatisch, dass das Fenster <b>beim
* Tastendruck auf "Escape" beendet wird</b>.<br /> Ist dies nicht gewuenscht, so muss der
* komplexere Konstruktor aufgerufen werden.
*
* @param x
* Die Breite des Fensters
* @param y
* Die Hoehe des Fenstsers
* @param titel
* Der Titel des Spielfensters
* @param vollbild
* Ob das Fenster ein echtes Vollbild sein soll.<br /> Manche Computer unterstuetzen kein
* Vollbild, in diesem Fall wird ein moeglichst grosses Fenster erzeugt.<br /> Im
* Vollbildmodus nimmt das Fenster den gesamten Bildschirm ein, die Eingabeparameter
* <code>x</code> und <code>y</code> beschreiben dann die Masse dieses Fensters, <b>der
* Bildschirm wird also an die gewuenschte X-Y-Groesse angepasst!!!</b>
*/
public Game (int x, int y, String titel, boolean vollbild) {
this(x, y, titel, vollbild, true);
}
/**
* Setzt das übergebene Bild als Icon des Fensters
*
* @param icon
* zu setzendes Icon
*/
public void iconSetzen (Bild icon) {
fenster.setIconImage(icon.bild());
}
/**
* Die aus <code>TastenReagierbar</code> implemetierte Methode zum Reagieren auf einen
* einfachen, einmaligen Tastendruck.
*
* @param code
* Der Code dieser Taste zu den Codes:<br /> Siehe http://engine-alpha.org/wiki/Tastaturtabelle
* für eine vollständige Tabelle
*
* @see ea.Taste
*/
public void reagieren (int code) {
if (exitOnEsc && code == Taste.ESCAPE) {
beenden();
}
tasteReagieren(code);
}
/**
* Diese Methode beendet das Spiel gaenzlich.<br /> Das heisst, dass das Fenster geschlossen,
* alle belegten Ressourcen freigegeben und auch die virtuelle Maschine von JAVA beendet
* wird.<br /> Also <b>beendet diese Methode die gesamte Applikation</b>!
*
* @see #schliessen()
*/
public void beenden () {
fenster.loeschen();
}
/**
* Diese Methode wird von der Klasse automatisch aufgerufen, sobald eine Taste einfach gedrueckt
* wurde.<br /> Sie wird dann erst wieder aufgerufen, wenn die Taste erst losgelassen und dann
* wieder gedreuckt wurde.<br /> Sollte allerdings eine Methode vonnoeten sein, die immer wieder
* in Regelmaessigen abstaenden aufgerufen wird, solange die Taste <b>heruntergedrueckt ist, so
* bietet sich dies im Interface <code>TasteGedruecktReagierbar</code> an</b>.
*
* @param code
* Code der gedrückten Taste<br />Siehe http://engine-alpha.org/wiki/Tastaturtabelle für eine
* vollständige Tabelle
*
* @see ea.TastenReagierbar
*/
public abstract void tasteReagieren (int code);
/**
* Fordert vom Benutzer eine Texteingabe (maximal 40 Zeichen) durch ein neues Fenster.<br />
* Dieses Fenster muss erst wieder geschlossen werden, damit das Spielfenster wieder in den
* Vordergrund ruecken kann. Diese Methode ist auch erst dann zu ende, wenn das Fenster
* geschlossen wurde.<br /> <br /> <b>Achtung:<br /> Bei dem Einsatz zusaetzlicher Fenster
* sollte vor dem Oeffnen eines solchen der Spielbetrieb angehalten werden, da das Fenster
* nunmehr den Vordergrund abdeckt und die Aufmerksamkeit und den Einfluss (Tastatur, Maus) auf
* das Spiel wegnimmt.</b>
*
* @param nachricht
* Ein Text, der ebenfalls angezeigt wird und erlaeutern sollte, wozu die Eingabe dient, und
* was die Eingabe sein sollte.
*
* @return Die Eingabe vom Benutzer.<br /> Wurde das Fenster ueber den "X"-Knopf "gewaltsam"
* geschlossen, so ist die Rueckgabe <code>null</code>.
*/
public String eingabeFordern (String nachricht) {
new Eingabe(fenster, nachricht, font);
return Eingabe.ergebnis;
}
/**
* Stellt eine Sicherheitsfrage, also eine Frage auf die mit "OK" oder "Abbrechen" geantwortet
* werden kann, in einem neuen Fenster.<br /> Dieses Fenster muss erst wieder geschlossen
* werden, damit das Spielfenster wieder in den Vordergrund ruecken kann. Diese Methode ist auch
* erst dann zu ende, wenn das Fenster geschlossen wurde.<br /> <br /> <b>Achtung:<br /> Bei dem
* Einsatz zusaetzlicher Fenster sollte vor dem Oeffnen eines solchen der Spielbetrieb
* angehalten werden, da das Fenster nunmehr den Vordergrund abdeckt und die Aufmerksamkeit und
* den Einfluss (Tastatur, Maus) auf das Spiel wegnimmt.</b>
*
* @param frage
* Die Frage, die im Fenster angezeigt wird.
*
* @return <code>true</code>, wenn die Frage mit "OK" beantwortet wurde; <code>false</code>,
* wenn die Frage mit "Abbrechen" beantwortet oder das Fenster geschlossen wurde.
*/
public boolean sicherheitsFrage (String frage) {
new Frage(fenster, frage, false, font);
return Frage.ergebnis;
}
/**
* Stellt eine einfache Frage, also eine Frage, auf die mit "Ja" oder "Nein" geantwortet werden
* kann, in einem neuen Fenster.<br /> Dieses Fenster muss erst wieder geschlossen werden, damit
* das Spielfenster wieder in den Vordergrund ruecken kann. Diese Methode ist auch erst dann zu
* ende, wenn das Fenster geschlossen wurde.<br /> <br /> <b>Achtung:<br /> Bei dem Einsatz
* zusaetzlicher Fenster sollte vor dem Oeffnen eines solchen der Spielbetrieb angehalten
* werden, da das Fenster nunmehr den Vordergrund abdeckt und die Aufmerksamkeit und den
* Einfluss (Tastatur, Maus) auf das Spiel wegnimmt.</b>
*
* @param frage
* Die Frage, die im Fenster angezeigt wird.
*
* @return <code>true</code>, wenn die Frage mit "Ja" beantwortet wurde; <code>false</code>,
* wenn die Frage mit "Nein" beantwortet oder das Fenster geschlossen wurde.
*/
public boolean frage (String frage) {
new Frage(fenster, frage, true, font);
return Frage.ergebnis;
}
/**
* Schickt eine einfache Nachricht in einem Fenster nach draussen.<br /> Dieses Fenster muss
* erst wieder geschlossen werden, damit das Spielfenster wieder in den Vordergrund ruecken
* kann. Diese Methode ist auch erst dann zu ende, wenn das Fenster geschlossen wurde.<br /> <br
* /> <b>Achtung:<br /> Bei dem Einsatz zusaetzlicher Fenster sollte vor dem Oeffnen eines
* solchen der Spielbetrieb angehalten werden, da das Fenster nunmehr den Vordergrund abdeckt
* und die Aufmerksamkeit und den Einfluss (Tastatur, Maus) auf das Spiel wegnimmt.</b>
*
* @param nachricht
* Die Nachricht, die Angezeigt werden soll
*/
public void nachrichtSchicken (String nachricht) {
new Nachricht(fenster, true, nachricht, font);
}
/**
* Öffnet ein titelloses Fenster, das die Highscores des Spiels anzeigt.
*
* @param namen
* Die Namen der Liste als Array. Von <b>Index 0 als dem besten</b> bis zum schlechtesten auf
* der Liste
* @param punkte
* Die Punktestaende der Liste als Array. Von <b>Index 0 als dem besten</b> bis zum
* schlechtesten auf der Liste
*
* @see #highscoreAnzeigen(String[], int[])
*/
public void highscoreAnzeigen (String[] namen, int[] punkte) {
highscoreAnzeigen(namen, punkte, "");
}
/**
* Öffnet ein Fenster, das die Highscores des Spiels anzeigt.
*
* @param namen
* Die Namen der Liste als Array. Von <b>Index 0 als dem besten</b> bis zum schlechtesten auf
* der Liste
* @param punkte
* Die Punktestaende der Liste als Array. Von <b>Index 0 als dem besten</b> bis zum
* schlechtesten auf der Liste
* @param fenstertitel
* Der Titel des sich oeffnenden Fensters. Dieser Parameter kann weggelassen werden, hierfuer
* gibt es eine alternative Methode, die diesen Titel nicht erwartet.
*/
public void highscoreAnzeigen (String[] namen, int[] punkte, String fenstertitel) {
new HighScoreFenster(fenster, fenstertitel, namen, punkte, font);
}
/**
* Gibt ein <b>zufälliges</b> <code>boolean</code>-Attribut zurück.<br /> Mit relativ
* zuverlässiger Sicherheit sind die Wahrscheinlichkeiten für <code>false</code> und
* <code>true</code> gleich groß.
*
* @return <code>false</code> oder <code>true</code>, mit gleich hoher Wahrscheinlichkeit.
*/
public boolean zufallsBoolean () {
return zufall.nextBoolean();
}
/**
* Gibt ein <b>zufälliges</b> <code>int</code>-Attribut zwischen <code>0</code> und einer
* festgelegten Obergrenze zurück.<br /> Mit relativ zuverlässiger Sicherheit sind die
* Wahrscheinlichkeiten für die Werte zwischen <code>0</code> und der Obergrenze gleich groß.
*
* @param obergrenze
* Die höchste Zahl, die vorkommen kann.<br /> <b>Die Zahl kann also ebenfalls Ergebnis der
* Rückgabe sein!</b>
*
* @return Eine Zahl zwischen 0 (inklusiv) und der Obergrenze (inklusiv).<br /> Bei Eingabe
* einer negativen Zahl ist das Ergebnis 0.
*/
public int zufallsZahl (int obergrenze) {
if (obergrenze < 0) {
throw new IllegalArgumentException("Achtung! Für eine Zufallszahl muss die definierte Obergrenze (die inklusiv in der Ergebnismenge ist) eine nichtnegative Zahl sein!");
}
return zufall.nextInt(obergrenze + 1);
}
/**
* Beendet dieses Game auf softe weise:<br /> - Das Fenster wird geschlossen<br /> - Die Physik
* wird beendet (alle bestehenden Raum-Objekte werden automatisch neutral<br /> - Alle
* Animationen werden beendet<br /> - Der <b>ABER:</b><br /> -> Die virtuelle Maschine wird
* <b>nicht</b> beendet.
*/
public void schliessen () {
manager.kill();
fenster.loeschen();
}
/**
* Dieser Methodenaufruf dauert eine bestimmte Zeit. So kann man sozusagen eine gewisse Zeit
* "Pause machen" und warten.<br /> <br /> <b>ACHTUNG!</b><br /> Der Aufruf dieser Methode haelt
* technisch gesehen diesen <i>Thread</i> an, das bedeutet zum Beispiel, dass - sollte dies in
* einer Tick-Methode ausgefuehrt werden, <b>alle anderen Ticker nicht aufgerufen werden, bevonr
* diese Methode beendet ist</b>. <br /> <br /> <i>Daher sollte diese Methode nur mit Bedacht
* verwendet werden!</i>
*
* @param millisekunden
* Die Anzahl an Millisekunden, die dieser Methodenaufruf dauert. So lange "wartet" man also
* durch den Aufruf dieser Methode.
*/
public void warten (int millisekunden) {
try {
Thread.sleep(millisekunden);
} catch (InterruptedException e) {
Logger.error(e.getLocalizedMessage());
}
}
/**
* Setzt den Font, der ab sofort von den Fenstern standartmaessig verwendet wird mit einer
* Standartgroesse von 12.
*
* @param fontname
* Der Name des zu verwendenden Fonts. <br /> Ein Blick auf das <b>Fontprotokoll</b> (in der
* Klasse <code>Text</code> ist empfehlenswert!
*
* @see Text
*/
public void fensterFontSetzen (String fontname) {
fensterFontSetzen(fontname, 12);
}
/**
* Setzt den Font, der ab sofort von den Fenstern standartmaessig verwendet wird.
*
* @param fontname
* Der Name des zu verwendenden Fonts. <br /> Ein Blick auf das <b>Fontprotokoll</b> (in der
* Klasse <code>Text</code> ist empfehlenswert!
* @param schriftgroesse
* Die Schriftgroesse, in der die texte dargestellt werden sollen.
*
* @see Text
*/
public void fensterFontSetzen (String fontname, int schriftgroesse) {
this.font = Text.holeFont(fontname).deriveFont(0, schriftgroesse);
}
/**
* Minimiert das Fenster.<br /> Dadurch wird es in die Taskleiste hinein minimiert.
*/
public void fensterMinimieren () {
fenster.minimieren();
}
/**
* Maximiert das Fenster.<br /> Dadurch wird es - sofern es sich in der Taskleiste minimiert
* befindet - wieder maximiert.
*/
public void fensterMaximieren () {
fenster.maximieren();
}
/**
* Meldet ein "<code>TastenReagierbar</code>"-Objekt an. Ab diesem Moment wird seine
* "<code>reagieren</code>"-Methode immer dann aufgerufen, wenn eine Taste heruntergedrueckt
* wird.
*
* @param g
* Das anzumeldende <code>TastenReagierbar</code>-Objekt
*/
public void tastenReagierbarAnmelden (TastenReagierbar g) {
if(g instanceof Game) {
Logger.error("Der Eingabe-Parameter g leitet sich von der Klasse Game ab. Das würde für einen"
+ " internen Fehler sorgen und ist daher nicht möglich. Stattdessen kann man die tasteReagieren-"
+ "Methode verwenden oder über eine andere mit diesem Interface den selben Effekt erzeugen.");
return;
}
fenster.anmelden(g);
}
/**
* Prüft, ob eine bestimmte Taste gerade jetzt heruntergedrückt wird.
*
* @param code
* der Code der zu prüfenden Taste.
*
* @return <code>true</code>, falls die gewählte Taste gerade jetzt heruntergedrückt wird. Sonst
* <code>false</code>.
*/
public boolean tasteGedrueckt (int code) {
return fenster.istGedrueckt(code);
}
/**
* Meldet ein <code>TastenLosgelassenReagierbar</code>-Objekt an. Ab diesem Moment wird seine
* "<code>tasteGedrueckt</code>"-Methode immer aufgerufen, wenn eine Taste losgelassen wird.
*
* @param g
* Das anzumeldende <code>TastenLosgelassenReagierbar</code>-Objekt.
*/
public void tastenLosgelassenReagierbarAnmelden (TastenLosgelassenReagierbar g) {
fenster.tastenLosgelassenAnmelden(g);
}
/**
* Meldet einen Ticker an. Nach Ausführung dieser Methode wird die <code>tick</code>-Methode
* dieses Tickers regelmäßig und unerlässlich im angegebenen Intervall ausgeführt.
* @param ticker Der Ticker, dessen <code>tick</code>-Methode ab sofort regelmäßig
* ausgeführt werden soll.
* @param intervall Das Intervall (<i>in Millisekunden</i>), in dem die <code>tick</code>-Methode
* des angegebenen Tickers ausgeführt werden soll. Zwischen zwei
* <code>tick</code>-Aufrufen vergehen <code>intervall</code> Millisekunden.
* @see #tickerAbmelden(Ticker)
* @see ea.Ticker
*/
public void tickerAnmelden(Ticker ticker, int intervall) {
this.manager.anmelden(ticker, intervall);
}
/**
* Meldet einen (aktiven) Ticker ab. Nach ausführen dieser Methode wird die <code>tick</code>-Methode
* des übergebenen Ticker-Objekts nicht mehr ausgeführt, bis der Ticker erneut angemeldet wird.
* @param ticker Der Ticker, dessen <code>tick</code>-Methode ab sofort nicht mehr ausgeführt
* werden soll. Ist dieser Ticker noch gar nicht angemeldet, wird eine Fehlermeldung
* ausgegeben.
* @see #tickerAnmelden(Ticker, int)
* @see ea.Ticker
*/
public void tickerAbmelden(Ticker ticker) {
this.manager.abmelden(ticker);
}
/**
* Meldet ein <code>KollisionsReagierbar</code>-Interface an. Ab sofort wird es mit dem
* spezifizierten <code>code</code> aufgerufen, sollten sich die <code>Raum</code>-Objekte
* <code>r1</code> und <code>r2</code> schneiden.
*
* @param reagierbar
* Das anzumeldende <code>KollisionsReagierbar</code>-Interface, das ab sofort von Kollisionen
* von <code>r1</code> und <code>r2</code> informiert werden soll.
* @param r1
* Ein <code>Raum</code>-Objekt
* @param r2
* Ein zweites <code>Raum</code>-Objekt
* @param code
* Ein beliebiger Code. Dieser kann verwendet werden, um mit einem Interface mehrere
* Kollisionen <i>unterscheidbar</i> zu behandeln. Er wird im Aufruf der
* <code>kollision(int)</code> als Parameter übergeben.
*/
public void kollisionsReagierbarAnmelden (KollisionsReagierbar reagierbar, Raum r1, Raum r2, int code) {
Physik.getPhysik().anmelden(reagierbar, r1, r2, code);
}
/**
* Meldet ein Mausobjekt an.<br /> Ab sofort wird die anzumeldende Maus im Fenster dargestellt
* und Klicks werden auf die Maus uebertragen.
*
* @param maus
* Die anzumeldende Maus
*
* @see Maus
*/
public void mausAnmelden (Maus maus) {
mausAnmelden(maus, false);
}
/**
* Meldet ein Mausobjekt an.<br /> Ab sofort wird die anzumeldende Maus im Fenster dargestellt
* und Klicks werden auf die Maus uebertragen.
*
* @param maus
* Die anzumeldende Maus
* @param listenerUebernehmen
* Ist dieser Wert <code>true</code>, so uebernimmt die neue Maus <b>alle Listener der alten
* Maus</b>
*
* @see Maus
*/
public void mausAnmelden (Maus maus, boolean listenerUebernehmen) {
if (maus == null) {
Logger.error("Die anzumeldende Maus war ein nicht instanziertes Objekt (sprich: null)!");
return;
}
Maus alteMaus = fenster.getMaus();
fenster.mausLoeschen();
if (alteMaus != null && listenerUebernehmen) {
maus.uebernehmeAlleListener(alteMaus);
}
fenster.anmelden(maus);
}
/**
* Gibt ein BoundingRechteck zurueck, dass die Masse des Fensters beschreibt.<br /> Die Hoehe
* und Breite geben die Hoehe und Breite des Fensters wieder. Die Position ist immer (0|0), da
* dies nicht relevant ist fuer die Masse des Fensters.
*
* @return Das besagte BoundingRechteck mit den Fenstermassen.
*/
public BoundingRechteck fensterGroesse () {
return fenster.fenstermasse();
}
/**
* Setzt einen Hintergrund fuer das Spiel.<br /> Das kann ein beliebiges
* <code>Raum</code>-Objekt sein, vorwiegend bieten sich jedoch Bilder an.<br /> Dieses Objekt
* wird dann immer im absoluten Hintergrund sein und wird auch weiter entfernt wirken, wenn
* Bewegungen stattfinden. Daher sollte dieses Objekt nicht in Berechnungen, wie Kollisionstests
* oder den Physik-Modus, eingebunden werden!<br /> <br /> <br /> <br /> Soll der Hintergrund
* etwas besonderes sein, das aus vielen Objekten besteht, so bietet es sich an, all diese an
* einem Knoten zu binden und diesen Knoten hier anzumelden.
*
* @param m
* Das Raum-Objekt, das ab jetzt der Hintergrund sein wird.
*/
public void hintergrundSetzen (Raum m) {
fenster.hintergrundAnmelden(m);
}
/**
* Setzt, ob das Spiel eine allueberstehende Ueberblende ausfuehren soll, die bis zum widerruf
* alles ueberdeckt.<br /> Diese ist niemals gaenzlich durchsichtig, da sie nur ueberblendet und
* nicht ueberdeckt.
*
* @param aktiv
* ob die Blende aktiviert oder deaktiviert werden soll
* @param farbe
* Die neue Farbe der Blende
*
* @see #ueberblendeSetzen(boolean)
*/
public void ueberblendeSetzen (boolean aktiv, Farbe farbe) {
if (farbe.undurchsichtig()) {
farbe = farbe.halbesAlpha();
}
blende.farbeSetzen(farbe);
ueberblendeSetzen(aktiv);
}
/**
* Setzt, ob das Spiel eine allueberstehende Ueberblende ausfuehren soll, die bis zum widerruf
* alles ueberdeckt.<br /> Diese ist niemals gaenzlich durchsichtig, da sie nur ueberblendet und
* nicht ueberdeckt.
*
* @param aktiv
* ob die Blende aktiviert oder deaktiviert werden soll
*/
public void ueberblendeSetzen (boolean aktiv) {
blende.sichtbarSetzen(aktiv);
}
/**
* Setzt, ob die gesamte Engine Alpha rechenintensiv, oder ressourcensparsam arbeiten soll.
* <p/>
* <b>IM REGELFALL GILT FOLGENDES:</b>
* <p/>
* Die Engine arbeitet auf einem Niveau, das laufzeittechnisch bei normalen Computern
* standardmäßig funktioniert. <i>Sollte es jedoch Probleme mit der Laufzeit geben (haengender
* Bildschirm; Zeitverzögerungen, die klar an der Engine und nicht am eigenen Projekt liegen
* usw.), so sollte mit dieser Methode die rechenintensive Arbeit ausgeschaltet werden.</i>
* <p/>
* <b>Standardmaessig ist die Arbeit rechenintensiv</b>.
*
* @param rechenintensiv
* Ist dieser Wert <code>true</code> (Standard), so ist die Arbeit der Engine exakt und
* rechenintensiv. Ist dieser Wert <code>false</code> (fuer langsame Computer zu empfehlen -
* aber immer erst testen!!!!), so ist die Arbeit ressourcensparender und ungenauer.
*/
public void rechenintensiveArbeitSetzen (boolean rechenintensiv) {
Raum.heavyComputingSetzen(rechenintensiv);
}
/**
* Macht vom aktuell sichtbaren Bereich (also dem von der Kamera derzeit erfassten Bereich)
* einen Screenshot.
*
* @param pfad
* Der Pfad, in dem das Bild gespeichert werden soll. Ein Wert wie {@code screenshot.jpg}
* speichert den Screenshot im Projektordner. Für eingabeabhängige Pfade kann
* <code>pfadAuswaehlen(String[])</code> benutzt werden.<br /> <br /> <b> ACHTUNG!! </b><br />
* Als Endung wird bisher nur ".jpg" und ".png" unterstützt!
*/
public void screenshot (String pfad) {
screenshot(pfad, cam.position());
}
/**
* Macht einen Screenshot von einem bestimmten Bildbereich und speichert diesen ab.
*
* @param pfad
* Der Pfad, in dem das Bild gespeichert werden soll. Ein Wert wie {@code screenshot.jpg}
* speichert den Screenshot im Projektordner. Für eingabeabhängige Pfade kann
* <code>pfadAuswaehlen(String[])</code> benutzt werden.<br /> <br /> <b> ACHTUNG!! </b><br />
* Als Endung wird bisher nur ".jpg" und ".png" unterstützt!
* @param ausschnitt
* Der Ausschnitt aus der Zeichenebene, der als Bild gespeichert werden soll als
* <code>BoundingRechteck</code>.
*
* @see #pfadAuswaehlen(java.lang.String[])
* @see #screenshot(java.lang.String, int, int, int, int)
*/
public void screenshot (final String pfad, final BoundingRechteck ausschnitt) {
final String ext = pfad.toLowerCase().substring(pfad.length() - 3);
if (!ext.equals("png") && !ext.equals("jpg")) {
throw new IllegalArgumentException("Pfad muss auf .jpg oder .png enden!");
}
new Thread() {
public void run () {
BufferedImage img = new BufferedImage((int) ausschnitt.breite, (int) ausschnitt.hoehe, BufferedImage.TYPE_INT_RGB);
Graphics2D g = img.createGraphics();
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED);
cam.wurzel().zeichnen(g, ausschnitt);
try {
ImageIO.write(img, ext, new File(pfad));
} catch (IOException e) {
Logger.error("Schreibfehler beim Speichern des Screenshots!");
e.printStackTrace();
}
}
}.start();
}
/**
* Macht einen Screenshot von einem bestimmten Bildbereich und speichert diesen ab,
*
* @param pfad
* Der Pfad, in dem das Bild gespeichert werden soll. Ein Wert wie {@code screenshot.jpg}
* speichert den Screenshot im Projektordner. Für eingabeabhängige Pfade kann
* <code>pfadAuswaehlen(String[])</code> benutzt werden.<br /> <br /> <b> ACHTUNG!! </b><br />
* Als Endung wird bisher nur ".jpg" und ".png" unterstützt!
* @param x
* Die X-Koordinate der oberen linken Ecke des Bildausschnitts.
* @param y
* Die Y-Koordinate der oberen linken Ecke des Bildausschnitts.
* @param breite
* Die gewuenschte Breite des Bildes
* @param hoehe
* Die gewuenschte Laenge des Bildes
*
* @see #pfadAuswaehlen(java.lang.String[])
* @see #screenshot(java.lang.String, ea.BoundingRechteck)
*/
public void screenshot (String pfad, int x, int y, int breite, int hoehe) {
screenshot(pfad, new BoundingRechteck(x, y, breite, hoehe));
}
/**
* Öffnet einen Such-Dialog, der die Auswahl eines Pfades ermöglicht.
*
* @param akzeptierteEndungen
* Eine Reihe beliebig vieler akzeptierter Endungen (Gross/Kleinschreibung vollkommen egal)<br
* /> z.B. : <code>pfadAuswaehlen("jpg", "bmp", "gif");</code><br /> Wird <code>null</code>
* als Parameter gegeben, so sind saemtliche Dateien waehlbar.<br /> z.B. :
* <code>pfadAuswaehlen(null);</code>
*
* @return Der Pfad der ausgewaehlten Datei als String. Ist "null", wenn kein Pfad ausgewaehlt
* wurde, sondern das Fenster manuell geschlossen wurde
*/
public String pfadAuswaehlen (final String... akzeptierteEndungen) {
FileFilter filter = new FileFilter() {
public boolean accept (File pathname) {
if (akzeptierteEndungen == null) {
return true;
} else if (pathname.isDirectory()) {
return true;
} else {
for (int i = 0; i < akzeptierteEndungen.length; i++) {
if (pathname.getName().toLowerCase().endsWith("." + akzeptierteEndungen[i].toLowerCase())) {
return true;
}
}
return false;
}
}
@Override
public String getDescription () {
if (akzeptierteEndungen == null) {
return "Alle Dateien wählbar";
} else {
String sel = "";
for (int i = 0; i < akzeptierteEndungen.length; i++) {
sel += "." + akzeptierteEndungen[i].toLowerCase() + " ";
}
return "Ausgewählte Formate (" + sel + ")";
}
}
};
JFileChooser ch = new JFileChooser();
ch.setFileFilter(filter);
int erg = ch.showOpenDialog(fenster);
if (erg == JFileChooser.APPROVE_OPTION) {
return ch.getSelectedFile().getPath();
} else {
return null;
}
}
/**
* Diese Methode kopiert eine beliebige Datei von einem Pfad in einen neuen.
*
* @param von
* Das Verzeichnis der Datei, die kopiert werden soll
* @param nach
* Das Verzeichnis, in das die Datei kopiert werden soll
* @param nameNeu
* Der Name der neuen Datei, die entstehen soll (z.B. "neuedatei.pdf")
*
* @return <code>true</code>, wenn das kopieren vollends erfolgreich war, sonst
* <code>false</code>.
*/
public boolean kopieren (String von, String nach, String nameNeu) {
try {
Files.copy(Paths.get(von), Paths.get(nach, nameNeu));
} catch (FileNotFoundException e) {
Logger.error("Die Datei konnte nicht gefunden werden!");
return false;
} catch (IOException e) {
Logger.error("Fehler beim Lesen!");
e.printStackTrace();
return false;
}
return true;
}
}