> show canvas only <


/* built with Studio Sketchpad: 
 *   https://sketchpad.cc
 * 
 * observe the evolution of this sketch: 
 *   https://studio.sketchpad.cc/sp/pad/view/ro.LnB5YbzqOMx/rev.6934
 * 
 * authors: 
 *   Jared Counts
 *   
 *   

 * license (unless otherwise specified): 
 *   creative commons attribution-share alike 3.0 license.
 *   https://creativecommons.org/licenses/by-sa/3.0/ 
 */ 



/* Author's notes
    I need tabs!
*/
int tileWidth = 48;
boolean [][] map = new boolean[640/tileWidth][480/tileWidth];
Character player;
void setup () {  // this is run once. 
    size(640,480);
    player = new Character();
    genMap();
    for (int i = 0; i < keys.length; i++)
        keys[i] = false;
}   
void draw () {  // this is run repeatedly.  
    background(255);
    stroke(0);
    fill(0);
    // Render tiles
    for (int x = 0; x < map.length; x++) {
        for (int y = 0; y < map[x].length; y++) {
            if (map[x][y])
                rect(x*tileWidth, y*tileWidth, tileWidth, tileWidth);
        }   
    }
    // Player
    player.updatePosition();
    player.solveConstraints();
    player.draw();
}
class Character {
    float x, y;
    float lastX, lastY;
    float vX, vY;
    boolean touchingFloor = false;
    int charWidth = 30, charHeight = 30;
    Character () {
        x = width/2;
        y = 50;
    }
    void draw () {
        stroke(0,255,0);
        fill(255,0,0);
        rect(x,y, charWidth, charHeight);
        
        lastX = x;
        lastY = y;
    }
    void updatePosition () {
        // Gravity
        vY += 1.5;
        
        // Move the player if user is pressing keys
        if (checkKey(87) && touchingFloor) { // w
            vY -= 50;
            touchingFloor = false;
        }
 //       if (checkKey(83)) // s
 //           vY += 2.5;
        if (checkKey(65)) // a
            vX -= 2.5;
        else if (checkKey(68)) // d
            vX += 2.5;
        else
            vX *= 0.7;
        
        // Cap vX and vY
        vX = min(vX, 8);
        vX = max(vX, -8);
        
        vY = min(vY, 15);
        vY = max(vY, -15);
        
        x += vX;
        y += vY;
        vX *= 0.95;
        vY *= 0.95;
    }
    void solveConstraints () {
        touchingFloor = false;
        if (x < 1)
            x = 1;
        if (y < 1)
            y = 1;
        if (x > width-charWidth-1)
            x = width-charWidth-1;
        if (y > height-charWidth-1) {
            y = height-charWidth-1;
            touchingFloor = true;
        }
        
        //loat [] topLeft = correctCollision(x,y, x-lastX, y-lastY);
        //x = topLeft[0];
        //y = topLeft[1];
        //float [] topRight = correctCollision(x+charWidth,y, x-lastX, y-lastY);
        //x = topRight[0]-charWidth;
        //y = topRight[1];
        //float [] bottomRight = correctCollision(x+charWidth,y+charWidth, x-lastX, y-lastY);
        //x = bottomRight[0]-charWidth;
        //y = bottomRight[1]-charWidth;
        //float [] bottomLeft = correctCollision(x,y+charWidth, x-lastX, y-lastY);
        //x = bottomLeft[0];
        //y = bottomLeft[1]-charWidth;
        
        if (isTileSolid(x + charWidth, y)) // right
            x = tileToX(xToTile(x + charWidth)) - charWidth - 1;
        if (isTileSolid(x, y)) // left
            x = tileToX(xToTile(x)) + tileWidth + 1;
        
        if (isTileSolid(x, y)) // top left
             y = tileToY(yToTile(y)) + tileWidth + 1;
        if (isTileSolid(x + charWidth, y)) // top right
             y = tileToY(yToTile(y)) + tileWidth + 1;

        if (isTileSolid(x, y + charWidth)) { // bottom left
            y = tileToY(yToTile(y + charWidth)) - charWidth - 1;
            touchingFloor = true;    
        }
        if (isTileSolid(x + charWidth, y + charWidth)) { // bottom right
           y = tileToY(yToTile(y + charWidth)) - charWidth - 1;
            touchingFloor = true;
       }
   
    }
}

