/* autogenerated by Processing revision 1293 on 2025-01-16 */
import processing.core.*;
import processing.data.*;
import processing.event.*;
import processing.opengl.*;

import ddf.minim.*;
import ddf.minim.analysis.*;
import ddf.minim.effects.*;
import ddf.minim.signals.*;
import ddf.minim.spi.*;
import ddf.minim.ugens.*;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.Hashtable;

import java.util.HashMap;
import java.util.ArrayList;
import java.io.File;
import java.io.BufferedReader;
import java.io.PrintWriter;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;

public class MusicGame extends PApplet {












final int FRAME_RATE = 60;
final int REAL_WIDTH = 960;
final int REAL_HEIGHT = 540;
float NOTE_SPEED = 300;
int VISUAL_AUDIO_BUFFER = 2;
final float GAME_SPEED = 1; //does NOT affect music playback speed, only everything else in the game. used for dev reasons only.
final float MEASURES_PER_PHASE = 8;

int screen_resolution_x = 960;
int screen_resolution_y = 540;

//Music player
Minim minim;
AudioPlayer AP_percussion;
AudioPlayer AP_bass;

Scene main; 
Scene settingsMenu;
Scene gameScene;
Scene instructions;

Scene current;

GlobalData globalData; 

public void setup() {
  //resolution of in-game space
  /* size commented out by preprocessor */;
  
  //fullScreen();
  frameRate(FRAME_RATE);
  
  minim = new Minim(this);
  AP_percussion = minim.loadFile("song0\\percussion.mp3");
  AP_bass = minim.loadFile("song0\\bass.mp3");
  
  
  globalData = new GlobalData(); 
  
  main = new MainMenu();
  settingsMenu = new SettingsMenu();
  gameScene = new GameScene();
  instructions = new Instructions();
  current = main;
}

public void draw() {
  //scales the drawings of everything after this
  //scale(((float) screen_resolution_x)/((float)REAL_WIDTH));
  
  background(color(204));
  
  current.draw();
  
  //black border to cut off extra space
  fill(color(0));
  rect(960, -100, 10000, 10000);
  rect(-10, 540, 10000, 10000);
}

public void keyPressed() {
  
  switch (keyCode) {
    case LEFT:
    case 65:
    case 49:
      ((GameScene) gameScene).rhythmPanel.keyPress(0);
      break;
    case DOWN:
    case 83:
    case 50:
      ((GameScene) gameScene).rhythmPanel.keyPress(1);
      break;
    case RIGHT:
    case 68:
    case 51:
      ((GameScene) gameScene).rhythmPanel.keyPress(2);
      break;
    case UP:
    case 87:
    case 52:
      ((GameScene) gameScene).rhythmPanel.keyPress(3);
      break;
  }
  
}

public void keyReleased() {
  
  switch (keyCode) {
    case LEFT:
    case 65:
    case 49:
      ((GameScene) gameScene).rhythmPanel.keys[0].setPressed(false);
      break;
    case DOWN:
    case 83:
    case 50:
      ((GameScene) gameScene).rhythmPanel.keys[1].setPressed(false);
      break;
    case RIGHT:
    case 68:
    case 51:
      ((GameScene) gameScene).rhythmPanel.keys[2].setPressed(false);
      break;
    case UP:
    case 87:
    case 52:
      ((GameScene) gameScene).rhythmPanel.keys[3].setPressed(false);
      break;
  }
  
}


public int mouseX() {
  return (int) (mouseX * (((float)REAL_WIDTH/(float) screen_resolution_x)));
}

public int mouseY() {
  return (int) (mouseY * (((float)REAL_HEIGHT/(float) screen_resolution_y)));
}

public void setResolution(int x) {
  screen_resolution_x = x;
  screen_resolution_y = (int)(x * (9.f/16.f));
}
public class Button {
  
  int x;
  int y;
  int w;
  int h;
  
  final int BUFFER = 1;
  
  int col;
  int hover_col;
  int press_col;
  
  boolean pressed = false;
  boolean last_frame_pressed=false;
  
  public Button(int xi, int yi, int wi, int hi) {
    x = xi;
    y = yi;
    w = wi;
    h = hi;
    
    col = color(255);
    hover_col = color(191);
    press_col = color(127);
  }
  
  public Button(int xi, int yi, int wi, int hi, int c, int hc, int pc) {
    x = xi;
    y = yi;
    w = wi;
    h = hi;
    
    col = c;
    hover_col = hc;
    press_col = pc;
  }
  
  public boolean isHovered() {
    boolean hover;
    if (mouseX() - BUFFER < x) hover = false;
    else if (mouseY() - BUFFER < y) hover = false;
    else if (mouseX() + BUFFER > x + w) hover = false;
    else if (mouseY() + BUFFER > y + h) hover = false;
    else hover = true;
    return hover;
  }
  
  public boolean isPressed() {
    return (mousePressed && isHovered());
  }
  
  public boolean isJustPressed() {
    //if (last_frame_pressed && !mousePressed && isHovered()) println(x);
    return (last_frame_pressed && !mousePressed && isHovered());
  }
  
  public void draw() {
    last_frame_pressed = pressed;
    pressed = isPressed();
    int c = col;
    if (isPressed()) c = press_col;
    else if (isHovered()) c = hover_col;
    fill(c);
    rect(x, y, w, h);
  }
  
  public void setColors(int c, int hc, int pc) {
    col = c;
    hover_col = hc;
    press_col = pc;
  }
  
  public void setWhite() {
    setColors(color(255), color(191), color(127));
  }
  
  public void setGray() {
    setColors(color(127), color(63), color(0));
  }
  
  public void setBlack() {
    setColors(color(0), color(63), color(127));
  }
  
  public void setBlue() {
    setColors(color(0, 0, 255), color(0, 0, 191), color(0, 0, 127));
  }
  
  public void setLightBlue() {
    setColors(color(127, 127, 255), color(127, 127, 191), color(127, 127, 127));
  }
  
  public void setRed() {
    setColors(color(255, 0, 0), color(191, 0, 0), color(127, 0, 0));
  }
  
  public void setLightRed() {
    setColors(color(255, 127, 127), color(191, 127, 127), color(127, 127, 127));
  }
  
}


public class TextButton extends Button {
  
  String text;
  int text_size;
  
  public TextButton(int xi, int yi, int wi, int hi, String t, int ts) {
    super (xi, yi, wi, hi);
    text = t;
    text_size = ts;
  }
  
  public TextButton(int xi, int yi, int wi, int hi, int c, int hc, int pc, String t, int ts) {
    super(xi, yi, wi, hi, c, hc, pc);
    text = t;
    text_size = ts;
  }
  
  public void draw() {
    super.draw();
    
    fill(0);
    textSize(text_size);
    textAlign(CENTER);
    text(text, (x+w/2), (y+((h+text_size)/2)));
  }
  
}


public class ImgButton extends Button {
  
