/*

  // DEBUGGING: waitForThePlotter
  
 // -------------------------------
 // PLOTARCADE
 // PLOTOLEC & OSC
 // 
 // PlotterGamez
 // -------------------------------
 // rene.bauer@zhdk.ch
 // PlotterArcade or Computergames
 
 // ToDo: OSC in / out: plotter ready ... 
 
 // attention: to use OSC
 // > stateRunningStart =  "osc"; // !!!!
 
 
 // debug ... 
 // waitForThePlotter = false;  !!!
 
 // One Game Setting
 // 
 // [GAMEPICTURE] [PLOTTER]
 //
 // Computergame
 //
 // [PLOTTER]
 //   [TITLESCREEN]
 //   [MENUSCREEN]
 //   [INGAME]
 // 
 
 // description
 
 - "simple plotter game engine"

 // ADD endless paper
 // ADD A SMALL DISPLAY? FOR SCORE? (LIKE IN PINBALLS?
 
 // How this works
 
 1. "realtime record" : pen
 
    moveTo();
    lineTo();
      drawShapeAt();
    
    > arrList 
      > addLine
 
 2. "realtime idealistic printing" : plotterPen
 
    based on arrList
    
    processLines()
    > plotterMoveTo()
    > plotterLineTo()
    
    > plotterPosition: plotterPen

 3. "realtime":
    here is the plotter pen really

Multimedia
- unterlegte Grafiken (kopiert oder gemalt)
- unterlegte Grafiken Farbe oder Schwarz-Weiss ..
- der Stift kann farbig sein ... 

Levels
- Line-Effekt - hinzufügen und weiter mit dem System
 
 Getroffen
 - - | (additiv)
 - \ / > X 
 - neue Objekte
 
 Übertatowieren
 - umwandeln in etwas anderes
 - Geile Mann > der Gelbe Mann
 
 GameIdeen
 
 - Rogue like
 - Rumgehen und wenn man auf etwas trifft, dann wird die Welt gezeichnet
 - madPlotter done
 - Kolonialkartenzeichner*
 
 - xouoooxzzzz 
 
 - jump ... pen/up down ... nothing more ... !!! 
 
 - check interreput?
 - longer distances!
 
 - ideen: inverse ballerburg ... tippex ... oder weissem kulli !!!  
 
 Real-time painting software for AxiDraw
 https://github.com/evil-mad/AxiDraw-Processing
 
 Based on RoboPaint RT: 
 https://github.com/evil-mad/robopaint-rt
 
 */

/*

    // draw ... wait for next movetime
    MoveXY > with draw !!!
    
    nextMoveTime 


*/



import de.looksgood.ani.*;
import processing.serial.*;

import processing.sound.*;
import java.util.Vector;

// OSC
import oscP5.*;
import netP5.*;
OscP5 oscP5;

boolean oscActive = true;

// --------------------------------
// IMPORTANT SETTINGS 
// --------------------------------
/*
    setup
      initHardwareSettings
      
    draw
     initGame
       updateGame
*/ 


String gameName = "PLOTOLEC";

boolean soundOn = true;
boolean debugVectorGraphics = false; // show svg file in the gui

// dont disable
boolean simulatePenLinesGUI = true;  // green lines
boolean showPenUpLines = true;
boolean realTimeSimulation = true; // visual

// !!!!!!!!!!!!!!!!!!!!!!!!
// DEBUG
// !!!!!!!!!!!!!!!!!!!!!!!!
// wait for the plotter
boolean waitForThePlotter = false; // true - false for fast developping online!

// plotter active = connected? 
boolean plotterConnected = false;

// work on osc ... 
// here ... 

// startState / ingamelinejewels ingamerp
String stateRunningStart =  "ingamemadpen"; // "osc", "ingametennisfortwo", "ingamemines", "ingamejumpandrun"; // "ingametron"; // ingamelinejewels // "ingamemadpen"; // "menu"; // title | menu
// String stateRunningStart =  "osc"; 

boolean standAlone = false;  // set on true so a 

// future: take also preprinted levels ...
// boolean prePrinted = true; // skip frame and things around ... 
// even levels printed is possible! 

// scale
float gameRatio = 1.0f;   // size of the fields ... 
  // influences 
  //  > gameRatio

public void setGameVisualRatio( float newR ) {
  gameRatio = newR;
  plotterScale = (int) (1000*gameRatio);
  guiPaperScale = (int) (50* gameRatio);  
  
}

// tmp
PShape tmpShape;


void setup() 
{

  background(colorGUIWhite);
  size(840+400, 676+200);

  Ani.init(this); // Initialize animation library
  Ani.setDefaultEasing(Ani.LINEAR);

  firstPath = true;

  //// Allow frame to be resized?
  //  if (frame != null) {
  //    frame.setResizable(true);
  //  }

  

  shiftKeyDown = false;

  frameRate(60);  // sets maximum speed only

  initHardwareSettings();
  
  // Game Ping
  gamePing = new SoundFile(this, "soundPing.wav");
  

  // Set initial position of indicator at carriage minimum 0,0
  int[] pos = getMotorPixelPos();

  pos[0] = 0;
  pos[1] = 0;
 
  // vectorgraphis load
  vectorgraphics = loadShape("vectorgraphics.svg"); 
  // SAVED AS SVG1.1 Tiny
  // Format: <line fill="none" stroke="#000000" stroke-miterlimit="10" x1="26.79" y1="84.97" x2="48.19" y2="84.97"/>
  
  // titleSVG.rotate(0.2);
  // titleSVG.scale()

  
  // println("params "+titleSVG.getParams());

  // resetPlayfield
  resetPlayfield();

  nextMoveTime = millis();
}

// -----------------
// graphics
// -----------------

// svg
PShape vectorgraphics;

float importScale = 0.1f;

// -----------------
// game pen setup
// -----------------

int penSpeedUpdate = 0;
int penSpeedUpdateIndex = 0;

int offsetX = 0;
int offsetY = 0;
int plotterScale = 1000;

// actual pen position in the simulation (!= gamepen && real plotterpen!)
PVector pen = new PVector();
PVector penOld = new PVector();

// the ideal pen for the next line !!!!
// goto 
PVector plotterPen = new PVector();

// simulated Plotter pen - actual now!!
PVector plotterPenInThisMoment = new PVector();

// pen in a game
PVector avatarPosition = new PVector();
PVector avatarPositionOld = new PVector();

// -----------------
// game state 
// -----------------

// only hardware state
int gameState = 0; // init
  // 0 start
  // 1 connect
  // 2 gameon / running
  
// starting with gameState
  String gameStateRunning = ""; // 
  String gameStateSubRunning = "";

int vx = 1;
int vy = 0;

int speed = 3;

int zz = 0;

// soundz
// -------------
SoundFile gamePing;
    

// -----------------
// field
// -----------------

int fieldWidth = 840;
int fieldHeight = 1188;

int[][] gameField = new int[fieldHeight][fieldWidth];


int gameTimerStart = 0;
int score = 0;
int life = 3;
int gameTimer = 0;
int level = 0;

// INITGAME
public void initGame() {
          
   println("# -----------------");
   println("# InitGame");
   println("# -----------------");
   
   // do game setup here!
   // start-screen

   nextMoveTime = millis();

   // tmpShape = vectorgraphics.getChild("a");  
   // addPShapeAt(tmpShape,5,5);

   //  drawGraphicAt( "a", 0.0f, 0.0f);
    
   // nullpoint   
   moveToNullPoint();

//   delay(1000);        

   gameState= 2; 
  
   gameStateRunning = ""+stateRunningStart; // title | menu | ingame | gameover | won
   gameStateSubRunning = "";
   
   if ((!gameStateRunning.equals("title"))&&(!gameStateRunning.equals("menu"))) {
      standAlone = true; 
   }
   
   // mainscreen or! (visual?)
   // menuscreen .. 
 

   
}


// --------------------------------
// UPDATE TITLE MENU
// --------------------------------


float titleX = 00.0f+18.0;
float titleY = 0.0f;

int menupointSelected = 0;

public void updateGameTitle() {
  
  if (gameStateSubRunning=="") {
    println("updateGameTitle() draw");
    gameStateSubRunning="waiting";

    // test
    /*
    moveTo(7,1);
    moveTo(10,2);
    drawLine( 1,2, 5,8);
    drawLine( 3,3, 6,8);
    */
    
    drawGraphicAt("title",0.0f+titleX,0.0f+titleY);
    // drawTextAt("PRESS FIRE",1.0f+titleX,2.0f+titleY,0.2f);

    drawTextAt(".PLOTARCADEZ",0.0f+titleX-0.5,2.0f+1.5f+ titleY,0.3f);
    
    // move to ...
    // moveTo(0.5f+titleX,2.0f+0.25f+titleY);
    
    // skip all this! take too long time!
      gameStateRunning = "menu";
        gameStateSubRunning="";
        pressedKeyChar = "";     
  }
  
  // menu wait .. 
  if (gameStateSubRunning.equals("waiting")) {
    // println("updateGameTitle() wait");

      // wait for the key ...
      if (pressedKeyChar.equals(" ")) {
        // next ... 
        
        // check if all is painted?
        gameStateRunning = "menu";
        gameStateSubRunning="";
        pressedKeyChar = ""; 
    }
    
  }
    
}

// --------------------------------
// UPDATE GAME MENU
// --------------------------------

float menuX = 0.0f+18.0;
float menuY = 3.0f;

String[] arrMenuPoints = {  /*"WALK",*/ "vTRON", "MINES!", "JEWELS","MADPEN", "T42", "RolePlay", "JUMP!" /*, "ABOUT"*/ };
String[] arrMenuStates = { /*"ingame",*/ "ingametron", "ingamemines", "ingamelinejewels","ingametennis4two","ingamerp","ingamemadpen", "ingamejumpandrun" /*, "about"*/ };

public void updateGameMenu() {
  // println("updateGameMenu()");

  if (gameStateSubRunning=="") {
      println("updateGameTitle() draw");
      gameStateSubRunning="waiting";
  
      // drawTextAt(""+gameName,0.0f+menuX,0.0f+menuY,0.5);
      
      // drawTextAt("f PLAY",0.5f+menuX,2.0f+menuY,0.5f);
      // drawTextAt("f ABOUT",0.5f+menuX,3.0f+menuY,0.5f);

  
  //    drawGraphicAt("title",0.0f+titleX,0.0f+titleY);
      for (int h=0;h<arrMenuPoints.length;h++) {
        String menuStr = arrMenuPoints[h];
        drawTextAt("f "+menuStr,0.5f+menuX,2.0f+h*0.5f+menuY,0.3f);
      }
      
      
      // move to ...
      menupointSelected = 0; 
          moveTo(0.5f+0.25f+menuX,2.0f+menupointSelected*0.5f+0.25f+menuY);
  }
  
  // menu wait .. 
  if (gameStateSubRunning=="waiting") {
    // println("updateGameTitle() wait");

    // up and down 
    // menupointSelected
    
       if (pressedKeyChar.equals("w")) {
          menupointSelected -= 1;
          if (menupointSelected==-1) menupointSelected = 1;      
          moveTo(0.5f+0.25f+menuX,2.0f+menupointSelected*0.5f+0.25f+menuY);
    
          pressedKeyChar = "";
        }
       if (pressedKeyChar.equals("s")) {
          menupointSelected += 1;
          if (menupointSelected==arrMenuPoints.length) menupointSelected = 0;
          
          // go to menupoint
          moveTo(0.5f+0.25f+menuX,2.0f+menupointSelected*0.5f+0.25f+menuY);
          
          pressedKeyChar = "";
        }          
        
        // wait for the key ...
        if (pressedKeyChar.equals(" ")) {
          // next ... 
          println("menu selected "+menupointSelected+"   "+arrMenuStates[menupointSelected]);
          
          // state?
          // check if all is painted?
          gameStateRunning = ""+arrMenuStates[menupointSelected];
          gameStateSubRunning="";
          pressedKeyChar = ""; 
          
          moveTo(0,0);
      
       }
    
  }  
  
}

// ----------
// OSC 
// ----------
/*
  this is a very simple "realtime" osc-to-draw plugin.
  so sorry if it is too easy. 
  
  # oscport 9007
  the osc port in is 9007. send your data here. 

  # recording
  recording: movetoprocess and text will be recorded and executed

  # commands

  /clear              simulation only
  
  /penup           
  /pendown
  
  /movetox 0.0-1.0    xpos
  /movetoy 0.0-1.0    ypos
  /movetoprocess      goto xpos/ypos
  
  /square             creates a 1x1 square
  /cross              creates a 1x1 cross
  
  /text BLA           draw text BLA at xpos/ypos



  # if there is nothing more to do
  osc message to 

*/
float oscX = 0.0;
float oscY = 0.0;
float oscXLast = 0.0;
float oscYLast = 0.0;


// OSC-Processing 

void oscEvent(OscMessage theOscMessage) {
  if (theOscMessage.addrPattern().equals("/test")) {
    println("OSC: /test");
  }
  
  // penUp / penDown
  if (theOscMessage.addrPattern().equals("/penup")) {
    println("OSC: /penup");
    penUp();
  }  
  if (theOscMessage.addrPattern().equals("/pendown")) {
    println("OSC: /pendown");
    penDown();
  }  

  // splitted
  if (theOscMessage.addrPattern().equals("/movetox")) {
    float fx = theOscMessage.get(0).floatValue();
    oscX =  (fx*10.0f); 
    println("OSC: /movetox "+fx+" > "+oscX);
  }  
  if (theOscMessage.addrPattern().equals("/movetoy")) {
    float fy = theOscMessage.get(0).floatValue();
    oscY =  (fy*10.0f);  
    println("OSC: /movetoy "+fy+" > "+oscY);
  }  
  if (theOscMessage.addrPattern().equals("/movetoprocess")) {
    println("OSC: /movetoprocess");
    if (penState==1) lineTo(oscX,oscY);
    if (penState==0) moveTo(oscX,oscY);
    oscXLast = oscX;
    oscYLast = oscY; 
  }   

  // one call 
  if (theOscMessage.addrPattern().equals("/moveto")) {
    float fx = theOscMessage.get(0).floatValue();
    float fy = theOscMessage.get(1).floatValue();
    
    oscX = (fx*10.0f);
    oscY = (fy*10.0f);
    
    println("OSC:/moveto "+oscX+"/"+oscY);
    if (penState==1) lineTo(oscX,oscY);
    if (penState==0) moveTo(oscX,oscY);
    
    oscXLast = oscX;
    oscYLast = oscY;
  }
  
  if (theOscMessage.addrPattern().equals("/text")) {
    String txt = theOscMessage.get(0).stringValue();
    println("OSC: /text " +txt);
    drawTextAt(txt,oscX,oscY);
  } 
  
  if (theOscMessage.addrPattern().equals("/square")) {
    println("OSC: /square");
    float ox = oscX;
    float oy = oscY;
    lineTo(ox+1.0,oy);
    lineTo(ox+1.0,oy+1.0);
    lineTo(ox+0.0,oy+1.0);
    lineTo(ox+0.0,oy+0.0);
  } 

if (theOscMessage.addrPattern().equals("/cross")) {
    println("OSC: /square");
    float ox = oscX;
    float oy = oscY;
    lineTo(ox+1.0,oy+1.0);
    lineTo(ox+0.5,oy+0.5);
    lineTo(ox+0.0,oy+1.0);
    lineTo(ox+1.0,oy+0.0);
    moveTo(ox+0.0,oy+0.0);
  } 


  if (theOscMessage.addrPattern().equals("/clear")) {
    println("OSC: /clear");
    resetLines();    
  } 


}