// Taken from http://community.topcoder.com/tc?module=Static&d1=tutorials&d2=geometry2
float [] solveCollision(float lineAX1, float lineAY1, float lineAX2, float lineAY2,
                        float lineBX1, float lineBY1, float lineBX2, float lineBY2) {
    float A1 = lineAY2 - lineAY1;
    float B1 = lineAX2 - lineAX1;
    float C1 = A1 * lineAX1 + B1 * lineAY1;
    
    float A2 = lineBY2 - lineBY1;
    float B2 = lineBX2 - lineBX1;
    float C2 = A1 * lineBX1 + B1 * lineBY1; 
    
    float det = A1 * B2 - A2 * B1;
    if (det == 0)
        return new float[] { lineAX1, lineAY1 };
    return new float[] { (B2 * C1 - B1 * C2) / det, (A1 * C2 - A2 * C1) / det };
}

// Line Segment Intersection
// http://wiki.processing.org/w/Line-Line_intersection
float [] segIntersection(float lineAX1, float lineAY1, float lineAX2, float lineAY2,
                         float lineBX1, float lineBY1, float lineBX2, float lineBY2) { 
  float bx = b.x - a.x; 
  float by = b.y - a.y; 
  float dx = d.x - c.x; 
  float dy = d.y - c.y;
  float b_dot_d_perp = bx * dy - by * dx;
  if(b_dot_d_perp == 0) {
    return null;
  }
  float cx = c.x - a.x;
  float cy = c.y - a.y;
  float t = (cx * dy - cy * dx) / b_dot_d_perp;
  if(t < 0 || t > 1) {
    return null;
  }
  float u = (cx * by - cy * bx) / b_dot_d_perp;
  if(u < 0 || u > 1) { 
    return null;
  }
  return new PVector(a.x+t*bx, a.y+t*by);
}
float [] correctCollision (float x, float y, float vX, float vY) {
    if (isTileSolid(x, y)) {
        float tileX;
        float tileY;
        if (vX < 0)
            tileX = tileToX(xToTile(x)) + tileWidth;
        else
            tileX = tileToX(xToTile(x));
        if (vY < 0)
            tileY = tileToY(yToTile(y)) + tileWidth;
        else
            tileY = tileToY(yToTile(y));
            
        stroke(255,0,0);
        noFill();
        strokeWeight(3);
        line(tileX, 0, tileX, height);
        line(0, tileY, width, tileY);
        strokeWeight(1);
            
        // Use y = mx + b to step back
        // first find m
        float m;
        if (vX != 0)
            m = vY / vX;
        else
            m = 0; // close enough!
        m *= -1;
        // find b
        float b = y - m * x;
        fill(255,0,0);
        ellipse(x,y,3,3);
        if (abs(x - tileX) < abs(y - tileY)) { // Use x if it's closer to the side than y
            x = tileX;
            y = m * x + b;
        } 
        else {
            y = tileY;
            x = (y - b) / m;   
        }
        fill(0,255,0);
        ellipse(x,y,4,4);
    }
    return new float [] {x,y};
}

/* Map methods */
// check to see if tile is solid
boolean isTileSolid (float x, float y) {
    // first we find its coordinate in the array
    int tileX = xToTile(x);
    int tileY = yToTile(y);
    
    // next we see if it is within the array's range
    if (tileX >= 0 && tileX < map.length && tileY >= 0 && tileY < map[0].length)
        return map[tileX][tileY]; // return whether it's solid or not

    return false;
}
int xToTile (float x) {
    return floor(x / (float)tileWidth);
}
int yToTile (float y) {
    return floor(y / (float)tileWidth);
}
int tileToX (int x) {
    return x * tileWidth;    
}
int tileToY (int y) {
    return y * tileWidth;    
}
// generate a random map
void genMap () {
    for (int x = 0; x < map.length; x++) {
        for (int y = map[x].length/2; y < map[x].length; y++) {
            if (random(1) > 0.5)
                map[x][y] = true;
            else
                map[x][y] = false;
        }
    }
    map[xToTile(player.x)][yToTile(player.y)] = false;
}

/* Multikey, inspired by http://wiki.processing.org/w/Multiple_key_presses */
boolean [] keys = new boolean [526];
boolean checkKey (int k) {
    return keys[k];
}
void keyPressed () {
//    println(keyCode + " was pressed");
    keys[keyCode] = true;
}
void keyReleased () {
    keys[keyCode] = false;
}