  String img_path;
  PImage img;
  
  public ImgButton(int xi, int yi, int wi, int hi, String ip) {
    super (xi, yi, wi, hi);
    setImage(ip);
  }
  
  public ImgButton(int xi, int yi, int wi, int hi, int c, int hc, int pc, String ip) {
    super(xi, yi, wi, hi, c, hc, pc);
    setImage(ip);
  }
  
  public void setImage(String ip) {
    img_path = ip;
    if (ip != null) {
      img = globalData.images.get(ip);
    }
    else img = null;
  }
  
  public void draw() {
    super.draw();
    
    if (img != null) {
      image(img, x, y, w, h);
    }
  }
  
}
//left portion of the game scene that shows some data for units, etc.
public class DataPanel {
  
  int x_mod;
  int y_mod;
  
  boolean show = false;
  
  String nam;
  String typ;
  String lvl;
  String max_stam; //max hp
  String cur_stam; //min hp
  String speed; //spaces can move per turn
  String melody; //melody damage
  String rhythm; //rhythm damage
  String focus; //melody defense
  String tempo; //rhythm defense
  String equipped;
  
  public DataPanel(int xi, int yi) {
    x_mod = xi;
    y_mod = yi;
  }
  
  public void updateData(UnitData data) {
    if (data != null) {
      show = true;
      nam = data.unit_name;
      typ = data.instrument_type;
      lvl = "1";
      max_stam = Integer.toString( data.max_stamina );
      cur_stam = Integer.toString( data.cur_stamina );
      speed = Integer.toString( data.speed );
      melody = Integer.toString( data.melody );
      rhythm = Integer.toString( data.rhythm );
      focus = Integer.toString( data.focus );
      tempo = Integer.toString( data.tempo );
      if (data.inventory[0] != null) equipped = data.inventory[0].item_name;
      else equipped = "";
    }
    else show = false;
  }
  
  public void draw() {
    
    if (show) {
    
      fill(color(255));
      rect(0 + x_mod, 0 + y_mod, 128, 128); //image placeholder
      
      textAlign(LEFT);
      fill(color(0));
      textSize(32);
      Label(nam, 140, 30);
      textSize(24);
      Label(typ, 140, 60);
      Label("Lv " + lvl, 140, 90);
      Label("STAMINA: " + cur_stam + "/" + max_stam, 0, 160);
      Label("MOVEMENT: " + speed, 0, 190);
      
      Label("MELODY: " + melody, 0, 250);
      Label("RHYTHM: " + rhythm, 0, 280);
      Label("FOCUS: " + focus, 140, 250);
      Label("TEMPO: " + tempo, 140, 280);
      
      Label("Equipped: " + equipped, 0, 340);
      
    }
    
  }
  
  public void Label(String t, float xi, float yi) {
    text(t, xi + x_mod, yi + y_mod);
  }
  
}
public class GameScene extends Scene {
  
  //UI objects
  MapUI mapUI = new MapUI(292, 20);
  DataPanel dataPanel = new DataPanel(20, 20);
  RhythmPanel rhythmPanel = new RhythmPanel(698, 120);
  Button back = new TextButton(20, 490, 100, 40, "Menu", 24);
  
  //game data
  String turn = "player";
  String phase = "strategy";
  int turn_count = 0;
  MapUnit hoveredTile = null;
  MapUnit selectedTile = null; //The most recently selected tile
  MapUnit lastSelectedTile = null; //The previosuly selected tile
  MapUnit attackingUnit = null;
  int[] selectedCoords = null; //The coordinates of the most recently selected tile
  int[] lastSelectedCoords = null; //The coordinates of the previosuly selected tile
  MapUnit tileForDataPanel = null;
  boolean[][] movRange = null;  //range of movement when a unit is selected.
  boolean[][] atkRange = null;  //range of attack when a unit is selected for attacking.
  MapUnit currentRhythmUnit;
  
  MapUnit justPressed;
  int[] justCoords;
  
  public GameScene() {

  }
  
  public void draw() {
    mapUI.draw();
    
    setHovered();
    
    if (attackingUnit == null) {
      setSelected();
    }
    else {
      setAttackSelected();
    }
    
    setDataPannel();
    
    dataPanel.draw();
    
    drawBattleTempo();
    
    rhythmPanel.draw();
    
    back.draw();
    if (back.isJustPressed()) {
      current = main;
      AP_percussion.pause();
      AP_bass.pause();
    }
    
  }
  
  public void drawBattleTempo() {
    fill(color(0));
    rect(698, 20, 242, 30);
    
    if (phase == "rhythm") {
      float percent = ((rhythmPanel.measure%MEASURES_PER_PHASE) + rhythmPanel.beat) / MEASURES_PER_PHASE;
      int buffer = 1;
      if (percent > 0) {
        fill(color(0, 255*(1-percent), 255*percent));
        rect(698 + buffer, 20 + buffer, (242 - buffer)*percent, 30 - 2*buffer);
      }
    }
    else {
      float percent = ((rhythmPanel.measure%(MEASURES_PER_PHASE*2)) + rhythmPanel.beat) / (MEASURES_PER_PHASE*2);
      int buffer = 1;
      if (percent > 0) {
        fill(color(0, 255*(1-percent), 255*percent));
        rect(698 + buffer, 20 + buffer, (242 - buffer)*percent, 30 - 2*buffer);
      }
    }
    
    textAlign(LEFT);
    textSize(20);
    fill(color(0));
    
    if (phase == "strategy") text(phase + " phase", 698, 70);
    else text("rhythm phase", 698, 70);
    
    if (phase == "strategy") text("Next: rhythm phase", 698, 100);
    
    
  }
  
  public void setHovered() {
    hoveredTile = (MapUnit) mapUI.getHovered();
  }
  
  public void setSelected() {
    justPressed = (MapUnit) mapUI.getJustPressed();
    justCoords = mapUI.getJustPressedCoords();
    
    if (justPressed != null && justPressed.hasMoved == true) {
      justPressed = null;
      justCoords = null;
    }
    
    if (justPressed != null) {
      mapUI.updateTiles();
      lastSelectedTile = selectedTile;
      selectedTile = justPressed;
    }
    
    if (justCoords != null) {
      lastSelectedCoords = selectedCoords;
      selectedCoords = justCoords;
    }
    
    if (justPressed != null && selectedTile == lastSelectedTile) {
        //lastSelectedTile = selectedTile;
        deselect();
      }
    
    if (hoveredTile == null && mousePressed) {
      if (selectedTile != null) lastSelectedTile = selectedTile;
      deselect();
    }
    
    if (justCoords != null) {
      checkMovement();
    }
  }
  