public void updateOSC() {

 if (gameStateSubRunning.equals("")) {
     gameStateSubRunning="plotting";  
       // OSC?
     oscP5 = new OscP5(this, 9007);    
     lineTo(0,0);
 }
  
    // keys
// key / controls
  if (pressedKeyChar.equals("d")) {
      avatarPosition.x += 1;
      pressedKeyChar = "";
    }
  if (pressedKeyChar.equals("a")) {
      avatarPosition.x -= 1;
      pressedKeyChar = "";
    }
  if (pressedKeyChar.equals("w")) {
      avatarPosition.y -= 1;
      pressedKeyChar = "";
    }
  if (pressedKeyChar.equals("s")) {
      avatarPosition.y += 1;
      pressedKeyChar = "";
    }    
   
    
   //  print("("+pen.x+"/"+pen.y+")");

   // change pen
   if (avatarPosition.x!=avatarPositionOld.x || avatarPosition.y!=avatarPositionOld.y) {

     // pointer only!    
//     moveTo(avatarPosition.x,avatarPosition.y);
     lineTo(avatarPosition.x,avatarPosition.y);
     
     // draw
      // addLine(penOld,pen);
   }
   
   
   avatarPositionOld.x = avatarPosition.x;
   avatarPositionOld.y = avatarPosition.y;
  
}

int gameX = 0;
int gameY = 0;

public void updateInGame() {
 
 // println("-"+pressedKeyChar);
 
  // keys
// key / controls
  if (pressedKeyChar.equals("d")) {
      avatarPosition.x += 1;
      pressedKeyChar = "";
    }
  if (pressedKeyChar.equals("a")) {
      avatarPosition.x -= 1;
      pressedKeyChar = "";
    }
  if (pressedKeyChar.equals("w")) {
      avatarPosition.y -= 1;
      pressedKeyChar = "";
    }
  if (pressedKeyChar.equals("s")) {
      avatarPosition.y += 1;
      pressedKeyChar = "";
    }    
   
    
   //  print("("+pen.x+"/"+pen.y+")");

   // change pen
   if (avatarPosition.x!=avatarPositionOld.x || avatarPosition.y!=avatarPositionOld.y) {

     // pointer only!    
//     moveTo(avatarPosition.x,avatarPosition.y);
     lineTo(avatarPosition.x,avatarPosition.y);
     
     // draw
      // addLine(penOld,pen);
   }
   
   
   avatarPositionOld.x = avatarPosition.x;
   avatarPositionOld.y = avatarPosition.y;

 
    // special 
    if (pressedKeyChar.equals(" ")) {
      tmpShape = vectorgraphics.getChild("a");  
      // addPShapeAt(tmpShape,pen.x,pen.y);
      // println("fire");
      //addLine( new PVector(0,0), pen );
      addShapeXAt(avatarPosition.x,avatarPosition.y);
  
      pressedKeyChar = "";
    } 

   if (pressedKeyChar.equals("e")) {
      // println("SHOOT E");
//      tmpShape = vectorgraphics.getChild("b");  
      // addPShapeAt(tmpShape,pen.x,pen.y);
      // println("fire");
      //addLine( new PVector(0,0), pen );
//      addPShapeAt(tmpShape,pen.x,pen.y);
      
      drawGraphicAt("k",avatarPosition.x,avatarPosition.y);
  
    
  
      pressedKeyChar = "";
    } 

}


// --------------------------------------
// TRON
// --------------------------------------

int gameTronDirectionX = 0;
int gameTronDirectionY = 0;

int gameTronLifes = 3;

public void updateInGameTron() {
 
 // println("-"+pressedKeyChar);

// start up / set graphics
 if (gameStateSubRunning.equals("")) {
     gameStateSubRunning="plotting";  
     
     resetLines();
     
     println("gametron.start");
     
     gameTronDirectionX = 0;
     gameTronDirectionY = 0;

     // bigger field
     playfieldWith = 16;
     playfieldHeight = 12;
     
     level = 0;
     life = 0;
     score = 0;
 
     gameTronLifes = 3; 
 
     // display playfield 
     String[] aStr = { " "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," "," ","f","v"," "," "," "," "," "," "," " ," "," " ," "," "  };
     randomPlayfield( aStr );
     
     // random ... new objects .. 
     avatarPosition.x = playfieldWith/2;
     avatarPosition.y = playfieldHeight/2;

     playfieldRect(" ",0,0,playfieldWith-1,playfieldHeight-1);
   
     setPlayfield(" ",(int) avatarPosition.x,(int) avatarPosition.y);
  
     drawPlayfield();

     playfieldRect("f",0,0,playfieldWith-1,playfieldHeight-1);

     // draw by hand
     float os = playfieldTileSize*0.5f;
     float ps = playfieldTileSize;
     // always one line
     drawLine( os, os, (playfieldWith-1)*ps+os, os );
     drawLine( os+(playfieldWith-1)*ps, os, os+(playfieldWith-1)*ps, (playfieldHeight-1)*ps+os );
     drawLine( (playfieldWith-1)*ps+os, (playfieldHeight-1)*ps+os, os, (playfieldHeight-1)*ps+os );
     drawLine( os, (playfieldHeight-1)*ps+os, os, os );

     // drawPlayfield();

     // todo: check if there is something
     // own home?
 
     avatarPosition.x = playfieldWith/2;
     avatarPosition.y = playfieldHeight/2;

     moveTo( avatarPosition.x+playfieldTileSize*0.5f, avatarPosition.y+playfieldTileSize*0.5f);
 
     gameTimerStart = 0;
 
     // loading music ...
     
 }
 
 // finished
 //  gameStateSubRunning="run";
  if (gameStateSubRunning.equals("plotting")) {
    if (plotterReadForInput()) {
      println("gametron.start > running");
      gameStateSubRunning = "running";
      gameTimer = millis();
    }    
  }

   if (gameStateSubRunning.equals("running")) {
    
    
    // println("-"+pressedKeyChar);
    
    // -----------
    //  random 
    //  movement
    // -----------
    if (plotterReadForInput())
    if (gameTimer<millis()) {
       gameTimer = millis()+500; 
       // println("NEXT EVENT");
       if ((gameTronDirectionX!=0.0)||(gameTronDirectionY!=0.0)){
         avatarPosition.x += gameTronDirectionX;
         avatarPosition.y += gameTronDirectionY;
       }
    }
    
      // keys
    // key / controls
      if (pressedKeyChar.equals("d")) {
          pressedKeyChar = "";
          gameTronDirectionX = 1;
          gameTronDirectionY = 0;
          pressedKeyChar = "";
     }
      if (pressedKeyChar.equals("a")) {
          pressedKeyChar = "";
          gameTronDirectionX = -1;
          gameTronDirectionY = 0;
          pressedKeyChar = "";
        }
      if (pressedKeyChar.equals("w")) {
          pressedKeyChar = "";
          gameTronDirectionX = 0;
          gameTronDirectionY = -1;
          pressedKeyChar = "";
        }
      if (pressedKeyChar.equals("s")) {
          pressedKeyChar = "";
          gameTronDirectionX = 0;
          gameTronDirectionY = 1;
        }    
   
   }
    
   
  

   // change pen
   if (avatarPosition.x!=avatarPositionOld.x || avatarPosition.y!=avatarPositionOld.y) {

     lineTo(avatarPosition.x+playfieldTileSize*0.5f,avatarPosition.y+playfieldTileSize*0.5f);     
     
     String actualPlayfieldX = getPlayfield((int)avatarPosition.x,(int)avatarPosition.y);
     // gameover !!!
     if (actualPlayfieldX.equals("v")) {
     
         // drawPlayfieldGraphicNameAt("y",avatarPosition.x,avatarPosition.y);
         // not working correctly! 
        
         // todo: ping ...
         score += 3;
         playPing();
         
         
     }
     
     // gameover !!!
     if (actualPlayfieldX.equals("f")) {
         
         drawPlayfieldGraphicNameAt("q",avatarPosition.x,avatarPosition.y);
         
         gameTronLifes--;
         
         // display lifes 
         if (gameTronLifes>0) {
           // write go back to start
           drawTextAt("."+gameTronLifes,gameTronLifes*3*playfieldTileSize,1*playfieldTileSize,0.3f);
           
           gameTronDirectionX = 0;
           gameTronDirectionY = 0;
            
           avatarPosition.x = playfieldWith/2;
           avatarPosition.y = playfieldHeight/2;
      
           moveTo( avatarPosition.x+playfieldTileSize*0.5f, avatarPosition.y+playfieldTileSize*0.5f);
            
         }
    
         if (gameTronLifes==-1) {
           gameStateSubRunning = "lost";                 
           drawTextAt(".WON   "+score+"  PTS ",1*playfieldTileSize,12*playfieldTileSize,0.3f);
           drawTextAt(".NEW PAPER ... FIRE ",1*playfieldTileSize,13*playfieldTileSize,0.3f);
           penUp();
         }
         
     }

     setPlayfield("f",(int)avatarPosition.x,(int)avatarPosition.y);
     score += 1;  
     playPing();


   }
   
   
   avatarPositionOld.x = avatarPosition.x;
   avatarPositionOld.y = avatarPosition.y;

 
  // lost 
  if (gameStateSubRunning.equals("lost")) {
  
    // collect - restart ...  
    if ((pressedKeyChar.equals("e"))||(pressedKeyChar.equals(" "))) {
      // direct      
      gameStateSubRunning = "";         
  
      pressedKeyChar = "";
    } 
  
  }


}

// --------------------------------------
// MADPEN
// --------------------------------------


public void updateInGameMadPen() {
 
 // start up / set graphics
 if (gameStateSubRunning.equals("")) {
     gameStateSubRunning="plotting";  
     
     println("updateInGameMadPen()");
     
     resetLines();

     // bigger field
     playfieldWith = 16;
     playfieldHeight = 12;
     
     level = 0;
     life = 0;
     score = 0;
     
 
     // display playfield 
//     String[] aStr = { " "," "," "," "," "," "," "," "," "," "," "," ","d","a"," "," "," "," "," "," "," " ," "," " ," "," "  };
//     randomPlayfield( aStr );
    
     // a
     var inserted = 0;
     do {
        var tx = (int) (random(0,playfieldWith-2)+1);   
        var ty = (int) (random(0,playfieldHeight-2)+1);   
        if (getPlayfield(tx,ty)==" ") {
          setPlayfield("d",tx,ty);
          inserted++;
        }
     } while (inserted<10);

     // d
     inserted = 0;
     do {
        var tx = (int) (random(0,playfieldWith-2)+1);   
        var ty = (int) (random(0,playfieldHeight-2)+1);   
        if (getPlayfield(tx,ty)==" ") {
          setPlayfield("a",tx,ty);
          inserted++;
        }
     } while (inserted<4);

     
     // random ... new objects .. 


     avatarPosition.x = playfieldWith/2;
     avatarPosition.y = playfieldHeight/2;

     setPlayfield(" ",(int) avatarPosition.x,(int) avatarPosition.y);

     playfieldRect(" ",0,0,playfieldWith-1,playfieldHeight-1);
  
     drawPlayfield();

     playfieldRect("a",0,0,playfieldWith-1,playfieldHeight-1);

     // draw by hand
     float os = playfieldTileSize*0.5f;
     float ps = playfieldTileSize;
      drawLine( os, os, (playfieldWith-1)*ps+os, os );
     drawLine( os+(playfieldWith-1)*ps, os, os+(playfieldWith-1)*ps, (playfieldHeight-1)*ps+os );
     drawLine( (playfieldWith-1)*ps+os, (playfieldHeight-1)*ps+os, os, (playfieldHeight-1)*ps+os );
     drawLine( os, (playfieldHeight-1)*ps+os, os, os );

     // drawPlayfield();

     // todo: check if there is something
     // own home?
 
     moveTo( avatarPosition.x, avatarPosition.y );
 
     gameTimerStart = 0;
 
     // loading music ...
     
 }
 
 // finished
 //  gameStateSubRunning="run";
  if (gameStateSubRunning.equals("plotting")) {
    if (plotterReadForInput()) {
      gameStateSubRunning = "running";
      gameTimer = millis();
    }    
  }


  if (gameStateSubRunning=="running") {
    
    
    // println("-"+pressedKeyChar);
    
    // -----------
    //  random 
    //  movement
    // -----------

// stop here ... 
    
    if (gameTimer<millis()) {
       gameTimer = millis()+500; 
       // println("NEXT EVENT");
       int irnd = (int) random(30);
       if (irnd==0) { if (avatarPosition.x<(playfieldWith-2)) avatarPosition.x += 1; }
       if (irnd==1) { if (avatarPosition.x>1) avatarPosition.x -= 1; }
       if (irnd==2) { if (avatarPosition.y<(playfieldHeight-2)) avatarPosition.y += 1; }
       if (irnd==3) { if (avatarPosition.y>1) avatarPosition.y -= 1; }
    }
    
    // life <1 black trail!
    
    String actualPlayfield = getPlayfield((int)avatarPosition.x,(int)avatarPosition.y);
    
    // -------------
    // take chrisie
    // ---------------
    
    // level 0 
    if (actualPlayfield.equals("d")) {
        setPlayfield("/",(int)avatarPosition.x,(int)avatarPosition.y);
        drawPlayfieldGraphic((int)avatarPosition.x,(int)avatarPosition.y); 
        score +=1;
        
        // check if there is still an a
        
        // create new as
        
        
        // and add second ...
        // println("playfield "+checkPlayfieldFor("d"));
        if (checkPlayfieldFor("d")==0) {
          level = 1;
          // search for "/" > "\"
        }
     
               
               
    }

    
    if (level==1) {
       if (actualPlayfield.equals("/")) {
        setPlayfield("\\",(int)avatarPosition.x,(int)avatarPosition.y);
        drawPlayfieldGraphic((int)avatarPosition.x,(int)avatarPosition.y); 
        score +=1;
        
        // check if there is still an a
        
        // create new as
        
        
          // and add second ...
          if (checkPlayfieldFor("/")==0) {
            level = 2;
            // search for "/" > "\"
            int timeInSecs = (int) (millis()-gameTimerStart)/1000;
            
            println("score: "+timeInSecs+" "+life*10);
            
            timeInSecs += life*10;    
            
            
            
            drawTextAt(""+timeInSecs+" S INC LIFES "+life+" * 10S",1*playfieldTileSize,12*playfieldTileSize,0.25f);
            
            gameStateSubRunning = "won";
            
            // enter a new page and press key !!!
            drawTextAt("PAPER!",1*playfieldTileSize,13*playfieldTileSize,0.25f);
              
          }
                 
        }
    }
    
 
    
    // -------------
    // dead?
    // ---------------   
    
    // level 1
    if (actualPlayfield.equals("a")) {
        // setPlayfield("X",(int)avatarPosition.x,(int)avatarPosition.y);
        // drawPlayfieldGraphic((int)avatarPosition.x,(int)avatarPosition.y); 
        life += 1;
  

        // start again ... !!! ...
//        moveTo(0,0);
        drawPlayfieldGraphicNameAt("q",avatarPosition.x+0.25,avatarPosition.y+0.25);
        
        // do something
       avatarPosition.x = playfieldWith/2;
       avatarPosition.y = playfieldHeight/2;

 //       moveTo(avatarPosition.x,avatarPosition.y);

        
        
    }
    
     
    // -----------
    // keys
    // -----------
    // key / controls
      if (pressedKeyChar.equals("d")) {
          if (avatarPosition.x<(playfieldWith-2)) avatarPosition.x += 1;
          pressedKeyChar = "";
        }
      if (pressedKeyChar.equals("a")) {
          if (avatarPosition.x>1) avatarPosition.x -= 1;
          pressedKeyChar = "";
        }
      if (pressedKeyChar.equals("w")) {
          if (avatarPosition.y>1) avatarPosition.y -= 1;
          pressedKeyChar = "";
        }
      if (pressedKeyChar.equals("s")) {
          if (avatarPosition.y<(playfieldHeight-2)) avatarPosition.y += 1;
          pressedKeyChar = "";
        }    
       
        
       //  print("("+pen.x+"/"+pen.y+")");
    
       // change pen
       if (avatarPosition.x!=avatarPositionOld.x || avatarPosition.y!=avatarPositionOld.y) {
    
         // pointer only!    
         if (life<5) {
             moveTo(avatarPosition.x+0.5f,avatarPosition.y+0.5f);
         } else {
             lineTo(avatarPosition.x+0.5f,avatarPosition.y+0.5f);
           
         }
         
       }
       
       
       avatarPositionOld.x = avatarPosition.x;
       avatarPositionOld.y = avatarPosition.y;

  }
  
  // start again ... 
  if (plotterReadForInput()) {
    if (gameStateSubRunning.equals( "won")) {
      // wait for " "
      if ((pressedKeyChar.equals(" "))||(pressedKeyChar.equals("e"))) {
          gameStateSubRunning = "";
          
          pressedKeyChar = "";
      } 
    }
  }
  
  
}

