{
  "name": "Spielwerkstatt: nach Linearen Datenstrukturen",
  "settings": {
    "language": "Java",
    "libraries": [
      "scratch",
      "nrw"
    ]
  },
  "modules": [
    {
      "name": "Welt.java",
      "text": "/// Deine Spielwelt. Hier stellst du alles auf, was im Spiel vorkommt.\npublic class Welt extends Spielwelt {\n\n   /// So viele Münzen hat der Spieler schon eingesammelt.\n   private int punkte = 0;\n   /// So viele Münzen kann der Spieler erreichen.\n   private int anzahlMuenzen = 0;\n   /// So viele Münzen liegen hinter Mauern, an die niemand herankommt.\n   private int unerreichbar = 0;\n   /// So oft darf ein Gegner den Spieler noch erwischen.\n   private int leben = 3;\n   /// Hier steht der Spieler am Anfang und nach jedem Treffer.\n   private double startX;\n   private double startY;\n   private int startZeile;\n   private int startSpalte;\n   /// So viele Sekunden bleiben noch.\n   private double zeit = 30;\n   /// Solange das true ist, läuft das Spiel noch.\n   private boolean laeuft = true;\n   /// Die Anzeige oben links.\n   private Text anzeige;\n   /// Die besten Zeiten in Sekunden, aufsteigend sortiert. Nur die ersten anzahlBestzeiten Stellen zählen.\n   private double[] bestzeiten = new double[5];\n   private int anzahlBestzeiten = 0;\n   /// Die Anzeige der Bestenliste.\n   private Text bestenliste;\n\n   /// Meldungen, die noch gezeigt werden sollen - in der Reihenfolge, in der sie kamen.\n   private Queue<String> meldungen = new Queue<String>();\n   /// Die Zeile am unteren Rand, in der die Meldungen erscheinen.\n   private Text meldungszeile;\n   /// So viele Bilder lang bleibt die aktuelle Meldung noch stehen.\n   private int restBilder = 0;\n\n   /// Die Plätze, an denen der Spieler war. Oben liegt der jüngste.\n   private Stack<Platz> spur = new Stack<Platz>();\n   /// Zählt die Bilder, damit nur alle 15 Bilder ein Platz auf die Spur kommt.\n   private int bildZaehler = 0;\n   /// Die Spielfigur. Sie ist eine Variable der Welt, damit run() sie anhalten kann.\n   private Spieler held;\n\n   /// Der Plan der Welt: jede Zeichenkette eine Zeile, jedes Zeichen ein Feld von 48 × 48 Pixeln.\n   /// `#` Stein, `B` Baum, `T` Tanne, `F` Fels, `M` Münze, `H` Herz, `X` Falle,\n   /// `G` Schleim, `V` Fledermaus, `S` Start, `.` Wiese\n   private String[] plan = {\n      \"################\",\n      \"#S.....M.......#\",\n      \"#..B.......F...#\",\n      \"#......M.....V.#\",\n      \"#.M.....T....M.#\",\n      \"#..X......G..###\",\n      \"#..M..B....M.#M#\",\n      \"#..G......M.H###\",\n      \"################\"\n   };\n\n   /// true für jedes Feld, das der Spieler vom Start aus erreichen kann.\n   private boolean[][] erreichbar = new boolean[plan.length][plan[0].length()];\n\n   /// Was auf welchem Feld liegt. Ein leeres Feld enthält null.\n   /// Das Gitter und die Bühne verweisen auf dieselben Objekte.\n   private Gegenstand[][] gegenstaende = new Gegenstand[plan.length][plan[0].length()];\n\n   public Welt() {\n      this.addSound(\"muenze\", \"assets/audio/sounds/bonus/coin.ogg\");\n      held = new Spieler(\"boy\");\n\n      for (int z = 0; z < plan.length; z++) {\n         for (int s = 0; s < plan[z].length(); s++) {\n            char zeichen = plan[z].charAt(s);\n            double x = this.zuX(s);\n            double y = this.zuY(z);\n            if (zeichen == '#') {\n               this.setze(new Stein(), x, y);\n            } else if (zeichen == 'B') {\n               this.setze(new Pflanze(0, 0), x, y);\n            } else if (zeichen == 'T') {\n               this.setze(new Pflanze(32, 0), x, y);\n            } else if (zeichen == 'F') {\n               this.setze(new Pflanze(256, 128), x, y);\n            } else if (zeichen == 'X') {\n               this.setzeGegenstand(new Falle(), z, s);\n            } else if (zeichen == 'G') {\n               this.setze(new Schleim(), x, y);\n            } else if (zeichen == 'V') {\n               this.setze(new Fledermaus(), x, y);\n            } else if (zeichen == 'S') {\n               startX = x;\n               startY = y;\n               startZeile = z;\n               startSpalte = s;\n               held.setPosition(x, y);\n            }\n         }\n      }\n\n      this.markiere(startZeile, startSpalte);\n      this.verteileGegenstaende();\n\n      anzeige = new Text(\"\", 0, 200, 700);\n      anzeige.setTextSize(20);\n      anzeige.setTextColor(255, 255, 255);\n      this.add(anzeige);\n\n      meldungszeile = new Text(\"\", 0, -200, 700);\n      meldungszeile.setTextSize(18);\n      meldungszeile.setTextColor(255, 255, 255);\n      this.add(meldungszeile);\n\n      this.melde(\"Sammle alle Münzen, die du erreichen kannst!\");\n      if (unerreichbar > 0) {\n         this.melde(unerreichbar + \" Münzen liegen hinter Mauern. Lass sie liegen.\");\n      }\n      this.melde(\"Mit R gehst du deine Spur zurück.\");\n\n      bestenliste = new Text(\"\", 290, 140, 160);\n      bestenliste.setTextSize(16);\n      bestenliste.setTextColor(255, 255, 255);\n      this.add(bestenliste);\n\n      this.add(held);\n   }\n\n   /// Legt auf jedes M im Plan eine Münze und auf jedes H ein Herz und zählt die Münzen.\n   private void verteileGegenstaende() {\n      anzahlMuenzen = 0;\n      unerreichbar = 0;\n      for (int z = 0; z < plan.length; z++) {\n         for (int s = 0; s < plan[z].length(); s++) {\n            char zeichen = plan[z].charAt(s);\n            if (zeichen == 'M') {\n               Muenze m = new Muenze();\n               this.setzeGegenstand(m, z, s);\n               if (erreichbar[z][s]) {\n                  anzahlMuenzen = anzahlMuenzen + 1;\n               } else {\n                  // Unerreichbare Münzen sind blass und zählen nicht mit.\n                  m.setTransparency(70);\n                  unerreichbar = unerreichbar + 1;\n               }\n            } else if (zeichen == 'H') {\n               this.setzeGegenstand(new Herz(), z, s);\n            }\n         }\n      }\n   }\n\n   /// Beginnt eine neue Runde: alle Münzen zurück, Zeit und Leben voll, Spieler an den Start.\n   private void neueRunde() {\n      this.remove(Muenze.class);\n      this.remove(Herz.class);\n      this.verteileGegenstaende();\n      punkte = 0;\n      leben = 3;\n      zeit = 30;\n      laeuft = true;\n      held.setPosition(startX, startY);\n      held.setTempo(3);\n      spur = new Stack<Platz>();\n   }\n\n   /// Stellt eine Meldung hinten an. Gezeigt wird sie erst, wenn alle früheren durch sind.\n   public void melde(String pText) {\n      meldungen.enqueue(pText);\n   }\n\n   /// Zeigt die vorderste Meldung etwa zwei Sekunden lang, dann die nächste.\n   private void zeigeMeldungen() {\n      if (restBilder > 0) {\n         restBilder = restBilder - 1;\n      } else if (!meldungen.isEmpty()) {\n         meldungszeile.showText(meldungen.front());\n         meldungen.dequeue();\n         restBilder = 120;\n      } else {\n         meldungszeile.showText(\"\");\n      }\n   }\n\n   /// Legt alle 15 Bilder den aktuellen Platz des Spielers auf die Spur.\n   private void merkeSpur() {\n      bildZaehler = bildZaehler + 1;\n      if (bildZaehler == 15) {\n         bildZaehler = 0;\n         spur.push(new Platz(held.getX(), held.getY()));\n      }\n   }\n\n   /// Setzt den Spieler auf den jüngsten Platz der Spur zurück und nimmt ihn von der Spur.\n   private void geheZurueck() {\n      if (!spur.isEmpty()) {\n         Platz p = spur.top();\n         spur.pop();\n         held.setPosition(p.getX(), p.getY());\n      }\n   }\n\n   /// Sortiert pSekunden in die Bestenliste ein, wie beim Sortieren durch Einfügen:\n   /// Alle langsameren Zeiten rücken eine Stelle nach hinten. Ist die Liste voll, fällt die letzte heraus.\n   private void trageEin(double pSekunden) {\n      int i = anzahlBestzeiten;\n      if (i == bestzeiten.length) {\n         i = i - 1;\n         if (bestzeiten[i] <= pSekunden) {\n            return;   // langsamer als alle in der vollen Liste\n         }\n      } else {\n         anzahlBestzeiten = anzahlBestzeiten + 1;\n      }\n      while (i > 0 && bestzeiten[i - 1] > pSekunden) {\n         bestzeiten[i] = bestzeiten[i - 1];\n         i = i - 1;\n      }\n      bestzeiten[i] = pSekunden;\n   }\n\n   /// Zeigt die Bestenliste an, eine Zeit je Zeile.\n   private void zeigeBestenliste() {\n      String text = \"Bestzeiten\";\n      for (int i = 0; i < anzahlBestzeiten; i++) {\n         text = text + \"\\n\" + (i + 1) + \". \" + (int) bestzeiten[i] + \" s\";\n      }\n      bestenliste.showText(text);\n   }\n\n   /// Mit der Leertaste beginnt nach dem Ende eine neue Runde.\n   public void whenKeyPressed(KeyCode pTaste) {\n      if (pTaste == KeyCode.SPACE && !laeuft) {\n         this.neueRunde();\n      } else if (pTaste == KeyCode.R && laeuft) {\n         this.geheZurueck();\n      }\n   }\n\n   /// Die x-Koordinate der Mitte von Spalte pSpalte. Spalte 0 liegt ganz links.\n   private double zuX(int pSpalte) {\n      return -360 + pSpalte * 48;\n   }\n\n   /// Die y-Koordinate der Mitte von Zeile pZeile. Zeile 0 liegt ganz oben.\n   private double zuY(int pZeile) {\n      return 192 - pZeile * 48;\n   }\n\n   /// Markiert das Feld in Zeile pZeile und Spalte pSpalte als erreichbar\n   /// und von dort aus alle Nachbarfelder, die keine Mauer sind (Flutfüllung).\n   private void markiere(int pZeile, int pSpalte) {\n      // Basisfälle: außerhalb des Plans, eine Mauer, oder schon markiert.\n      if (pZeile < 0 || pZeile >= plan.length || pSpalte < 0 || pSpalte >= plan[pZeile].length()) {\n         return;\n      }\n      if (this.istMauer(plan[pZeile].charAt(pSpalte)) || erreichbar[pZeile][pSpalte]) {\n         return;\n      }\n      erreichbar[pZeile][pSpalte] = true;\n      this.markiere(pZeile - 1, pSpalte);\n      this.markiere(pZeile + 1, pSpalte);\n      this.markiere(pZeile, pSpalte - 1);\n      this.markiere(pZeile, pSpalte + 1);\n   }\n\n   /// Liefert true, wenn pZeichen im Plan für etwas steht, durch das man nicht hindurchkommt.\n   private boolean istMauer(char pZeichen) {\n      return pZeichen == '#' || pZeichen == 'B' || pZeichen == 'T' || pZeichen == 'F';\n   }\n\n   /// Die Spalte, in der die x-Koordinate pX liegt.\n   private int zuSpalte(double pX) {\n      return (int) Math.round((pX + 360) / 48);\n   }\n\n   /// Die Zeile, in der die y-Koordinate pY liegt.\n   private int zuZeile(double pY) {\n      return (int) Math.round((192 - pY) / 48);\n   }\n\n   /// Legt pGegenstand auf das Feld in Zeile pZeile und Spalte pSpalte: auf die Bühne und ins Gitter.\n   private void setzeGegenstand(Gegenstand pGegenstand, int pZeile, int pSpalte) {\n      pGegenstand.setFeld(pZeile, pSpalte);\n      gegenstaende[pZeile][pSpalte] = pGegenstand;\n      this.setze(pGegenstand, this.zuX(pSpalte), this.zuY(pZeile));\n   }\n\n   /// Nimmt pGegenstand von der Bühne und aus dem Gitter.\n   /// Fehlt die zweite Zeile, steht im Gitter weiter ein Verweis auf ein Objekt, das man nicht mehr sieht.\n   public void entferne(Gegenstand pGegenstand) {\n      pGegenstand.remove();\n      gegenstaende[pGegenstand.getZeile()][pGegenstand.getSpalte()] = null;\n   }\n\n   /// Sagt allen Gegenständen auf den acht Nachbarfeldern des Spielers Bescheid.\n   private void meldeNachbarn() {\n      int zeile = this.zuZeile(held.getY());\n      int spalte = this.zuSpalte(held.getX());\n      for (int z = zeile - 1; z <= zeile + 1; z++) {\n         for (int s = spalte - 1; s <= spalte + 1; s++) {\n            boolean imGitter = z >= 0 && z < gegenstaende.length && s >= 0 && s < gegenstaende[z].length;\n            if (imGitter && gegenstaende[z][s] != null) {\n               gegenstaende[z][s].inDerNaehe();\n            }\n         }\n      }\n   }\n\n   /// Stellt pFigur an die Stelle (pX, pY) auf die Bühne.\n   private void setze(Sprite pFigur, double pX, double pY) {\n      pFigur.setPosition(pX, pY);\n      this.add(pFigur);\n   }\n\n   /// Liefert true, wenn alle Münzen eingesammelt sind.\n   private boolean istGewonnen() {\n      return punkte == anzahlMuenzen;\n   }\n\n   /// Hält das Spiel an und zeigt pMeldung an.\n   private void beende(String pMeldung) {\n      laeuft = false;\n      held.setTempo(0);\n      anzeige.showText(pMeldung);\n   }\n\n   /// Eine Münze meldet, dass sie eingesammelt wurde.\n   public void muenzeEingesammelt() {\n      punkte = punkte + 1;\n      this.playSound(\"muenze\");\n      if (punkte * 2 == anzahlMuenzen || punkte * 2 == anzahlMuenzen + 1) {\n         this.melde(\"Die Hälfte ist geschafft!\");\n      }\n   }\n\n   /// Ein Herz gibt ein Leben zurück, höchstens bis fünf.\n   public void heile() {\n      if (leben < 5) {\n         leben = leben + 1;\n         this.melde(\"Ein Leben mehr!\");\n      } else {\n         this.melde(\"Mehr als fünf Leben gehen nicht.\");\n      }\n   }\n\n   /// Ein Gegner oder eine Falle hat den Spieler erwischt: ein Leben weniger, zurück zum Start.\n   public void verletze() {\n      if (laeuft) {\n         leben = leben - 1;\n         held.setPosition(startX, startY);\n         // Die alte Spur führt nicht mehr vom Start weg, sie wird verworfen.\n         spur = new Stack<Platz>();\n         this.melde(\"Autsch! Zurück zum Start.\");\n         if (leben == 0) {\n            this.beende(\"Kein Leben mehr! \" + punkte + \" von \" + anzahlMuenzen + \" Münzen. Leertaste: neue Runde\");\n         }\n      }\n   }\n\n   /// Wird immer wieder ausgeführt, etwa 60-mal in der Sekunde.\n   public void run() {\n      this.zeigeMeldungen();\n      if (laeuft) {\n         this.merkeSpur();\n         zeit = zeit - 1.0 / 60;\n         this.meldeNachbarn();\n\n         if (this.istGewonnen()) {\n            this.trageEin(30 - zeit);\n            this.zeigeBestenliste();\n            this.beende(\"Gewonnen in \" + (int) (30 - zeit) + \" Sekunden! Leertaste: neue Runde\");\n         } else if (zeit <= 0) {\n            this.beende(\"Die Zeit ist um! \" + punkte + \" von \" + anzahlMuenzen + \" Münzen. Leertaste: neue Runde\");\n         } else {\n            anzeige.showText(\"Münzen: \" + punkte + \" von \" + anzahlMuenzen + \" (\" + unerreichbar + \" unerreichbar)   Leben: \" + leben + \"   Zeit: \" + (int) zeit);\n         }\n      }\n   }\n}\n"
    },
    {
      "name": "Main.java",
      "text": "/// Startet das Spiel.\nvoid main() {\n   // Pixelgrafik bleibt beim Vergrößern scharf.\n   Window.useTextureSampling(TextureSampling.POINT);\n   Window fenster = new Window(768, 432);\n   fenster.setStage(new Welt());\n}\n"
    },
    {
      "name": "Falle.java",
      "text": "/// Eine Stachelfalle. Sie ist versteckt, bis der Spieler direkt daneben steht,\n/// bleibt dann sichtbar und kostet jedes Mal ein Leben.\npublic class Falle extends Gegenstand {\n\n   public Falle() {\n      this.addCostume(\"falle\", \"assets/backgrounds/tilesets/tileset-dungeon.png\", 64, 16, 16, 16);\n      this.hide();\n   }\n\n   public void beruehrtVon(Welt pWelt) {\n      pWelt.verletze();\n   }\n\n   public void inDerNaehe() {\n      this.show();\n   }\n}\n"
    },
    {
      "name": "Fledermaus.java",
      "text": "/// Eine Fledermaus fliegt senkrecht auf und ab und kehrt an Hindernissen um.\npublic class Fledermaus extends Gegner {\n\n   private int tempo = 3;\n\n   public Fledermaus() {\n      super(\"assets/actor/monster/blue-bat/sprite-sheet.png\");\n   }\n\n   protected void bewege() {\n      this.changeY(tempo);\n      if (this.getTouchingSprite(Hindernis.class) != null) {\n         this.changeY(-tempo);\n         tempo = -tempo;\n      }\n   }\n}\n"
    },
    {
      "name": "Gegenstand.java",
      "text": "/// Etwas, das auf einem Feld des Plans liegt und etwas tut, wenn der Spieler es berührt.\n/// Was genau, legt jede Unterklasse selbst fest.\npublic abstract class Gegenstand extends AnimatedSprite {\n\n   /// Das Feld im Plan, auf dem der Gegenstand liegt.\n   private int zeile;\n   private int spalte;\n\n   public Gegenstand() {\n      this.setSize(300);\n   }\n\n   /// Wird aufgerufen, wenn der Spieler diesen Gegenstand berührt.\n   public abstract void beruehrtVon(Welt pWelt);\n\n   /// Wird aufgerufen, solange der Spieler auf einem Nachbarfeld steht. Normalerweise passiert nichts.\n   public void inDerNaehe() {\n   }\n\n   public void setFeld(int pZeile, int pSpalte) {\n      zeile = pZeile;\n      spalte = pSpalte;\n   }\n\n   public int getZeile() {\n      return zeile;\n   }\n\n   public int getSpalte() {\n      return spalte;\n   }\n}\n"
    },
    {
      "name": "Gegner.java",
      "text": "/// Ein Monster, das sich von selbst bewegt. Wie, legt jede Unterklasse in bewege() fest.\npublic abstract class Gegner extends AnimatedSprite {\n\n   /// pBild ist ein Bild aus assets/actor/monster/ im Aufbau der Figuren: eine Spalte je Blickrichtung.\n   public Gegner(String pBild) {\n      this.addAnimation(\"laufen\", pBild, 4, 16, 16, 0, true);\n      this.setAnimationInterval(200);\n      this.setSize(250);\n      // Nur die Füße zählen: So kann die Figur vor einem Baum stehen, ohne an ihm hängen zu bleiben.\n      this.setHitbox(4, 10, 12, 10, 12, 16, 4, 16);\n   }\n\n   public void run() {\n      this.playAnimation(\"laufen\");\n      this.bewege();\n   }\n\n   /// Bewegt den Gegner um einen Schritt.\n   protected abstract void bewege();\n}\n"
    },
    {
      "name": "Herz.java",
      "text": "/// Ein Herz gibt ein Leben zurück und verschwindet.\npublic class Herz extends Gegenstand {\n\n   public Herz() {\n      this.addCostume(\"herz\", \"assets/items/potion/heart.png\");\n   }\n\n   public void beruehrtVon(Welt pWelt) {\n      pWelt.entferne(this);\n      pWelt.heile();\n   }\n}\n"
    },
    {
      "name": "Hindernis.java",
      "text": "/// Fertig für dich: eine Figur, durch die der Spieler nicht hindurchlaufen kann.\n/// Ein Hindernis ist ein Sprite wie jedes andere. Sein Kostüm gibst du ihm selbst.\npublic class Hindernis extends Sprite {\n\n   public Hindernis() {\n      // Die Bilder sind winzig. Dreifach vergrößert passen sie zum Spieler.\n      this.setSize(300);\n   }\n\n   /// Wie addCostume von Sprite. Zusätzlich hält nur die untere Hälfte des Bildes auf,\n   /// also der Stamm eines Baums oder der Fuß eines Felsens. Hinter dem Rest kann man vorbeigehen.\n   public void addCostume(String pName, String pBild, int pX, int pY, int pBreite, int pHoehe) {\n      super.addCostume(pName, pBild, pX, pY, pBreite, pHoehe);\n      this.setHitbox(0, pHoehe / 2.0, pBreite, pHoehe / 2.0, pBreite, pHoehe, 0, pHoehe);\n   }\n}\n"
    },
    {
      "name": "Muenze.java",
      "text": "/// Eine Münze, die sich dreht, bis der Spieler sie einsammelt.\npublic class Muenze extends Gegenstand {\n\n   public Muenze() {\n      // Vier Bilder zu je 10 × 10 Pixeln nebeneinander.\n      this.addAnimation(\"drehen\", \"assets/items/treasure/coin2.png\", 4, 10, 10);\n      this.setAnimationInterval(150);\n   }\n\n   public void run() {\n      this.playAnimation(\"drehen\");\n   }\n\n   public void beruehrtVon(Welt pWelt) {\n      pWelt.entferne(this);\n      pWelt.muenzeEingesammelt();\n   }\n}\n"
    },
    {
      "name": "Pflanze.java",
      "text": "/// Eine große Pflanze oder ein Felsen, 32 × 32 Pixel aus dem Natur-Kachelbild.\n/// pBildX und pBildY geben die linke obere Ecke im Kachelbild an.\npublic class Pflanze extends Hindernis {\n\n   public Pflanze(int pBildX, int pBildY) {\n      this.addCostume(\"pflanze\", \"assets/backgrounds/tilesets/tileset-nature.png\", pBildX, pBildY, 32, 32);\n   }\n}\n"
    },
    {
      "name": "Platz.java",
      "text": "/// Eine Stelle auf der Bühne, an der der Spieler einmal war.\npublic class Platz {\n\n   private double x;\n   private double y;\n\n   public Platz(double pX, double pY) {\n      x = pX;\n      y = pY;\n   }\n\n   public double getX() {\n      return x;\n   }\n\n   public double getY() {\n      return y;\n   }\n}\n"
    },
    {
      "name": "Schleim.java",
      "text": "/// Ein Schleim kriecht waagerecht hin und her und kehrt an Hindernissen um.\npublic class Schleim extends Gegner {\n\n   private int tempo = 2;\n\n   public Schleim() {\n      super(\"assets/actor/monster/slime/slime.png\");\n   }\n\n   protected void bewege() {\n      this.changeX(tempo);\n      if (this.getTouchingSprite(Hindernis.class) != null) {\n         this.changeX(-tempo);\n         tempo = -tempo;\n      }\n   }\n}\n"
    },
    {
      "name": "Spieler.java",
      "text": "/// Die Figur, die du mit den Pfeiltasten steuerst.\n/// Sie bleibt an Hindernissen hängen, berührt Gegenstände und meldet der Welt, wenn ein Gegner sie erwischt.\npublic class Spieler extends AnimatedSprite {\n\n   /// So viele Pixel geht der Spieler in jedem Bild.\n   private int tempo = 3;\n\n   /// Erzeugt einen Spieler. pFigur ist ein Ordner in assets/actor/character/, zum Beispiel \"boy\".\n   public Spieler(String pFigur) {\n      String bild = \"assets/actor/character/\" + pFigur + \"/sprite-sheet.png\";\n      // Im Bild steht jede Blickrichtung in einer eigenen Spalte,\n      // die vier Schritte untereinander.\n      this.addAnimation(\"unten\", bild, 4, 16, 16, 0, true);\n      this.addAnimation(\"oben\", bild, 4, 16, 16, 1, true);\n      this.addAnimation(\"links\", bild, 4, 16, 16, 2, true);\n      this.addAnimation(\"rechts\", bild, 4, 16, 16, 3, true);\n      this.setAnimationInterval(150);\n      this.setSize(250);\n      // Nur die Füße zählen: So kann die Figur vor einem Baum stehen, ohne an ihm hängen zu bleiben.\n      this.setHitbox(4, 10, 12, 10, 12, 16, 4, 16);\n   }\n\n   public void run() {\n      double altX = this.getX();\n      double altY = this.getY();\n\n      if (this.isKeyPressed(KeyCode.RIGHT)) {\n         this.changeX(tempo);\n         this.playAnimation(\"rechts\");\n      } else if (this.isKeyPressed(KeyCode.LEFT)) {\n         this.changeX(-tempo);\n         this.playAnimation(\"links\");\n      } else if (this.isKeyPressed(KeyCode.UP)) {\n         this.changeY(tempo);\n         this.playAnimation(\"oben\");\n      } else if (this.isKeyPressed(KeyCode.DOWN)) {\n         this.changeY(-tempo);\n         this.playAnimation(\"unten\");\n      }\n\n      // In ein Hindernis hineingelaufen? Dann zurück auf den alten Platz.\n      if (this.getTouchingSprite(Hindernis.class) != null) {\n         this.setPosition(altX, altY);\n      }\n\n      Welt welt = (Welt) this.getStage();\n\n      // Was der Gegenstand tut, entscheidet der Gegenstand selbst.\n      Gegenstand g = this.getTouchingSprite(Gegenstand.class);\n      if (g != null) {\n         g.beruehrtVon(welt);\n      }\n\n      if (this.getTouchingSprite(Gegner.class) != null) {\n         welt.verletze();\n      }\n   }\n\n   /// Legt fest, wie viele Pixel der Spieler in jedem Bild geht. Mit 0 bleibt er stehen.\n   public void setTempo(int pTempo) {\n      tempo = pTempo;\n   }\n}\n"
    },
    {
      "name": "Spielwelt.java",
      "text": "/// Fertig für dich: die Wiese als Hintergrund und die Reihenfolge, in der gezeichnet wird.\npublic class Spielwelt extends Stage {\n\n   /// So viele Pixel ist eine Kachel auf der Bühne breit und hoch.\n   public static final int KACHEL = 48;\n\n   public Spielwelt() {\n      // Die Wiese ist ein einziges Bild, so groß wie die Bühne.\n      this.addBackdrop(\"wiese\", \"assets/backgrounds/wiese.png\");\n\n      // Wer weiter unten steht, ist näher am Betrachter: Er wird später gezeichnet\n      // und verdeckt, was hinter ihm steht. So läuft man hinter Bäumen vorbei.\n      this.getSorting().byY();\n   }\n}\n"
    },
    {
      "name": "Stein.java",
      "text": "/// Ein Stein für Mauern, 16 × 16 Pixel aus dem Dungeon-Kachelbild.\npublic class Stein extends Hindernis {\n\n   public Stein() {\n      this.addCostume(\"stein\", \"assets/backgrounds/tilesets/tileset-dungeon.png\", 128, 48, 16, 16);\n   }\n}\n"
    }
  ]
}