  public void setAttackSelected() {
    if ((MapUnit) mapUI.getJustPressed() != null) {
      attackingUnit.target = (MapUnit) mapUI.getJustPressed();
      attackingUnit.setTargeted(true);
      setRhythmPlayer(attackingUnit);
      if (attackingUnit == attackingUnit.target) attackingUnit.target = null;
      attackingUnit = null;
      mapUI.updateTiles();
      if (rhythmPanel.counting == false)  checkEndOfTurn(1);
    }
  }
  
  public void setDataPannel() {
    
    if (selectedTile != null) dataPanel.updateData(((MapUnit)selectedTile).data);
    else if (hoveredTile != null) dataPanel.updateData(((MapUnit)hoveredTile).data);
    
  }
  
  public void checkMovement() {
    
    if (selectedTile != null) {
      movRange = mapUI.showMovement(mapUI.mapData.getMovRange(selectedTile.x, selectedTile.y, selectedTile.obj_type), ((MapUnit)selectedTile).data.speed);
    }
    else {
      if (lastSelectedTile != null && selectedCoords != null) {
        if (movRange[selectedCoords[0]][selectedCoords[1]]) {
          mapUI.moveTile(selectedCoords[0], selectedCoords[1], lastSelectedTile);
          lastSelectedTile.setMoved(true);
          attackingUnit = lastSelectedTile;
          lastSelectedTile = null;
          mapUI.updateTiles();
          checkAttackRange();
        }
      }
    }
  }
  
  public void checkAttackRange() {
    atkRange = mapUI.showAttackRange(mapUI.mapData.getAtkRange(attackingUnit.x, attackingUnit.y, attackingUnit.obj_type), attackingUnit.data.getEquipped().range);
  }
  
  public void deselect() {
    selectedTile = null;
    selectedCoords = null;
    mapUI.updateTiles();
  }
  
  public String getTurn() {
    return turn;
  }
  
  public String getPhase() {
    return phase;
  }
  
  //index is 1 for playerturn, 2 for enemyturn
  public void checkEndOfTurn(int index) {
    boolean end = true;
    for (int i = 0; i < mapUI.mapData.wid; i++) {
      for (int j = 0; j < mapUI.mapData.hei; j++) {
        MapUnit unit = (MapUnit)mapUI.mapData.objects[i][j];
        if (unit != null) {
          if ((!unit.hasMoved || !unit.hasTargeted) && unit.obj_type == index) {
            end = false;
          }
        }
      }
    }
    if (end) endTurn();
  }
  
  public void endTurn() {
    swapPhase();
    if (rhythmPanel.counting == false) {
      rhythmPanel.initializeArrows();
      
    }
    turn_count++;
  }
  
  public void swapPhase() {
    if (phase == "strategy") {
      phase = "rhythm";
    }
    else {
      phase = "strategy";
      startStrategy();
    }
  }
  
  //garcoded when it should not be but whatever
  public void startStrategy() {
    rhythmPanel.perc_unit.startTurn();
    rhythmPanel.bass_unit.startTurn();
    mapUI.updateTiles();
  }
  
  public void setInstrument(String ins) {
    if (ins == "percussion") {
      //dataPanel.updateData();
    }
    
  }
  
  //hardcoded when it should not be but whatever
  public void setRhythmPlayer(MapUnit unit) {
    if (unit.data.instrument_type == "Percussion") {
      rhythmPanel.perc_unit = unit;
    }
    else if (unit.data.instrument_type == "Guitar") {
      rhythmPanel.bass_unit = unit;
    }
  }
  
}
public class GlobalData {
  
  public UnitData vocalist = new UnitData("Wyen", 42, 6, 7, 2, 4, 1, "Vocals");
  public UnitData guitarist = new UnitData("Pandora", 48, 5, 6, 4, 3, 3, "Guitar");
  public UnitData bassist = new UnitData("Benni", 60, 5, 3, 5, 2, 5, "Guitar");
  public UnitData drummer = new UnitData("Casey", 54, 4, 1, 7, 1, 4, "Percussion");
  public UnitData keyboardist = new UnitData("Alec", 45, 5, 5, 4, 2, 1, "Keyboard");
  
  public UnitData enemyDrummer = new UnitData("Drummer", 50, 5, 1, 1, 1, 1, "Percussion");
  public UnitData trainingDummy = new UnitData("Training Dummy", 100, 1, 1, 1, 1, 1, "Vocals");
  
  public ItemData microphone = new Microphone("Microphone", 1);
  public ItemData acoustic_guitar = new Guitar("Acoustic Guitar", 1);
  public ItemData electric_guitar = new Guitar("Electric Guitar", 1);
  public ItemData bass_guitar = new BassGuitar("Bass Guitar", 1);
  public ItemData mobile_drum_kit = new Drum("Mobile Drum Kit", "mid", 1);
  public ItemData marching_snare = new Drum("Marching Snare", "mid", 1);
  public ItemData marching_bass = new Drum("Marching Bass", "low", 1);
  public ItemData synthesizer = new Keyboard("Synthesizer", 1);
  public ItemData keytar = new Keyboard("Keytar", 1);
  
  Dictionary<String, PImage> images= new Hashtable<>();
  
  public GlobalData() {
    
    images.put("unit", loadImage("unit.png"));
    images.put("arrow_0", loadImage("arrow_left.png"));
    images.put("arrow_1", loadImage("arrow_down.png"));
    images.put("arrow_2", loadImage("arrow_right.png"));
    images.put("arrow_3", loadImage("arrow_up.png"));
    
    vocalist.giveItem(microphone);
    guitarist.giveItem(electric_guitar);
    bassist.giveItem(bass_guitar);
    drummer.giveItem(mobile_drum_kit);
    keyboardist.giveItem(synthesizer);
    
  }
  