// --------------------------------------
// MINES
// --------------------------------------

public void updateInGameMines() {
 
 // start up / set graphics
 if (gameStateSubRunning.equals("")) {
     gameStateSubRunning="plotting";  
     
     println("updateInGameMines()");
     
     resetLines();

     // bigger field
     playfieldWith = 16;
     playfieldHeight = 12;
     
     level = 0;
     life = 0;
     score = 0;
     
 
     // display playfield 
     String[] aStr = { " "," "," "," "," ","*","*","*","*","h"," "," "," "," "," "," " ," "," " ," "," "  };
     randomPlayfield( aStr );
     
     // random ... new objects .. 

     avatarPosition.x = 0;
     avatarPosition.y = 0; // playfieldHeight/2;

     setPlayfield(" ",(int) avatarPosition.x,(int) avatarPosition.y);

     // playfieldRect(" ",0,0,playfieldWith-1,playfieldHeight-1);
  
//     playfieldRect("a",0,0,playfieldWith-1,playfieldHeight-1);

      // draw houses !!!
     // drawPlayfield();


     // draw by hand
     float os = 0.0;
     float ps = playfieldTileSize;
     if (true) {
       drawLine( os, os, (playfieldWith-1)*ps+os, os );
       drawLine( os+(playfieldWith)*ps, os, os+(playfieldWith)*ps, (playfieldHeight)*ps+os );
       drawLine( (playfieldWith)*ps+os, (playfieldHeight)*ps+os, os, (playfieldHeight)*ps+os );
       drawLine( os, (playfieldHeight)*ps+os, os, os );
     }
     
     // drawPlayfield();

     // todo: check if there is something
     // own home?
 
     moveTo( avatarPosition.x+0.5, avatarPosition.y+0.5 );
 
     gameTimerStart = 0;
 
     // loading music ...
     
 }
 
 // finished
 //  gameStateSubRunning="run";
  if (gameStateSubRunning.equals("plotting")) {
    if (plotterReadForInput()) {
      gameStateSubRunning = "running";
      gameTimer = millis();
    }    
  }


  if (gameStateSubRunning=="running") {
    
    // println("plotterNothingToDo "+plotterNothingToDo);
    
    // println("-"+pressedKeyChar);
    
    // -----------
    //  random 
    //  movement
    // -----------

// stop here ... 
    
    /*
    if (gameTimer<millis()) {
       gameTimer = millis()+500; 
       // println("NEXT EVENT");
       int irnd = (int) random(30);
       if (irnd==0) avatarPosition.x += 1;
       if (irnd==1) avatarPosition.x -= 1;
       if (irnd==2) avatarPosition.y += 1;
       if (irnd==3) avatarPosition.y -= 1;
    }
    */
    
    // life <1 black trail!
    
    String actualPlayfield = getPlayfield((int)avatarPosition.x,(int)avatarPosition.y);

    
    // start again ... 
    if (gameStateSubRunning.equals( "won")) {
      /*
      // wait for " "
      if (pressedKeyChar.equals(" ")) {
          gameStateSubRunning = "";
          pressedKeyChar = "";
      } 
      */
       
    }
    
    // -------------
    // dead?
    // ---------------   
    
     
    // -----------
    // keys
    // -----------
    // key / controls
    // only if game is ready!!! 
    if (plotterReadForInput()) {
      
      if (pressedKeyChar.equals("d")) {
          avatarPosition.x += 1;
          pressedKeyChar = "";
        }
        
      if (pressedKeyChar.equals("a")) {
          avatarPosition.x -= 1;
          pressedKeyChar = "";
        }
      if (pressedKeyChar.equals("w")) {
          avatarPosition.y -= 1;
          pressedKeyChar = "";
        }
      if (pressedKeyChar.equals("s")) {
          avatarPosition.y += 1;
          pressedKeyChar = "";
        }  
        
      // correct here
      if (avatarPosition.x < 0) avatarPosition.x = 0;
      if (avatarPosition.y < 0) avatarPosition.y = 0;
      if (avatarPosition.x > playfieldWith) avatarPosition.x = playfieldWith;
      if (avatarPosition.y > playfieldHeight) avatarPosition.y = playfieldHeight;
       
        
       //  print("("+pen.x+"/"+pen.y+")");
    
       // change pen
       if (avatarPosition.x!=avatarPositionOld.x || avatarPosition.y!=avatarPositionOld.y) {
         lineTo(avatarPosition.x+0.5f,avatarPosition.y+0.5f);
       }
       
       // new position 
       String newPlayfield = getPlayfield((int)avatarPosition.x,(int)avatarPosition.y);

       // ---------------------
       // show a new field
       // ---------------------
      // ... show it now .. 
      if (getPlayfieldVar((int)avatarPosition.x,(int)avatarPosition.y)==0) {
        // on what are you?
        // mine or house? 
        // hit 
        // mine
        if (newPlayfield.equals("*")) {
         drawPlayfieldGraphic((int)avatarPosition.x,(int)avatarPosition.y); 
         // seen ... 
         setPlayfieldVar(1,(int)avatarPosition.x,(int)avatarPosition.y);
         moveTo((int)avatarPosition.x+0.5,(int)avatarPosition.y+0.5);
         drawPlayfieldGraphicNameAt("q",avatarPosition.x,avatarPosition.y);         
        
         // back to the start ...
          moveTo((int)avatarPosition.x+0.5,(int)avatarPosition.y+0.5);        
        }      
        // house
        if (newPlayfield.equals("h")) {
         drawPlayfieldGraphic((int)avatarPosition.x,(int)avatarPosition.y); 
         // seen ... 
         setPlayfieldVar(1,(int)avatarPosition.x,(int)avatarPosition.y);
         moveTo((int)avatarPosition.x+0.5,(int)avatarPosition.y+0.5);
        }
        // house - chest
        if (newPlayfield.equals("c")) {
         drawPlayfieldGraphic((int)avatarPosition.x,(int)avatarPosition.y); 
         // seen ... 
         setPlayfieldVar(1,(int)avatarPosition.x,(int)avatarPosition.y);
         moveTo((int)avatarPosition.x+0.5,(int)avatarPosition.y+0.5);
        }

     }       
       // ---------------------
       // detector
       // ---------------------
       // check new area
       // ...
       if (getPlayfieldVar((int)avatarPosition.x,(int)avatarPosition.y)==0) {

         String am = " ";
         var count = 0;
         for (var c=-1;c<2;c++) {
           for (var cc=-1;cc<2;cc++) {
             
             if (((int)avatarPosition.x+c)<0) continue;
             if (((int)avatarPosition.y+cc)<0) continue;
             
             // check for mines             
             am = getPlayfield((int)avatarPosition.x+c,(int)avatarPosition.y+cc);   
             if (am.equals("*")) {
               count++;
             }
           }
         }
         // set the field now ..
         // setPlayfield(""+count,(int)avatarPosition.x,(int)avatarPosition.y);
         // drawPlayfieldGraphic((int)avatarPosition.x,(int)avatarPosition.y); 
         // add 1 !         
         setPlayfieldVar(count+1,(int)avatarPosition.x,(int)avatarPosition.y);
         // display
         // 0 point
         if (count>0) {
           drawTextAt(""+count,avatarPosition.x+0.5,avatarPosition.y+0.5,0.2f);
         }
         
         
         // display
         // small point
      }
       
       avatarPositionOld.x = avatarPosition.x;
       avatarPositionOld.y = avatarPosition.y;

    }
     
  }
}

// --------------------------------------
// RolePlay
// --------------------------------------
// * prototype

public void updateInGameRolePlay() {
 
 // start up / set graphics
 if (gameStateSubRunning.equals("")) {
     gameStateSubRunning="plotting";  
     
     println("updateInGameMines()");
     
     resetLines();

     // bigger field
     playfieldWith = 16;
     playfieldHeight = 12;
     
     level = 0;
     life = 0;
     score = 0;
     
 
     // display playfield 
     String[] aStr = { " "," "," "," "," ","*","*","*","*","h"," "," "," "," "," "," " ," "," " ," "," "  };
     randomPlayfield( aStr );
     
     // random ... new objects .. 

     avatarPosition.x = 0;
     avatarPosition.y = 0; // playfieldHeight/2;

     setPlayfield(" ",(int) avatarPosition.x,(int) avatarPosition.y);

     // playfieldRect(" ",0,0,playfieldWith-1,playfieldHeight-1);
  
//     playfieldRect("a",0,0,playfieldWith-1,playfieldHeight-1);

      // draw houses !!!
     // drawPlayfield();


     // draw by hand
     float os = 0.0;
     float ps = playfieldTileSize;
     if (true) {
       drawLine( os, os, (playfieldWith-1)*ps+os, os );
       drawLine( os+(playfieldWith)*ps, os, os+(playfieldWith)*ps, (playfieldHeight)*ps+os );
       drawLine( (playfieldWith)*ps+os, (playfieldHeight)*ps+os, os, (playfieldHeight)*ps+os );
       drawLine( os, (playfieldHeight)*ps+os, os, os );
     }
     
     // drawPlayfield();

     // todo: check if there is something
     // own home?
 
     moveTo( avatarPosition.x+0.5, avatarPosition.y+0.5 );
 
     gameTimerStart = 0;
 
     // loading music ...
     
 }
 
 // finished
 //  gameStateSubRunning="run";
  if (gameStateSubRunning.equals("plotting")) {
    if (plotterReadForInput()) {
      gameStateSubRunning = "running";
      gameTimer = millis();
    }    
  }


  if (gameStateSubRunning=="running") {
    
    // println("plotterNothingToDo "+plotterNothingToDo);
    
    // println("-"+pressedKeyChar);
    
    // -----------
    //  random 
    //  movement
    // -----------

// stop here ... 
    
    /*
    if (gameTimer<millis()) {
       gameTimer = millis()+500; 
       // println("NEXT EVENT");
       int irnd = (int) random(30);
       if (irnd==0) avatarPosition.x += 1;
       if (irnd==1) avatarPosition.x -= 1;
       if (irnd==2) avatarPosition.y += 1;
       if (irnd==3) avatarPosition.y -= 1;
    }
    */
    
    // life <1 black trail!
    
    String actualPlayfield = getPlayfield((int)avatarPosition.x,(int)avatarPosition.y);

    
    // start again ... 
    if (gameStateSubRunning.equals( "won")) {
      /*
      // wait for " "
      if (pressedKeyChar.equals(" ")) {
          gameStateSubRunning = "";
          pressedKeyChar = "";
      } 
      */
       
    }
    
    // -------------
    // dead?
    // ---------------   
    
     
    // -----------
    // keys
    // -----------
    // key / controls
    // only if game is ready!!! 
    if (plotterReadForInput()) {
      
      if (pressedKeyChar.equals("d")) {
          avatarPosition.x += 1;
          pressedKeyChar = "";
        }
        
      if (pressedKeyChar.equals("a")) {
          avatarPosition.x -= 1;
          pressedKeyChar = "";
        }
      if (pressedKeyChar.equals("w")) {
          avatarPosition.y -= 1;
          pressedKeyChar = "";
        }
      if (pressedKeyChar.equals("s")) {
          avatarPosition.y += 1;
          pressedKeyChar = "";
        }  
        
      // correct here
      if (avatarPosition.x < 0) avatarPosition.x = 0;
      if (avatarPosition.y < 0) avatarPosition.y = 0;
      if (avatarPosition.x > playfieldWith) avatarPosition.x = playfieldWith;
      if (avatarPosition.y > playfieldHeight) avatarPosition.y = playfieldHeight;
       
        
       //  print("("+pen.x+"/"+pen.y+")");
    
       // change pen
       if (avatarPosition.x!=avatarPositionOld.x || avatarPosition.y!=avatarPositionOld.y) {
         lineTo(avatarPosition.x+0.5f,avatarPosition.y+0.5f);
       }
       
       // new position 
       String newPlayfield = getPlayfield((int)avatarPosition.x,(int)avatarPosition.y);

       // ---------------------
       // show a new field
       // ---------------------
      // ... show it now .. 
      if (getPlayfieldVar((int)avatarPosition.x,(int)avatarPosition.y)==0) {
        // on what are you?
        // mine or house? 
        // hit 
        // mine
        if (newPlayfield.equals("*")) {
         drawPlayfieldGraphic((int)avatarPosition.x,(int)avatarPosition.y); 
         // seen ... 
         setPlayfieldVar(1,(int)avatarPosition.x,(int)avatarPosition.y);
         moveTo((int)avatarPosition.x+0.5,(int)avatarPosition.y+0.5);
         drawPlayfieldGraphicNameAt("q",avatarPosition.x,avatarPosition.y);         
        
         // back to the start ...
          moveTo((int)avatarPosition.x+0.5,(int)avatarPosition.y+0.5);        
        }      
        // house
        if (newPlayfield.equals("h")) {
         drawPlayfieldGraphic((int)avatarPosition.x,(int)avatarPosition.y); 
         // seen ... 
         setPlayfieldVar(1,(int)avatarPosition.x,(int)avatarPosition.y);
         moveTo((int)avatarPosition.x+0.5,(int)avatarPosition.y+0.5);
        }
        // house - chest
        if (newPlayfield.equals("c")) {
         drawPlayfieldGraphic((int)avatarPosition.x,(int)avatarPosition.y); 
         // seen ... 
         setPlayfieldVar(1,(int)avatarPosition.x,(int)avatarPosition.y);
         moveTo((int)avatarPosition.x+0.5,(int)avatarPosition.y+0.5);
        }

     }       
       // ---------------------
       // detector
       // ---------------------
       // check new area
       // ...
       if (getPlayfieldVar((int)avatarPosition.x,(int)avatarPosition.y)==0) {

         String am = " ";
         var count = 0;
         for (var c=-1;c<2;c++) {
           for (var cc=-1;cc<2;cc++) {
             
             if (((int)avatarPosition.x+c)<0) continue;
             if (((int)avatarPosition.y+cc)<0) continue;
             
             // check for mines             
             am = getPlayfield((int)avatarPosition.x+c,(int)avatarPosition.y+cc);   
             if (am.equals("*")) {
               count++;
             }
           }
         }
         // set the field now ..
         // setPlayfield(""+count,(int)avatarPosition.x,(int)avatarPosition.y);
         // drawPlayfieldGraphic((int)avatarPosition.x,(int)avatarPosition.y); 
         // add 1 !         
         setPlayfieldVar(count+1,(int)avatarPosition.x,(int)avatarPosition.y);
         // display
         // 0 point
         if (count>0) {
           drawTextAt(""+count,avatarPosition.x+0.5,avatarPosition.y+0.5,0.2f);
         }
         
         
         // display
         // small point
      }
       
       avatarPositionOld.x = avatarPosition.x;
       avatarPositionOld.y = avatarPosition.y;

    }
     
  }
}



