import processing.core.*; import oscP5.*; import netP5.*; import sojamo.drop.*; import java.applet.*; import java.awt.*; import java.awt.image.*; import java.awt.event.*; import java.io.*; import java.net.*; import java.text.*; import java.util.*; import java.util.zip.*; import javax.sound.midi.*; import javax.sound.midi.spi.*; import javax.sound.sampled.*; import javax.sound.sampled.spi.*; import java.util.regex.*; import javax.xml.parsers.*; import javax.xml.transform.*; import javax.xml.transform.dom.*; import javax.xml.transform.sax.*; import javax.xml.transform.stream.*; import org.xml.sax.*; import org.xml.sax.ext.*; import org.xml.sax.helpers.*; public class sketch_080507g extends PApplet {


OscP5 oscP5;
NetAddress myRemoteLocation;


SDrop drop;

PFont font;

int sizx=500;
int sizy=500;

Runtime runtime;
Process process;

public void setup() {

  size(sizx,sizy);
  smooth();
  frameRate(25);
  background(0);
  colorMode(HSB);

  runtime = Runtime.getRuntime();
  //  ouvrePureData();

  oscP5 = new OscP5(this,12000);
  myRemoteLocation = new NetAddress("127.0.0.1",12001);  
  drop = new SDrop(this);

  initTiles();
  initComments();  
  initInfobulles();
  font = loadFont("VisitorTT2BRK-12.vlw");
  textFont(font, 12);
}

public void draw() {
  background(0);
    timer();
    drawTiles();
    gereEnnemis();
    gereTowers();
    indicLibres();
    gereTProj();
  gereInfobulles();
  dessineBoutons();
    origVisible();
    perdgagne();
}

public void ouvrePureData() {
  String file = dataPath("main.pd");
  String file2 = dataPath("pd\\bin\\pd.exe");
  //String file2 = dataPath("pd\\bin\\pd.exe -lib zexy -lib oscx -open "+file);
  try{
    process=runtime.exec(new String[]{
      file2,file            }
    );
  }
  catch(IOException e){
    println(e);
  }
}

public void EXTERNAL_STOP() {
  resetSeqBase(3);
}


int monnaie=10;
int vies=20;
int score=0;

boolean niveauOk=false;//niveau valid\ufffd d\ufffdfinitivement ?
boolean nViable=false;//niveau viable ?

int selectX=-1;
int selectY=-1;

String defComment;

Comments[] comment=new Comments[9];
Comments[] commentB=new Comments[9];

int nbLangues=2;
int langue=0;// 0=english 1=french

public void initComments() {
  if (langue==0) {
    commentB[3]=new Comments("Put this structure over empty spots. It must not step out of the screen. Right click to cancel.");
    commentB[4]=new Comments("You loose");
    commentB[5]=new Comments("You won");
    commentB[6]=new Comments("Money = ");
    commentB[7]=new Comments("Life = ");
    commentB[8]=new Comments("English");
    comment[0]=new Comments("Prevent the enemies to reach their goal.");
    comment[1]=new Comments("Drag & drop files to build new towers.");
    comment[2]=new Comments("You always get the same tower with the same file.");
    comment[4]=new Comments("When two towers are close enough, their ranges increase.");  
    comment[5]=new Comments("Drop a file to begin. Don't drop large files, be patient.");
    comment[6]=new Comments("Red towers are more powerfull than green ones.");
    comment[7]=new Comments("Press 'play' if this is ok, or drag another file.");  
  }  
  if (langue==1) {
    commentB[3]=new Comments("Posez cette tour sur des emplacements libres. Elle ne doit pas non plus sortir de l'\ufffdcran. Cliquez droit pour annuler.");
    commentB[4]=new Comments("Perdu");
    commentB[5]=new Comments("Gagn\ufffd");
    commentB[6]=new Comments("Monnaie = ");
    commentB[7]=new Comments("Vie = ");
    commentB[8]=new Comments("French");
    comment[0]=new Comments("Emp\ufffdchez les choses d'arriver \ufffd destination.");
    comment[1]=new Comments("Glissez des fichiers pour construire des tours.");
    comment[2]=new Comments("Un m\ufffdme fichier donne toujours la m\ufffdme tour.");
    comment[4]=new Comments("Lorsque deux tours sont proches, elles augmentent mutuellement leur range respectif.");  
    comment[5]=new Comments("Pour commencer, glissez un fichier ou un URL n'importe o\ufffd dans la fen\ufffdtre. Le chargement de ce fichier peut prendre un certain temps, soyez patient.");
    comment[6]=new Comments("La couleur d'une tour d\ufffdtermine sa puissance. Les rouges sont plus fortes que les vertes.");
    comment[7]=new Comments("Si ce niveau vous convient, cliquez sur le bouton 'play' pour confirmer. Sinon, glissez un autre fichier.");
  }
}

public void gereComments() {
  if (ennemiI==2 && towerI==0) {
    comment[1].use();
  }
}

class Comments{
  boolean used;
  String label;
  Comments(String label){
    this.label=label;
    this.used=false;
  }
  public String getlabel() {
    return label;
  }
  public void use() {
    if (used==false) {
      used=true;
      defComment=label;
    }
  }
}

Infobulles[] infobulle= new Infobulles[3];
Boutons bnOk=new Boutons(5,5,1,2);
Boutons bdTo=new Boutons(5,5,2,2);
Boutons baVi=new Boutons(30,5,3,2);

public void initInfobulles() {
  infobulle[0]=new Infobulles(sizx-20-50,20,50,50,0,1);
  infobulle[0].setvisible(true);
  infobulle[0].setlabel("");
  infobulle[1]=new Infobulles(20,sizy-20-80,100,80,0,0);
  infobulle[1].setvisible(true);
  comment[5].use();
  infobulle[1].setlabel(defComment);
  infobulle[2]=new Infobulles(sizx-20-55,sizy-20-35,55,35,0,0);
  infobulle[2].setvisible(true);
  infobulle[2].setlabel("");
}

public void dessineBoutons() {
  if (!niveauOk && nViable) {
    bnOk.setvisible(true);
  }
  else{
    bnOk.setvisible(false);
  }
  if (selectX!=-1 && selectY!=-1 && monnaie>=5) {
    bdTo.setvisible(true);
  }
  else{
    bdTo.setvisible(false);
  }
  if (nViable && monnaie>=8) {
    baVi.setvisible(true);
  }
  else{
    baVi.setvisible(false);
  }
  if (niveauOk) {
    bdTo.dessine();
    baVi.dessine();    
    if (!bdTo.getvisible()) {
      bdTo.dessineGris();
    }
    if (!baVi.getvisible()) {
      baVi.dessineGris();
    }    
  }
  else{
    bnOk.dessine();
    if (!bnOk.getvisible()) {
      bnOk.dessineGris();
    }
  }
}

class Infobulles{
  float x;
  float y;
  float sX;
  float sY;
  boolean visible;
  String label;
  float mB;//marge gauche
  int type;
  Infobulles(float x, float y, float sX, float sY, float mB, int type) {
    this.x=x;
    this.y=y;
    this.sX=sX;
    this.sY=sY;
    this.mB=mB;
    this.type=type;
  }
  public void setlabel(String label) {
    this.label=label; 
  }
  public boolean getvisible() {
    return visible;
  }
  public void setvisible(boolean visible) {
    this.visible=visible;
  }
  public boolean actif() {
    if (mouseX>=x && mouseY>=y && mouseX<x+sX && mouseY<y+sY && visible) {
      return true;
    }
    else{
      return false;
    }
  }
  public void dessine() {
    if (visible) {
      noStroke();
      if (actif() && !unBA()) {
        fill(0,0,255,192);
      }
      else{
        fill(0,0,255,128);
      }
      rect(x,y,sX,sY);
      fill(0);
      if (type==0) {
        text(label, x+5+mB, y+5,sX-10-mB,sY-10);
      }
      else if (type==1) {
        if (nViable) {
          noStroke();
          pushMatrix();
          translate(mB,0);
          fill(0,255,255);
          beginShape();
          vertex(x+15,y+11);
          vertex(x+11,y+7);
          vertex(x+13,y+5);
          vertex(x+15,y+7);
          vertex(x+17,y+5);
          vertex(x+19,y+7);
          vertex(x+15,y+11);
          endShape();
          popMatrix();   
          pushMatrix();        
          translate(mB,12);
          fill(32,255,255);
          ellipse(x+15,y+8,7,7);
          popMatrix();
          pushMatrix();        
          translate(mB,24);
          fill(64,255,255);
          rect(x+12,y+6,6,6);
          popMatrix();
          fill(0);
          textAlign(CENTER);
          text(str(vies), x+20+mB, y+5,sX-10-mB,sY-10);
          text(str(monnaie), x+20+mB, y+18,sX-10-mB,sY-10);
          text(str(score), x+20+mB, y+31,sX-10-mB,sY-10);
        }
      }
    }
  }
  public void deplace() {
    x=mouseX-bEDiX;
    y=mouseY-bEDiY;
  }
}

public void gereInfobulles() {
  gereComments();
  if (!niveauOk && nViable) {
    comment[7].use();
    infobulle[1].setlabel(defComment);    
  }
  if (niveauOk) {
    if (tProj.getactive()){
      infobulle[1].setlabel(commentB[3].getlabel());
    }
    else{
      infobulle[1].setlabel(defComment);
    }
  }
  if (towerI==3) {
    comment[6].use();
    infobulle[1].setlabel(defComment);    
  }
  dessineInfobulles();
}

public void dessineInfobulles() {
  if (bulleEnDepl!=-1) infobulle[bulleEnDepl].deplace();
  for (int i=0;i<infobulle.length;i++) {
    infobulle[i].dessine();
  }
}

class Boutons{
  float x;
  float y; 
  float s=20;
  int type=0;//0=vide, 1=play
  int att;//attach\ufffd \ufffd un infobulle ? (-1=non)
  boolean visible=false;
  Boutons(float x, float y, int type, int att) {
    this.x=x;
    this.y=y;
    this.type=type;
    this.att=att;
  }
  public boolean getvisible() {
    return visible;
  }
  public void setvisible(boolean visible) {
    this.visible=visible;
  }
  public boolean actif() {
    if (visible) {
      float x2=x;
      float y2=y;
      if (att!=-1) {
        x2=x+infobulle[att].x;
        y2=y+infobulle[att].y;
      }
      if (mouseX>=x2 && mouseY>=y2 && mouseX<x2+s && mouseY<y2+s) {
        return true;
      }
      else{
        return false;
      }
    }
    else{
      return false;
    }
  }
  public void dessine() {
    if (visible) {    
      float x2=x;
      float y2=y;
      if (att!=-1) {
        x2=x+infobulle[att].x;
        y2=y+infobulle[att].y;
      }
      noStroke();
      if (actif()) {
        fill(64,192,208);
        ellipse(x2+s/2,y2+s/2,s+1,s+1);
      }
      else{
        fill(64,128,128);
        ellipse(x2+s/2,y2+s/2,s,s);
      }
      if (type==1) {
        fill(0);      
        triangle(x2+s/5,y2+s/5,x2+s*4/5,y2+s/2,x2+s/5,y2+s*4/5);
      }
      if (type==2) {
        stroke(0);
        strokeWeight(3);
        line(x2+s/3,y2+s/3,x2+s*2/3,y2+s*2/3);
        line(x2+s*2/3,y2+s/3,x2+s/3,y2+s*2/3);
        fill(0);
        textAlign(CENTER);
        text("  5",x2+s/2,y2+s+7);
        fill(32,255,255);
        noStroke();
        ellipse(x2+s/2-8,y2+s+5,7,7);
      }
      if (type==3) {
        stroke(0);
        strokeWeight(3);
        line(x2+s/2,y2+s/3,x2+s/2,y2+s*2/3);
        line(x2+s*2/3,y2+s/2,x2+s/3,y2+s/2);
        fill(0);
        textAlign(CENTER);
        text("  8",x2+s/2,y2+s+7);
        fill(32,255,255);
        noStroke();
        ellipse(x2+s/2-8,y2+s+5,7,7);
      }
    }
  }
  public void dessineGris() {
    float x2=x;
    float y2=y;
    if (att!=-1) {
      x2=x+infobulle[att].x;
      y2=y+infobulle[att].y;
    }
    noStroke();
    if (actif()) {
      fill(64);
      ellipse(x2+s/2,y2+s/2,s+1,s+1);
    }
    else{
      fill(64);
      ellipse(x2+s/2,y2+s/2,s,s);
    }
    if (type==1) {
      fill(0);      
      triangle(x2+s/5,y2+s/5,x2+s*4/5,y2+s/2,x2+s/5,y2+s*4/5);
    }
    if (type==2) {
      stroke(0);
      strokeWeight(3);
      line(x2+s/3,y2+s/3,x2+s*2/3,y2+s*2/3);
      line(x2+s*2/3,y2+s/3,x2+s/3,y2+s*2/3);
    }
    if (type==3) {
      stroke(0);
      strokeWeight(3);
      line(x2+s/2,y2+s/3,x2+s/2,y2+s*2/3);
      line(x2+s*2/3,y2+s/2,x2+s/3,y2+s/2);
    }
  }
}

int bulleEnDepl=-1;
float bEDiX;//position initiale lors du deplacement d'une bulle en x
float bEDiY;//position initiale lors du deplacement d'une bulle en y

public boolean unIA() {//une bulle active qqpart ?
  boolean unIA=false;
  for (int i=0;i<infobulle.length;i++) {
    if (infobulle[i].actif()) unIA=true;
  }
  return unIA;
}

public boolean unBA() {//un bouton actif qqpart ?
  boolean unBA=false;
  if (bnOk.actif()) {
    unBA=true;
  }
  if (bdTo.actif()) {
    unBA=true;
  }
  if (baVi.actif()) {
    unBA=true;
  }
  return unBA;
}

public void mousePressed() {
  if (!unBA()) {
    for (int i=0;i<infobulle.length;i++) {
      if (infobulle[i].actif() && infobulle[i].getvisible()) {
        bulleEnDepl=i;
        bEDiX=mouseX-infobulle[bulleEnDepl].x;
        bEDiY=mouseY-infobulle[bulleEnDepl].y;
      }
    }
  }
  if (mouseButton==RIGHT) {
    if (tProj.getactive()) {
      tProj.setactive(false);
    }
  }
  if (niveauOk && nViable && !tProj.getactive() && !unIA() && !unBA()) {
    selectX=nTX();
    selectY=nTY();
    if (tile[selectX][selectY].t!=6) {
      selectX=-1;
      selectY=-1;
    }
  }
}

public void mouseReleased() {
  if (baVi.actif()) {
    monnaie-=8;
    vies+=1;
    playEffect(3);    
  } 
  else if (bnOk.actif()) {
    niveauOk=true;
    timerActif=true;
    comment[0].use();
  }
  else if (selectX!=-1 && selectY!=-1 && monnaie>=5 && bdTo.actif()) {
    tile[selectX][selectY].t=0;
    monnaie-=5;
    selectX=-1;
    selectY=-1;
    playEffect(2);
  }
  else if (tProj.getactive() && !unIA() && !unBA()) {
    tProj.confirm();
  }
  else if(bulleEnDepl!=-1){
    bulleEnDepl=-1;    
  }
  else {
    selectX=nTX();
    selectY=nTY();
    if (tile[selectX][selectY].t!=6) {
      selectX=-1;
      selectY=-1;
    }
    else {
    }
  }
}

public void keyPressed() {
  if (keyCode==112) {//f1
    if (origVisibleOn)origVisibleOn=false;
    else if (!origVisibleOn)origVisibleOn=true;
  }
  if (keyCode==113) {//f2
    if (!nViable) {
      langue=(langue+1)%nbLangues;
      initComments();
      infobulle[0].setlabel(commentB[8].getlabel());
      infobulle[1].setlabel(comment[5].getlabel());    
    }
  }
}

boolean origVisibleOn=false;

public void origVisible() {
  if (origVisibleOn){
    noStroke();
    for (int i=0;i<towerI;i++) {  
      tower[i].indicOrig();
    }
  }
}


public int nTX() {
  return constrain(floor((float)mouseX/width*nbTX),0,nbTX-1);//no de la tile sur laquelle il est en x
}
public int nTY() {
  return constrain(floor((float)mouseY/height*nbTY),0,nbTY-1);//no de la tile sur laquelle il est en y  
}

boolean perdu=false;
boolean gagne=false;

public void perdgagne() {
  if (vies<0) {
    exporteIm();
    perdu=true;
    resetSeqBase(1);
    noStroke();
    fill(0,128);
    rect(0,0,width,height);
    fill(255);
    textAlign(CENTER);
    text(commentB[4].getlabel(),width/2,height/2);
    text(score,width/2,height/2+30);
    text("drag another file to play again",width/2,height/2+60);
  }
  if (ennemiI==nbEnnemis-1) {
    boolean survivants=false;
    for (int i=0;i<nbEnnemis;i++) {
      if(ennemi[i].getactif()) survivants=true;
    }
    if (!survivants && vies>=0 && niveauOk) {
      exporteIm();
      gagne=true;
      resetSeqBase(1);
      noStroke();
      fill(0,128);
      rect(0,0,width,height);
      fill(255);
      textAlign(CENTER);      
      text(commentB[5].getlabel(),width/2,height/2);
      text(score,width/2,height/2+30);
      text("drag another file to play again",width/2,height/2+60);      
    }
  }
}

boolean exportFait;

public void exporteIm() {
  if (!exportFait) {
    exportFait=true;
    background(0);
    timer();
    drawTiles();
    gereEnnemis();
    gereTowers();
    stroke(255);
    fill(0);
    strokeWeight(1);
    rect(20,20,40,40);
    fill(255);  
    textAlign(CENTER); 
    text(score,40,40);
    save("final.png");
    background(0);
  }
}

public void resetFin() {
  towerI=0;
  ennemiI=-1;
  niveauOk=false;
  nViable=false;   
  nbTiles=0;
  nbTowers=0;
  nbEnnemis=0;
  selectX=-1;
  selectY=-1;
  timer=1;
  timerActif=false;
}


Ennemis[] ennemi;
int ennemiI=-1;//ennemi en cours
float timer=1;
int nbEnnemis=0;
boolean timerActif=false;

public void gereEnnemis() {
  for (int i=0;i<=ennemiI;i++) {
    if (ennemi[i].getactif()==true) {
      ennemi[i].followPath();
      ennemi[i].dessine();
    }
  }
}

class Ennemis{
  int nb;
  float speed;
  float vie;
  float vieMax;  
  float x;
  float y;
  float d;
  float teinte;
  boolean actif=false;
  int viseX;
  int viseY;
  Ennemis(int nb, float speed, float vie) {
    this.nb=nb;
    this.speed=speed;
    this.vie=vie;
    this.vieMax=vie;   
  }
  public float getx() {
    return x;
  }
  public float gety() {
    return y;
  }
  public void setvise(int viseX, int viseY) {
    this.viseX=viseX;
    this.viseY=viseY;
  }
  public boolean getactif() {
    return actif;
  }
  public void setactif(boolean actif) {
    this.actif=actif;
  }
  public void setteinte(float teinte) {
    this.teinte=teinte;
  }
  public void setposition(float x, float y) {
    this.x=x;
    this.y=y;
  }
  public void souffre(float dmg) {
    vie-=dmg;
    if (vie<=0) {
      score++;
      monnaie++;
      actif=false;
    }
  }
  public void followPath() {
    if (x==tile[viseX][viseY].x && y==tile[viseX][viseY].y) {
      if (tile[viseX][viseY].t==1) {
        viseX++;
      }
      else if (tile[viseX][viseY].t==2) {
        viseY--;
      }
      else if (tile[viseX][viseY].t==3) {
        viseX--;
      }
      else if (tile[viseX][viseY].t==4) {
        viseY++;      
      }
    }
    else{
      x+=constrain(tile[viseX][viseY].x-x,-speed,speed);
      y+=constrain(tile[viseX][viseY].y-y,-speed,speed);
    }
    if (x==tile[tAX][tAY].getx()&&y==tile[tAX][tAY].gety()){
      vies--;
      actif=false;
    }
  }
  public void dessine() {
    noStroke();
    fill(teinte,128,128);
    float d=map(vie/vieMax,0,1,tM*1/4,tM*2/3);
    ellipse(x,y,d,d);
  }
}

public void genereE() {
  //longueur chaine, couleur, speed, vie,
  float[] base=val(2,0,1);
  int tmpLC=floor(map(base[0],0,1,20,60));//longueur de la chaine
  int tmpTeinte=floor(map(base[1],0,1,0,256));//teinte des monstres
  base=val(tmpLC*2,0,0.11f);
  float[] tmpSpeed=subset(base,0,tmpLC);
  tmpSpeed[0]=1;
  tmpSpeed=incremF(tmpSpeed);
  float[] tmpVie=subset(base,tmpLC,tmpLC);
  tmpVie[0]=1;
  tmpVie=incremF(tmpVie);
  ennemi =new Ennemis[tmpLC];
  for (int i=0;i<tmpLC;i++) {
    ennemi[i]=new Ennemis(i,tmpSpeed[i],tmpVie[i]*10000);
    ennemi[i].setteinte(tmpTeinte);
    ennemi[i].setposition(tile[tDX][tDY].getx(),tile[tDX][tDY].gety());
    ennemi[i].setvise(tDX,tDY);
  }
  nbEnnemis=tmpLC; 
}

public void timer() {
  if (timerActif) {
    timer--;
    if (timer<0) {
      timer=100;
      if (ennemiI<nbEnnemis-1) {
        ennemiI++;
        ennemi[ennemiI].setactif(true);
      }
    }
  }
}


byte[] fInput;

float putX;
float putY;

boolean nPret;

String nomOrig;

public void dropEvent(DropEvent theDropEvent) {
  if (!niveauOk) nPret=false;
  if(theDropEvent.isURL())nomOrig=theDropEvent.url();
  if(theDropEvent.isFile())nomOrig=theDropEvent.filePath();
  fInput = loadBytes(nomOrig);
  int p=nomOrig.length();
  boolean ok=false;
  while(ok==false) {
    p--;
    if (nomOrig.charAt(p)==92 || p<0) {
      nomOrig=nomOrig.substring(++p,nomOrig.length());
      ok=true;
    }
  }
  putX=theDropEvent.x();
  putY=theDropEvent.y();  
  traiteDrops();
  nPret=true;  
}

public void traiteDrops() {
  if (!perdu && !gagne) {
    if (!niveauOk) {
      exportFait=false;
      initTiles();
      genereT();
      recentreMap();
      setSeqBase();
      genereE();
    }
    else{
      genereD();
    }
  }
  else{
    if (perdu) {
      score=0;
    }
    resetFin();    
    if (monnaie<10) monnaie=10;
    if (vies<20) vies=20;
    exportFait=false;
    perdu=false;
    gagne=false;
    niveauOk=false;
    initTiles();
    genereT();
    recentreMap();
    setSeqBase();
    genereE(); 
  }
}

public float[] val(int n,float mini,float maxi) {
  float[] val=new float[n];
  int seque=floor(fInput.length/n);
  for (int i=0;i<n;i++) {
    for (int i2=0;i2<seque;i2++) {
      val[i]+=fInput[i2+(i*seque)]+128;
    }
    val[i]=map(val[i]%256,0,256,mini,maxi);
  }
  return val;
}

public int[] valI(int n,float mini,float maxi) {
  int[] valI=new int[n];
  for (int i=0;i<n;i++) {
    valI[i]=floor(val(n,mini,maxi+1)[i]);
  }
  return valI;
}


public void setSK(int[] kicks) {
  OscMessage myOscMessage = new OscMessage("/setSK");
  for (int i=0;i<kicks.length;i++) {
    myOscMessage.add((int)kicks[i]);
  }
  oscP5.send(myOscMessage, myRemoteLocation);
}

public void setSS(int[] snares) {
  OscMessage myOscMessage = new OscMessage("/setSS");
  for (int i=0;i<snares.length;i++) {
    myOscMessage.add((int)snares[i]);
  }
  oscP5.send(myOscMessage, myRemoteLocation);
}

public void setSH(int[] hhs) {
  OscMessage myOscMessage = new OscMessage("/setSH");
  for (int i=0;i<hhs.length;i++) {
    myOscMessage.add((int)hhs[i]);
  }
  oscP5.send(myOscMessage, myRemoteLocation);
}

public void resetSeqBase(int choix) {
  OscMessage myOscMessage = new OscMessage("/resetSB");
  myOscMessage.add((int)choix);  
  oscP5.send(myOscMessage, myRemoteLocation);
}

public void setTempo(float tempo) {
  OscMessage myOscMessage = new OscMessage("/setTempo");
  myOscMessage.add((int)tempo);
  oscP5.send(myOscMessage, myRemoteLocation);
}

public void setPropBase(float kP, float sP, float hP) {
  OscMessage myOscMessage = new OscMessage("/setPropBase");
  myOscMessage.add((int)kP);
  myOscMessage.add((int)sP);
  myOscMessage.add((int)hP);
  oscP5.send(myOscMessage, myRemoteLocation);
}

public void setNewNote(int noteNo, float noteFreq, float notePan) {
  OscMessage myOscMessage = new OscMessage("/setNewNote");
  myOscMessage.add((int)noteNo);
  myOscMessage.add((float)noteFreq);
  myOscMessage.add((float)notePan);  
  oscP5.send(myOscMessage, myRemoteLocation);
}

public void playEffect(int effectNb) {
  OscMessage myOscMessage = new OscMessage("/playEffect");
  myOscMessage.add((int)effectNb);
  oscP5.send(myOscMessage, myRemoteLocation);
}

public void playLaser(float freq, float pan, int joue, int nb) {
  OscMessage myOscMessage = new OscMessage("/playLaser");
  myOscMessage.add((float)freq);
  myOscMessage.add((float)pan);
  myOscMessage.add((int)joue);
  myOscMessage.add((int)nb);  
  oscP5.send(myOscMessage, myRemoteLocation);
}


int nbTX;//nb de tiles en x
int nbTY;//nb de tiles en y
int nbTiles;
Tiles[][] tile;
int tDX;//tile de d\ufffdpart en x
int tDY;//tile de d\ufffdpart en y
int tAX;//tile d'arriv\ufffde en x
int tAY;//tile d'arriv\ufffde en y
float tM;//taille moyenne d'un tile

class Tiles {
  int cX;//case x
  int cY;//case y
  float x;
  float y;
  int t;
  int nb;
  boolean depart=false;
  float teinte;
  Tiles(float x, float y, int cX, int cY) {
    this.x=x;
    this.y=y;
    this.cX=cX;
    this.cY=cY;
  }
  public float getx() {
    return x;
  }
  public float gety() {
    return y;
  }
  public void setteinte(float teinte) {
    this.teinte=teinte;    
  }
  public float getteinte() {
    return teinte;    
  }  
  public void setdepart(boolean depart) {
    this.depart=depart;
  }
  public boolean getdepart() {
    return depart;
  }    
  public void sett(int t) {
    this.t=t;
  }
  public int gett() {
    return t;
  }
  public void dessine() {
    strokeWeight((tX()+tY())/3);
    if (t==1 || t==2 || t==3 || t==4) stroke(teinte,64,64);
    if (t==5) stroke(teinte,64,128);
    if (depart) stroke(teinte,64,128);
    if (t==1) {
      line(x,y,x+tX(),y);
    }
    if (t==2) {
      line(x,y,x,y-tY());
    }
    if (t==3) {
      line(x,y,x-tX(),y);
    }
    if (t==4) {
      line(x,y,x,y+tY());
    }
    if (t==5) {
      point(x,y);
    }
  }
  public void dessinePave() {
    if (t==6) {
      noFill();
      stroke(128,0,64);
      strokeWeight(tM/5);
      rect(x-tX()/3,y-tY()/3,tX()*2/3,tY()*2/3);
      if (cX==selectX&&cY==selectY) {
        strokeWeight(1);
        stroke(16,192,192);
        noFill();
        rect(x-tX()/2,y-tY()/2,tX(),tY());
      }      
    }
  }  
  public void indicLibre() {
    if (niveauOk) {  
      noStroke();
      if (t==0) {
        fill(144,256,256,32);
      }
      else{
        fill(16,256,256,32);          
      }
      rect(x-tX()/2,y-tY()/2,tX(),tY());
    }
  }
}

public void initTiles() {
  nbTX=50;//nb de tiles en x
  nbTY=50;//nb de tiles en y
  nbTiles=nbTX*nbTY;
  tile=new Tiles[nbTX][nbTY];
  for (int x=0;x<nbTX;x++) {
    for (int y=0;y<nbTY;y++) {
      tile[x][y]=new Tiles((float)x*width/nbTX+mX(),(float)y*height/nbTY+mY(),x,y);
    }
  }
}

public void genereT() {
  //0=vide
  //1=droite
  //2=haut
  //3=gauche
  //4=bas
  //5=arriv\ufffde
  //6=tour
  float[] base=val(4,0,1);
  float lMin=constrain(map(fInput.length,100,1000000,50,200),10,500);
  float lMax=constrain(map(fInput.length,1000,1000000,100,500),10,500);
  int tmpLongueur=floor(map(base[0],0,1,lMin,lMax));//longueur de la chaine
  int tmpDX =floor(map(base[1],0,1,nbTX*1/3,nbTX*2/3));
  int tmpDY =floor(map(base[2],0,1,nbTY*1/3,nbTY*2/3));
  float tmpTeinte =floor(map(base[3],0,1,0,256));
  for (int x=0;x<nbTX;x++) {
    for (int y=0;y<nbTY;y++) {
      tile[x][y].sett(0);
      tile[x][y].setteinte(tmpTeinte);
    }
  }
  int[] tmpTypes=valI(tmpLongueur,1,4);
  int tmpTAX=tmpDX;//tile actuelle trait\ufffde en X
  int tmpTAY=tmpDY;//tile actuelle trait\ufffde en Y
  int tmpI=0;//num\ufffdro trait\ufffd dans la chaine
  int nbVerifs=0;//nombre de modif/verifs faites
  tile[tmpTAX][tmpTAY].setdepart(true);
  while(tmpI<tmpLongueur) {
    tile[tmpTAX][tmpTAY].sett(tmpTypes[tmpI]);
    if (tmpI==tmpLongueur-1) tile[tmpTAX][tmpTAY].sett(5);
    if (verifT(tmpTAX,tmpTAY,tmpTypes[tmpI])) {
      if (tmpTypes[tmpI]==1) tmpTAX++;
      if (tmpTypes[tmpI]==2) tmpTAY--;
      if (tmpTypes[tmpI]==3) tmpTAX--;
      if (tmpTypes[tmpI]==4) tmpTAY++;
      tmpI++;
      nbVerifs=0;
    }
    else {
      nbVerifs++;
      tmpTypes[tmpI]=max((tmpTypes[tmpI]+1)%5,1);
      if (nbVerifs>3) {
        tile[tmpTAX][tmpTAY].sett(5);
        tmpI=tmpLongueur;
      }
    }
  }
}

public boolean verifT(int tAX, int tAY, int t) {//est vrai si la tile est libre et existante
  boolean verifT=true;
  if (t==1) {
    if (tAX+1>=nbTX) {
      verifT=false;
    }
    else{
      if (tile[tAX+1][tAY].gett()!=0) verifT=false;
    }
  }
  if (t==2) {
    if (tAY-1<0) {
      verifT=false;    
    }
    else{
      if (tile[tAX][tAY-1].gett()!=0) verifT=false;
    }
  }
  if (t==3) {
    if (tAX-1<0) {
      verifT=false;    
    }
    else{
      if (tile[tAX-1][tAY].gett()!=0) verifT=false;
    }
  }
  if (t==4) {
    if (tAY+1>=nbTY) {
      verifT=false;    
    }
    else{
      if (tile[tAX][tAY+1].gett()!=0) verifT=false;
    }
  }
  return verifT;
}

public void recentreMap() {
  if (tile.length>2) {
    nViable=true;
  }
  else{
    nViable=false;
  }
  int dX=-1;//debut en x
  int dY=-1;//debut en y
  int fX=-1;//fin en x
  int fY=-1;//fin en y
  boolean ok=false;
  for (int x=0;x<nbTX&&!ok;x++) {
    for (int y=0;y<nbTY&&!ok;y++) {
      if (tile[x][y].gett()!=0) {
        dX=constrain(x-3,0,nbTX-1);
        ok=true;
      }
    }
  }
  ok=false;
  for (int y=0;y<nbTY&&!ok;y++) {  
    for (int x=0;x<nbTX&&!ok;x++) {
      if (tile[x][y].gett()!=0) {
        dY=constrain(y-3,0,nbTY-1);
        ok=true;
      }
    }
  }
  ok=false;
  for (int x=nbTX-1;x>=0&&!ok;x--) {
    for (int y=nbTY-1;y>=0&&!ok;y--) {
      if (tile[x][y].gett()!=0) {
        fX=constrain(x+3,0,nbTX-1);
        ok=true;
      }
    }
  }
  ok=false;  
  for (int y=nbTY-1;y>=0&&!ok;y--) {
    for (int x=nbTX-1;x>=0&&!ok;x--) {
      if (tile[x][y].gett()!=0) {
        fY=constrain(y+3,0,nbTY-1);
        ok=true;        
      }
    }
  }
  Tiles[][] tmpTile=tile;
  nbTX=(fX-dX)+1;//nb de tiles en x
  nbTY=(fY-dY)+1;//nb de tiles en y
  nbTiles=nbTX*nbTY;
  float mX=(float)width/(nbTX*2);
  float mY=(float)height/(nbTY*2);  
  for (int x=0;x<nbTX;x++) {
    for (int y=0;y<nbTY;y++) {
      tile[x][y]=new Tiles((float)x*width/nbTX+mX,(float)y*height/nbTY+mY,x,y);
      tile[x][y].sett(tmpTile[x+dX][y+dY].gett());
      tile[x][y].setdepart(tmpTile[x+dX][y+dY].getdepart());
      tile[x][y].setteinte(tmpTile[x+dX][y+dY].getteinte());      
      if (tile[x][y].getdepart()) {
        tDX=x;
        tDY=y;
      }
      if (tile[x][y].gett()==5) {
        tAX=x;
        tAY=y;
      }
    }
  }
  tM=(width/nbTX+height/nbTY)/2;
  nbTowers=nbTX*nbTY;
  tower=new Towers[nbTowers];
}

public void setSeqBase() {
  int[] nbElem=valI(3,0,16);
  int nbKicks=nbElem[0];
  int nbSnares=nbElem[1];
  int nbHhs=nbElem[2];
  int[] kicks=valI(nbKicks,0,16);
  int[] snares=valI(nbSnares,0,16);
  int[] hhs=valI(nbHhs,0,16);
  resetSeqBase(1);
  setSK(kicks);
  setSS(snares);
  setSH(hhs);
  float tempo=val(1,100,300)[0];
  setTempo(tempo);
  float[] props=val(3,0,1);
  float kInitPitch=map(props[0],0,1,500,2000);
  float sFiltreCut=map(props[1],0,1,500,2000);
  float hFadeTime=map(props[2],0,1,1,300);
  setPropBase(kInitPitch,sFiltreCut,hFadeTime);
}

public void drawTiles() {
  pushMatrix();
  if(!niveauOk) {
    translate(width/4,height/4);
    scale(0.5f,0.5f);
  }
  if (nPret) {
  for (int x=0;x<nbTX;x++) {
    for (int y=0;y<nbTY;y++) {
      tile[x][y].dessinePave();
    }
  }
  for (int x=0;x<nbTX;x++) {
    for (int y=0;y<nbTY;y++) {
      tile[x][y].dessine();
    }
  }  
  tile[tAX][tAY].dessine();
  tile[tDX][tDY].dessine();
  }
  popMatrix();  
}

public void indicLibres() {
  if (tProj.getactive()) {
    for (int x=0;x<nbTX;x++) {
      for (int y=0;y<nbTY;y++) {
        tile[x][y].indicLibre();
      }
    }
  }
}

public float tX() {
  return (float)width/nbTX;
}
public float tY() {
  return (float)height/nbTY;
}

public float mX() {
  return (float)width/(nbTX*2);
}

public float mY() {
  return (float)height/(nbTY*2);  
}

public void selectionT() {  
}


Towers[] tower;
int nbTowers;//nombre de tours
int towerI=0;//nb de vraies tours
TowerProject tProj=new TowerProject(false);

int sonLaserNo=0;

class TowerProject{
  boolean active;
  float x;
  float y;
  float range;
  float dmg;
  float teinte;
  int cible;
  boolean[] tstM;//map de trois sur trois
  int vraieTour;
  String nomOrig;  
  int noteNo;
  float noteFreq;
  float notePan;
  TowerProject(float x, float y, float range, float dmg, boolean[] tstM,int vraieTour,String nomOrig,int noteNo,float noteFreq,float notePan) {
    this.x=x;
    this.y=y;
    this.range=range;
    this.dmg=dmg;
    this.teinte=(100-dmg)*2;//th\ufffdoriquement de 100 \ufffd 0
    this.tstM=tstM;
    active=true;
    this.vraieTour=vraieTour;
    this.nomOrig=nomOrig;
    this.noteNo=noteNo;
    this.noteFreq=noteFreq;
    this.notePan=notePan;
  }
  TowerProject(boolean active) {  
    this.active=active;
  }
  public void setactive(boolean active) {
    this.active=active;
  }  
  public boolean getactive() {
    return active;
  }
  public void dessine() {
    x=tile[nTX()][nTY()].getx();
    y=tile[nTX()][nTY()].gety();
    for (int i=0;i<9;i++) {
      pushMatrix();
      translate(((i%3)-1)*tX()+x,(floor(i/3)-1)*tY()+y);
      if (tstM[i]) {
        noFill();
        stroke(128,0,64);
        strokeWeight(tM/5);
        rect(-tX()/3,-tY()/3,tX()*2/3,tY()*2/3);
        if (nTX()+(i%3)-1<nbTX&&nTX()+(i%3)-1>=0&&nTY()+floor(i/3)-1<nbTY&&nTY()+floor(i/3)-1>=0) {
          if (tile[nTX()+(i%3)-1][nTY()+floor(i/3)-1].gett()!=0) {
            noFill();
            stroke(0,192,192);
            strokeWeight(tM/6);
            line(-10,-10,10,10);
            line(10,-10,-10,10);
          }
        }
      }
      if (i==vraieTour) {
        noFill();
        stroke(teinte,128,128);
        strokeWeight(tM/6);
        rect(-tX()*1/5,-tY()*1/5,tX()*2/5,tY()*2/5);
        noStroke();
        fill(teinte,128,128,32);
        ellipse(0,0,range*2,range*2);
      }
      popMatrix();
    }
  }
  public void confirm() {
    boolean placementBon=true;
    for (int i=0;i<9;i++) {
      if (tstM[i]) {
        if (nTX()+(i%3)-1<0 || nTX()+(i%3)-1>nbTX-1 || nTY()+floor(i/3)-1<0 || nTY()+floor(i/3)-1>nbTY-1){
          placementBon=false;        
        }
        else{
          if (tile[nTX()+(i%3)-1][nTY()+floor(i/3)-1].gett()!=0) placementBon=false;
        }
      }
    }
    if (placementBon) {
      for (int i=0;i<9;i++) {
        if (tstM[i]) {
          tile[nTX()+(i%3)-1][nTY()+floor(i/3)-1].sett(6);
        }
        if (i==vraieTour) {
          tile[nTX()+(i%3)-1][nTY()+floor(i/3)-1].sett(6);
          boolean libre=true;
          int quelleOccupe=0;
          for (int i2=0;i2<towerI;i2++) {
            if (tower[i2].gettX()==nTX()+(i%3)-1 && tower[i2].gettY()==nTY()+floor(i/3)-1) {
              libre=false;
              quelleOccupe=i2;
            }
          }
          if(libre==false){
            float bonusDmg=tower[quelleOccupe].getdmg()+1;
            float bonusRange=tower[quelleOccupe].getrange()+1;
            String tmpNO=tower[quelleOccupe].getnomOrig();
            tower[quelleOccupe]=new Towers(towerI,tile[nTX()+(i%3)-1][nTY()+floor(i/3)-1].getx(),tile[nTX()+(i%3)-1][nTY()+floor(i/3)-1].gety(),max(range,bonusRange),max(dmg,bonusDmg),nTX()+(i%3)-1,nTY()+floor(i/3)-1,tmpNO+"+"+nomOrig,noteFreq);
            setNewNote(noteNo,noteFreq,notePan);
            playEffect(1);
          }
          else{
            tower[towerI]=new Towers(towerI,tile[nTX()+(i%3)-1][nTY()+floor(i/3)-1].getx(),tile[nTX()+(i%3)-1][nTY()+floor(i/3)-1].gety(),range,dmg,nTX()+(i%3)-1,nTY()+floor(i/3)-1,nomOrig,noteFreq);
            setNewNote(noteNo,noteFreq,notePan);
            playEffect(1);            
            towerI=(towerI+1)%nbTowers;//tour suivante
          }
          comment[2].use();
        }
      }
      active=false;
    }
  }
}

class Towers{
  int nb;
  float x;
  float y;
  float range;
  float dmg;
  float teinte;
  int cible;
  float rangeBonus;
  int tX;
  int tY;
  String nomOrig;
  boolean tuer=false;
  float freq;
  int laserNo;
  Towers(int nb,float x, float y, float range, float dmg, int tX, int tY, String nomOrig, float freq) {
    this.nb=nb;
    this.x=x;
    this.y=y;
    this.range=range;
    this.dmg=dmg;
    this.teinte=constrain((100-dmg)*2,0,255);//th\ufffdoriquement de 100 \ufffd 0
    this.tX=tX;
    this.tY=tY;
    this.nomOrig=nomOrig;
    this.freq=freq;
  }
  public int gettY() {
    return tY;
  }
  public int gettX() {
    return tX;
  }
  public String getnomOrig() {
    return nomOrig;
  }
  public float getx() {
    return x;
  }
  public float gety() {
    return y;
  }
  public float getrange() {
    return range;
  }
  public float getdmg() {
    return dmg;
  }
  public float getrangeBonus() {
    return rangeBonus;
  }
  public void setrangeBonus(float rangeBonus) {
    this.rangeBonus=rangeBonus;
  }
  public void dessine() {
    pushMatrix();
    translate(x,y);
    noFill();
    stroke(teinte,128,128);
    strokeWeight(tM/6);
    rect(-tX()*1/5,-tY()*1/5,tX()*2/5,tY()*2/5);
    noStroke();
    fill(teinte,128,128,32);
    ellipse(0,0,(range+rangeBonus)*2,(range+rangeBonus)*2);
    popMatrix();
  }
  public void indicOrig() {
    fill(0,16);
    rect(0,0,width,height);
    noStroke();
    fill(teinte,255,255);
    text(nomOrig, x-tX()/2, y-tY()/2,tX()*3,tY()*5);
  }
  public boolean gettuer() {
    return tuer;
  }
  public int getlaserNo() {
    return laserNo;
  }
  public void tue() {
    if (longueur(ennemi[cible].getx(),ennemi[cible].gety(),x,y)<=range+rangeBonus && ennemi[cible].actif) {
      if (!tuer) {
        laserNo=sonLaserNo;
        playLaser(freq*32, x/width,1,laserNo);
        tuer=true;
        sonLaserNo=(sonLaserNo+1)%8;
      }
      //ok, bah continue alors
      stroke(teinte,64,192,64);
      strokeWeight(3);
      line(x,y,ennemi[cible].getx(),ennemi[cible].gety());
      stroke(teinte,128,224,64);
      strokeWeight(1);
      line(x,y,ennemi[cible].getx(),ennemi[cible].gety());
      ennemi[cible].souffre(dmg);
    }
    else{//sinon prends-en un autre
      if (tuer) {
        tuer=false;
        boolean vaArreter=true;
        for (int i=0;i<towerI-1;i++) {
          if (tower[i].getlaserNo()==laserNo && tower[i].gettuer()) vaArreter=false;
        }
        if (vaArreter) playLaser(freq*32, x/width,0,laserNo);
      }
      int tmpProche=0;
      float tmpLProche=0;
      for(int i=0;i<nbEnnemis;i++) {
        if (longueur(ennemi[i].getx(),ennemi[i].gety(),x,y)<=range+rangeBonus && ennemi[i].actif) {
          if (longueur(ennemi[i].getx(),ennemi[i].gety(),x,y)>tmpLProche) {
            tmpProche=i;
            tmpLProche=longueur(ennemi[i].getx(),ennemi[i].gety(),x,y);
          }
        }
        cible=tmpProche;
      }  
    }
  }
  public void cRangeBonus() {
    for (int i=0;i<towerI;i++) {
      if (i!=nb) {
        if (longueur(x,y,tower[i].getx(),tower[i].gety())<range+rangeBonus) {
          tower[i].setrangeBonus(tower[i].getrangeBonus()+(range+rangeBonus)/5);
          comment[4].use();
        }
      }
    }
  }
}

public void genereD() {
  int nTX=floor(putX/width*nbTX);//no de la tile sur laquelle il est en x
  int nTY=floor(putY/height*nbTY);//no de la tile sur laquelle il est en y  
  putX=tile[nTX][nTY].getx();
  putY=tile[nTX][nTY].gety();
  float[] base=val(2,0,1);
  float tmpRange=map(base[0],0,1,tM,tM*2);
  float tmpDmg=map(base[1],0,1,50,100);
  int prix=constrain(floor(map(tmpRange+tmpDmg,tM+50,tM*2+100,1,10)),1,9);
  int[] tmpTstP=valI(prix,0,8);
  int vraieTour=5;
  boolean[] tstM=new boolean[9];
  for (int i=0;i<tmpTstP.length;i++) {
    tstM[i]=false;
  }
  for (int i=0;i<tmpTstP.length;i++) {
    if (tstM[tmpTstP[i]]==false) {
      tstM[tmpTstP[i]]=true;
      if (i==0) {
        vraieTour=tmpTstP[i];
      }
    }
    else{
      tmpTstP[i]=(tmpTstP[i]+1)%9;
      i--;
    }
  }
  if (!tstM[0]&&!tstM[1]&&!tstM[3]&&!tstM[4]&&!tstM[6]&&!tstM[7]) tstM[4]=true ;//si rien \ufffd gauche
  if (!tstM[1]&&!tstM[2]&&!tstM[4]&&!tstM[5]&&!tstM[7]&&!tstM[8]) tstM[4]=true ;//si rien \ufffd droite  
  if (!tstM[0]&&!tstM[1]&&!tstM[2]&&!tstM[3]&&!tstM[4]&&!tstM[5]) tstM[4]=true ;//si rien en haut
  if (!tstM[3]&&!tstM[4]&&!tstM[5]&&!tstM[6]&&!tstM[7]&&!tstM[8]) tstM[4]=true ;//si rien en bas
  float[] nn=val(3,0,1);
  int noteNo=floor(nn[0]*16);
  float noteFreq=nn[1]*200;
  float notePan=nn[2];
  tProj=new TowerProject(putX,putY,tmpRange,tmpDmg,tstM,vraieTour,nomOrig,noteNo,noteFreq,notePan);
}

public void gereTowers() {
  calculatesRangeBonus();
  for (int i=0;i<towerI;i++) {
    tower[i].dessine();
    tower[i].tue();
  }
}

public void calculatesRangeBonus() {
  for (int i=0;i<towerI;i++) {
    tower[i].setrangeBonus(0);
  }
  for (int i=0;i<towerI;i++) {  
    tower[i].cRangeBonus();
  }  
}

public void gereTProj() {
  if (tProj.getactive() && !unIA()) {
    tProj.dessine();
  }
}


public float[] incremF(float[] chaine) {
  float[] incremF=new float[chaine.length];
  for (int i=0;i<incremF.length;i++) {
    for (int i2=i;i2>=0;i2--) {
      incremF[i]+=chaine[i2];
    }
  }
  return incremF;
}

public float longueur(float x, float y, float x2, float y2) {
  return sqrt(sq(x2-x)+sq(y2-y));
}

  static public void main(String args[]) {     PApplet.main(new String[] { "sketch_080507g" });  }}