  public MapData Map1() {
    
    int[][] terrainMap = new int[][] {
      {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
      {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
      {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
      {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
      {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
      {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
      {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
      {1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1},
      {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1},
      {1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1},
      {0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1},
      {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0},
      {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
      {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
      {0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
      {0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
      {0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
      {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
      {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
      {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    };
    
    MapData map = new MapData(terrainMap);
    
    //map.initTiles();
    
    MapObj playerA = new MapUnit(1, vocalist);
    MapObj playerB = new MapUnit(1, guitarist);
    MapObj playerC = new MapUnit(1, bassist);
    MapObj playerD = new MapUnit(1, drummer);
    MapObj playerE = new MapUnit(1, keyboardist);
    
    MapObj enemyA = new MapUnit(2, enemyDrummer);
    
    //map.setTile(5, 19, playerA);
    //map.setTile(4, 18, playerB);
    map.setTile(2, 18, playerC);
    map.setTile(3, 19, playerD);
    //map.setTile(1, 19, playerE);
    
    //map.setTile(10, 15, enemyA);
    map.setTile(6, 16, new MapUnit(2, trainingDummy.copyUnit()));
    map.setTile(8, 14, new MapUnit(2, trainingDummy.copyUnit()));
    map.setTile(10, 15, new MapUnit(2, trainingDummy.copyUnit()));
    map.setTile(1, 11, new MapUnit(2, trainingDummy.copyUnit()));
    map.setTile(2, 12, new MapUnit(2, trainingDummy.copyUnit()));
    
    return map;
  }
  
}

public class ItemData {
  
  public String item_name;
  
  public ItemData(String nam) {
    item_name = nam;
  }
  
}

public class InstrumentData extends ItemData {
  
  public String instrument_type;
  public String damage_type;
  public String pitch;
  public int damage_mod;
  public int range;
  
  public InstrumentData(String nam, String instr, String dmg, String pit, int mod, int ran) {
    super(nam);
    instrument_type = instr;
    damage_type = dmg;
    pitch = pit;
    damage_mod = mod;
    range = ran;
  }
  
}

public class Microphone extends InstrumentData {
  public Microphone(String nam, int mod) {
    super(nam, "microphone", "melody", "high", mod, 1);
  }
}

public class Guitar extends InstrumentData {
  public Guitar(String nam, int mod) {
    super(nam, "guitar", "melody", "mid", mod, 5);
  }
}

public class BassGuitar extends InstrumentData {
  public BassGuitar(String nam, int mod) {
    super(nam, "guitar", "rhythm", "low", mod, 3);
  }
}

public class Drum extends InstrumentData {
  public Drum(String nam, String pit, int mod) {
    super(nam, "percussion", "rhythm", pit, mod, 4);
  }
}

public class Keyboard extends InstrumentData {
  public Keyboard(String nam, int mod) {
    super(nam, "keyboard", "melody", "high", mod, 1);
  }
}
public class MapData {
  
  int wid;
  int hei;
  
  public MapObj[][] objects;
  
  //0-> plains
  //1-> walls
  int[][] terrain;
  
  public MapData(int[][] ter) {
    wid = ter[0].length;
    hei = ter.length;
    
    objects = new MapObj[wid][hei];
    terrain = ter;
  }
  
  public MapObj getTile(int x, int y) {
    return objects[x][y];
  }
  
  public void setTile(int x, int y, MapObj obj) {
    if (obj != null) obj.setCoords(x, y);
    objects[x][y] = obj;
  }
  
  public void moveTile(int x, int y, MapObj obj) {
    int[] oldCoords = obj.getCoords();
    setTile(x, y, obj);
    setTile(oldCoords[0], oldCoords[1], null);
  }
  
  public void initTiles() {
    for (int i = 0; i < wid; i++) {
      for (int j = 0; j < hei; j++) {
        setTile(i, j, new MapObj(i, j, -1) );
      }
    }
  }
  
  public int[][] getMovRange(int x_pos, int y_pos, int typ) {
    int[][] tiles = new int[wid][hei];
    
    for (int i = 0; i < wid; i++) {
      for (int j = 0; j < hei; j++) {
        tiles[i][j] = 999;
      }
    }
    
    tiles = checkSpace(tiles, 0, x_pos, y_pos, typ, false);
    
    tiles = blockOffFullTiles(tiles);
    
    return tiles;
  }
  
  public int[][] getAtkRange(int x_pos, int y_pos, int typ) {
    int[][] tiles = new int[wid][hei];
    
    for (int i = 0; i < wid; i++) {
      for (int j = 0; j < hei; j++) {
        tiles[i][j] = 999;
      }
    }
    
    tiles = checkSpace(tiles, 0, x_pos, y_pos, typ, true);
    
    return tiles;
  }
  
  public int[][] checkSpace(int[][] tiles, int range, int x, int y, int typ, boolean noclip) {
    if (x >= 0 && x < wid && y >= 0 && y < hei) {
      
      if (canWalkThrough(x, y, typ) || noclip) {
        if (range < tiles[x][y]) {
          tiles[x][y] = range;
          checkSpace(tiles, range+1, x-1, y, typ, noclip);
          checkSpace(tiles, range+1, x, y-1, typ, noclip);
          checkSpace(tiles, range+1, x+1, y, typ, noclip);
          checkSpace(tiles, range+1, x, y+1, typ, noclip);
        }
      }
      else {
        tiles[x][y] = 999;
      }
      
    }
    
    return tiles;
  }
  
  public boolean canWalkThrough(int x, int y, int typ) {
    boolean can = true;
    
    if (terrain[y][x] == 1) can = false;
    
    switch (typ) {
      case 1:
        if (getTile(x, y) != null && getTile(x, y).obj_type == 2) can = false;
        break;
      case 2:
        if (getTile(x, y) != null && getTile(x, y).obj_type == 1) can = false;
        break;
    }
    
    
    
    return can;
  }
  
  public int[][] blockOffFullTiles(int[][] tiles) {
    for (int i = 0; i < wid; i++) {
      for (int j = 0; j < hei; j++) {
        if (objects[i][j] != null) {
          tiles[i][j] = 999;
        }
      }
    }
    
    return tiles;
  }
  
  
  
}
public class MapObj {
  
  int x;
  int y;
  
  //-1-> empty, 0->terrain, 1-> player, 2-> enemy
  public int obj_type;
  
  public MapObj(int typ) {
    obj_type = typ;
  }
  
  public MapObj(int typ, int xi, int yi) {
    obj_type = typ;
    setCoords(xi, yi);
  }
  
  public void setCoords(int xi, int yi) {
    x = xi;
    y = yi;
  }
  
  public int[] getCoords() {
    return new int[] {x, y};
  }
  
}

public class MapUnit extends MapObj {
  
  public UnitData data;
  
  public boolean hasMoved = false;
  public boolean hasTargeted = false;
  public MapUnit target;
  
  public MapUnit(int typ, UnitData d) {
    super(typ);
    data = d;
  }
  
  public void setMoved(boolean mov) {
    hasMoved = mov;
  }
  
  public void setTargeted(boolean tar) {
    hasTargeted = tar;
  }
  
  public void startTurn() {
    hasMoved = false;
    hasTargeted = false;
  }
  
}
public class MapUI {
  
  MapData mapData;
  
  ImgButton[][] tiles;
  
  int x_mod;
  int y_mod;
  
  int tile_wid = 25;
  int tile_hei = 25;
  
  public MapUI(int x, int y) {
    x_mod = x;
    y_mod = y;
    mapData = new GlobalData().Map1();
    
    tiles = new ImgButton[mapData.wid][mapData.hei];
    
    //initializes tiles and sets the terrain.
    initializeMap();
    showTerrain();
    updateUnitTiles();
  }
  
  public void moveTile(int x, int y, MapObj unit) {
    mapData.moveTile(x, y, unit);
  }
  
  public void updateUnitTiles() {
    
    for (int i = 0; i < mapData.wid; i++) {
      for (int j = 0; j < mapData.hei; j++) {
        
        MapUnit unit = (MapUnit) mapData.getTile(i, j);
        ImgButton tile = tiles[i][j];
        
        if (unit != null ) {
          
          //removes dead units
          if (unit.data.isDead()) {
            unit = null;
            mapData.setTile(i, j, null);
            tile.setImage(null);
          }
          else {
          
            switch (unit.obj_type) {
              case -1:
                tile.setImage(null);
                break;
              case 1:
                if (unit.hasMoved) {
                  tile.setGray();
                }
                else {
                  tile.setBlue();
                }
                tile.setImage("unit");
                break;
              case 2:
                tile.setRed();
                tile.setImage("unit");
                break;
            }
          }
          
        }
        
        else {
          tile.setImage(null);
        }
        
      }
    }
  }
  
  public void updateTiles() {
    showTerrain();
    updateUnitTiles();
  }
  
  public void initializeMap() {
    for (int i = 0; i < mapData.wid; i++) {
      for (int j = 0; j < mapData.hei; j++) {
        tiles[i][j] = new ImgButton(x_mod + tile_wid*i, y_mod + tile_hei*j, tile_wid, tile_hei, null);
      }
    }
  }
  
  //reset the colors of the map based on the terrain map.
  public void showTerrain() {
    for (int i = 0; i < mapData.wid; i++) {
      for (int j = 0; j < mapData.hei; j++) {
        tiles[i][j].setWhite();
        if (mapData.terrain[j][i] == 1) tiles[i][j].setBlack();
      }
    }
  }
  
  //modify the colors of the map to show movement based on a movement map.
  public boolean[][] showMovement(int[][] map, int spaces) {
    boolean[][] movTiles = new boolean[tiles.length][tiles[0].length];
    for (int i = 0; i < mapData.wid; i++) {
      for (int j = 0; j < mapData.hei; j++) {
        if (map[i][j] <= spaces) {
          tiles[i][j].setLightBlue();
          movTiles[i][j] = true;
        }
        else {
          movTiles[i][j] = false;
        }
      }
    }
    return movTiles;
  }
  
  //modify the colors of the map to show movement based on a movement map.
  public boolean[][] showAttackRange(int[][] map, int spaces) {
    boolean[][] movTiles = new boolean[tiles.length][tiles[0].length];
    for (int i = 0; i < mapData.wid; i++) {
      for (int j = 0; j < mapData.hei; j++) {
        if (map[i][j] <= spaces) {
          tiles[i][j].setLightRed();
          movTiles[i][j] = true;
        }
        else {
          movTiles[i][j] = false;
        }
      }
    }
    return movTiles;
  }
  
  public void drawTiles() {
    for (ImgButton[] row : tiles) {
      for (ImgButton tile : row) {
        tile.draw();
      }
    }
  }
  
  public MapObj getHovered() {
    for (int i = 0; i < mapData.wid; i++) {
      for (int j = 0; j < mapData.hei; j++) {
        if (tiles[i][j].isHovered()) {
          return mapData.getTile(i, j);
        }
      }
    }
    return null;
  }
  
  public MapObj getJustPressed() {
    for (int i = 0; i < mapData.wid; i++) {
      for (int j = 0; j < mapData.hei; j++) {
        if (tiles[i][j].isJustPressed()) {
          return mapData.getTile(i, j);
        }
      }
    }
    return null;
  }
  
  public int[] getJustPressedCoords() {
    for (int i = 0; i < mapData.wid; i++) {
      for (int j = 0; j < mapData.hei; j++) {
        if (tiles[i][j].isJustPressed()) {
          return new int[]{i, j};
        }
      }
    }
    return null;
  }
  
  public void draw() {
    drawTiles();
    
  }
  
}
public class RhythmKey {
  
  int x;
  int y;
  int w;
  int h;
  
  int col;
  int press_col;
  
  boolean isPressed;
  
  public RhythmKey(int xi, int yi, int wi, int hi, int c, int pc) {
    x = xi;
    y = yi;
    w = wi;
    h = hi;
    
    col = c;
    press_col = pc;
  }
  
  public void setPressed(boolean p) {
    isPressed = p;
  }
  
  public void draw() {
    int c = col;
    if (isPressed) c = press_col;
    fill(c);
    rect(x, y, w, h);
  }
  
}

public class RhythmArrow {
  
  int x;
  float y;
  int index;
  int key_y_value; //the y-value at which the arrow lines perfectly with its button
  boolean visible = true;
  
  RhythmPanel panel;
  int bpm = 134;
  int measure;
  float beat;
  
  final int IMG_HEIGHT = 48;
  final int POINT_CUTOFF = 20;
  final int Y_TO_MISS = 50;
  
  public RhythmArrow(RhythmPanel p, int xi, int ki, int ind, int m, float b) {
    x = xi;
    y = 9999;
    key_y_value = ki;
    index = ind;
    measure = m;
    beat = b;
    panel = p;
  }
  
  public void setPos() {
    //When the panel's measure and beat line up with this arrow's measure and beat, the y-value should be at key_y_value.
    //If the panel's measure and beat are less than this arrow's measure and beat, the y-value should be greater than key_y_value.
    float panel_time = panel.measure + panel.beat;
    float this_time = measure + beat;
    
    y = (this_time * NOTE_SPEED) - (NOTE_SPEED * panel_time) + key_y_value + VISUAL_AUDIO_BUFFER;
    
    //y = key_y_value + note_speed*(panel_time + this_time);
    
  }
  
  public int getPoints() {
    
    int dist = (int) (y - key_y_value);    
    int points;
    if (dist < 0 - POINT_CUTOFF) {
      points = -1;
    }
    else if (dist < 0) {
      points = 2;
    }
    else if (dist < POINT_CUTOFF) {
      points = 3;
    }
    else if (dist < POINT_CUTOFF*2) {
      points = 2;
    }
    else if (dist < POINT_CUTOFF*3) {
      points = 1;
    }
    else if (dist < POINT_CUTOFF*4) {
      points = -1;
    }
    else {
      points = 0;
    }
    
    return points;
  }
  
  public boolean isInBounds() {
    return (y < height || y > 0);
  }
  
  public void hide() {
    visible = false;
  }
  
  public void draw() {
    setPos();
    
    if (y < key_y_value - Y_TO_MISS) {
      hide();
    }
    
    if (isInBounds() && visible) image(globalData.images.get("arrow_" + index), x, y);
  }
  
}
public class SongMaps1 extends SongMaps {
  
  public SongMaps1() {
    bpm = 134;
    beats_in_measure = 4;
    
    p = new RhythmMap[5];
    p[0] = new RhythmMap("percussion", 8);
    for (int i = 0; i < 8; i++) {
      p[0].addNote(1, i, 0);
      p[0].addNote(1, i, 0.5f);
      p[0].addNote(2, i, 0.5f);
    }
    
    p[1] = new RhythmMap("percussion", 16);
    for (int i = 8; i < 16; i++) {
      p[1].addNote(1, i, 0);
      p[1].addNote(1, i, 0.5f);
      p[1].addNote(2, i, 0.5f);
    }
    
    p[2] = new RhythmMap("percussion", 24);
    for (int i = 0; i < 8; i++) {
      p[2].addNote(1, i, 0);
      p[2].addNote(1, i, 0.5f);
    }
    p[2].addNote(2, 7, 0.625f);
    p[2].addNote(2, 7, 0.75f);
    
    p[3] = new RhythmMap("percussion", 32);
    
    for (int i = 0; i < 8; i++) {
      p[3].addNote(2, i, 0.25f);
      p[3].addNote(2, i, 0.375f);
      p[3].addNote(2, i, 0.75f);
      if (i % 4 == 0 && i != 0) p[3].addNote(0, i, 0.25f);
      else if (i < 8 || i > 11) {
      p[3].addNote(3, i, 0); 
      p[3].addNote(3, i, 0.125f);
      p[3].addNote(3, i, 0.25f); }
      p[3].addNote(3, i, 0.375f);
      p[3].addNote(3, i, 0.5f);
      p[3].addNote(3, i, 0.625f);
      p[3].addNote(3, i, 0.75f);
      p[3].addNote(3, i, 0.875f);
    }
    
    p[4] = new RhythmMap("percussion", 32);
    for (int i = 8; i < 16; i++) {
      p[4].addNote(2, i, 0.25f);
      p[4].addNote(2, i, 0.375f);
      p[4].addNote(2, i, 0.75f);
      if (i % 4 == 0 && i != 0) p[4].addNote(0, i, 0.25f);
      else if (i < 8 || i > 11) {
      p[4].addNote(3, i, 0); 
      p[4].addNote(3, i, 0.125f);
      p[4].addNote(3, i, 0.25f); }
      p[4].addNote(3, i, 0.375f);
      p[4].addNote(3, i, 0.5f);
      p[4].addNote(3, i, 0.625f);
      p[4].addNote(3, i, 0.75f);
      p[4].addNote(3, i, 0.875f);
    }
    
    b = new RhythmMap[5];
    b[0] = new RhythmMap("guitar", 0);
    b[0].addNote(2, 0, 0);
    b[0].addNote(2, 1, 0);
    b[0].addNote(2, 1, 0.375f);
    b[0].addNote(2, 1, 0.625f);
    b[0].addNote(1, 1, 0.875f);
    b[0].addNote(2, 4, 0);
    b[0].addNote(2, 5, 0);
    b[0].addNote(2, 5, 0.375f);
    b[0].addNote(2, 5, 0.625f);
    b[0].addNote(1, 5, 0.875f);
    b[0].addNote(0, 7, 0.625f);
    b[0].addNote(0, 7, 0.75f);
    
    
    b[1] = new RhythmMap("guitar", 8);
    b[1].addNote(2, 0, 0);
    b[1].addNote(2, 1, 0);
    b[1].addNote(2, 1, 0.375f);
    b[1].addNote(2, 1, 0.625f);
    b[1].addNote(1, 1, 0.875f);
    b[1].addNote(2, 4, 0);
    b[1].addNote(2, 5, 0);
    b[1].addNote(2, 5, 0.375f);
    b[1].addNote(2, 5, 0.625f);
    b[1].addNote(1, 5, 0.875f);
    b[1].addNote(0, 7, 0.625f);
    b[1].addNote(0, 7, 0.75f);
    b[1].addNote(3, 8, 0);
    b[1].addNote(3, 9, 0);
    b[1].addNote(2, 9, 0.25f);
    b[1].addNote(1, 9, 0.375f);
    b[1].addNote(2, 9, 0.625f);
    b[1].addNote(1, 9, 0.875f);
    b[1].addNote(0,11, 0);
    b[1].addNote(0,11, 0.375f);
    b[1].addNote(1,11, 0.625f);
    b[1].addNote(2,11, 0.875f);
    b[1].addNote(2,13, 0);
    b[1].addNote(2,13, 0.375f);
    b[1].addNote(3,13, 0.625f);
    b[1].addNote(1,13, 0.875f);
    
    b[2] = new RhythmMap("guitar", 24);
    b[2].addNote(3, 0, 0);
    b[2].addNote(3, 1, 0);
    b[2].addNote(3, 1, 0.375f);
    b[2].addNote(3, 1, 0.625f);
    b[2].addNote(1, 1, 0.875f);
    b[2].addNote(3, 4, 0);
    b[2].addNote(3, 5, 0);
    b[2].addNote(3, 5, 0.375f);
    b[2].addNote(3, 5, 0.625f);
    b[2].addNote(1, 5, 0.875f);
    b[2].addNote(0, 7, 0.625f);
    b[2].addNote(0, 7, 0.75f);
    
    b[3] = new RhythmMap("guitar", 32);
    b[3].addNote(2, 0, 0);
    b[3].addNote(1, 0, 0.25f);
    b[3].addNote(2, 0, 0.375f);
    b[3].addNote(1, 0, 0.75f);
    b[3].addNote(2, 1, 0);
    b[3].addNote(1, 1, 0.125f);
    b[3].addNote(2, 1, 0.375f);
    b[3].addNote(1, 1, 0.75f);
    b[3].addNote(2, 2, 0);
    b[3].addNote(1, 2, 0.125f);
    b[3].addNote(2, 2, 0.375f);
    b[3].addNote(1, 2, 0.625f);
    b[3].addNote(2, 3, 0);
    b[3].addNote(1, 3, 0.125f);
    b[3].addNote(2, 3, 0.375f);
    b[3].addNote(3, 3, 0.625f);
    
    b[3].addNote(1, 4, 0.25f);
    b[3].addNote(1, 4, 0.5f);
    b[3].addNote(0, 4, 0.75f);
    b[3].addNote(1, 5, 0);
    b[3].addNote(0, 5, 0.125f);
    b[3].addNote(1, 5, 0.375f);
    b[3].addNote(1, 5, 0.75f);
    b[3].addNote(0, 5, 0.825f);
    b[3].addNote(1, 6, 0);
    b[3].addNote(0, 6, 0.125f);
    b[3].addNote(1, 6, 0.375f);
    b[3].addNote(0, 6, 0.625f);
    b[3].addNote(1, 7, 0);
    b[3].addNote(0, 7, 0.125f);
    b[3].addNote(1, 7, 0.375f);
    b[3].addNote(2, 7, 0.625f);
    
    b[4] = new RhythmMap("guitar", 32);
    b[4].addNote(2, 8, 0);
    b[4].addNote(1, 8, 0.25f);
    b[4].addNote(2, 8, 0.375f);
    b[4].addNote(1, 8, 0.75f);
    b[4].addNote(2, 9, 0);
    b[4].addNote(1, 9, 0.125f);
    b[4].addNote(2, 9, 0.375f);
    b[4].addNote(1, 9, 0.75f);
    b[4].addNote(2,10, 0);
    b[4].addNote(1,10, 0.125f);
    b[4].addNote(2,10, 0.375f);
    b[4].addNote(1,10, 0.625f);
    b[4].addNote(2,11, 0);
    b[4].addNote(1,11, 0.125f);
    b[4].addNote(2,11, 0.375f);
    b[4].addNote(3,11, 0.625f);
    
    b[4].addNote(1,12, 0.25f);
    b[4].addNote(1,12, 0.5f);
    b[4].addNote(0,12, 0.75f);
    b[4].addNote(1,13, 0);
    b[4].addNote(0,13, 0.125f);
    b[4].addNote(1,13, 0.375f);
    b[4].addNote(1,13, 0.75f);
    b[4].addNote(0,13, 0.825f);
    b[4].addNote(1,14, 0);
    b[4].addNote(0,14, 0.125f);
    b[4].addNote(1,14, 0.375f);
    b[4].addNote(0,14, 0.625f);
    b[4].addNote(1,15, 0);
    b[4].addNote(0,15, 0.125f);
    b[4].addNote(1,15, 0.375f);
    b[4].addNote(2,15, 0.625f);
  }
  
}

public class SongMaps {
  //for this song: 134
  int bpm; //beats per MINUTE
  int beats_in_measure;
  RhythmMap[] g; //guitar
  RhythmMap[] b; //bass
  RhythmMap[] k; //keyboard
  RhythmMap[] p; //percussion
  RhythmMap[] v; //vocals
}


public class RhythmMap {
  
  String instrument;
  int measure;
  ArrayList<MapNote> notes;
  
  public RhythmMap(String i, int m) {
    instrument = i;
    measure = m;
    notes = new ArrayList<MapNote>();
  }
  
  public void addNote(int d, int m, float b) {
    notes.add(new MapNote(d, m, b));
  }
  
}

public class MapNote {
  int dir; //0-left, 1-down, 2-right, 3-up
  int measure; //starting from the beginning of the "part", not the song.
  float beat; // when the note is within the measure, values are on the range [0,1).
  
  public MapNote(int d, int m, float b) {
    dir = d;
    measure = m;
    beat = b;
  }
}
public class RhythmPanel {
  
  int x_mod;
  int y_mod;
  
  int measure = -4;
  float beat = 0;
  boolean counting = false;
  
  final int KEY_SIZE = 50;
  final int SPACING = 10;
  
  RhythmKey[] keys;
  ArrayList<RhythmArrow> arrows;
  RhythmArrow[] nextArrows; //the most recent arrow in each category. 
  
  SongMaps song = new SongMaps1();
  RhythmMap[] curMaps;
  
  MapUnit bass_unit;
  MapUnit perc_unit;
  
  boolean show = false;
  boolean flag0 = false;
  
  public RhythmPanel(int xi, int yi) {
    x_mod = xi;
    y_mod = yi;
    
    keys = new RhythmKey[]{
      new RhythmKey(0 + x_mod, 0 + y_mod, KEY_SIZE, KEY_SIZE, color(0, 255, 0), color(0, 127, 0)),
      new RhythmKey(KEY_SIZE + SPACING + x_mod, 0 + y_mod, KEY_SIZE, KEY_SIZE, color(255, 0, 0), color(127, 0, 0)),
      new RhythmKey(2*(KEY_SIZE + SPACING) + x_mod, 0 + y_mod, KEY_SIZE, KEY_SIZE, color(255, 255, 0), color(127, 127, 0)),
      new RhythmKey(3*(KEY_SIZE + SPACING) + x_mod, 0 + y_mod, KEY_SIZE, KEY_SIZE, color(0, 0, 255), color(0, 0, 127)),
    };
    nextArrows = new RhythmArrow[4];
    
  }
  
  public void draw() {
    
    soundProcess();
    
    if (((GameScene)gameScene).phase == "rhythm" || counting) {
    
      for (RhythmKey k : keys) {
          k.draw();
        }
        
      for (RhythmArrow a : arrows) {
        a.draw();
      }
    
    }
    
    if (counting) gameTimerProcess();
    
    
  }
  /*
  void initializeArrows_old(RhythmMap[] c) {
    curMaps = c;
    arrows = new ArrayList<RhythmArrow>();
    if (curMaps != null) {
      for (RhythmMap r : curMaps) {
        for (MapNote n : r.notes) {
          arrows.add( new RhythmArrow( this, keys[n.dir].x, keys[n.dir].y, n.dir, r.measure + n.measure, n.beat ) );
        }
      }
      updateAllArrows();
    }
  }*/
  
  public void initializeArrows() {
    counting = true;
    measure = -4;
    beat = 0;
    curMaps = new RhythmMap[4];
    curMaps[0] = song.b[0];
    curMaps[1] = song.p[0];
    curMaps[2] = song.p[3];
    curMaps[3] = song.b[4];
    arrows = new ArrayList<RhythmArrow>();
    if (curMaps != null) {
      for (RhythmMap r : curMaps) {
        for (MapNote n : r.notes) {
          arrows.add( new RhythmArrow( this, keys[n.dir].x, keys[n.dir].y, n.dir, r.measure + n.measure, n.beat ) );
        }
      }
      updateAllArrows();
    }
  }
  
  public void updateAllArrows() {
    updateNextArrow(0);
    updateNextArrow(1);
    updateNextArrow(2);
    updateNextArrow(3);
  }
  
  public void updateNextArrow(int index) {
    RhythmArrow cur = null;
    
    for (RhythmArrow a : arrows) {
      if (a.index == index) {
        if (cur == null || a.getPoints() > cur.getPoints()) {
          cur = a;
        }
      }
    }
    nextArrows[index] = cur;
  }
  
  public void keyPress(int index) {
    ((GameScene) gameScene).rhythmPanel.keys[index].setPressed(true);
    
    if (nextArrows[index] != null) {
      if (nextArrows[index].getPoints() != 0) {
        //(nextArrows[index].getPoints());
        nextArrows[index].hide();
        getHit();
        arrows.remove(nextArrows[index]);
        updateNextArrow(index);
      }
    }
    
  }
  
  public void soundProcess() {
    if (measure >= 0 && !AP_percussion.isPlaying()) {
      AP_percussion.play();
      AP_bass.play();
    }
  }
  
  public void gameTimerProcess() {
    beat += GAME_SPEED * ( (float)song.bpm / (60.f * FRAME_RATE * (float)song.beats_in_measure));
    
    if (beat >= 1) {
      beat = 0;
      measure++;
    }
    if (measure >= 16 && measure < 32 && ((GameScene)gameScene).phase == "rhythm") {
      ((GameScene)gameScene).swapPhase();
    }
    else if (measure >= 32 && measure < 48 && ((GameScene)gameScene).phase == "strategy") {
      ((GameScene)gameScene).swapPhase();
    }
    else if (measure >= 48 && ((GameScene)gameScene).phase == "rhythm") {
      ((GameScene)gameScene).swapPhase();
      AP_percussion.rewind();
      AP_bass.rewind();
      AP_percussion.pause();
      AP_bass.pause();
      counting = false;
      
    }
    println(measure + beat);
    
  }
  
  //hardcoded when it shouldn't be
  //this should check a variable that says who the current notes belong to instead of just check what time it is.
  public void getHit() {
    if ((0 <= measure && measure < 8) || (40 <= measure && measure < 48)) {
      if (bass_unit.target != null) {
        bass_unit.data.attackUnit(bass_unit.target.data, bass_unit.data.getEquipped());
        ((GameScene)gameScene).dataPanel.updateData(bass_unit.target.data);
      }
      else {
        bass_unit.data.changeStamina(1);
        ((GameScene)gameScene).dataPanel.updateData(bass_unit.data);
      }
    }
    if ((8 <= measure && measure < 16) || (32 <= measure && measure < 40)) {
      if (perc_unit.target != null) {
        perc_unit.data.attackUnit(perc_unit.target.data, perc_unit.data.getEquipped());
        ((GameScene)gameScene).dataPanel.updateData(perc_unit.target.data);
      }
      else {
        perc_unit.data.changeStamina(1);
        ((GameScene)gameScene).dataPanel.updateData(perc_unit.data);
      }
    }
  }
  
  
}
public abstract class Scene {
  
  public void draw() {}
  
}

public class MainMenu extends Scene {
  
  TextButton play = new TextButton(50, 150, 200, 50, "Play", 32);
  TextButton info = new TextButton(50, 220, 200, 50, "Instructions", 32);
  TextButton gear = new TextButton(50, 290, 200, 50, "Settings", 32);
  TextButton exit = new TextButton(50, 360, 200, 50, "Exit", 32);
  
  public MainMenu() {
  }
  
  public void draw() {
    play.draw();
    info.draw();
    gear.draw();
    exit.draw();
    
    if (play.isJustPressed()) {
      current = gameScene;
    }
    if (gear.isJustPressed()) current = settingsMenu;
    if (info.isJustPressed()) current = instructions;
    if (exit.isJustPressed()) exit();
  }
}

public class SettingsMenu extends Scene {
  
  TextButton[] resolutions = new TextButton[] {
    new TextButton(50, 100, 200, 50, "640 x 360", 32),
    new TextButton(50, 170, 200, 50, "1280 x 720", 32),
    new TextButton(50, 240, 200, 50, "1360 x 768", 32),
    new TextButton(50, 310, 200, 50, "1600 x 900", 32),
    new TextButton(50, 380, 200, 50, "1920 x 1080", 32)
  };
  
  TextButton back = new TextButton(10, 490, 160, 40, "Back", 24);
  
  public void draw() {
    textAlign(CENTER);
    textSize(48);
    text("Resolution", 150, 75);
    for (TextButton i : resolutions) i.draw();
    back.draw();
    
    if (resolutions[0].isJustPressed()) setResolution(640);
    if (resolutions[1].isJustPressed()) setResolution(1280);
    if (resolutions[2].isJustPressed()) setResolution(1360);
    if (resolutions[3].isJustPressed()) setResolution(1600);
    if (resolutions[4].isJustPressed()) setResolution(1920);
    
    if (back.isJustPressed()) current = main;
  }
  
}

public class Instructions extends Scene {
  
  TextButton back = new TextButton(20, 20, 100, 40, "Menu", 24);
  TextButton prev = new TextButton(20, 490, 100, 40, "Prev", 24);
  TextButton next = new TextButton(840, 490, 100, 40, "Next", 24);
  
  int page = 0;
  
  final int MIN_PAGE = 0;
  final int MAX_PAGE = 5;
  
  public Instructions() {
  }
  
  public void draw() {
    image(loadImage("instructions\\" + page + ".png"), 0, 0, width, height);
    
    back.draw();
    prev.draw();
    next.draw();
    
    if (back.isJustPressed()) current = main;
    if (prev.isJustPressed() && page > MIN_PAGE) page--;
    if (next.isJustPressed() && page < MAX_PAGE) page++;
    
  }
  
}
public class UnitData {
  
  public String unit_name;
  
  public int max_stamina; //max hp
  public int cur_stamina; //min hp
  public int speed; //spaces can move per turn
  public int melody; //melody damage
  public int rhythm; //rhythm damage
  public int focus; //melody defense
  public int tempo; //rhythm defense
  
  public ItemData[] inventory = new ItemData[5];
  
  public String instrument_type;
  
  public UnitData(String nam, int sta, int spd, int mel, int rhy, int foc, int tem, String typ) {
    unit_name = nam;
    max_stamina = sta;
    cur_stamina = sta/2;
    speed = spd;
    melody = mel;
    rhythm = rhy;
    focus = foc;
    tempo = tem;
    instrument_type = typ;
  }
  
  public void attackUnit(UnitData other, InstrumentData weapon) {
    int damage = weapon.damage_mod;
    if (weapon.damage_type == "melody") {
      damage += this.melody;
      damage -= other.focus;
    }
    else if (weapon.damage_type == "rhythm") {
      damage += this.melody;
      damage -= other.focus;
    }
    else println("Oopsies! Problem in the attackUnit function!! Weapon damage type wasn't put as melody or rhythm, which shouldn't happen.");
    
    if (damage < 0) damage = 0;
    
    other.changeStamina(damage * -1);
    
  }
  
  public void changeStamina(int s) {
    cur_stamina += s;
    if (cur_stamina > max_stamina) cur_stamina = max_stamina;
    if (cur_stamina <= 0) {
      cur_stamina = 0;
      killUnit();
    }
  }
  
  public void killUnit() {
  }
  
  public boolean isDead() {
    return cur_stamina <= 0;
  }
  
  //true if there is enough space for the item, false otherwise
  public boolean giveItem(ItemData item) {
    for (int i = 0; i < inventory.length; i++) {
      if (inventory[i] == null) {
        inventory[i] = item;
        return true;
      }
    }
    return false;
  }
  
  public InstrumentData getEquipped() {
    ItemData equipped = null;
    for (ItemData i : inventory) {
      if (i != null) equipped = i;
      break;
    }
    return (InstrumentData)equipped;
  }
  
  public UnitData copyUnit() {
    UnitData unit = new UnitData(unit_name, max_stamina, speed, melody, rhythm, focus, tempo, instrument_type);
    return unit;
  }
  
  
}


  public void settings() { size(960, 540); }

  static public void main(String[] passedArgs) {
    String[] appletArgs = new String[] { "MusicGame" };
    if (passedArgs != null) {
      PApplet.main(concat(appletArgs, passedArgs));
    } else {
      PApplet.main(appletArgs);
    }
  }
}