// --------------------------------------
// LINE JEWELS
// --------------------------------------

// REMAKE OF THE LINEJEWELS@CHLUDENS.CH

// IDEA: 2 stones !!! 
// IDEA: 2 D
// PROBLEM: HAND?

float jplayfieldOffsetX = 1;
float jplayfieldOffsetY = 1;

float JewelsPositionX = 0;
float JewelsPositionY = 0;

  float JewelsPositionXSize = 0.5;
  float JewelsPositionYSize = 0.5;

String actualJewl = "w";
int actualLinePressedState = 0;
String stoneToSet = "";

int maxSizeLine = 1+7 ; 

int scrollLineScore = 0;

int JewelsPositionYMax = 18; // max lines to the end of the game!

boolean oldplotter = false;

// check ... 
/*
float[] arrCheckX = new float[50];
float[] arrCheckY = new float[50];
int arrCheckIndex = 0;
*/

public void updateInGameLineJewels() {
 
 // start up / set graphics
 if (gameStateSubRunning.equals("")) {
     gameStateSubRunning="start";  
     
     println("updateInGameMines()");
     
     resetLines();

     // bigger field
     playfieldWith = 16;
     playfieldHeight = 12;
     
     level = 0;
     life = 0;
     score = 0;

      jplayfieldOffsetX = 1;
       jplayfieldOffsetY = 1;

      
     
 
     // loading music ...
     
   }
   
   // finished
   //  gameStateSubRunning="start";
    if (gameStateSubRunning.equals("start")) {

       JewelsPositionX = 0;
       JewelsPositionY = 0;
          
          
     // random ... new objects .. 
     avatarPosition.x = 0;
     avatarPosition.y = 0; // playfieldHeight/2;

     // playfieldRect("*",0,0,playfieldWith-1,playfieldHeight-1);
     // playfieldRect("a",0,0,playfieldWith-1,playfieldHeight-1);
     // draw houses !!!
     // drawPlayfield();

     // draw by hand
     /*
     float os = 0.0;
     float ps = playfieldTileSize;
     if (true) {
       drawLine( os, os, (playfieldWith-1)*ps+os, os );
       drawLine( os+(playfieldWith)*ps, os, os+(playfieldWith)*ps, (playfieldHeight)*ps+os );
       drawLine( (playfieldWith)*ps+os, (playfieldHeight)*ps+os, os, (playfieldHeight)*ps+os );
       drawLine( os, (playfieldHeight)*ps+os, os, os );
     }
     */
     
     actualLinePressedState = 0;
     
     // drawPlayfield();

     // todo: check if there is something
     // own home?

    
 
    float posix = jplayfieldOffsetX+JewelsPositionX*JewelsPositionXSize;
    float posiy = jplayfieldOffsetY+JewelsPositionY*JewelsPositionYSize;    
    moveTo(posix+JewelsPositionXSize-0.1,posiy);
    lineTo(posix+JewelsPositionXSize-0.1,posiy+JewelsPositionXSize*21);   
    moveTo(posix,posiy);
 
     gameTimerStart = 0;
      
      gameStateSubRunning = "plotting";
    }

   
   // finished
   //  gameStateSubRunning="run";
    if (gameStateSubRunning.equals("plotting")) {
      if (plotterReadForInput()) {
        gameStateSubRunning = "running";
        gameTimer = millis();
      }    
    }


  if (gameStateSubRunning=="running") {
    
    // println("plotterNothingToDo "+plotterNothingToDo);
    
    boolean op = plotterReadForInput();
    if (op!=oldplotter) {
       // println("plotterready? "+op); 
    }
    oldplotter = op; 
    
    // println("-"+pressedKeyChar);
    
    // -----------
    //  random 
    //  movement
    // -----------

// stop here ... 


    // visual position
    float posix = jplayfieldOffsetX+JewelsPositionX*JewelsPositionXSize;
    float posiy = jplayfieldOffsetY+JewelsPositionY*JewelsPositionYSize;    

    int posixInt = (int)(jplayfieldOffsetX+JewelsPositionX);
    int posiyInt = (int)(jplayfieldOffsetY+JewelsPositionY);    

   String ds = "LJ DEBUG" // JewelsPositionXSize
       +"\njplayfieldOffsetX: "+jplayfieldOffsetX
       +"\njplayfieldOffsetY: "+jplayfieldOffsetY
       +"\nJewelsPositionX: "+JewelsPositionX
       +"\nJewelsPositionY: "+JewelsPositionY
       +"\n\nscore: "+score
       +"\nscrollLineScore: "+scrollLineScore
       +"\nactualLinePressedState: "+actualLinePressedState;
         
       text(ds,width/2,200);

    // go forward
    if (plotterReadForInput()) // only if the plotter is ready ... 
    if (gameTimer<millis()) {
//      if (plotterReadForInput()) {
      if (true) {

       gameTimer = millis()+2000; 
      // println("NEXT MOVE");
       
       // int irnd = (int) random(30);
       
       String[] arrC = { "x", "y","z" }; // ,"u" };
       int ridiot = (int)random(arrC.length);
       // println("ridiot "+ridiot);
       String NExT = arrC[ridiot];
       
      
       
       if (JewelsPositionX==0) {
          actualJewl = arrC[(int)random(arrC.length)];
          NExT = actualJewl;
          actualLinePressedState = 0;       
          scrollLineScore = 0;
          // println("NEWLINE: char: "+NExT);
           
          // drawTextAt("",posix,posiy,0.25);
         drawGraphicAt(""+NExT,posix,posiy,false); // optimized
       }

       if (JewelsPositionX > 0) {
  
          if (actualLinePressedState==1) {
           NExT = actualJewl;
           setPlayfield( NExT, (int)(posixInt), (int)(posiyInt) );
          }
                 // drawTextAt("",posix,posiy,0.25);
          drawGraphicAt(""+NExT,posix,posiy,false); // optimized
         
          setPlayfield( NExT, (int)(posixInt), (int)(posiyInt) );
          // println(debugPlayfield((int)(jplayfieldOffsetX), (int)(jplayfieldOffsetY),10,10));
         
         if (actualLinePressedState==1) {
           NExT = actualJewl;
           setPlayfield( NExT, (int)(posixInt), (int)(posiyInt) );
           
           // playPing();
         
           actualLinePressedState = 2;           
           // println(JewelsPositionY+" - "+JewelsPositionX+" SET STONE NOW!!!!!!!");
           // check now ... 
           // checkit - fast check?
           // ... 
           
           // results
           // arrResults 
           
           //
           // show lines now ...
           // skip all waitings ... 
           // > wait till end of line ...
           //
           int startX = (int)(posixInt);
           int startY = (int)(posiyInt);
           
           int lineSpec = 0;
           // max ... 
           int foundSomething = 0;
           
           // debug playfield
           var foundT = 0;
           var val = "";

           // debug here .. 
           // println(debugPlayfield((int)jplayfieldOffsetX,(int)jplayfieldOffsetY,10,10));
         
         
           var debugThis = false;
           
           var minMax = 0;
           
           // --------------
           // *
           // --------------
           debugThis = false;
           println("check up vs "+NExT);
           foundT = 0;
           val = "";
           int checkX = (int)(jplayfieldOffsetX+JewelsPositionX);          
           int checkY = (int)(jplayfieldOffsetY+JewelsPositionY);          
           println("----"+startY);
            float itxZ = jplayfieldOffsetX+(JewelsPositionX+0.5f)*JewelsPositionXSize;
            float ityZ = jplayfieldOffsetY+(JewelsPositionY+0.5f)*JewelsPositionYSize;  
  
           float itx = 0.0f;
           float ity = 0.0f;

           scrollLineScore = 0;

           // -----
           // ^
           // | 
           debugThis = true;
           minMax = 0; 
           foundT = 0;  
           for (int ix=checkY;ix>=0;ix--) {
              val = getPlayfield(checkX,ix);
              // break; 
              if (debugThis) println("   v "+ix+": "+val);
              if (val.equals(NExT)) {
                foundT++;
                minMax = ix-1;
              } else {                
                break; 
              }
           }
           // new score
           if (foundT>2) {
             // lineSpec = (foundT*10 + (foundT-3)*10)*3;
             lineSpec = foundT;
             
             if (debugThis) println("line up: "+lineSpec);
             scrollLineScore += lineSpec;
             // add line now ...
              moveTo(itxZ,ityZ);

//            float itxZ = jplayfieldOffsetX+(JewelsPositionX+0.5f)*JewelsPositionXSize;
//            float ityZ = jplayfieldOffsetY+(JewelsPositionY+0.5f)*JewelsPositionYSize;  

           //   itx = jplayfieldOffsetX+(checkX-1.0f+0.5f)*JewelsPositionXSize;
           //   ity = jplayfieldOffsetY+(minMax+0.5f)*JewelsPositionYSize; 

              itx = jplayfieldOffsetX+(checkX-jplayfieldOffsetX+0.5f)*JewelsPositionXSize;
              ity = jplayfieldOffsetY+(minMax-jplayfieldOffsetY+1.0f+0.5f)*JewelsPositionYSize; 

              
              lineTo(itx,ity);
              moveTo(itxZ,ityZ);              
              playPing();
           }

           // -----
           // ^
           //  \ 
           debugThis = false;
           minMax = 0; 
           foundT = 0;
           int cxt = checkY;
           for (int ix=checkX;ix>=1;ix--) { // not x? 
              val = getPlayfield(ix,cxt);
              // break; 
              if (debugThis)  println("    "+ix+"/"+cxt+": "+val);
              if (val.equals(NExT)) {
                foundT++;
                minMax = ix;
                cxt--;                
              } else {
                break; 
              }
           }
           if (debugThis) {
               println("line left: size: "+foundT);
           }
           // new score
           if (foundT>2) {
             // lineSpec = (foundT*10 + (foundT-3)*10)*3;
             lineSpec = foundT*2;

              if (debugThis)  println("line left: "+lineSpec);
             scrollLineScore += lineSpec;
             // add line now ...
              moveTo(itxZ,ityZ);
              
              itx = jplayfieldOffsetX+(minMax-jplayfieldOffsetX-1.0f+0.5f)*JewelsPositionXSize;
              ity = jplayfieldOffsetY+(cxt-jplayfieldOffsetY+0.5f)*JewelsPositionYSize; 


              lineTo(itx,ity);
              moveTo(posix,posiy);              
              playPing();
           }

           // -----
           //  ^
           // / 
           debugThis = false;
           minMax = 0; 
           foundT = 0;
           cxt = checkY;
           for (int ix=checkX;ix<maxSizeLine;ix++) { // not x? 
              val = getPlayfield(ix,cxt);
              // break; 
              if (debugThis)  println("    "+ix+"/"+cxt+": "+val);
              if (val.equals(NExT)) {
                foundT++;
                minMax = ix;
                cxt--;                
              } else {
                break; 
              }
           }
           if (debugThis) {
               println("line left: size: "+foundT);
           }
           // new score
           if (foundT>2) {
             // lineSpec = (foundT*10 + (foundT-3)*10)*3;
             lineSpec = foundT*2;

              if (debugThis)  println("line left: "+lineSpec);
             scrollLineScore += lineSpec;
             // add line now ...
              moveTo(itxZ,ityZ);
              
              itx = jplayfieldOffsetX+(minMax-jplayfieldOffsetX+0.5f)*JewelsPositionXSize+0.1;
              ity = jplayfieldOffsetY+(cxt-jplayfieldOffsetY+0.5f)*JewelsPositionYSize+0.1; 

              lineTo(itx,ity);
              moveTo(posix,posiy);              
              playPing();
           }          

           // -----
           //
           // <----
           debugThis = false;
           minMax = 0; 
           foundT = 0;
           for (int ix=checkX;ix>=1;ix--) {
              val = getPlayfield(ix,checkY);
              // break; 
              if (debugThis) println("   v "+ix+": "+val);
              if (val.equals(NExT)) {
                foundT++;
                minMax = ix;
              } else {
                // minMax = ix-1;
                break; 
              }
           }
           // new score
           if (foundT>2) {
             // lineSpec = ((foundT*10 + (foundT-3)*10)*3)*2;
             lineSpec = foundT*3;

             if (debugThis) println("line left only: "+lineSpec);
             scrollLineScore += lineSpec;
             // add line now ...
              moveTo(itxZ,ityZ);
              itx = jplayfieldOffsetX+(minMax-jplayfieldOffsetX+0.5f)*JewelsPositionXSize; // +0.5f;
              ity = jplayfieldOffsetY+(checkY-jplayfieldOffsetY+0.5f)*JewelsPositionYSize; // +0.5f;  
              lineTo(itx,ity);
              moveTo(itxZ,itxZ);              
              playPing();
           }
           if (debugThis) println("scoreLine: "+scrollLineScore);

           // score
           score += scrollLineScore;

         }

       }
       

       // go forward
       JewelsPositionX +=1;
       
       
       // -------------------------
       // next game
       // -------------------------
       // 10
       if (JewelsPositionX==maxSizeLine) {
          
          // finish old 
          JewelsPositionX = 0;
          JewelsPositionY +=1;
          
          // end of a game
          if (JewelsPositionY>JewelsPositionYMax) {
             // score !!!
             // draw 
             drawTextAt(""+score,jplayfieldOffsetX+JewelsPositionXSize+0.4,jplayfieldOffsetY+(JewelsPositionYMax+2)*JewelsPositionXSize,0.4f);
             gameStateSubRunning = "waitnextgame";   
             
          }
          
          // actualLinePressedState = 0;
          
//          drawGraphicAt(""+actualJewl,posix,posiy,false);


        }
      }
    // faster all line to !!!
    
    // next line .. 
    // moveTo(posix,posiy);
        
      
      
      /*
          int jplayfieldOffsetX = 0;
          int jplayfieldOffsetY = 0;
          
          int JewelsPositionX = 0;
          int JewelsPositionY = 0;
      */
  
   }

    
    // life <1 black trail!
    
    String actualPlayfield = getPlayfield((int)avatarPosition.x,(int)avatarPosition.y);

    
    // start again ... 
    if (gameStateSubRunning.equals( "won")) {
      /*
      // wait for " "
      if (pressedKeyChar.equals(" ")) {
          gameStateSubRunning = "";
          pressedKeyChar = "";
      } 
      */
       
    }
    
     
    // -----------
    // keys
    // -----------
    // key / controls
    // only if game is ready!!! 

    if ((pressedKeyChar.equals(" "))||(pressedKeyChar.equals("e"))) {
     // println("space");
        // set 
        if (actualLinePressedState==0) {          
          actualLinePressedState = 1;
          println(JewelsPositionY+" PRESS E > SET STONE");
          
          // todo: bling sound !!!
          playPing();
          
          
        } else {
           // println("SORRY PRINTED!"); 
        }
        
        pressedKeyChar = "";
      }
             
  }
  
  // waitfornextgame
  if (gameStateSubRunning.equals("waitnextgame")) {
    
    if (plotterReadForInput()) {
        
        if ((pressedKeyChar.equals(" "))||(pressedKeyChar.equals("e"))) {
           
          int opv = (int) jplayfieldOffsetX;
          jplayfieldOffsetX +=  ((int)(maxSizeLine*JewelsPositionXSize))+1;
          
          JewelsPositionX = 0;
          JewelsPositionY = 0;
          
          score = 0;
          
          gameStateSubRunning = "start"; 
          
          if (jplayfieldOffsetX>playfieldWith) {
           
             // new ... paper ... 
             gameStateSubRunning = "restart"; 
             drawTextAt("PAPER!",opv+JewelsPositionXSize+0.4,jplayfieldOffsetY+(JewelsPositionYMax+4)*JewelsPositionXSize,0.4f);
             moveTo( opv+JewelsPositionXSize+0.4,jplayfieldOffsetY+(JewelsPositionYMax+4)*JewelsPositionXSize );             
          }
        
          pressedKeyChar = "";
        }
      
    }

  
      

  }
  
  // restart
   if (gameStateSubRunning.equals("restart")) {
      
      if (plotterReadForInput()) {
          
          if ((pressedKeyChar.equals(" "))||(pressedKeyChar.equals("e"))) {
              gameStateSubRunning = "";
              
              pressedKeyChar = "";
          }
      }
   }
      
  
}

