Spiritus Runner

Code Übersicht

                /***********************************************************************
                 
                 Spiritus Runner - 2D Runner
                 Gestartet am: 22.03.2023
                 Mitglieder: Nicolò Tonarelli, Mika Pfeifer, Andrea Krauss
                 
                 Hauptentwickler Processing: Nicolò
                 Hauptentwickler Website: Andrea Krauss & Mika Pfeifer
                 
                 *************************************************************************/
                
                
                
                //************************************************************************************************************************************
                /*************************************************************************************************************************************
                 **                                                                                                                                  **
                 **                                                        MAIN                                                                      **
                 **                                                                                                                                  **
                 **   Hier befinden sich alle Funktionen / Variablen, die für das grobe Spielsystem verantwortlich sind                              **
                 **                                                                                                                                  **
                 **************************************************************************************************************************************/
                //************************************************************************************************************************************
                import processing.sound.*;
                
                // Schriftarten
                PFont mono;
                
                // Verschiedene Spielzustände
                int gameState = 0; // 0 = Menü, 1 = Spiel, 2 = settings, 3 = Shop, 4 = GameOver, 5 = Animation
                
                // Menü Variablen / Settings
                int buttonWidth  = 250;
                int buttonHeight = 100;
                int textSize = 50;
                int textHeight = textSize/2;
                color menuThemeColor = #F5DC19;
                
                // Zeit / Tickers etc.
                int ticker = 0;
                int tickerMemory;
                
                // Bilder
                PImage background;
                PImage coinPreview;
                PImage left;
                PImage up;
                
                // Sounds
                SoundFile backgroundMusic;
                SoundFile animationMusic;
                
                SoundFile hoverSound;
                SoundFile gameOverSound;
                SoundFile cashSound;
                SoundFile victorySound;
                
                SoundFile jumpSound;
                SoundFile damageSound;
                SoundFile levelChangeSound;
                SoundFile coinSound;
                
                // Array wo die Musik Dateien beinhaltet
                SoundFile[] music = new SoundFile[4];
                
                // Buttons
                Button playButton;
                Button shopButton;
                Button quitButton;
                Button settingsButton;
                Button settingsDelete;
                Button backButton;
                
                // Shop Buttons
                Button shopItem1;
                Button shopItem2;
                Button shopItem3;
                
                // Shop Preise
                int shopItem1Price = 100;
                int shopItem2Price = 300;
                int shopItem3Price = 50;
                
                // Wert für ob der Game Over ein Sieg war
                boolean isVictory = false;
                
                // Liste an Bildern von Spielerlaufanimation
                ArrayList<PImage> playerImages = new ArrayList<PImage>();
                
                /*/************************************************************************************************************************************
                 **                                                                                                                                  **
                 **                                                        SETUP (Startvariablen werden gesetzt)                                     **
                 **                                                                                                                                  **
                //************************************************************************************************************************************/
                void setup() {
                  fullScreen(); //Vollbild
                
                
                  // Bilder werden geladen
                  background = loadImage("images/background.jpeg");
                  coinPreview = loadImage("images/coin.png");
                  left = loadImage("images/debug/left.png");
                  up = loadImage("images/debug/up.png");
                
                  // Fonts
                  mono = createFont("fonts/pixel.ttf", 128);
                
                  // Sounds werden geladen
                  backgroundMusic = new SoundFile(this, "sounds/background-music.wav");
                  hoverSound = new SoundFile(this, "sounds/hover.wav");
                  animationMusic = new SoundFile(this, "sounds/animation-music.wav");
                  gameOverSound = new SoundFile(this, "sounds/gameover.wav");
                  cashSound = new SoundFile(this, "sounds/cash.wav");
                  victorySound = new SoundFile(this, "sounds/victory.wav");
                  
                  jumpSound = new SoundFile(this, "sounds/jump.wav");
                  damageSound = new SoundFile(this, "sounds/damage.wav");
                  levelChangeSound = new SoundFile(this, "sounds/levelchange.wav");
                  coinSound = new SoundFile(this, "sounds/coin.wav");
                  
                  // Die 4 Muskdateien werden geladen
                  for(int i = 1; i <= 4; i++) {
                    music[i-1] = new SoundFile(this, "sounds/music"+i+".wav");
                  }
                  
                
                  // Programminhalte werden generiert
                  createScreen(true);                                                                                              
                
                  // Settingsdelete Button wird geladen
                  settingsDelete = new Button("Spielstand löschen", width/2, height/2, menuThemeColor, #D3C02D, #FFF079, buttonWidth*3, buttonHeight, 0, textSize); // Settingsbutton
                
                  // Spielerbilder werden in die ArrayList geladen
                  for (int i = 1; i <= 5; i++) {                                           // For Schleife von 1 - 5
                    PImage tempImage = loadImage("images/player"+i+".png");                // Temporäres Bild
                    tempImage.resize(int(tempImage.width*2/3), int(tempImage.height*2/3)); // Bild wird resized
                    playerImages.add(tempImage);                                           // Bild wird zur Liste hinzugefügt
                  }
                }
                
                /*/************************************************************************************************************************************
                 **                                                                                                                                  **
                 **                                                        DRAW (Spielstoppuhr um 1 höher und createScreen())                        **
                 **                                                                                                                                  **
                //************************************************************************************************************************************/
                void draw() {
                  ticker++;            // Zeitrechnung für Animationen & Systeme
                  createScreen(false); // Programminhalte werden generiert
                }
                
                
                /*/************************************************************************************************************************************
                 **                                                                                                                                  **
                 **                                                        createScreen (Funktion, die alle States / Spielzustände verwaltet         **
                 **                              Je nach boolean gameStateSwitch werden Startwerte gesetzt oder nur Funktionen ausgeführt            **
                 **                                                                                                                                  **
                //************************************************************************************************************************************/
                void createScreen(boolean gameStateSwitch) {
                  switch(gameState) {
                
                  case 0: ///////////////////////////////////////////////////////////////////////// GAMESTATE: MENU
                      if (!gameStateSwitch)  return;               // -> Funktion nur 1x ausgeführt
                
                    // Menü Background
                    background.resize(1920, 1080);                 // Resize
                    image(background, 0, 0);                       // Menühintergrund Bild
                
                    restartMusic();                                // Nur Menümusik wird gestartet
                    createButtons();                               // Buttons werden gezeichnet
                    drawMainMenu();                                // Menütitel werden gezeichnet
                    loadGameData();                                // Daten aus data.txt werden geladen
                    loadScores();                                  // Münzen & Highscore werden angezeigt
                    break;
                
                  case 1: ///////////////////////////////////////////////////////////////////// GAMESTATE: GAME
                    if (gameStateSwitch) {                         // (Nur 1x mal)
                      background(#FFFFFF);                         // Hintergrundfarbe weiss
                      ticker = 0;                                  // Stoppuhr auf 0
                      tickerMemory = ticker;                       // Stopppuhrspeicher auf ticker
                      setupGame();                                 // Spielstartwerte
                      animationMusic.stop();                       // Erklärungsmusik wird gestoppt
                    }
                    gameHandler();                                 // Spielfunktionen
                    break;
                  case 2: ///////////////////////////////////////////////////////////////////////////// SETTINGS
                    if (!gameStateSwitch)  return;                // nur 1x mal
                
                    // Menü Background
                    background.resize(1920, 1080);
                    image(background, 0, 0);
                
                    // Settingsbutton wird angezeigt
                    settingsDelete.draw(1, true);                // settingsDelete-Button wird im Knopfzustand 1 (unberührt) gezeichnet
                
                    // Menü Musik wird geändert
                    backgroundMusic.stop();                     
                    animationMusic.play();
                    animationMusic.loop();
                
                    // Zurück/Back Button wird angezeigt
                    backButton.draw(1, true);                    // Zurückbutton im Knopfzustand 1 gezeichnet
                    break;
                
                  case 3: /////////////////////////////////////////////////////////////////////////////// SHOP
                
                    if (gameStateSwitch) {                      // nur 1x mal
                      background(#FFFFFF);  
                      backgroundMusic.stop();
                      animationMusic.play();
                      animationMusic.loop();
                
                      backButton.draw(1, true);                // Backbuttobn im Zustand 1 gezeichnet        
                    }
                
                    break;
                  case 4: /////////////////////////////////////////////////////////////////////////// GAME OVER
                
                    if (gameStateSwitch) {                    // nur 1xmal
                      saveGameData();
                      ticker = 0;                             // Stoppuhr auf 0
                      tickerMemory = ticker;                  // Stoppuhrspeicher auf Stoppuhr (0)
                
                      if (isVictory) {                        // Falls man gewonnen hat
                        victorySound.play();                  //   Siegmusik wird abgespielt
                      } else gameOverSound.play();            //   GameOvermusik wird abgespielt
                    }
                    currentMusic.stop();                      // Spielmusik wird gestoppt
                    drawGameOver();                           // GameOver wird gezeichnet
                
                    if (ticker >= 100) {                      // Nach 100 Frames 
                      if (!gameOverSound.isPlaying()) {       // GameOverSound gestoppt
                        backButton.draw(1, true);             // Backbutton wird angezeigt
                      }
                    }
                
                    break;
                  case 5: /////////////////////////////////////////////////////////////// ANIMATIONS LOADING SCREEN
                
                    // VOREINSTELLUNGEN UND TIMER STARTEN
                    if (gameStateSwitch) {                    // nur 1x
                      frameRate(200);                         // Frame Rate auf 200
                      tickerMemory = ticker;
                
                      backgroundMusic.stop();
                      animationMusic.play();
                      animationMusic.loop();
                      //backgroundMusic.stop();
                    }
                
                
                    // KÄSTCHEN ANIMATION
                    if (ticker - 570  < tickerMemory) {      // Nach 570 Frames Loading Screen
                      fill(menuThemeColor);
                      noStroke();
                      square(int(random(0, width)), int(random(0, height)), int(random(0, 400)));
                
                      fill(#FFFFFF);
                      textSize(textSize*2);
                      text("Loading...", width/2, height/2);
                    }
                
                    // Erste mal Hintergrund färben (gelb)
                    if (ticker - 571 == tickerMemory) {     // Nach 571 Frames Fenster färben
                      background(menuThemeColor);
                    }
                    if (ticker - 600 == tickerMemory) {      // Nach 600 Frames Steuerung angzeigen
                      background(menuThemeColor);
                
                      stroke(#FFFFFF);                      // Linie
                      strokeWeight(5);
                      line(620, 230, 1290, 230);
                
                      stroke(#FFFFFF);                       // Linie
                      strokeWeight(5);
                      line(0, height-100, width, height-100);
                
                      stroke(#FFFFFF);                        // Linie
                      strokeWeight(5);
                      line(0, 100, width, 100);
                
                      fill(#FFFFFF);                          // Steuerungstext
                      text("Steuerung", width/2, height/5*1);
                      textSize(textSize);                  // Springen Text
                      text("[Leertaste]: \u2191 - Springen", width/2, height/6*2);
                      text("[Pfeiltaste]: \u2193 - Ducken", width/2, height/6*3);
                      text("[Ziel]: \u2193 - Laufe durch alle Levels!", width/2, height/6*4);
                      text("[!]: \u2193 - Meide Objekte & Lehrer!", width/2, height/6*5);
                
                    }
                
                    if (ticker - 1400 == tickerMemory) {     // Nach 1400 Frames Lets Go anzeigen
                      background(menuThemeColor);
                
                      fill(#FFFFFF);
                      text("Lets go!", width/2, height/2);
                    }
                
                    if (ticker - 1750 == tickerMemory) {    // Nach 1750 Frames Bildschirm färben
                      background(menuThemeColor);
                    }
                
                    if (ticker - 1900 == tickerMemory) {    // Nach 1900 Frames State wechseln
                      gameState = 1;
                      frameRate(60);
                      animationMusic.stop();
                      createScreen(true);
                    }
                    break;
                  }///////////////////////////////////////////////////////////////////////////////////////
                }
                
                /*/************************************************************************************************************************************
                 **                                                                                                                                  **
                 **                                                        Funktion, die zu Beginn des Menü geladen wird                             **
                 **                                                                                                                                  **
                //************************************************************************************************************************************/
                void restartMusic() {                     
                  backgroundMusic.play();
                  backgroundMusic.loop();
                  backgroundMusic.amp(0.1);
                
                  animationMusic.stop();
                  gameOverSound.stop();
                }
                
                /*/************************************************************************************************************************************
                 **                                                                                                                                  **
                 **                                                        Startbuttons (Buttons werden geladen)                                     **
                 **                                                                                                                                  **
                //************************************************************************************************************************************/
                void createButtons() {
                  playButton = new Button("PLAY", width/2, height/4*1, menuThemeColor, #D3C02D, #FFF079, buttonWidth, buttonHeight, 0, textSize);
                  playButton.draw(1, true);
                
                  shopButton = new Button("SHOP", width/2, height/4*2, menuThemeColor, #D3C02D, #FFF079, buttonWidth, buttonHeight, 0, textSize);
                  shopButton.draw(1, true);
                
                  quitButton = new Button("QUIT", width/2, height/4*3, menuThemeColor, #D3C02D, #FFF079, buttonWidth, buttonHeight, 0, textSize);
                  quitButton.draw(1, true);
                
                  shopItem1 = new Button("Doppelsprung (100$)", width/2, height/4*1, menuThemeColor, #D3C02D, #FFF079, buttonWidth*3, buttonHeight, 0, textSize);
                  //shopItem1.draw(1, true);
                
                  shopItem2 = new Button("x2 Leben (300$)", width/2, height/4*2, menuThemeColor, #D3C02D, #FFF079, buttonWidth*3, buttonHeight, 0, textSize);
                  //shopItem2.draw(1, true);
                
                  shopItem3 = new Button("Mütze kaufen (50$)", width/2, height/4*3, menuThemeColor, #D3C02D, #FFF079, buttonWidth*3, buttonHeight, 0, textSize);
                  //shopItem3.draw(1, true);
                
                
                  settingsButton = new Button("SETTINGS", 130, height-120, menuThemeColor, #D3C02D, #FFFFFF, 200, 50, 0, 30);
                  settingsButton.draw(1, true);
                
                  backButton = new Button("BACK", 1840, 1030, menuThemeColor, #D3C02D, #FFFFFF, 200, 50, 0, 30);
                }
                
                /*/************************************************************************************************************************************
                 **                                                                                                                                  **
                 **                                                        Score & Highscore werden angezeigt                                        **
                 **                                                                                                                                  **
                //************************************************************************************************************************************/
                void loadScores() {  
                  fill(menuThemeColor);
                  textSize(textSize/2);
                  textAlign(RIGHT);
                  text(highscore + " Highscore", 1700, 40);
                
                  fill(menuThemeColor);
                  textSize(textSize/2);
                  textAlign(RIGHT);
                  text(coins + " $", 1700, 40+(textSize/1.5));
                
                  coinPreview.resize(70, 70);  
                  image(coinPreview, 1710, 15);
                }
                
                /*/************************************************************************************************************************************
                 **                                                                                                                                  **
                 **                                                        Hauptmenü Titel wird gezeichnet                                           **
                 **                                                                                                                                  **
                //************************************************************************************************************************************/
                void drawMainMenu() {
                  stroke(menuThemeColor);
                  strokeWeight(10);
                  line(100, 555, 660, 555);
                
                  stroke(menuThemeColor);
                  strokeWeight(10);
                  line(1295, 555, 1770, 555);
                
                  fill(menuThemeColor);
                  textSize(textSize*2);
                  text("SPIRITUS", width/10*2, height/2);
                
                  fill(menuThemeColor);
                  textSize(textSize*2);
                  text("RUNNER", width/10*8, height/2);
                }
                
                /*/************************************************************************************************************************************
                 **                                                                                                                                  **
                 **                                                        Game Over wird gezeichnet                                                 **
                 **                                                                                                                                  **
                //************************************************************************************************************************************/
                void drawGameOver() {
                  if(isVictory) { background(menuThemeColor); } else { background(255,0,0); } // Je Nach GameOverTyp wird die Hintergrundfarbeanders
                  textAlign(CENTER);
                  fill(#FFFFFF);
                  textSize(200);
                  text("Game Over", width/2, height/2);
                
                  textAlign(CENTER);
                  fill(#FFFFFF);
                  textSize(50);
                  text("Score: " + score + " Highscore: " + highscore, width/2, 650);
                }
                
                
                    //***************************************************************************************************************************
                    /********************************************************g*******************************************************************
                     **                                                                                                                         **
                     **                       BUTTONS / KNÖPFE                                                                                  **
                     **                                                                                                                         **
                     **   Hier befindet sich die Klasse für Buttons, die uns das erstellen von Knöpfen vereinfacht                              **
                     **                                                                                                                         **
                     ****************************************************************************************************************************/
                    //***************************************************************************************************************************
                    
                    
                    
                    /*/************************************************************************************************************************************
                     **                                                                                                                                  **
                     **                                      Button Klasse, die berechnet ob der Button gedrückt wird und den Button erleuchten lässt    **
                     **                                                                                                                                  **
                     //************************************************************************************************************************************/
                    class Button {
                    
                      String buttonName;    // Button Name & Text
                      int posX;             // X-Kooordinate 
                      int posY;             // Y-Koordinate
                      color buttonColor;    // Buttonfarbe normal (Buttonzustand 1)
                      color buttonHoverColor;// Buttonfarbe wenn die Maus übergeht (Buttonzustand 2)
                      color clickedColor;    // Buttonfarbe angeklickt (Buttonzustand 3)
                    
                      int buttonWidth;        // Boxlänge
                      int buttonHeight;       // Boxhöhe
                    
                      color textColor;      // Textfarbe
                      int textSize;         // Schriftartengrösse
                    
                      // Klassenkonstruktor der die Werte setzt
                      Button(String buttonName, int posX, int posY, color buttonColor, color buttonHoverColor, color clickedColor, int buttonWidth, int buttonHeight, color textColor, int textSize) {
                    
                        this.buttonName = buttonName;
                    
                        this.posX = posX;
                        this.posY = posY;
                        this.buttonWidth = buttonWidth;
                        this.buttonHeight = buttonHeight;
                    
                        this.buttonColor = buttonColor;
                        this.buttonHoverColor = buttonHoverColor;
                        this.clickedColor = clickedColor;
                    
                        this.textColor = textColor;
                        this.textSize = textSize;
                      }
                    
                    
                      // mode 1 = normal, mode 2 = hovered, mode 3 = clicked
                      // Funktion um den Button anzeigen zu lassen
                      void draw(int mode, boolean playSound) {
                        color finalColor = color(0, 0, 0);                     // Buttonhintergrundfarbe zu der gewechselt wird
                        color finalStrokeColor = color(0, 0, 0);               // Buttonrandfarbe zu der gewechselt wird
                    
                        switch(mode) {                                         // Je nach Buttonzustand werden andere Farben gewählt
                        case 1:  //////////////////////////////// KNOPFZUSTAND 1
                          finalColor = this.buttonColor;
                          finalStrokeColor = this.buttonColor;
                          break;
                        case 2: //////////////////////////////// KNOPFZUSTAND 2
                          finalColor = this.buttonColor;
                          finalStrokeColor = this.buttonHoverColor;
                          break;
                        case 3: //////////////////////////////// KNOPFZUSTAND 3
                          finalColor = this.clickedColor;
                          finalStrokeColor = this.clickedColor;
                          if (playSound) hoverSound.play();                    // Falls playSound true ist, wird ein Sound abgespielt
                          break;
                        }
                    
                        // Buttonbox wird gezeichnet
                        stroke(finalStrokeColor);                               
                        fill(finalColor);
                        rectMode(CENTER);
                        rect(this.posX, this.posY, this.buttonWidth, this.buttonHeight);
                        
                        // Buttontext wird gezeichnet
                        fill(this.textColor);                                
                        textFont(mono);
                        textSize(this.textSize);
                        textAlign(CENTER);
                        text(this.buttonName, this.posX, this.posY+textSize/2);
                      }
                    
                      // Funktion, die schaut, ob die Maus auf dem Button ist
                      boolean mouseOnIt() {
                        int x = mouseX;
                        int y = mouseY;
                    
                        if (! (x > (posX-buttonWidth/2) && x < (posX+buttonWidth/2) && y > (posY-buttonHeight/2) && y < (posY+buttonHeight/2)) ) { // Falls sich die Maus darauf befindet, wird der Button angeleuchtet
                          this.draw(1, true); // Button wird angeleuchtet, da die Maus sich über ihm befindet
                        }
                    
                        return (x > (posX-buttonWidth/2) && x < (posX+buttonWidth/2) && y > (posY-buttonHeight/2) && y < (posY+buttonHeight/2));
                      }
                    
                      // Funktion, die schaut, ob die Maus auf dem Button ist mit gameState Bedingung
                      boolean mouseOnIt(int gameStateNeeded) {
                        int x = mouseX;
                        int y = mouseY;
                    
                        boolean correctSection = (x > (posX-buttonWidth/2) && x < (posX+buttonWidth/2) && y > (posY-buttonHeight/2) && y < (posY+buttonHeight/2)); // Falls sich die Maus darauf befindet, wird der Button angeleuchtet
                        if (!correctSection) {
                          if (gameState == gameStateNeeded) this.draw(1, true);  // Button wird angeleuchtet, da die Maus sich über ihm befindet
                        }
                    
                        return ((correctSection) && gameStateNeeded == gameState);
                      }
                    }
                    
                    /*/************************************************************************************************************************************
                     **                                                                                                                                  **
                     **                                                        Funktion, die das Klicken auf Buttons verwaltet                           **
                     **                                                                                                                                  **
                     //************************************************************************************************************************************/
                    void menuClickHandler(boolean clicked) {
                      if (clicked) println("X:", mouseX, "Y:", mouseY); // DEBUG
                    
                    
                      if (!clicked) { ////////////////////////////////////// FALLS DIE MAUS NUR ÜBER DEN BUTTON GEHT ABER NICHT DRÜCKT /////////////////////
                    
                        if (playButton.mouseOnIt(0)) playButton.draw(2, true);
                        if (shopButton.mouseOnIt(0)) shopButton.draw(2, true);
                        if (quitButton.mouseOnIt(0)) quitButton.draw(2, true);
                        if (settingsButton.mouseOnIt(0)) settingsButton.draw(2, true);
                        if (settingsDelete.mouseOnIt(2)) settingsDelete.draw(2, true);
                        if (backButton.mouseOnIt(2) || backButton.mouseOnIt(3)) backButton.draw(2, true);
                        if (shopItem1.mouseOnIt(3)) shopItem1.draw(2, true);
                        if (shopItem2.mouseOnIt(3)) shopItem2.draw(2, true);
                        if (shopItem3.mouseOnIt(3)) shopItem3.draw(2, true);
                      } else { ////////////////////////////////////// FALLS DIE MAUS AUF DEN BUTTON DRÜCKT  ////////////////////////////////////////////////
                    
                        if (shopItem1.mouseOnIt(3)) { // SHOP ITEM 1
                          buyItem(1);         // Item 1 wird gekauft
                          gameState=0;        // Gamestate wird gewechselt                 
                          createScreen(true); // Gamestate wird aktualisiert
                          return;
                        } else if (shopItem2.mouseOnIt(3)) { // SHOP ITEM 2
                          buyItem(2);        // Item 2 wird gekauft...
                          gameState=0;       // `
                          createScreen(true);// `
                          return;
                        } else if (shopItem3.mouseOnIt(3)) { // SHOP ITEM 3
                          buyItem(3);       // `
                          gameState=0;      // `
                          createScreen(true);// `
                          return;
                        }
                    
                        if (playButton.mouseOnIt(0)) { // PLAY BUTTON
                          playButton.draw(3, true); 
                          gameState = 5;            // Animation wird gestartet
                          createScreen(true);       // Gamestate wird aktualisiert
                          return;
                        }
                    
                        if (shopButton.mouseOnIt(0)) { // SHOP BUTTON (IM HAUPTMENÜ)
                          shopButton.draw(3, true);
                          gameState = 3;           // Es wird zu Shopstate gewechselt
                          createScreen(true);
                          delay(100);
                          return;
                        }
                    
                        if (settingsButton.mouseOnIt(0)) { // SETTINGS BUTTON
                          settingsButton.draw(3, true);
                          gameState = 2;
                          createScreen(true);
                          return;
                        }
                    
                        if (settingsDelete.mouseOnIt(2)) { // SPIELSTAND LÖSCHEN BUTTON
                          settingsButton.draw(3, true);
                          deleteGameData();  // data.txt wird gelöscht
                          loadGameData();    // neues data.txt wird erstellt
                          gameState = 0;
                          createScreen(true);
                          return;
                        }
                    
                        if (backButton.mouseOnIt(2) || backButton.mouseOnIt(3) || backButton.mouseOnIt(4)) { // ZUERECK ZUM MENU BUTTON
                          gameState = 0;
                          createScreen(true);
                          return;
                        }
                    
                        if (quitButton.mouseOnIt(0)) { // QUIT BUTTON
                          exit();   // Spiel wird geschlossen
                          return;
                        }
                      }
                    }
                    
                    void buyItem(int item) {
                      loadGameData(); // Münzen werden aus der Datei geladen
                      
                      switch(item) {  // Switch-Statement für welchen Gegenstand man versucght zu kaufen
                      case 1:
                        if (coins >= shopItem1Price && !boughtItem1) { // Überprüfung ob man genug Geld hat & Gegenstand noch nicht gekauft wurde
                          coins-=shopItem1Price;                       // Preis wird abgezogen 
                          boughtItem1 = true;                          // Item 1 ist nun gekauft
                          saveGameData();                              // Daten werden in Datei gespeichert
                          cashSound.play();                            // Kaufsound wird abgespielt
                        }
                        break;
                      case 2:
                        if (coins >= shopItem2Price && !boughtItem2) { // Überprüfunng ob man genug Geld hat & `
                          coins-=shopItem2Price;                       //
                          boughtItem2 = true;                          //
                          saveGameData();                              //
                          cashSound.play();                            //
                        }
                        break;
                      case 3:
                        if (coins >= shopItem3Price && !boughtItem3) { // Überprüfunng ob man genug Geld hat & `
                          coins-=shopItem3Price;                       //
                          boughtItem3=true;                            //
                          saveGameData();                              //
                          cashSound.play();                            //
                        }
                        break;
                      }
                    }
                    
                    
                        //***************************************************************************************************************************
                        /****************************************************************************************************************************
                         **                                                                                                                         **
                         **                       Steuerung                                                                                         **
                         **                                                                                                                         **
                         **   Hier befinden sich alle Funktionen die was mit Tasten & Berührungen zutun haben                                       **
                         **                                                                                                                         **
                         ****************************************************************************************************************************/
                        //***************************************************************************************************************************
                        int lastButtonClick; // Knopfcooldown
                        
                        /*/************************************************************************************************************************************
                         **                                                                                                                                  **
                         **                                                        Falls Linksklick gemacht wird                                             **
                         **                                                                                                                                  **
                        //************************************************************************************************************************************/
                        void mousePressed() {
                        
                          if (lastButtonClick + 1f < frameCount) { // Falls der Buttoncooldown vorbei ist (Dazu da, das nicht durch das schnelle State wechseln mehrere Buttons aufeinmal gedrückt werden)
                            lastButtonClick=frameCount;            // Cooldown wird zurückgesetzt
                            menuClickHandler(true);                // Es wird geschaut, ob ein Button gedrückt wurde
                          }
                        }
                        
                        void mouseMoved() {
                          menuClickHandler(false);                  // Überprüfen ob die Maus über einem Knopf ist
                        }
                        
                        /*/************************************************************************************************************************************
                         **                                                                                                                                  **
                         **                                                        Falls eine Taste gedrückt wird                                            **
                         **                                                                                                                                  **
                        //************************************************************************************************************************************/
                        void keyPressed() {
                          if (key == 'd') {                        // Falls d gedrückt wird, wird der Debugmode aktiviert/deaktiviert
                            showHitboxes = showHitboxes ? false : true;  // Variable anhängig von ob showHitboxes true oder false ist (Conditional)
                          }
                          
                          if(gameState==1)
                          // Spieler springen lassen, wenn er springen kann und er Leertaste drückt
                          if (key == ' ' && player.canJump && gameState==1) {
                            player.jump();
                            jumpCount++; // Sprungcount (für Doppelsprungextra) um 1 höher
                          }
                          if (keyCode == DOWN && player.canJump) player.crouch = true; // Bei Pfeiltaste runter geht der Spieler in die Knie
                        }
                        
                        /*/************************************************************************************************************************************
                         **                                                                                                                                  **
                         **                                                        Falls eine Taste losgelassen wird                                         **
                         **                                                                                                                                  **
                        //************************************************************************************************************************************/
                        void keyReleased() {
                          if(!(gameState==1)) return; // Fehlervemidung da der player zu Beginn noch null ist
                          
                          // Sprungstatus zurücksetzen, wenn der Spieler die Leertaste loslässt und sich am Boden befindet
                          //if (key == ' ' && player.y == height - player.playerImg.height/2) {
                          //  player.canJump = true;
                          //}
                          
                          if (key == ' ' && boughtItem1 && jumpCount < 1) player.canJump=true;
                        
                        
                          if (keyCode == DOWN) {
                            player.crouch = false;
                          }
                        
                        }
                        
                        
                            //***************************************************************************************************************************
                            /********************************************************g*******************************************************************
                             **                                                                                                                         **
                             **                       GAME                                                                                              **
                             **                                                                                                                         **
                             **   Hier befinden sich alle Funktionen die während des Spiels ausgeführt werden                                           **
                             **                                                                                                                         **
                             ****************************************************************************************************************************/
                            //***************************************************************************************************************************
                            
                            // Münzenbild
                            PImage coinImage;
                            
                            int score; // Punktzahl
                            
                            boolean canJump; // Sprungstatus des Spielers (ob der wieder springen kann)
                            boolean showHitboxes; // Debugmode (Zeigt die Kollisionsboxen an, kann mit 'd' aktiviert/deaktiviert werden)
                            
                            PImage bg1, bg2, bg3, bg3R; // Hintergründe
                            PImage accessoirHelmet;     // Mützenbild
                            
                            float bg1Speed = 0.2;       // Hintergrund 1 Geschwindigkeit
                            float bg2Speed = 0.5;       // Hintergrund 2 Geschwindigkeit
                            float bg3Speed = 1.0;       // Hintergrund 3 Geschwindigkeit
                            
                            float bg1Offset = 0;       // Hintergrund 1 offset
                            float bg2Offset = 0;       // Hintergrund 2 offset
                            float bg3Offset = 0;       // Hintergrund 3 offset
                            
                            int gameTicker;            // Stoppuhr (für Timer, Funktionen, Cooldowns...)
                            int tickSaveTheme;         // Stoppuhrspeicher (für Timer, Funktionen, Cooldowns...)
                            int herzen;                // Anzahl an Herzen
                            int levelTime = 20;        // Zeit, die ein Level benötigt
                            int theme;                 // Aktuelle ID vom Level (Mensa, Zimmer, Gym...)
                            int jumpCount = 0;         // Anzahl bereits gemachte Sprünge (um Doppelsprung zu berechnen)
                             
                            int currentMusicNumber;    // Aktuelle Musik ID
                            SoundFile currentMusic;    // Aktuelle Musik Datei
                            
                            ArrayList<Obstacle> obstacles = new ArrayList<Obstacle>(); // Liste von allen Gegern, Münzen & Objekten
                            ArrayList<Integer> alreadyThemesBeen = new ArrayList<Integer>(); // Liste von den Levels die man schon durchquert hat
                            
                            Player player;              // Spielerobjekt
                            
                            /*/************************************************************************************************************************************
                             **                                                                                                                                  **
                             **                                                        Funktion, die die ganzen Werte, Bilder etc. zu Spielbeginn ladet          **
                             **                                                                                                                                  **
                             //************************************************************************************************************************************/
                            void setupGame() {
                            
                              // Hintergrundbilder werden geladen und resized
                              bg1 = loadImage("images/themes/tree_layer.png");
                              bg1.resize(width, height);
                              bg2 = loadImage("images/themes/mountain_layer.png");
                              bg2.resize(width, height);
                              bg3 = loadImage("images/themes/wallpaper.png");
                              bg3.resize(width, height);
                              bg3R = loadImage("images/themes/wallpaper.png");
                              bg3.resize(width, height);
                            
                              // Geschwindigkeit Hintergründe
                              bg1Speed = 5;
                              bg2Speed = 3;
                              bg3Speed = 1;
                            
                              // Timer / Stoppuhren zurückgesetzt
                              gameTicker = 0;
                              tickSaveTheme = 0;
                              jumpCount = 0;
                            
                              // Annzahl Herzen
                              herzen = 10;
                              if (boughtItem2) herzen*=2; // Falls x2 Herzen gekauft wurde (im Shop)
                            
                              // Zufälliges Level wird ausgewählt
                              theme = Math.round(random(1, 5));
                              alreadyThemesBeen.clear();  // Liste zurückgesetzt
                              alreadyThemesBeen.add(theme); // Neues Level zur Liste
                            
                              score = 0;                    // Score zurückgesetzt
                              showHitboxes = false;         // Debugmode deaktiviert
                              isVictory = false;            // Sieg wird deaktiviert
                            
                              // Zufällige Musik wird ausgewählt und abgespielt
                              currentMusicNumber = int(random(1, 4));
                              currentMusic = music[currentMusicNumber-1];
                            
                              // Spielerobjekt wird definiert
                              player = new Player(width/2, height/2, 0.0, 0.6, 1, 4, true);//(float x, float y, float speed, float gravity, int currentImage, int maxImages, boolean canJump)
                            
                              // Falls Item 3 im  Shop gekauft wurde, wird der Helm geladen
                              if (boughtItem3) {
                                accessoirHelmet = loadImage("./images/extra/helmets/" + Math.round(random(1, 6)) + ".png");
                              }
                            }
                            
                            
                            /*/************************************************************************************************************************************
                             **                                                                                                                                  **
                             **                                                        Funktion, die jeden Frame ausgeführt wird und alle Spielabläufe verwaltet **
                             **                                                                                                                                  **
                             //************************************************************************************************************************************/
                            void gameHandler() {
                              gameTicker++; // Stoppuhr um 1 höher
                            
                              background(#FFFFFF);   // Hintergrund zeichnen
                              player.move();         // Spieler bewegen
                              moveBackgrounds();     // Verschiebung des hintersten Hintergrundbild von rechts nach links
                            
                            
                              if (player.y >= height - player.playerImg.height/2) {  // Spieler Spring Animation, falls er am Springen ist / über dem Boden ist
                                player.displayJump();
                              }
                            
                              player.display();  // Spieler wird gezeichnet
                              displayPoints();   // Punktzahl anzeigen
                              obstacleHandler(); // Hindernis bewegen und zeichnen
                              levelChanger();    // Nach einiger Zeit wird das Level gewechselt / Victory (Game over) aktiviert
                              spawnObstacles();  // Gegner werden erschaffen
                              spawnCoin();       // Münzen werden erschaffen
                              drawHearts();      // Herzensanzeige wird gezeichnet
                              checkIfMusicEnded(); // Musik wird neugstartet falls gestoppt
                            }
                            
                            void checkIfMusicEnded() {
                              if (!(currentMusic.isPlaying())) {
                                int newMusicNumber = int(random(1, 4));
                                do {
                                  newMusicNumber=int(random(1, 4));
                                } while (newMusicNumber==currentMusicNumber);
                            
                                currentMusicNumber = newMusicNumber;
                                currentMusic = music[currentMusicNumber-1];
                                currentMusic.play();
                              }
                            }
                            
                            /*/************************************************************************************************************************************
                             **                                                                                                                                  **
                             **                                                        Funktion, die das Level ändert oder Game Over (bei Sieg) aktiviert        **
                             **                                                                                                                                  **
                             //************************************************************************************************************************************/
                            void levelChanger() {
                            
                            
                            
                              if (tickSaveTheme / 60f + levelTime < ticker / 60f ) { // Alle 'levelTime' Sekunden (60 Frames) wird das Level gewechselt
                            
                                if (alreadyThemesBeen.size() == 5) { // Falls alle Level durchgespielt wurden
                                  isVictory=true;
                                  currentMusic.stop();
                                  doGameOver();  // Statewechsel / Game Over aktiviert
                                  return;
                                }
                            
                                int n;
                            
                                do { // Solange kein neues Level gefunden wurde, wird der Random Integer neugeneriert
                                  n =  Math.round(random(1, 5));
                                } while (alreadyThemesBeen.contains(n));
                            
                                theme = n;
                                alreadyThemesBeen.add(theme);
                                tickSaveTheme = ticker;
                              }
                            }
                            
                            /*/************************************************************************************************************************************
                             **                                                                                                                                  **
                             **                                                        Funktion, die die Hintergründe verschiebt                                 **
                             **                                                                                                                                  **
                             //************************************************************************************************************************************/
                            void moveBackgrounds() {
                              bg3Offset -= bg3Speed;
                              bg3Offset = bg3Offset % bg3.width;
                              for (float x = bg3Offset; x > -width; x -= bg3.width) { // Hintergrund wird bewegt sobald er das Fenster verlässt
                                image(bg3, x, 0);
                                image(bg3R, width+bg3Offset, 0);
                                if (showHitboxes) showDebugRect(color(255, 0, 0), int(x), 0, bg3.width, bg3.height);
                                if (showHitboxes) showDebugRect(color(255, 0, 0), int(width+bg3Offset), 0, bg3R.width, bg3R.height);
                              }
                            
                              // Verschiebung des zweiten Hintergrundbild von rechts nach links
                              bg2Offset -= bg2Speed;
                              bg2Offset = bg2Offset % bg2.width;
                              for (float x = bg2Offset; x > -width; x -= bg2.width) { // Hintergrund wird bewegt sobald er das Fenster verlässt
                                image(bg2, x, 0);
                                image(bg2, width+bg2Offset, 0);
                                if (showHitboxes) showDebugRect(color(0, 255, 0), int(x), 0, bg2.width, bg2.height);
                                if (showHitboxes) showDebugRect(color(0, 255, 0), int(width+bg2Offset), 0, bg2.width, bg2.height);
                              }
                            
                              // Verschiebung des dritten Hintergrundbild von rechts nach links
                              bg1Offset -= bg1Speed;
                              bg1Offset = bg1Offset % bg1.width;
                              for (float x = bg1Offset; x > -width; x -= bg1.width) { // Hintergrund wird bewegt sobald er das Fenster verlässt
                                image(bg1, x, 0);
                                image(bg1, width+bg1Offset, 0);
                                if (showHitboxes) showDebugRect(color(0, 255, 0), int(x), 0, bg1.width, bg2.height);
                                if (showHitboxes) showDebugRect(color(0, 0, 255), int(width+bg1Offset), 0, bg2.width, bg2.height);
                              }
                            }
                            
                            /*/************************************************************************************************************************************
                             **                                                                                                                                  **
                             **                                                        Score wird angezeigt                                                      **
                             **                                                                                                                                  **
                             //************************************************************************************************************************************/
                            void displayPoints() {
                              textAlign(RIGHT);
                              textSize(32);
                              fill(255);
                              text("Score: " + score, width - 20, 40);
                            }
                            
                            /*/************************************************************************************************************************************
                             **                                                                                                                                  **
                             **                                                        Anzahl Herzen wird mit dem Symbol \u2764 gezeichnet                       **
                             **                                                                                                                                  **
                             //************************************************************************************************************************************/
                            void drawHearts() {
                              String finalHeartText = "";
                              for (int i = 0; i < herzen; i++) finalHeartText += "\u2764"; // Von 0 - i (For Schleife) wird dem endgültigen String 'finalHeartText' das Herzsymbol hinzugefügt
                            
                              fill(255, 0, 0);
                              textSize(80);
                              textAlign(LEFT);
                              text(finalHeartText, 40, 80); // Finale String wird angezeigt
                            }
                            
                            /*/************************************************************************************************************************************
                             **                                                                                                                                  **
                             **                                                        Funktion die deen Game Over Screen einleitet                              **
                             **                                                                                                                                  **
                             //************************************************************************************************************************************/
                            void doGameOver() {
                              gameState = 4; // State wird gewechselt
                              createScreen(true);
                              currentMusic.stop(); // Spielmusik wird gestoppt
                            }
                            
                            
                            /*/************************************************************************************************************************************
                             **                                                                                                                                  **
                             **                                                Funktion fürs Debuggen, die Ränder von Rechtecken zeichnet                        **
                             **                                                                                                                                  **
                             //************************************************************************************************************************************/
                            void showDebugRect(color c, int x, int y, int w, int h) {
                              stroke(c);
                              strokeWeight(3);
                              noFill();
                              rectMode(CORNER);
                              rect(x, y, w, h);
                            }
                            
                            /*/************************************************************************************************************************************
                             **                                                                                                                                  **
                             **                                             Spielerobjekt das den Sprung, Bewegung & Animation vom Spieler verwaltet             **
                             **                                                                                                                                  **
                             //************************************************************************************************************************************/
                            class Player {
                            
                              float x, y, speed, gravity; // X, Y, Sprungeschwindigkeit, Schwerkraft
                              int currentImage;           // Aktuelles Bild der Animation
                              int maxImages;              // Wie viele versch. Spielerversionen es gibt (Animation)
                              PImage playerImg;           // Aktuelles Spielerbild was angezeigt wird
                              boolean canJump;            // Boolean für ob der Spieler gerade springen kann
                              boolean crouch;             // Ob der Spieler gerade am Crouchen ist
                            
                              // Konstruktor
                              Player(float x, float y, float speed, float gravity, int currentImage, int maxImages, boolean canJump) {
                                this.x = x;
                                this.y = y;
                                this.speed = speed;
                                this.gravity = gravity;
                                this.currentImage = currentImage;
                                this.maxImages = maxImages;
                                this.canJump = canJump;
                                crouch = false;
                            
                                this.playerImg = playerImages.get(0);
                              }
                            
                              void display() {
                                if (ticker % 6 == 0) currentImage = currentImage+1>=maxImages ? 1 : currentImage+1; // Alle 6 Frames (wenn der frameCount durch 6 teilbar ist) wird das Spielerbild der Animation mit den nächsten getauscht
                                this.playerImg = playerImages.get(currentImage);                                    // Neues Spielerbild wird gesetzt
                                if (crouch) this.playerImg = playerImages.get(4);                                   // Falls der Spieler am Schleichen ist, wird der Schleichbild angezeigt
                            
                                // Falls der Spieler den Hut gekauft hat im Shop wird er hier angezeigt
                                imageMode(CENTER);
                                if (boughtItem3) image(accessoirHelmet, this.x, this.y-25);
                                image(this.playerImg, this.x, this.y);
                                imageMode(CORNER);
                              }
                            
                              // Falls man Leertaste drückt
                              void jump() {
                                this.speed = -20;                                         // Spielersprung (wirkt sich auf Y-Koordinate des Spielers aus)  wird auf -20 gesetzt -> Spieler geht hoch
                                if (!boughtItem1 || jumpCount >= 1) this.canJump = false; // Spieler kann nicht erneut springen, bis er den Boden erreicht hat
                                jumpSound.play();                                         // Sprungsound
                              }
                            
                              void move() {
                                this.y += this.speed;                                     // Spielersprung wirkt sich auf Y aus
                                this.speed += this.gravity;                               // Schwerkraft wirkt sich auf Spielersprung aus -> Spieler geht wieder runter
                                this.speed = constrain(this.speed, -10, 10);              // Sprungkraft wird begrenzt
                                if (this.x < width/2) this.x++;                           // Falls der Spieler nicht in der Mitte ist, wird er sich dahin wieder bewegen (NICHT IN VERWENDUNG)
                                if (this.x < 0) {                                         // Falls der Spieler aus dem Fenster geht kriegt er Schaden und wird wieder verschoben
                                  herzen--;
                                  this.x = width/2;
                                  this.y = 500;
                                }
                              }
                            
                            
                              void displayJump() {                                        // Sprung wird angezeigt
                                this.y = height - this.playerImg.height/2;                // Y-Koordinate wird nochmals korrigiert
                                this.speed = 0;                                           // Sprungkraft auf 0
                                this.canJump = true;                                      // Spieler kann erneut springen, da er den Boden erreicht hat
                                jumpCount=0;                                              // jumpCount auf 0
                              }
                            }
                            
                            
                                //***************************************************************************************************************************
                                /********************************************************g*******************************************************************
                                 **                                                                                                                         **
                                 **                       HIGHSCORE                                                                                         **
                                 **                                                                                                                         **
                                 **   Hier befindet sich die Klasse für Buttons, die uns das erstellen von Knöpfen vereinfacht                              **
                                 **                                                                                                                         **
                                 ****************************************************************************************************************************/
                                //***************************************************************************************************************************
                                
                                int highscore = 0;
                                int coins = 0;
                                boolean boughtItem1, boughtItem2, boughtItem3 = false;
                                
                                /*/************************************************************************************************************************************
                                 **                                                                                                                                  **
                                 **                                          Funktion, die die Scores & Shop Daten in der Datei speichert                            **
                                 **                                                                                                                                  **
                                 //************************************************************************************************************************************/
                                void saveGameData() {
                                  // Öffne die "data.txt" Datei im Schreibmodus
                                  PrintWriter writer = createWriter("data.txt");
                                  // Schreibe den Highscore und die Coins in die Datei
                                  writer.println(highscore); 
                                  writer.println(coins);
                                  writer.println(boughtItem1);
                                  writer.println(boughtItem2);
                                  writer.println(boughtItem3);
                                  // Schließe die Datei
                                  writer.close();
                                }
                                
                                /*/************************************************************************************************************************************
                                 **                                                                                                                                  **
                                 **                                          Funktion, die die Daten aus der Datei ladet                                             **
                                 **                                                                                                                                  **
                                 //************************************************************************************************************************************/
                                void loadGameData() {
                                  // Versuche die "data.txt" Datei zu öffnen
                                  BufferedReader reader = createReader("data.txt");
                                  if (reader != null) {
                                    try {
                                      // Lese den Highscore aus der Datei
                                      String scoreStr = reader.readLine();
                                      // Konvertiere den String in eine Zahl
                                      highscore = Integer.parseInt(scoreStr);
                                      // Lese die Coins aus der Datei
                                      String coinsStr = reader.readLine();
                                      // Konvertiere den String in eine Zahl
                                      coins = Integer.parseInt(coinsStr);
                                      // Schließe die Datei
                                      
                                      String boughtItem1Str = reader.readLine();
                                      boughtItem1 = Boolean.parseBoolean(boughtItem1Str);
                                      String boughtItem2Str = reader.readLine();
                                      boughtItem2 = Boolean.parseBoolean(boughtItem2Str);
                                      String boughtItem3Str = reader.readLine();
                                      boughtItem3 = Boolean.parseBoolean(boughtItem3Str);
                                      
                                      reader.close();
                                    } 
                                    catch (IOException e) {
                                      println("Fehler beim Lesen der Datei: " + e.getMessage());
                                    }
                                  } else {
                                    // Wenn die Datei nicht existiert, erstelle sie und schreibe den Highscore (0) und die Coins (0) hinein
                                    saveGameData();
                                  }
                                }
                                /*/************************************************************************************************************************************
                                 **                                                                                                                                  **
                                 **                                          Funktion, die die Daten Datei löscht                                                    **
                                 **                                                                                                                                  **
                                 //************************************************************************************************************************************/
                                void deleteGameData() {
                                  // Lösche die "data.txt" Datei, falls sie vorhanden ist
                                  File file = new File(sketchPath("data.txt"));
                                  if (file.exists()) {
                                    file.delete();
                                  }
                                  // Setze den Highscore und die Coins auf 0
                                  highscore = 0;
                                  coins = 0;
                                }
                                
                                
                                    //***************************************************************************************************************************
                                    /********************************************************g*******************************************************************
                                     **                                                                                                                         **
                                     **                       LEVELS                                                                                            **
                                     **                                                                                                                         **
                                     **   Hier werden die Hintergrundbilder und Gegner der Levels verwalten                                                     **
                                     **                                                                                                                         **
                                     ****************************************************************************************************************************/
                                    //***************************************************************************************************************************
                                    
                                    void spawnObstacles() {
                                      switch(theme) {
                                      case 1: // DRAUSSEN / AUSSERHALB SCHULE
                                        if (ticker-tickSaveTheme <= 1) {                       // nur 1x-mal
                                          bg3 = loadImage("images/themes/building.jpg");
                                          bg3R = loadImage("images/themes/building-r.jpg");
                                          bg2 = loadImage("images/themes/mountain_layer.png");
                                          bg1 = loadImage("images/themes/tree_layer.png");
                                        }
                                        spawnOutsideElements();
                                        break;
                                      case 2: // GANG
                                        if (ticker-tickSaveTheme <= 1) {                       // nur 1x-mal
                                          bg3 = loadImage("images/themes/gang.jpeg");
                                          bg3R = loadImage("images/themes/gang-r.jpeg");
                                          bg2 = loadImage("images/themes/gang_layer.png");
                                          bg1 = loadImage("images/themes/gang_floor.png");
                                        }
                                        if (ticker % 60 == 0 && random(0, 100) <= 50) {        // Alle 60 Frames mit 50% Chance wird ein Lehrer erschaffen
                                          spawnRandomTeacher();  
                                        } else if (ticker % 180 == 0 && random(0, 100) < 30) {
                                          spawnTable();                                        // Alle 180 Frames mit 30% Chance wird falls kein Lehrer erschaffen wird ein Tisch erschaffen
                                        }
                                        break;
                                      case 3: // KLASSENZIMMER
                                        if (ticker-tickSaveTheme <= 1) {                       // nur 1x-mal
                                          bg3 = loadImage("images/themes/klassenzimmer.jpg");
                                          bg3R = loadImage("images/themes/klassenzimmer-r.jpg");
                                          bg2 = loadImage("images/themes/klassenzimmer_layer.png");
                                          bg1 = loadImage("images/themes/klassenzimmer_floor.png");
                                        }
                                        spawnFurniture();                                      // Möbel wird erschaffen
                                        break;
                                      case 4:// KRAFTRAUM
                                        if (ticker-tickSaveTheme <= 1) {                       // nur 1x-mal
                                          bg3 = loadImage("images/themes/kraftraum.jpeg");
                                          bg3R = loadImage("images/themes/kraftraum-r.jpeg");
                                          bg2 = loadImage("images/themes/gym_layer.png");
                                          bg1 = loadImage("images/themes/gym_floor.png");
                                        }
                                        spawnGymElements();                                   // Gymelemente werden erschaffen
                                        break;
                                      case 5: // MENSA
                                        if (ticker-tickSaveTheme <= 1) {                       // nur 1x-mal
                                          bg3 = loadImage("images/themes/mensa.jpeg");
                                          bg3R = loadImage("images/themes/mensa-r.jpeg");
                                          bg2 = loadImage("images/themes/mensa_layer.png");
                                          bg1 = loadImage("images/themes/mensa_floor.png");
                                        }
                                        spawnFood();                                          // Essensgegner werden erschaffen
                                        break;
                                      }
                                    }
                                    
                                    void spawnTable() {
                                      if (Math.round(random(1, 2)) == 1) { // 50% Chance
                                        Enemy tisch = new Enemy("Tisch", "images/enemys/objects/depressionTable.png", int(random(2, 8))); // Tischelement das Schaden macht
                                        tisch.spawn(); // Tisch erschaffen
                                      } else {
                                        Solid tisch = new Solid("Tisch", "images/enemys/objects/lehrerpult.png", int(random(4, 15))); //  Tischelement das solide ist
                                        tisch.spawn(); // Tisch erschaffen
                                      }
                                    }
                                    
                                    void spawnFood() {
                                      if ((frameCount % 100 == 0) && random(0, 100) > 30) {                                                     // Delay & Chance
                                        Enemy food = new Enemy("Essen", "images/enemys/food/food-" + Math.round(random(0, 97)) + ".png", 10);   // Enemyelement
                                        food.resizeFactor(2.2, 2.2); // Bild wird resized
                                        food.obstacleY+=10;          // Y-Wert korrigiert
                                        food.spawn();                // Wird erschaffen
                                      }
                                      if (frameCount % 60*50 == 0 && int(random(0, 10)) <= 2) spawnRandomTeacher(); // Das gleiche auch hier
                                      if (frameCount % 60*30 == 0 && int(random(0, 10)) <= 1) spawnWoodStair(); // Das gleiche auch hier
                                    }
                                    
                                    //
                                    // Ab hier keine Kommentare mehr, da das Prinzip überall gleich ist
                                    //
                                    
                                    void spawnWoodStair() {
                                      for (int i = 0; i < 3; i++) {
                                        for (int b=0; b < i+1; b++) {
                                          Solid holzKiste = new Solid("Holzkiste", "images/enemys/objects/kiste.png", 6);
                                          holzKiste.spawn();
                                          holzKiste.resizeFactor(0.6, 0.6);
                                          holzKiste.obstacleX = holzKiste.obstacleX + ((holzKiste.obstacleImg.width+200)*i);
                                          holzKiste.obstacleY = holzKiste.obstacleY - (holzKiste.obstacleImg.height*b);
                                        }
                                      }
                                    }
                                    
                                    void spawnFurniture() {
                                      String[] possFurnitureElements = new String[]{"bookshelf.png", "lehrerpult.png", "schrank.png", "schrank2.png", "schrank3.png"};
                                      if (frameCount % 120 == 0 && int(random(0, 100)) < 50) {
                                        String fString = possFurnitureElements[int(random(0, possFurnitureElements.length))];
                                        Enemy furniture = new Enemy("Möbel", "images/enemys/objects/" + fString, int(random(4, 10)));
                                        furniture.spawn();
                                      }
                                    }
                                    
                                    void spawnGymElements() {
                                      String[] possGymElements = new String[]{"bank", "beinpresse", "beinstrecke", "hantelbank", "kabelzug", "langhantel", "laufband", "laufrad", "multigym"};
                                      if (ticker % 40 == 0 && random(0, 100) < 40) {
                                        if (!(int(random(1, 10)) == 1)) {
                                          Obstacle element = new Enemy("Gymelement", "images/enemys/objects/"+possGymElements[int(random(0, possGymElements.length))]+".png", 6);
                                          element.resizeFactor(0.9, 0.9);
                                          element.spawn();
                                        } else if (random(1, 100) < 30) {
                                          String teacherImageName = "sport";
                                          String teacherName = "Sportlehrer";
                                    
                                          Obstacle teacherObject = new Enemy(teacherName, "/images/enemys/teachers/" + teacherImageName + ".png", 6);
                                          teacherObject.spawn();
                                          teacherObject.showName();
                                        } else if ((random(1, 100) > 50)) {
                                          spawnWoodStair();
                                        } else {
                                          PowerUp trampolin = new PowerUp("Trampolin", "/images/enemys/objects/trampolin.png", 6, 0);
                                          trampolin.resizeFactor(0.06, 0.06);
                                          trampolin.spawn();
                                        }
                                      }
                                    }
                                    
                                    void spawnOutsideElements() {
                                    
                                      if (ticker % 80 == 0 && random(0, 100) < 40) {
                                        if (int(random(1, 2)) == 1) {
                                          if (int(random(1, 5)) == 1) {
                                            Obstacle holzKiste = new Enemy("Holzkiste", "images/enemys/objects/kiste.png", 6);
                                            holzKiste.resize(50, 50);
                                            holzKiste.spawn();
                                            holzKiste.obstacleX = holzKiste.obstacleX + int(random(0, 300));
                                          } else {
                                            Obstacle holzKiste = new Enemy("Holzkiste", "images/enemys/objects/kiste.png", 6);
                                            holzKiste.resize(50, 50);
                                            holzKiste.spawn();
                                          }
                                        } else spawnRandomTeacher();
                                      }
                                    
                                      if (ticker % 110 == 0 && random(0, 100) < 10) {
                                        Obstacle bus = new Enemy("Bus", "images/enemys/objects/bus.png", int(random(10, 15)));
                                        bus.resizeFactor(0.8, 0.8);
                                        bus.spawn();
                                      }
                                    }
                                    
                                    void spawnRandomTeacher() {
                                      // Hier wird ein Randombild ausgewählt
                                      String[] teachers = new String[]{"abwart-Abwart", "bio-Biolehrer", "chemie-Chemielehrer", "geografie-Geografielehrer", "info-Informatiklehrer", "mathe-Mathematiklehrer", "musik-Musiklehrer", "rektor-Rektor", "rektor-Prorektor", "sport-Sportlehrer"};
                                    
                                      String teacher = teachers[int(random(0, teachers.length))];
                                      String teacherImageName = teacher.split("-")[0];
                                      String teacherName = teacher.split("-")[1];
                                    
                                      Obstacle teacherObject = new Enemy(teacherName, "/images/enemys/teachers/" + teacherImageName + ".png", 6);
                                      teacherObject.spawn();
                                    }
                                    
                                    
                                        //***************************************************************************************************************************
                                        /********************************************************g*******************************************************************
                                         **                                                                                                                         **
                                         **                       Obstacles                                                                                         **
                                         **                                                                                                                         **
                                         **   Hier befinden sich alle Obstacle Funktionen & Klassen die von der Hauptklasse Obstacle erben                          **
                                         **                                                                                                                         **
                                         ****************************************************************************************************************************/
                                        //***************************************************************************************************************************
                                        
                                        
                                        /*/************************************************************************************************************************************
                                         **                                                                                                                                  **
                                         **                      Code der für die Berührungen & Hitboxen der Objetke verantwortlich ist)                                     **
                                         **                                                                                                                                  **
                                        //************************************************************************************************************************************/
                                        void obstacleHandler() {
                                          for (int i = 0; i < obstacles.size(); i++) {                         // For Schleife durch alle Elemente
                                            Obstacle currentObstacle = obstacles.get(i);                       // Obstacle in der Schleife
                                        
                                            if (currentObstacle != null) {
                                        
                                              // Hitboxes Obstacle nur zum Debuggen
                                              if (showHitboxes) showDebugRect(color(255, 0, 0), int(currentObstacle.obstacleX), int(currentObstacle.obstacleY), currentObstacle.obstacleImg.width, currentObstacle.obstacleImg.height);
                                              if (showHitboxes) showDebugRect(color(255, 0, 0), int(player.x-player.playerImg.width/2), int(player.y-player.playerImg.height/2), player.playerImg.width, player.playerImg.height);
                                        
                                              currentObstacle.obstacleX -= currentObstacle.obstacleSpeed;      // Obstacle-X wird mit Obastclespeed verändert
                                              if (currentObstacle.obstacleX < -100) obstacles.remove(currentObstacle); // Falls das Obstacle aus dem Fenster geht, wird es gelöscht
                                        
                                              image(currentObstacle.obstacleImg, currentObstacle.obstacleX, currentObstacle.obstacleY);
                                              if (currentObstacle.showName) { // Falls der boolean true ist, wird über dem Objekt der Name angezeigt
                                                textSize(10);
                                                fill(#FFFFFF);
                                                textAlign(CENTER);
                                                text(currentObstacle.obstacleName, currentObstacle.obstacleX, currentObstacle.obstacleY-30);
                                              }
                                        
                                              // DEBUGMODE
                                              if (showHitboxes) {
                                                showDebugRect(color(255, 255, 255), int(player.x-player.playerImg.width/2), int(player.y+player.playerImg.height/2), player.playerImg.width, 1);
                                                showDebugRect(color(255, 255, 255), int(currentObstacle.obstacleX), int(currentObstacle.obstacleY), currentObstacle.obstacleImg.width, 1);
                                                text(currentObstacle.obstacleImg.width + "x"+ currentObstacle.obstacleImg.height, currentObstacle.obstacleX, currentObstacle.obstacleY-20);
                                                textAlign(LEFT);
                                                fill(255, 0, 0);
                                                textSize(10);
                                                text("canJump: " + player.canJump, 40, 100);
                                                text("gameTicker: " + gameTicker, 40, 100+20);
                                                text("tickSaveTheme: " + tickSaveTheme, 40, 100+40);
                                                text("herzen: " + herzen, 40, 100+60);
                                                text("levelTime: " + levelTime, 40, 100+80);
                                                text("theme: " + theme, 40, 100+100);
                                                text("obstacles length: " + obstacles.size(), 40, 100+120);
                                                text("alreadyThemesBeen length: " + alreadyThemesBeen.size(), 40, 100+140);
                                                text("player gravity: " + player.gravity, 40, 100+160);
                                                text("player x: " + player.x, 40, 100+180);
                                                text("player y: " + player.y, 40, 100+200);
                                                text("player speed: " + player.speed, 40, 100+220);
                                                text("player crouch: " + player.crouch, 40, 100+240);
                                                text("player animation: " + player.currentImage, 40, 100+260);
                                                text("framecount: " + frameCount, 40, 100+280);
                                                text("framerate: " + frameRate, 40, 100+300);
                                                text("jumpcount: " + jumpCount, 40, 100+320);
                                              }
                                        
                                        
                                              // Kollisionserkennung
                                              if (player.x + player.playerImg.width/2 >= currentObstacle.obstacleX && player.x <= currentObstacle.obstacleX + currentObstacle.obstacleImg.width &&
                                                player.y + player.playerImg.height/2 >= currentObstacle.obstacleY) {
                                        
                                                if (player.y+player.playerImg.height/2-10 <= currentObstacle.obstacleY && currentObstacle instanceof Solid) { // Falls der Spieler über dem Objekt ist und das Objekt solide ist
                                                  player.y-=10;                                                                                               // Spieler wird hochgedrückt / Steht auf dem Objekt
                                                  player.canJump = true;                                                                                      // Spieler kann wieder springen
                                                  if (showHitboxes) {                                                                                         // DEBUGMODE
                                                    imageMode(CENTER);
                                                    image(up, player.x, player.y);
                                                    imageMode(CORNER);
                                                  }
                                                }
                                                //else if (currentObstacle instanceof Solid) {
                                                //  player.canJump = false;
                                                //  if (player.x+player.playerImg.width/2+5 >= currentObstacle.obstacleX) {
                                                //    player.x-=currentObstacle.obstacleSpeed*2-1;
                                                //    player.canJump = true;
                                                //    if (showHitboxes) {
                                                //      imageMode(CENTER);
                                                //      image(left, player.x+player.playerImg.width/2+5, player.y);
                                                //      imageMode(CORNER);
                                                //    }
                                                //  } else player.canJump = false;
                                                //}
                                        
                                                currentObstacle.collide();
                                              }
                                            }
                                          }
                                        }
                                        
                                        
                                        // Funktion die jede 100 Frames mit 50% Chance einen Coin erschafft
                                        void spawnCoin() {
                                          if (!(ticker % 100 == 0 && random(0, 100) > 50)) return;
                                          Obstacle c = new Coin("Coin", "images/coin.png", 5);
                                          c.resize(30, 30);
                                          c.spawn();
                                        }
                                        
                                        
                                        
                                        abstract class Obstacle {
                                        
                                          String obstacleName; // Name
                                          String obstacleImage;// Bilddateipfad
                                          PImage obstacleImg;  // Bild
                                          int obstacleX;       // X
                                          int obstacleY;       // Y
                                          int obstacleSpeed;   // Geschwindigkeit
                                          boolean showName = false; // Wert ob der Name angezeigt werden soll
                                        
                                          Obstacle(String obstacleName, String obstacleImage, int obstacleSpeed) {  //////////////////////////////////////////////////////////////////////// VORLAGE / MUTTERKLASSE / ABSTRAKTE KLASSE 
                                            this.obstacleName = obstacleName;
                                            this.obstacleImage = obstacleImage;
                                            this.obstacleX = width+50;
                                            this.obstacleSpeed = obstacleSpeed;
                                        
                                            obstacleImg = loadImage(obstacleImage);
                                            int imgHeight = obstacleImg.height;
                                        
                                            this.obstacleY = height-imgHeight;
                                            
                                            if (showHitboxes) { // Falls Debugmode aktiv ist, wird der Name angezeigt
                                              this.showName = true;
                                              this.obstacleName = obstacleImage;
                                            }
                                          }
                                        
                                          void spawn() { // .spawn fügt das Obstacle der Liste zu
                                            obstacles.add(this);
                                          }
                                        
                                          void resize(int w, int h) { // Obstaclebild resizen
                                            obstacleImg.resize(w, h);
                                            this.obstacleY = height-h;
                                          }
                                        
                                          void resizeFactor(float w, float h) { // Obstaclebild mit float-multiplizieren
                                            obstacleImg.resize(int(obstacleImg.width*w), int(obstacleImg.height*h));
                                            this.obstacleY = height-obstacleImg.height;
                                          }
                                        
                                          void showName() { // 
                                            showName = true;
                                          }
                                        
                                          abstract void collide(); // Abstrakte Funktion / Vorlage für alle Obstacles
                                        }
                                        
                                        //
                                        //    Ab hier kommen die verschiedenen Obstacletypen die von der Hauptklasse erben
                                        //
                                        
                                        class Enemy extends Obstacle {  //////////////////////////////////////////////////////////////////////// ENEMY 
                                        
                                          Enemy(String obstacleName, String obstacleImage, int obstacleSpeed) {
                                            super(obstacleName, obstacleImage, obstacleSpeed);
                                          }
                                        
                                          void collide() {
                                            obstacles.remove(this);
                                        
                                            herzen--;
                                            damageSound.play();
                                        
                                            if (herzen <= 0) {
                                              loadGameData();
                                              coins+=score;
                                              if (highscore < score) highscore = score;
                                              saveGameData();
                                              obstacles.clear();
                                              gameState = 4;
                                              frameRate(60);
                                              createScreen(true); //DEBUG
                                            }
                                          }
                                        }
                                        
                                        class PowerUp extends Obstacle { //////////////////////////////////////////////////////////////////////// POWER UP 
                                          /*
                                            Effect 0 = Spieler wird hochgeschossen
                                           */
                                          int effect;
                                          PowerUp(String obstacleName, String obstacleImage, int obstacleSpeed, int effect) {
                                            super(obstacleName, obstacleImage, obstacleSpeed);
                                            this.effect = effect;
                                          }
                                        
                                          void collide() {
                                            obstacles.remove(this); // Obstacle wird nicht mehr angezeigt
                                        
                                            switch(effect) {
                                            case 0:
                                              player.speed = -600; // Spieler wird hochgespickt
                                              player.canJump = true; // Spieler kann nochmals springen
                                              break;
                                            }
                                          }
                                        }
                                        
                                        class Solid extends Obstacle {
                                        
                                          Solid(String obstacleName, String obstacleImage, int obstacleSpeed) {
                                            super(obstacleName, obstacleImage, obstacleSpeed);
                                          }
                                        
                                          void collide() {
                                          }
                                        }
                                        
                                        class Coin extends Obstacle { //////////////////////////////////////////////////////////////////////// COIN 
                                        
                                          Coin(String obstacleName, String obstacleImage, int obstacleSpeed) {
                                            super(obstacleName, obstacleImage, obstacleSpeed);
                                          }
                                        
                                          void collide() {
                                            score++; // Score um 1 höher
                                            coinSound.play();
                                            obstacles.remove(this);
                                          }
                                        }
                                        
                                            //***************************************************************************************************************************
                                            /********************************************************g*******************************************************************
                                             **                                                                                                                         **
                                             **                       QUELLEN & BEMERKUNGEN                                                                             **
                                             **                                                                                                                         **                                                                                                                   **
                                             ****************************************************************************************************************************/
                                            //***************************************************************************************************************************
                                            
                                            
                                            
                                            /*
                                            
                                              Menü Musik: https://youtu.be/uIfD2BKaD2k
                                              Background Image: spiritus.ch
                                              Coin Texture: https://opengameart.org/content/gold-cointoken (cc0)
                                              Animation Music:  Ultimate 8-bit Electro Gaming Music Mix 2020 - Chiptune Music Mix
                                              Game Over Sound bei Verloren: https://freesound.org/people/EVRetro/sounds/533034/ 
                                              Game Over Sound bei Gewonnen: https://freesound.org/people/Mrthenoronha/sounds/518305/
                                              Kaufsound: https://freesound.org/people/kiddpark/sounds/201159/
                                              Lehrerbilder: https://opengameart.org/content/pixel-game-characters
                                              Spielerbilder: https://opengameart.org/content/character-sprite-walk-animation
                                              Schrankbild: https://opengameart.org/content/cupboard-0
                                              Bücherregalbild: https://opengameart.org/content/bookshelf-3
                                              Busbild: https://opengameart.org/content/school-bus
                                              Inspiration Gymelemente: https://www.vhv.rs/viewpic/iTohbmx_fitness-clipart-gym-tool-gym-equipment-pixel-art/
                                              Tischbild: https://opengameart.org/content/table-to-do-the-only-thing-worth-it-in-life
                                              Möbel als Inspiration: https://opengameart.org/content/bad-kitchen-tileset  
                                              Musik:
                                                - https://opengameart.org/content/quirky-fast-dnb-8bit-drums-and-bass-lmfao
                                                - https://opengameart.org/content/8bit-theme-upbeat-overworld
                                                - https://opengameart.org/content/boss-battle-5-8-bit
                                                - https://opengameart.org/content/8-bit-theme-moving-right-along
                                               Coinsound: https://freesound.org/people/rigor789/sounds/341979/
                                               Damagesound: https://freesound.org/people/Taira%20Komori/sounds/214209/
                                               Jumpsound: https://freesound.org/people/Prof.Mudkip/sounds/422087/
                                               
                                            */
                                            
                                            // Bemerkungen
                                            
                                            /*
                                              - Die Kollisionsboxen können manchmal ungenau sein, was das Spiel vereinfachen soll.
                                              - Es gibt eine Schleichfunktion, die aber nicht gebaucht werden muss, da wir sie letztendlich doch unnötig gefunden haben
                                              - Die Hintergrundgrafiken sind meist entweder von der Spiritus.CH Galerie oder selbstgemacht
                                              - Nicht alle Möbel & Objekte sind vom Internet, sondern manche wurden auch selber gezeichnet
                                            */