// --------------------------------------
// TENNIS 4 TWO
// --------------------------------------
// THE WORLDS FIRST RADICAL GAME
// TRY OUT: SMALLER AND FASTER

/*
  0 wait for start 
  1 in the 
  2 interactionDone
  
*/


int playerStart = 1;

float ballVxSpeed = 0.40;
float ballVySpeed = 0.001; // graviation

float ballVx = ballVxSpeed; // default
float ballVy = 0.003;

float ballVyGravitation = 0.015;

float breakIt = 0.001f; //  0.001f; // slow down / air friction

float player1Alpha = 0.0f; // 0-1 (0-360)
float player2Alpha = 0.0f; // 0-1 (0-360)


// ball 

int ballWait = 0;
int ballStart = 0; 

int ballSide = 0;
int ballSideOld = 0;

int ballSideInteraction = 0;

int ballSideChange = 0;

int ballOnGroundThisSide = 0;

boolean ballLocked = false;




// 0-1.0 
PVector getVectorFromAlpha( float alpha ) {

    PVector pv = new PVector();

    float alphaRad = alpha * 6.28f; 
    
    // sin(0) 
    
    pv.x = sin(alphaRad);
    pv.y = cos(alphaRad);

    return pv;
}

void lostBall() {
 
  ballVx = 0.0f;
  ballVy = 0.0f;
  
  ballSideChange = 0;
  ballOnGroundThisSide = 0;
  
  ballSideInteraction = 0;
  
  ballLocked = true;
  
  if (playerStart==0) { playerStart=1; }
  else { playerStart = 0; } 
 
  
  // go to the other side 
  
  // start again ...
  if (playerStart==0) {
     avatarPosition.x = playfieldWith/8;
     avatarPosition.y = playfieldHeight*7/8; // playfieldHeight/2;
     
     moveTo(avatarPosition.x,avatarPosition.y);      
  }
  if (playerStart==1) {
     avatarPosition.x = playfieldWith*7/8;
     avatarPosition.y = playfieldHeight*7/8; // playfieldHeight/2;
     
     moveTo(avatarPosition.x,avatarPosition.y);      
  }
    
}


public void updateInGameTennisForTwo() {
 
 // start up / set graphics
 if (gameStateSubRunning.equals("")) {
     gameStateSubRunning="plotting";  
     
     println("updateInGameTennisForTwo()");
     
     resetLines();

     // bigger field
     playfieldWith = 16;
     playfieldHeight = 8; // 8 - less distance less drawtime
     
     level = 0;
     life = 0;
     score = 0;

      jplayfieldOffsetX = 1;
       jplayfieldOffsetY = 1;
  
     // playfield "tennis"          

     // bottom
 //    moveTo(0,playfieldHeight);
 //    lineTo(playfieldWith,playfieldHeight);
     // net 
     // playfieldWith/2
     moveTo(playfieldWith/2,playfieldHeight);
     lineTo(playfieldWith/2,playfieldHeight-1.5f);
     

     // playfieldRect("*",0,0,playfieldWith-1,playfieldHeight-1);
     // playfieldRect("a",0,0,playfieldWith-1,playfieldHeight-1);
     // draw houses !!!
     // drawPlayfield();

     // draw by hand
     /*
     float os = 0.0;
     float ps = playfieldTileSize;
     if (true) {
       drawLine( os, os, (playfieldWith-1)*ps+os, os );
       drawLine( os+(playfieldWith)*ps, os, os+(playfieldWith)*ps, (playfieldHeight)*ps+os );
       drawLine( (playfieldWith)*ps+os, (playfieldHeight)*ps+os, os, (playfieldHeight)*ps+os );
       drawLine( os, (playfieldHeight)*ps+os, os, os );
     }
     */
     
     actualLinePressedState = 0;
     
     // drawPlayfield();

     // todo: check if there is something
     // own home?

      // start position
 
     lostBall(); // start

     /*
     // random ... new objects .. 
     avatarPosition.x = playfieldWith/4/2;
     avatarPosition.y = playfieldHeight*7/8; // playfieldHeight/2;
     
     moveTo(avatarPosition.x,avatarPosition.y);


      PVector dir = getVectorFromAlpha(0.25f+0.125f);          
      ballVx = dir.x*ballVxSpeed;
      ballVy = dir.y*ballVxSpeed;
    */
 
     gameTimerStart = 0;     
 
     // loading music ...
     
   }
   
   // finished
   //  gameStateSubRunning="run";
    if (gameStateSubRunning.equals("plotting")) {
      if (plotterReadForInput()) {
        gameStateSubRunning = "running";
        gameTimer = millis();
      }    
    }


  if (gameStateSubRunning=="running") {
    
    // println("plotterNothingToDo "+plotterNothingToDo);

    
    boolean op = plotterReadForInput();
    if (op!=oldplotter) {
       // println("plotterready? "+op); 
    }
    oldplotter = op; 


    // debug
    String txt = "";
    
    txt += "\nballWait: "+ballWait;
    txt += "\nballStart: "+ballStart;
    txt += "\nballSide: "+ballSide;
    txt += "\nballSideOld: "+ballSideOld;
    txt += "\nballSideChange: "+ballSideChange;
    txt += "\nballOnGroundThisSide: "+ballOnGroundThisSide;
    txt += "\nballLocked: "+ballLocked;
    txt += "\nplayerStart: "+playerStart;
    txt += "\nballSideInteraction "+ballSideInteraction;
    
    text("DEBUG T42"+txt,820,140);

  
   /*
   fill(30);
   line(0,0,400,400);
   for (var z=0;z<628/4;z++) {
         
         float zx = 350.0f;
         float zy = 350.0f;
         
         float zxx = zx+sin(z/100.0f)*100;
         float zyy = zy+cos(z/100.0f)*100;
         
         line(zx,zy,zxx,zyy);
                 
    }  
    */
    
    // -------------------------------
    // BALL SIDE
    // -------------------------------
    // ballSideChange = 0;
    if (avatarPosition.x<(playfieldWith/2)) {
      ballSide = 0;  
    } else {
      ballSide = 1;
    }
    if (ballSide!=ballSideOld) {
      ballSideChange++;
      ballOnGroundThisSide = 0;
      ballSideInteraction = 0;
    }
    ballSideOld = ballSide;
      
      // -------------------------------
      // KEYS
      // -------------------------------
      // control player 1 | player 2
      if (ballSideInteraction==0) {
        if (pressedKeyChar.equals(" ")) {
            pressedKeyChar = "";
  
            println("smash");        
  
            // new direction
            float rnda = random(0.0f,0.2f);
            float al = 0.25+rnda;
            if (ballSide==1) al = 0.75-rnda;
            PVector dir = getVectorFromAlpha(al);          
            ballVx = dir.x*ballVxSpeed;
            ballVy = dir.y*ballVxSpeed;
           
            ballLocked = false;            
            ballSideInteraction = 1;
            
        }
      }
      
      // test aiming 
      if (true) {
        
          float middleCourt = playfieldWith/2;
          float topNetX = playfieldHeight-1.5f;
                         
          float diffX = middleCourt-avatarPosition.x;
          float diffY = (topNetX - 1.5f)-avatarPosition.y;
            
          fill(35);
          stroke(30);
          
          float mx = 200;
          float my = 200;
          
          line(avatarPosition.x,avatarPosition.y,middleCourt,topNetX);
          
          line(0,0,100,200);
          
          
        
      }
      
      
      // use ai?
      if (op)
      if (false) {
     
      if (ballSideInteraction==0) {
        
         
           // random ai
           int rdx = (int)(random(0,50));
           if (rdx==0) {
  
             println("shoot");        
  
            // new direction
            float middleCourt = playfieldWith/2;
            float topNetX = playfieldHeight-1.5f;

            line(avatarPosition.x,avatarPosition.y,middleCourt,topNetX);

            float diffX = middleCourt-avatarPosition.x;
            float diffY = (topNetX - 1.5f)-avatarPosition.y;
            
            
            float alTarget = atan2(diffY,diffX);            
            // println("alpha-target: "+ alTarget);
            
            // 1.0 = 6.28f
            // 1.0/6.28 = t
            alTarget = alTarget*1.0f/6.28f;
            println("alpha: "+ alTarget);
            if (alTarget<0.0f) alTarget += 1.0f;
            
            // alTarget = alTarget + 0.25f;
                  
            PVector dir = getVectorFromAlpha(alTarget);          
            ballVx = dir.x*ballVxSpeed;
            ballVy = dir.y*ballVxSpeed;
           
             ballLocked = false;
            
             // ballSideChange = 0;          
             
             ballSideInteraction = 1;
           }
                 
        }
      }

    // ------------------------------
    // ball locked?
    // > no mechanics
    // ------------------------------
    if (ballLocked) return;

    // ------------------------------
    // mechanics
    // ------------------------------
    /*
          txt += "\nballWait: "+ballWait;
    txt += "\nballStart: "+ballStart;
    txt += "\nballSide: "+ballSide;
    txt += "\nballSideOld: "+ballSideOld;
    txt += "\nballSideChange: "+ballSideChange;
    txt += "\nballOnGroundThisSide: "+ballOnGroundThisSide;
    
    
    */
   
    // Start: Hit Ground in own side
    //    \     |
    // ____\/_____|_________
    
    if (ballSideChange==0) {
      if (ballOnGroundThisSide>0) {
         println("Forbidden first on the own ground! ");
         lostBall();
        return;
      }
    }
 
    // LostPoint
    //          |   ___
    // _________|_\/___\/___
    if (ballOnGroundThisSide>1) {
       println("lostBall");
       lostBall();
        return;
    }
    
    // Net!
    //          |
    // _________|_________

    float sizeNet = 0.3f;
    float topNet = playfieldHeight-1.5f;
    if (avatarPosition.y>topNet) {

      if (avatarPosition.x>(playfieldWith/2-sizeNet)) {
        if (avatarPosition.x<playfieldWith/2+sizeNet) {
           println("NET!"); 
           lostBall();       
          return;
        }
      }
      
    
    }

    // BackLine
    // 
    //   |
    //   |
    // directly into the off
    float courtBorder = 0.2f;
    if ((avatarPosition.x<courtBorder)||(avatarPosition.x>(playfieldWith-courtBorder))) {
       
        if (ballOnGroundThisSide>0) {
           println("hit ground & off"); 
           lostBall();
           return;
        }
  
        // direct
        if (ballOnGroundThisSide==0) {
           println(" off");     
           lostBall();
          return;
        }

    }
    
      
    // ------------------------------
    // animating and painting
    // ------------------------------


    // only go if the plotter is ready
    
    // try here .-)
            
    
    if (op) {
//    if (true) {
    
      // logic
      // Y - ground
      if ((avatarPosition.y+ballVy)>playfieldHeight) {
        ballVy *= -1.0f;
        avatarPosition.x += ballVx;
        avatarPosition.y += ballVy;
        ballOnGroundThisSide++;
      }
      
  
      // X
      if (avatarPosition.x>playfieldWith) {
        ballVx *= -1.0f;
        avatarPosition.x += ballVx;
        avatarPosition.y += ballVy;
      }    
  
      if (avatarPosition.x<0.2) {
        ballVx *= -1.0f;
        avatarPosition.x += ballVx;
        avatarPosition.y += ballVy;
      }    
      
      // slow down?
        ballVx *= 1.0f-breakIt;
        ballVy *= 1.0f-breakIt;
      
      
  
      // graviation
      ballVy += ballVyGravitation;
  
      // ball vx
      avatarPosition.x += ballVx;
      avatarPosition.y += ballVy;
      
      lineTo( avatarPosition.x, avatarPosition.y );
          
      avatarPositionOld.x = avatarPosition.x;
      avatarPositionOld.y = avatarPosition.y;  
    }
    
  }

}


// ----------------------
// JumpAndRun 3D
// ----------------------


int gameJumpAndRunDirectionX = 1;
int gameJumpAndRunDirectionY = 0;
int gameJumpAndRunJumpState = 0;

int gameTimerJump = 0;
int gameJumpState = 0; // on ground

public void updateInGameJumpAndRun() {
 
   // start up / set graphics
   if (gameStateSubRunning.equals("")) {
       gameStateSubRunning="plotting";  
       
       setGameVisualRatio(1.0f);
       
       resetLines();
       
       // bigger field
       playfieldWith = 16;
       playfieldHeight = 12;
       playfieldTileSize = 1.0f;
       
       level = 0;
       life = 0;
       score = 0;
              
       // loadLevel
       String lv = "          \n"+
                   " fffffff \n"+
                   " f     f  \n"+
                   " f     f  \n"+
                   " ff-ffffffffffff \n"+
                   " f        f    f \n"+
                   " ff-f-ffffffffff \n"+
                   "          f  f   \n"+
                   "          ffff   \n";
       insertLevel( lv );      
    
        avatarPosition.x = 4;
        avatarPosition.y = 1;
        
    
       drawPlayfield();

       moveTo(avatarPosition.x+playfieldTileSize/2,avatarPosition.y+playfieldTileSize/2);

   }
 
// finished
 //  gameStateSubRunning="run";
  if (gameStateSubRunning.equals("plotting")) {
    if (plotterReadForInput()) {
      gameStateSubRunning = "running";
      gameTimer = millis();
      gameTimerJump = millis(); 
      gameJumpState = 0;
    }    
  }


    if (gameStateSubRunning=="running") {
      
      
      // println("-"+pressedKeyChar);
      
      // -----------
      //  random 
      //  movement
      // -----------
      if (gameTimer<millis()) {
         gameTimer = millis()+500; 
         // println("NEXT EVENT");
         // int irnd = (int) random(10);
         // if (irnd==0) avatarPosition.x += 1;  
         PVector avatarPositionOld = new PVector();
         avatarPositionOld.x = avatarPosition.x;
         avatarPositionOld.y = avatarPosition.y;
         
         avatarPosition.x += gameJumpAndRunDirectionX;
         avatarPosition.y += gameJumpAndRunDirectionY;
         
         // ...
         // position check ...
         int pX16 = (int)avatarPosition.x;
         int pY16 = (int)avatarPosition.y;

         
          
         /*   W
             AMD
              S
         */         
         String actualPlayfieldM = getPlayfield(pX16,pY16);
         
         boolean randomDirection = false;
         
         // RANDOM DIRECTION?
         if (true) {
           
           
           int direction = getMovementType();
           
           int actualDirection = direction;
           
           int count = 0;           
           int[] allDirections = {0,0,0,0};           
           for (int h=0;h<4;h++) {
             direction = h;
             setMovementType(direction);
//             if (h!=antiDirection(actualDirection)) {
             if (true) {
               int pX16N = (int)avatarPosition.x+gameJumpAndRunDirectionX;  
               int pY16N = (int)avatarPosition.y+gameJumpAndRunDirectionY;
               String actualPlayfieldNext = getPlayfield(pX16N,pY16N);             
               if (!actualPlayfieldNext.equals(" ")) {
                 count++;
                 allDirections[h] = 1; 
               }
             }
           }
           
           allDirections[antiDirection(actualDirection)] = 0;
           direction = actualDirection;
           setMovementType(actualDirection);
           
           if (count>2) {
               String ost = "";
               for (int z=0;z<allDirections.length;z++) {
                 ost += ""+allDirections[z];
               }
               // println("decision point! "+ost); 
           }
           
           // println("count "+count);
           if (true) {
               if (count>2) {
                  // println("decision point!"); 
                  boolean ft = true;
                  do {
                    int ind = (int) random(4);
                    if (allDirections[ind]==1) {
                      ft=false;
                      setMovementType(ind);
                      randomDirection = true;
                      direction = ind; 
                    }
                  } while(ft);
                  
                  
               }
               direction = getMovementType();
           }
        }
         

        if (!randomDirection) { 
          
         // NEXT STEP
         int pX16N = (int)avatarPosition.x+gameJumpAndRunDirectionX;
         int pY16N = (int)avatarPosition.y+gameJumpAndRunDirectionY;
         String actualPlayfieldNext = getPlayfield(pX16N,pY16N);
         if (!actualPlayfieldNext.equals(" ")) {
           // Random check here ... 
           
           // random check ... 
           // 3? 
           
           
           
         } else {
           // stop
           int direction = getMovementType();
           int firstDirection = direction;
           // println("STOP "+direction);
           for (int h=0;h<4;h++) {
             if (antiDirection(firstDirection)==h) continue;
             direction++;
             direction = direction % 4;
             setMovementType(direction);             
             pX16N = (int)avatarPosition.x+gameJumpAndRunDirectionX;  
             pY16N = (int)avatarPosition.y+gameJumpAndRunDirectionY;
             actualPlayfieldNext = getPlayfield(pX16N,pY16N);             
             if (!actualPlayfieldNext.equals(" ")) {
               // println("BREAK "+direction);
               break;
             }
           }
           
           
         } // else
         
       } // not random direction
    
//
// 
// 
// tot?
// 
// boden springen?
// 

         // end ..         
                   
         
    }
      
      // life <1 black trail!
      
    // -----------
    // keys
    // -----------
      
    if (gameJumpState==1) {
      if (gameTimerJump<millis()) {
           gameJumpState = 0;
           penDown();
           println("COMING DOWN");
      }
    }

    // -----------
    // keys
    // -----------
    // key / controls
    
    /*
      if (pressedKeyChar.equals("d")) {
          avatarPosition.x += 1;
          pressedKeyChar = "";
        }
      if (pressedKeyChar.equals("a")) {
          avatarPosition.x -= 1;
          pressedKeyChar = "";
        }
      if (pressedKeyChar.equals("w")) {
          avatarPosition.y -= 1;
          pressedKeyChar = "";
        }
      if (pressedKeyChar.equals("s")) {
          avatarPosition.y += 1;
          pressedKeyChar = "";
        }    
       */

      if (pressedKeyChar.equals(" ")) {
          // avatarPosition.x += 1;
          
          // jump ... 
          // jump here ... 
          // jump distance ...
          // on a 
          String actualPlayfieldX = getPlayfield((int)avatarPosition.x,(int)avatarPosition.y);
           // gameover !!!
           if (actualPlayfieldX.equals("f")) {
              if (gameJumpState==0) {
                gameTimerJump = millis()+2000;
                gameJumpState = 1;
                println("JUMP!JUMP!");
                penUp();          
              }
          }
           
          pressedKeyChar = "";
        }
   
       //  print("("+pen.x+"/"+pen.y+")");
    
       // change pen
       if (avatarPosition.x!=avatarPositionOld.x || avatarPosition.y!=avatarPositionOld.y) {
    
         // default pen down!!
         moveFreeTo(avatarPosition.x+0.5f,avatarPosition.y+0.5f);
         
       }
       
       
       avatarPositionOld.x = avatarPosition.x;
       avatarPositionOld.y = avatarPosition.y;    
    
   }   
   
   
}

  public int getMovementType() {
    
     if (gameJumpAndRunDirectionX==1) return 0; 
     if (gameJumpAndRunDirectionY==1) return 1; 
     if (gameJumpAndRunDirectionX==-1) return 2; 
     if (gameJumpAndRunDirectionY==-1) return 3;
   
     return 0;
  }
  
  public int antiDirection( int direct ) {

     if (direct==0) return 2; 
     if (direct==1) return 3; 
     if (direct==2) return 0; 
     if (direct==3) return 1; 
    
    return 0;
  }

  public void setMovementType( int mv ) {
    
     if (mv==0) {
       gameJumpAndRunDirectionX = 1;
       gameJumpAndRunDirectionY = 0;
     }
     if (mv==1) {
       gameJumpAndRunDirectionX = 0;
       gameJumpAndRunDirectionY = 1;
     }
     if (mv==2) {
       gameJumpAndRunDirectionX = -1;
       gameJumpAndRunDirectionY = 0;
     }

     if (mv==3) {
       gameJumpAndRunDirectionX = 0;
       gameJumpAndRunDirectionY = -1;
     }
     
  }
  
  
// ----------------------
// WON AND GAMEOVER
// ----------------------  

public void updateGameWon() {
  println("updateGameWon()");
}


public void updateGameOver() {
  println("updateGameOver()");
  
}


// ----------------------
// updateGUI()
// ----------------------
// update the 
// processing gui
// != game gui
// 

int guiFieldX = 0;
int guiFieldY = 50;

int guiFieldTextSize = 14;


color colorGUIWhite = color(255, 255, 255);
color colorGUI = color(0, 0, 0);  // BLACK
color colorGUIPen = color(155, 155, 155);  // BLACK
color colorGUIFrame = color(255, 155, 155);  // BLACK

// ON PAPER

int offsetPaperX = 10;
int offsetPaperY = 50;
float guiPaperScale = 50.0f;

// POSITION 
Line penPosition = new Line();
Line penPositionX = new Line();



public void updateGUI() {

    // header
    background(colorGUIWhite);    
    fill(colorGUI);
    textSize(guiFieldTextSize*2);
    text(""+gameName, 0, guiFieldTextSize*2);   
    
    // state
    textSize(guiFieldTextSize);
    text(""+gameStateRunning+"/"+gameStateSubRunning, guiFieldTextSize*2, guiFieldTextSize*3);   
    
    // stats
    textSize(guiFieldTextSize);
    text("stats ls: "+processLineIndex+"/"+arrLines.size()+" p:"+penState, 600, guiFieldTextSize*3);   

    text("level "+level+" score: "+score+" life: "+life, 250, guiFieldTextSize*3);   

    // state plotter and co
    textSize(guiFieldTextSize*3/2);
    if (!plotterConnected) {
       text("PLOTTER NOT CONNECTED! SIMULATION!", width*1/3, guiFieldTextSize*2);        
    }
   
    // playground
    // simulation of all !!!
    stroke(colorGUIFrame);
    line(offsetPaperX,offsetPaperY,offsetPaperX+width,offsetPaperY);
    line(offsetPaperX,offsetPaperY,offsetPaperX,offsetPaperY+height);
    
    // draw Pen
    penPosition.p1.x = pen.x-0.1;
    penPosition.p1.y = pen.y-0.1;
    penPosition.p2.x = pen.x+0.1;
    penPosition.p2.y = pen.y+0.1;
    
    penPosition.plotted = false;

    if (plotterNothingToDo) penPosition.plotted = true;

    
    penPosition.colR = 255;
    penPosition.colG = 0;
    penPosition.colB = 0;
    penPosition.colA = 255;

    penPositionX.p1.x = pen.x+0.1;
    penPositionX.p1.y = pen.y-0.1;
    penPositionX.p2.x = pen.x-0.1;
    penPositionX.p2.y = pen.y+0.1;

    penPositionX.colR = 255;
    penPositionX.colG = 0;
    penPositionX.colB = 0;
    penPositionX.colA = 255;
 
     penPositionX.plotted = false;

    // only true if awa
    if (plotterNothingToDo) penPositionX.plotted = true;

    // tmp
    // shape(tmpShape);
    
    // debugVectorGraphics
    if (debugVectorGraphics) {
      if (vectorgraphics!=null) {
         shape(vectorgraphics,10,10);
      }
    }

    if (simulatePenLinesGUI) {

      // position pointer (virtual!)
      drawGUILine(penPosition);  
      drawGUILine(penPositionX);  

      // draw all lines
      Line xl;
      for (int z=0;z<arrLines.size();z++) {
         xl = (Line) arrLines.elementAt(z);
         // check line type ..  
         // move vs line ... 
         drawGUILine(xl); 
      }
    }    
    // 
}

// -----------------------
// PLAYFIELD
// -----------------------
String[][] arrPlayfield = new String[100][100];
int[][] arrPlayfieldVar = new int[100][100];

int playfieldWithMax = 100;
int playfieldHeightMax = 100;

// generate all to " "
public void resetPlayfield() {
   resetPlayfield(" "); 
}

public void resetPlayfield( String str ) {

  
  try {
     for (int u=0;u<playfieldWithMax;u++) {
      for (int uu=0;uu<playfieldWithMax;uu++) {
           arrPlayfield[u][uu] = str; 
      }
    }
  } catch( Exception e ) {
    println("resetPlayfield() ERROR! "+e);
  }

  try {
     for (int u=0;u<playfieldWithMax;u++) {
      for (int uu=0;uu<playfieldWithMax;uu++) {
           arrPlayfieldVar[u][uu] = 0; 
      }
    }
  } catch( Exception e ) {
    println("resetPlayfield() ERROR! "+e);
  }
   
}

// --------------------
// playfield 
// --------------------
// playable 
int playfieldWith = 10;
int playfieldHeight = 10;

// " abc d \n"+
// "   def \n";
public void insertLevel( String v ) {
  
  int p=0;
  int pp = 0;
  for (int i=0;i<v.length();i++) {
     String chs = ""+v.charAt(i);
     if (chs.equals("\n")) {
       pp++;
       p=0;
     } else {
        arrPlayfield[p][pp] = chs; 
       p++;
     }
     
  }
  
  
}

public int checkPlayfieldFor( String val ) {

  int count = 0;
  
  for (int u=0;u<playfieldWith;u++) {
    for (int uu=0;uu<playfieldHeight;uu++) {
        if (arrPlayfield[u][uu].equals(val)) {
          count++; 
        }
    }
  }

  return count;
}

public void randomPlayfield( String[] arrRandom ) {
   
  for (int u=0;u<playfieldWith;u++) {
    for (int uu=0;uu<playfieldHeight;uu++) {
        int index = (int) random(arrRandom.length);        
        arrPlayfield[u][uu] = arrRandom[index]; 
    }
  }
  
}

public void playfieldRect(String val, int x, int y, int xx, int yy ) {
  for (int i=x;i<xx+1;i++) {
       arrPlayfield[i][y] = val;
  }
  
  for (int i=x;i<xx+1;i++) {
       arrPlayfield[i][yy] = val;
  }

  for (int i=y;i<yy+1;i++) {
       arrPlayfield[x][i] = val;
  }

  for (int i=y;i<yy+1;i++) {
       arrPlayfield[xx][i] = val;
  }

}

// offset? / size
float playfieldTileSize = 1.0f;

public void drawPlayfield() {
  
  for (int u=0;u<playfieldWith+1;u++) {
    for (int uu=0;uu<playfieldHeight+1;uu++) {
        try {
          drawPlayfieldGraphic( u, uu );
        } catch (Exception e) {
           println("drawPlayfield() "+e); 
        }
    }
  }  
  
}

String debugPlayfield(int startXT,int startYT,int sizeXL,int sizeYL) {
  
  
  String vax = "debugPlayfield()\n";
   for (int iy=startYT;iy<(startYT+sizeYL);iy++) {
     vax += iy+" ";
     for (int ix=startXT;ix<(startXT+sizeXL);ix++) {
       vax += ""+getPlayfield(ix,iy);
     }       
     vax += "  "+startXT+"  "+(sizeYL)+"\n";
   }
           
  return vax;
}

// --------------------
// playfield one field
// --------------------

public String getPlayfield( int px, int py ) {
   return arrPlayfield[px][py];
}

// will not draw it!
public void setPlayfield( String val, int px, int py ) {
    // println("setPlayfield() "+val+" "+px+" "+py);
    arrPlayfield[px][py] = val;
}

public void drawPlayfieldGraphic( int u, int uu ) {

    String pf = ""+arrPlayfield[u][uu];       
    drawTextAt(pf,u*playfieldTileSize,uu*playfieldTileSize);
  
}

public void drawPlayfieldGraphicNameAt( String name, float u, float uu ) {

    String pf = ""+name;       
    drawTextAt(pf,u*playfieldTileSize,uu*playfieldTileSize);
  
}

// --------------------
// playfield vars
// --------------------
// for example mines etc
// 

public int getPlayfieldVar( int px, int py ) {
   return arrPlayfieldVar[px][py];
}

// will not draw it!
public void setPlayfieldVar( int val, int px, int py ) {
    arrPlayfieldVar[px][py] = val;
}


// -----------------------
// DRAW
// -----------------------

boolean nextMoveAvailable = true;

boolean mousepressedOn = false;
boolean mouseclicked = false;

int moveStatusO = 0;

void draw() {
  
  
//  if (moveStatusO!=moveStatus) println("moveStatus "+moveStatus);
//  moveStatus = moveStatusO;

  // check if move is finished!
  int waitTime = nextMoveTime - millis();
  // nextMoveAvailable = false;
  // println("waitTime "+waitTime);  
  if (waitTime < 0)
  {
    // moveStatus = 1;  // Flag this move as not yet completed.
    nextMoveAvailable = true;
  }
  
  if (pressedKeyChar.equals("h")) { 
      waitForThePlotter = false;
  }
  
  // println("nextMoveAvailable "+nextMoveAvailable);
  
  // println("nextMoveAvailable "+nextMoveAvailable+"  "+floor(millis()/1000));
  
  // detect click here
  mouseclicked = false;
  if (mousepressedOn==false) {
    if (mousePressed == true) {
      mouseclicked = true;
    }
  }
  mousepressedOn = mousePressed;

  // connect now ... 
  if (doSerialConnect)
  {
    // FIRST RUN ONLY:  Connect here, so that 
    connectToAxiDraw();
  } 
  
  // startup
  // gamestate ... 
  if (SerialOnline)
  {
      if (gameState==0) {    
          initGame();            
      }
   }
   
  
  // update ui
  updateGUI();
  
  /*
  // simple solution ... 
  // wait till end of movement !!! 
  int diff = millis() - nextMoveTime;
//   println(diff);
//  println("available: "+nextMoveAvailable);
 // nextMoveAvailable = true;
  if (diff>10) {
    nextMoveAvailable = false;
    // return;
  }
  
  if (processLineIndex==-1) {
    nextMoveAvailable = true;
  }
  */
  
  // process one by one ... 
  processLines();

  // println(diff+" available: "+nextMoveAvailable+"   index: "+processLineIndex+"/"+arrLines.size());
    
  // println("gameStateRunning: -"+gameStateRunning+"-"+gameStateSubRunning+"-");  
  
// update ingame
 if (gameStateRunning.equals("osc"))  {
   updateOSC();
   
   // stupid things ...
   
    
   textSize(14);
   
   // special gui
   String str = "# OSC (UDP) \n";

       str += "# oscport 127.0.0.1 9007\n";
       str += "#  the osc port in is 9007. send your data here. \n";
       str += "\n";
       str += "  # recording\n";
       str += "  recording: movetoprocess and text will be recorded and executed\n";
       str += "  # commands\n";
       str += "  /clear              simulation only\n";
       str += "  /penup           \n";
       str += "  /pendown\n";
       str += "  /movetox 0.0-1.0    xpos\n";
       str += "  /movetoy 0.0-1.0    ypos\n";
       str += "  /movetoprocess      goto xpos/ypos\n";
       str += "  /square             creates a 1x1 square\n";
       str += "  /cross              creates a 1x1 cross\n";
       str += "  /text BLA           draw text BLA at xpos/ypos\n";

   
   text(str,width/2,70);
   

   // move there
   if (mouseclicked) {
     float gfx = (mouseX-offsetPaperX)/guiPaperScale;
     float gfy = (mouseY-offsetPaperY)/guiPaperScale;
     oscX = gfx;
     oscY = gfy;
     lineTo(gfx,gfy);
   }
   
 }
 
 // add gui for not osc
 // controls
 if (!gameStateRunning.equals("osc"))  {
   
   textSize(18);
   text("USE wasd + e FOR CONTROL THIS GAMES\n",50,700);
   
   
 }
    
  // update ingame
  if (gameStateRunning.equals("title")) updateGameTitle();
  
  // update ingame
  if (gameStateRunning.equals("menu")) updateGameMenu();

  // update ingame
  if (gameStateRunning.equals("ingame")) updateInGame();

    // collection ... 

    // update ingame madpen
    if (gameStateRunning.equals("ingamemadpen")) updateInGameMadPen();
  
    // update ingame tron / baracade
    if (gameStateRunning.equals("ingametron")) updateInGameTron();

    // update mines
    if (gameStateRunning.equals("ingamemines")) updateInGameMines();

    // update mines
    if (gameStateRunning.equals("ingamelinejewels")) updateInGameLineJewels();

    // update mines
    if (gameStateRunning.equals("ingametennisfortwo")) updateInGameTennisForTwo();

    // update role play
    if (gameStateRunning.equals("ingamerp")) updateInGameRolePlay();
    

    // update ingame tron / baracade
    if (gameStateRunning.equals("ingamejumpandrun")) updateInGameJumpAndRun();

  // update ingame
  if (gameStateRunning=="won") updateGameWon();

  // update ingame
  if (gameStateRunning=="gameover") updateGameOver();


}

// -----------------------
// DRAW
// -----------------------

boolean plotterNothingToDo = false;
int processLineIndex = -1;

int processLineStep = 0;

public void processLines() {
  
   // if (moveStatus==1) {
   
   if (nextMoveAvailable) {
     
     // if (nextMoveAvailable || processLineIndex==-1) {
       
       plotterNothingToDo = false;
  
       if (processLineIndex<arrLines.size()) {
          if (processLineIndex==-1) processLineIndex = 0;
          
          
                Line li = (Line) arrLines.elementAt(processLineIndex);    
      
                // line to second 
                if (processLineIndex<arrLines.size()) {
      
                  li.plotted = true;
                
                  if (li.lineType==0) {
                    plotterLineTo(li.p2.x,li.p2.y);
                  }            
                  if (li.lineType==1) {
                    plotterMoveTo(li.p2.x,li.p2.y);
                  }            
                  if (li.lineType==2) {
                    plotterMoveFreeTo(li.p2.x,li.p2.y);
                  }            
                  
                processLineIndex++; 
                
            }
       }
        else {
              plotterNothingToDo = true;
            }  
          
//    }
  
   }

}



// --------------------
// draw
// --------------------
// > will be added
// to lowlevel addLine()
//

// move line
public void addMoveLine( float x, float y, float xx, float yy ) {
  Line lxz = addLine( new PVector(x,y),new PVector(xx,yy));
  lxz.colR = 0;
  lxz.colG = 255;
  lxz.colB = 0;
  lxz.colA = 64;
  lxz.lineType = 1; // move only 
}

public void addMoveFreeLine( float x, float y, float xx, float yy ) {
  Line lxz = addLine( new PVector(x,y),new PVector(xx,yy));
  lxz.colR = 0;
  lxz.colG = 255;
  lxz.colB = 255;
  lxz.colA = 128;
  lxz.lineType = 2; // free move only  - not clear if pen / up down actual !!! 
}


public void drawLine( float x, float y, float xx, float yy ) {
   println("drawLine() "+x+"/"+y+"   "+xx+"/"+yy);
   moveTo(x,y);
   lineTo(xx,yy);
}


public void drawXAt( float xx, float yy ) {
  addShapeXAt(  xx,  yy);
}

/*
    higher case: B chars
    lower case specials chars 
    f: field
    b: bug
    
*/

public void drawTextAt( String text, float x, float y) {
   
  drawTextAt(  text,  x,  y,  1.0f );
  
}

public void drawTextAt( String text, float x, float y, float sizex ) {
 
    // so much easiert than in assembly .-)
    for (int z=0;z<text.length();z++) {
       String c = ""+text.charAt(z);

      shapeScale = sizex;
      drawGraphicAt( c, x, y );       
       x += 1.0f*shapeScale*sizex;
    }
    
    // auto go back to 1.0f
    // shapeScale = 1.0f;
    
}

public void drawLayerAt( String name, float xx, float yy) {
    drawGraphicAt(  name,  xx,  yy);
}

public void drawGraphicAt( String name, float xx, float yy) {
  drawGraphicAt(  name,  xx,  yy, true);
}

public void drawGraphicAt( String name, float xx, float yy, boolean moveBack) {
  
  if (name.charAt(0)!=32) {
  // if (name!=" ") {
    
    tmpShape = vectorgraphics.getChild(name);  
      // addPShapeAt(tmpShape,pen.x,pen.y);
      // println("fire");
      //addLine( new PVector(0,0), pen );
      if (tmpShape!=null) {
          addPShapeAt(tmpShape,xx,yy, moveBack);
      } else {
        
         println("drawLayerAt() not found: "+name); 
         tmpShape = vectorgraphics.getChild("?");  
         if (tmpShape!=null) addPShapeAt(tmpShape,xx,yy, moveBack);
     
      }
  }
}

// from vectorgraphics
public void drawVectorGraphicsLayerAt( String name, float x, float y ) {
    
}


// ----------------------
// simulated lines
// ----------------------
Vector<Line> arrLines= new Vector<Line>();

public void resetLines() {
  
  arrLines.clear(); 
  
  processLineIndex = -1;
  processLineStep = 0;

}

// -------------------------------
// process != add !!
// -------------------------------

public void addLine( Line li ) {
    arrLines.add(li);
    
    // println("   "+(arrLines.size()-1)+": addLine("+li.p1+"/"+li.p2+") "+((int)li.p1.x)+"/"+((int)li.p1.y));
    // process line !!!!
    // move to first
    
    // delay(100);
}


public Line addLine(PVector p1, PVector p2) {
  Line lv = new Line();
  lv.p1 = new PVector(p1.x,p1.y);
  lv.p2 = new PVector(p2.x,p2.y);
  addLine(lv);
  return lv;
}

public Line addLine(float x, float y, float xx, float yy) {
  Line lv = new Line();
  lv.p1 = new PVector(x,y);
  lv.p2 = new PVector(xx,yy);
  addLine(lv);
  return lv;
}

public void addShapeXAt( float xx, float yy) {
  
  Line lv = new Line();
  lv.p1 = new PVector(xx-0.25f,yy-0.25f);
  lv.p2 = new PVector(xx+0.25f,yy+0.25f);  
  moveTo(lv.p1.x,lv.p1.y);
  lineTo(lv.p2.x,lv.p2.y);
  
  // delay(1000);
  
  Line lx = new Line();
  lx.p1 = new PVector(xx-0.25,yy+0.25);
  lx.p2 = new PVector(xx+0.25,yy-0.25);  
  moveTo(lx.p1.x,lx.p1.y);
  lineTo(lx.p2.x,lx.p2.y);
  
  // delay(1000);

  moveTo( xx, yy );
}



// addShapeAt > addPShapeAt

float shapeScale = 1.0f;

public void addPShapeAt( PShape psenough, float x, float y ) { // .-)
  
  addPShapeAt( psenough,  x,  y, true );
  
}

public void addPShapeAt( PShape psenough, float x, float y, boolean moveBack ) { // .-)

  for (int zz=0;zz<psenough.getChildCount();zz++) {
   PShape childchild = psenough.getChild(zz);
   
   // println( " "+zz+". kind: "+childchild.getKind());
   // println( " "+zz+". children "+childchild.getChildCount());
   if (childchild.getKind()==4) { // Lines
   
     // SVG - lines ins params !!!! not correct in the processing datastructure
     float[] arrF = childchild.getParams();
       if (arrF!=null) {
         if (arrF.length>3) {
              // println( "   "+zz+". element "+r+".param:  "+arrF[r]);
              Line lv = new Line();
              lv.p1 = new PVector();
              lv.p1.x = arrF[0]*importScale*shapeScale+x;
              lv.p1.y = arrF[1]*importScale*shapeScale+y;
              
              moveTo(lv.p1.x,lv.p1.y);
              
              lv.p2 = new PVector();
              lv.p2.x = arrF[2]*importScale*shapeScale+x;
              lv.p2.y = arrF[3]*importScale*shapeScale+y;
              
              lineTo(lv.p2.x,lv.p2.y);  
              
              // println(zz+": addPShapeAt("+lv.p1+"/"+lv.p2+")");
       }
     }
     
     
   } else {
       println("Sorry only supporting LINES");
       // look into processing source code
       // PConstants
       /* 
           
             int POINT           = 2;   // primitive
              int POINTS          = 3;   // vertices
            
              int LINE            = 4;   // primitive
              int LINES           = 5;   // beginShape(), createShape()
              int LINE_STRIP      = 50;  // beginShape()
              int LINE_LOOP       = 51;
            
              int TRIANGLE        = 8;   // primitive
              int TRIANGLES       = 9;   // vertices
              int TRIANGLE_STRIP  = 10;  // vertices
              int TRIANGLE_FAN    = 11;  // vertices
            
              int QUAD            = 16;  // primitive
              int QUADS           = 17;  // vertices
              int QUAD_STRIP      = 18;  // vertices
            
              int POLYGON         = 20;  // in the end, probably cannot
              int PATH            = 21;  // separate these two
            
              int RECT            = 30;  // primitive
              int ELLIPSE         = 31;  // primitive
              int ARC             = 32;  // primitive
            
              int SPHERE          = 40;  // primitive
              int BOX             = 41;  // primitive
                */
   }
  }

  
  shapeScale = 1.0f; 

  // todo: problem ... 
  if (moveBack) moveTo(x,y);

}


public void drawGUILine(Line li) {

    int colorA = li.colA;
    
    if (!li.plotted) {
      colorA = 32;
      if (realTimeSimulation) colorA = 0;
    }
    if (colorA<20) colorA = 35;
    
    stroke(li.colR,li.colG,li.colB,colorA);

    // free pen up/down
    if (li.lineType==2) {
      if (penState==1) stroke(0,0,0,0);
      if (penState==0) stroke(0,0,0,255);
    }
    
    boolean showF = true;
    
    if (li.lineType==1) {
       if (!showPenUpLines) showF = false;
    }
    
    if (showF)    
    line(offsetPaperX+li.p1.x*guiPaperScale,offsetPaperY+li.p1.y*guiPaperScale,
         offsetPaperX+li.p2.x*guiPaperScale,offsetPaperY+li.p2.y*guiPaperScale);

}

public class Line {
 
  // Types?
  
  int lineType = 0; // 0: normal line  1: just move ..  2: life - pen can be up or down
  
  boolean plotted = false;
  
  int colR = 0;
  int colG = 0;
  int colB = 0;
  int colA = 128;
  
  
  PVector p1 = new PVector();
  PVector p2 = new PVector();
    
}


// ----------------------
// SOUND
// ----------------------

  public void playPing() {
   
     if (soundOn) 
      gamePing.play();
      
  }
 
// -----------------------
// PLOTTER HARDWARE
// PEN LINE MOVE
// -----------------------
public void connectToAxiDraw() {
 
   doSerialConnect = false;

    scanSerial();

    // now online ... 
    if (SerialOnline)
    {    
      
      playPing();
      
      println("# ---------------------------------------------");
      println("# INIT: DoSerialConnect. Send SetUp");
      println("# ---------------------------------------------");
      myPort.write("EM,2\r");  //Configure both steppers to 1/8 step mode

      // Configure brush lift servo endpoints and speed
      myPort.write("SC,4," + str(ServoPaint) + "\r");  // Brush DOWN position, for painting
      myPort.write("SC,5," + str(ServoUp) + "\r");  // Brush UP position 

      myPort.write("SC,10,1024\r"); // Set brush raising and lowering speed.

      // delay(1000);

      println("# ---------");
      println("# Init DONE");
      println("# ---------");
      
      playPing();
      
      plotterConnected = true;
      
      initGame();
      
    } else
    { 
      println("\n\nNow entering offline simulation mode.\n\n");
      // audio tune .. 
      plotterConnected = false;
      initGame();
      
    }
}
 
 
// ---------------
// pen
// ---------------
int penState = 1; // down ...

int upDownDelay = 300;

public void penUp() {
    offsetDelay = 0;

     
     if (penState==1)   {
      if (myPort!=null)
        myPort.write("SP,0\r");
       // check if up!
       offsetDelay = 0;
       if (waitForThePlotter) {
        offsetDelay = upDownDelay;
        delay(upDownDelay); // todo: dirty solution!!! > draw()
      }
   }
   penState = 0;
    
   // println(penState+" penState.up");
}
public void penDown() {
   offsetDelay = 0;

   

   // check if down!
   if (penState==0) {
     if (myPort!=null)
      myPort.write("SP,1\r");

     if (waitForThePlotter) {
      delay(upDownDelay+100);
       offsetDelay = upDownDelay+100;
     }
   }
   penState = 1;
   // println(penState+" penState.down");

}



// ----------------------------
// plotter record
// ----------------------------
// record
// 



public void lineTo(float x, float y) {
    // println("lineTo() "+x+"/"+y);

   addLine(pen.x, pen.y, x, y); 
   pen.x = x;
   pen.y = y;
   
}
 
public void moveTo(float x, float y) {
    
    addMoveLine( pen.x, pen.y, x, y );
    // println("moveTo() "+x+"/"+y);
    pen.x = x;
    pen.y = y;
    
}

// line without clear state up or down!!!
// jump and run
public void moveFreeTo(float x, float y) {
    
    addMoveFreeLine( pen.x, pen.y, x, y );
    // println("moveTo() "+x+"/"+y);
    pen.x = x;
    pen.y = y;
    
}

  

// ----------------------------
// plotter primitives
// ----------------------------
// really plot it
// 


public void plotterLineTo(float x, float y) {
    // println("lineTo() "+x+"/"+y);
  
  penDown();
  
  float pmx = (x+offsetX)*plotterScale;      
  float pmy = (y+offsetY)*plotterScale;           
  
  if ((pmx!=x)||(pmy!=y)) {
    MoveToXY( (int) pmx, (int) pmy);  
  }
  // delay(100);

  plotterPen.x = x;
  plotterPen.y = y;
}  
  
public void plotterMoveTo(float x, float y) {
        
    // println("moveTo() "+x+"/"+y);
    
    penUp();
    
    float pmx = (x+offsetX)*plotterScale;      
    float pmy = (y+offsetY)*plotterScale;           
 
   if ((pmx!=x)||(pmy!=y)) {
      MoveToXY( (int) pmx, (int) pmy);    
   }
    // delay(100);
       
    plotterPen.x = x;
    plotterPen.y = y;
} 

public void plotterMoveFreeTo(float x, float y) {
        
    // println("moveTo() "+x+"/"+y);
        
    float pmx = (x+offsetX)*plotterScale;      
    float pmy = (y+offsetY)*plotterScale;           
    
    if ((pmx!=x)||(pmy!=y)) {
      MoveToXY( (int) pmx, (int) pmy);    
    }
    // delay(100);
       
    plotterPen.x = x;
    plotterPen.y = y;
} 



public void moveToNullPoint() {
  moveTo(0,0);
} 

// ----------------------------
// Plotter ready? 
// ----------------------------


public boolean plotterReadForInput() {
 
  return plotterNothingToDo;
}
  
// ----------------------------
// QUIT
// ----------------------------

// go back to 0 ...
// ----------------------------
// back to zero 
// back to garage
// ----------------------------
public void backToGarage() {
 
  if (soundOn) {
    playPing();
  }

  penUp();
  
  MoveToXY( 0, 0 );
  

}

// ------------------------
// key events
// ------------------------

void keyReleased()
{

  // pressedKeyChar = "";
  
  if (key == CODED) {

    if (keyCode == UP) keyup = false; 
    if (keyCode == DOWN) keydown = false; 
    if (keyCode == LEFT) keyleft = false; 
    if (keyCode == RIGHT) keyright = false; 

  } else {
    key = Character.toLowerCase(key);
    pressedKeyChar = ""+key;
  }

}

String pressedKeyChar = "";

void keyPressed()
{

  //String keyString = new String({key});

  //  keyString.toLowerCase();
  //key = keyString.charAt(0);

  pressedKeyChar = "";

  if (key == CODED) {

    // Arrow keys are used for nudging, with or without shift key.

    if (keyCode == UP) 
    {
      keyup = true;
    }
    if (keyCode == DOWN)
    { 
      keydown = true;
    }
    if (keyCode == LEFT) keyleft = true; 
    if (keyCode == RIGHT) keyright = true; 
    if (keyCode == SHIFT) shiftKeyDown = true;
  } else
  {
    key = Character.toLowerCase(key);
    // println("Key pressed " + key); 

    // pressedKeyChar = ""+key; 

    // ----------------
    // check keys 
    // ----------------

    if ( key == 'b')   // Toggle brush up or brush down with 'b' key
    {
      if (BrushDown) {
        println("RaiseBrush");
        raiseBrush();
      } else {
        println("LowerBrush");
        lowerBrush();
      }
    }

    if ( key == 'z')  // Zero motor coordinates
      zero();

    if ( key == ' ')  //Space bar: Pause
      pause();

    if ( key == 'q')  // Move home (0,0)
    {
      raiseBrush();
      MoveToXY(0, 0);
    }


    if ( key == 't')  // Disable motors, to manually move carriage.  
      MotorsOff();

    if ( key == '1')
      MotorSpeed = 500;  
    if ( key == '2')
      MotorSpeed = 1000;        
    if ( key == '3')
      MotorSpeed = 1500;        
    if ( key == '4')
      MotorSpeed = 2000;        
    if ( key == '5')
      MotorSpeed = 2500;        
    if ( key == '6')
      MotorSpeed = 2900;        
    if ( key == '7')
      MotorSpeed = 3200;        
    if ( key == '8')
      MotorSpeed = 3500;        
    if ( key == '9')
      MotorSpeed = 4000;
  }
}

// ------------------------
// exit
// ------------------------
public void exit() {
  
  print("exit()");

  backToGarage();

  super.exit();
}

// --------------------------------
// IMPORTANT SETTINGS 
// --------------------------------
public void initHardwareSettings() {
 
    MotorMinX = 0;
  MotorMinY = 0;
  MotorMaxX = int(floor(float(MousePaperRight - MousePaperLeft) * MotorStepsPerPixel)) ;
  MotorMaxY = int(floor(float(MousePaperBottom - MousePaperTop) * MotorStepsPerPixel)) ;

  lastPosition = -1;

  //  if (debugMode) {
  //    println("MotorMinX: " + MotorMinX + "  MotorMinY: " + MotorMinY);
  //    println("MotorMaxX: " + MotorMaxX + "  MotorMaxY: " + MotorMaxY);
  //  }


  ServoUp = 7500 + 175 * ServoUpPct;    // Brush UP position, native units
  ServoPaint = 7500 + 175 * ServoPaintPct;   // Brush DOWN position, native units. 

  MotorX = 0;
  MotorY = 0; 

  raiseBrushStatus = -1;
  lowerBrushStatus = -1; 
  moveStatus = -1;
  MoveDestX = -1;
  MoveDestY = -1;


  Paused = false;
  BrushDownAtPause = false;

  zero();

  
}


// User Settings: 
float MotorSpeed = 4000; // 2500.0;  // Steps per second, 1500 default

// PEN UP AND DOWN!
int ServoUpPct =   80;    // Brush UP position, %  (higher number lifts higher). 
int ServoPaintPct = 10;    // Brush DOWN position, %  (higher number lifts higher). 

boolean reverseMotorX = false;
boolean reverseMotorY = false;

int delayAfterRaisingBrush = 300; //ms
int delayAfterLoweringBrush = 300; //ms // grafitiy?

int minDist = 4; // Minimum drag distance to record

//boolean debugMode = true;
boolean debugMode = false;


float MotorStepsPerPixel = 16.05;// Good for 1/8 steps-- standard behavior.
float PixelsPerInch = 63.3; 

// Hardware resolution: 1016 steps per inch @ 50% max resolution
// Horizontal extent in this window frame is 740 px.
// 1016 steps per inch * (11.69 inches (i.e., A4 length)) per 740 px gives 16.05 motor steps per pixel.
// Vertical travel for 8.5 inches should be  (8.5 inches * 1016 steps/inch) / (16.05 steps/px) = 538 px.
// PixelsPerInch is given by (1016 steps/inch) / (16.05 steps/px).


// Positions of screen items

// -----------------------------
// papersize - virtual!!! 
// -----------------------------

int MousePaperLeft =  0;
int MousePaperRight =  1400;
int MousePaperTop =  0;
int MousePaperBottom =  1200;

int yBrushRestPositionPixels = 6;


int ServoUp;    // Brush UP position, native units
int ServoPaint;    // Brush DOWN position, native units. 

int MotorMinX;
int MotorMinY;
int MotorMaxX;
int MotorMaxY;

color Black = color(25, 25, 25);  // BLACK
color PenColor = Black;

boolean firstPath;
boolean doSerialConnect = true;
boolean SerialOnline;
Serial myPort;  // Create object from Serial class
int val;        // Data received from the serial port

boolean BrushDown;
boolean BrushDownAtPause;
boolean DrawingPath = false;

int xLocAtPause;
int yLocAtPause;

int MotorX;  // Position of X motor
int MotorY;  // Position of Y motor
int MotorLocatorX;  // Position of motor locator
int MotorLocatorY; 
int lastPosition; // Record last encoded position for drawing

int selectedColor;
int selectedWater;
int highlightedWater;
int highlightedColor; 


boolean recordingGesture;
boolean forceRedraw;
boolean shiftKeyDown;
boolean keyup = false;
boolean keyright = false;
boolean keyleft = false;
boolean keydown = false;
boolean hKeyDown = false;
int lastButtonUpdateX = 0;
int lastButtonUpdateY = 0;

boolean lastBrushDown_DrawingPath;
int lastX_DrawingPath;
int lastY_DrawingPath;


int nextMoveTime = 0;          //Time we are allowed to begin the next movement (i.e., when the current move will be complete).
int SubsequentWaitTime = -1;    //How long the following movement will take.
int UIMessageExpire;
int raiseBrushStatus;
int lowerBrushStatus;
int moveStatus;
int MoveDestX;
int MoveDestY; 
int PaintDest; 

boolean Paused;