> show canvas only <


/* built with Studio Sketchpad: 
 *   https://sketchpad.cc
 * 
 * observe the evolution of this sketch: 
 *   https://studio.sketchpad.cc/sp/pad/view/ro.KPy7eW-ovzS/rev.8622
 * 
 * authors: 
 *   Dillon Hayford
 *   stefkeB
 *   Michael Schroder
 *   Thomas Van Bouwel
 *   Jose Hernandez
 *   
 *   guobing
 *   Alex Graveley
 *   
 *   Mark Webster
 *   
 *   Steren
 *   open architecture open design
 *   Ali Jaya Meilio
 *   
 *   

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



//Eventua
//Written by Michael Schroder

public static final string MAP1 = "/static/uploaded_resources/p.218/map1.png";
/* @pjs preload="/static/uploaded_resources/p.218/map1.png"; */
 
public static final int TILE_NONE = 0;
public static final int TILE_SOLID = 1;
 
public static final double TILE_SIZE = .05;
 
public class Tile{
    public int type;
    
    public Tile(){
        type=TILE_NONE;
    }
    
    public void draw(int x, int y){
        if(type == TILE_SOLID){
            stroke(255, 255, 255, 100);
            fill(0, 120, 0, 255);
            strokeWeight(6);
            rect(x, y, TILE_SIZE*height, TILE_SIZE*height);
        }
    }
}
 
public class Level{
    private Tile tiles[];
    private double w;
    private double h;
    private double offx;
    private double offy;
    
    public Level(int W, int H){
        w=W;
        h=H;
        offx=0;
        offy=0;
        tiles = new Tile[w*h];
        for(int i=0; i<w*h; i++){
            tiles[i] = new Tile();
        }
    }
    
    public Tile getTile(int index){
        if(index < w*h)
            return tiles[index];
        Tile temp = new Tile();
        return temp;
    }
    public Tile getTile(double x, double y){
        y = (int)((double)(y)/(TILE_SIZE)+.001);
        x = (int)((double)(x)/(TILE_SIZE)+.001);
        if(x < w && x >= 0 && y < h && y >= 0){
            return tiles[y*w+x];
        }
        Tile temp = new Tile();
        return temp;
    }
    
    public bool setTile(int index, Tile tile){
        if(index < w*h && index >= 0){
            tiles[index] = tile;
            return true;
        }
        return false;
    }
    public bool setTile(int x, int y, Tile tile){
        if(x < w && y < h){
            tiles[(int)((double)(offy+y)/(TILE_SIZE))*w+(int)((double)(offx+x)/(TILE_SIZE))] = tile;
            return true;
        }
        return false;
    }
    
    public void Draw(){
        for(int x=offx; x<=w/h+offx+TILE_SIZE; x+=TILE_SIZE){
            for(int y=offy; y<1+offy+TILE_SIZE; y+=TILE_SIZE){
                //println(x);
                getTile(x,y).draw(((int)((double)(x)/(TILE_SIZE)+.001)*TILE_SIZE-offx)*height,((int)((double)(y)/(TILE_SIZE)+.001)*TILE_SIZE-offy)*height);
            }
        }
    }
    
    public double getOffsetX(){
        return offx;
    }
    public double getOffsetY(){
        return offy;
    }
    
    public void setOffsetX(double off){
        offx=off;
    }
    public void setOffsetY(double off){
        offy=off;
    }
    
    public double getWidth(){
        return w;
    }
    public double getHeight(){
        return h;
    }
    
    public boolean isOnScreen(double x, double y, double width, double height){
        if(x+width < offx || x > offx+w || y+height < offy || y > offy+h) return false;
        return true;
    }
}

public class IntVec{
    private int[] vals;
    int max, cur;
    
    public IntVec(){
        vals = new int[1];
        max=1;
        cur=0;
    }
    
    public void add(int val){
        if(cur==max){
            int[] nvals = new int[max*2];
            for(int i=0; i<max; i++){
                nvals[i] = vals[i];
            }
            max = max*2;
            vals = nvals;
        }
        vals[cur++] = val;
    }
    public void remove(int index){
        vals[index] = vals[--cur];
    }
    
    public int getVal(int index){
        return vals[index];
    }
    
    public int size(){
        return cur;
    }
}
 
int numSteps;
 
boolean Keys[256] = {0};
 
public class Mover{
    protected double x;
    protected double y;
    protected double w;
    protected double h;
    protected double xspeed;
    protected double yspeed;
    protected double friction;
    protected double gravity;
    protected boolean onGround;
    protected boolean moverCollisions;
    protected boolean levelCollisions;
    
    public Mover(){
        x=.4;
        y=.5;
        w=.04;
        h=.05;
        xspeed=0;
        yspeed=0;
        friction = 1;
        gravity = 0;
        onGround=false;
        moverCollisions=false;
        levelCollisions=false;
    }
    
    protected void checkLevelCollisions(Level l){
        onGround=false;
        //Up&Down Collision Checking
        if(yspeed > 0 && (l.getTile(x+w/2,y+h).type==TILE_SOLID || (l.getTile(x+w*.01,y+h).type==TILE_SOLID && l.getTile(x,y+h/2).type!=TILE_SOLID) || (l.getTile(x+w*.99,y+h).type==TILE_SOLID && l.getTile(x+w,y+h/2).type!=TILE_SOLID))){
            //println(""+(l.getTile(x+w/2,y+h-.0001).type==TILE_SOLID));
            yspeed=.0001;
            y=(double)((int)((double)(y+h)/(TILE_SIZE)+.001))*TILE_SIZE-h;
            //println(""+(l.getTile(x+w/2,y+h-.0001).type==TILE_SOLID));
            onGround=true;
        }
        if(yspeed < 0 && l.getTile(x+w/2,y).type==TILE_SOLID){
            yspeed=0;
            y+=((TILE_SIZE*height)-(y)*height%(TILE_SIZE*height)+.001)/height;
        }
        
        //Left&Right Collision
        if(xspeed < 0 && l.getTile(x,y+h/2).type==TILE_SOLID){
            xspeed=0;
            x+=((TILE_SIZE*height)-(x-w*.01)*height%(TILE_SIZE*height)+.001)/height;
        }
        if(xspeed > 0 && l.getTile(x+w,y+h/2).type==TILE_SOLID){
            xspeed=0;
            x-=((x+w*1.01)*height%(TILE_SIZE*height)-.001)/height;
        }
    }
    
    protected void collisionAction(MoverHandler m, IntVec movers){
        //stuff
    }
    
    protected void checkMoverCollisions(Level l, MoverHandler m){
        IntVec tiles = getContainingTiles(l);
        IntVec foundMovers = new IntVec();
        boolean foundM[1000] = {false};
        for(int i=0; i<tiles.size(); i++){
            IntVec moversOnTile = m.getOccupationTile(tiles.getVal(i));
            for(int j=0; j<moversOnTile.size(); j++){
                if(foundM[moversOnTile.getVal(j)] == false){
                    foundM[moversOnTile.getVal(j)] = true;
                    if(
    (x<m.getMover(moversOnTile.getVal(j)).x || x<m.getMover(moversOnTile.getVal(j)).x+m.getMover(moversOnTile.getVal(j)).w) &&
    (x+w>m.getMover(moversOnTile.getVal(j)).x || x+w>m.getMover(moversOnTile.getVal(j)).x+m.getMover(moversOnTile.getVal(j)).w) &&
    (y<m.getMover(moversOnTile.getVal(j)).y || y<m.getMover(moversOnTile.getVal(j)).y+m.getMover(moversOnTile.getVal(j)).h) &&
    (y+h>m.getMover(moversOnTile.getVal(j)).y || y+h>m.getMover(moversOnTile.getVal(j)).y+m.getMover(moversOnTile.getVal(j)).h)){
                        foundMovers.add(moversOnTile.getVal(j));
                        println(moversOnTile.getVal(j));
                    }
                }
            }
        }
        collisionAction(m, foundMovers);
    }
    
     protected void controller(Level l){
        //for other objects
    }
    
    public void act(Level l, MoverHandler m){
        //println(""+(y)+" "+((int)((double)(y+h)/(TILE_SIZE)+.001)*TILE_SIZE-h))
        yspeed+=gravity;
        xspeed*=friction;
        
        numSteps=(int)(abs(max(xspeed,yspeed))/(TILE_SIZE/2)+1);
        //numSteps=1;
        int xorig=x;
        int yorig=y;
        for(int i=numSteps; i>0; i--){
            x=xorig+(numSteps-i+1)*xspeed/numSteps;
            y=yorig+(numSteps-i+1)*yspeed/numSteps;
        
            if(levelCollisions) checkLevelCollisions(l);
            if(moverCollisions) checkMoverCollisions(l, m);
        }
        
        controller(l, m);
        
    }
    
    public IntVec getContainingTiles(Level l){
        IntVec tiles = new IntVec();
        for(int i=(int)(x/TILE_SIZE); i<(int)((x+w)/TILE_SIZE+1); i++){
            for(int j=(int)(y/TILE_SIZE); j<(int)((y+h)/TILE_SIZE+1); j++){
                if(i>0 && i<l.getWidth() && j>0 && j<l.getHeight())
                    tiles.add(i+j*l.getWidth());
            }
        }
        return tiles;
    }
    
    public void draw(Level l){
        if(l.isOnScreen(x,y,width,height)){
            stroke(255, 255, 255, 100);
            fill(255, 255, 255, 255);
            strokeWeight(12);
            rect((x-l.getOffsetX())*height, (y-l.getOffsetY())*height, w*height, h*height);
        }
    }
}

public class Bullet extends Mover{
    public Bullet(){
        super();
        w=.01;
        h=.01;
        moverCollisions=true;
        levelCollisions=false;
    }
    
    protected void collisionAction(MoverHandler m, IntVec movers){
        for(int i=0; i<movers.size(); i++){
            m.removeMover(movers.getVal(i));
        }
    }
}

public class PhysicsMover extends Mover{
    public PhysicsMover(){
        super();
        friction = .8;
        gravity = .0025;
    }
}
 
public class Player extends PhysicsMover{
    
    public Player(){
        super();
        levelCollisions=true;
    }
    
    protected void controller(Level l, MoverHandler m){
        if(onGround){
            if(Keys[UP]){
                yspeed=-.03;
            }
        }
        
        //if(Keys[DOWN]) yspeed=.03;
        
        //Left&Right Acceleration
        if(Keys[RIGHT] && xspeed < .02){
            xspeed+=.005;
            if(xspeed > .02) xspeed = .02
        }
        if(Keys[LEFT] && xspeed > -.02){
            xspeed-=.005;
            if(xspeed < -.02) xspeed = -.02
        }
        
        //Left&Right Stage Scrolling
        if(x-l.getOffsetX() > width/height*.6)
            l.setOffsetX(l.getOffsetX()-.5*(l.getOffsetX()-(x-width/height*.6)));
        if(x-l.getOffsetX() < width/height*.4){
            l.setOffsetX(l.getOffsetX()-.5*(l.getOffsetX()-(x-width/height*.4)));
            if(l.getOffsetX() < 0) l.setOffsetX(0);
        }
        
        //Up&Down Stage Scrolling
        if(y-l.getOffsetY() > .6)
            l.setOffsetY(l.getOffsetY()-.5*(l.getOffsetY()-(y-.6)));
        if(y-l.getOffsetY() < .6){
            l.setOffsetY(l.getOffsetY()-.5*(l.getOffsetY()-(y-.6)));
            if(l.getOffsetY() < 0) l.setOffsetY(0);
        }
        
        if(Keys[DOWN]){
            Bullet b = new Bullet();
            b.x = x+w*2;
            b.y = y+h/2;
            //b.xspeed = .05;
            m.addMover(b);
        }
        
        if(Keys[CONTROL]){
            Box b = new Box();
            b.x = x;
            b.y = y;
            m.addMover(b);
        }
    }
}
 
public class Box extends PhysicsMover{
    
    public Box(){
        super();
        levelCollisions=true;
    }
    
     protected void controller(Level l){
        /*if(Keys[UP] && xspeed > -.02){
            xspeed-=.005;
            if(xspeed < -.02) xspeed = -.02
        }*/
    }
}

public class MoverHandler{
    private Mover[] movers;
    private IntVec[] occupied;
    private int numMovers;
    
    public MoverHandler(){
        movers = new Mover[1000];
        numMovers = 0;
    }
    
    public void updateOccupationMap(Level l){
        occupied = new IntVec[l.getWidth()*l.getHeight()];
        for(int i=0; i<l.getWidth()*l.getHeight(); i++){
            occupied[i] = new IntVec();
        }
        
        for(int i=0; i<numMovers; i++){
            IntVec tempvec = movers[i].getContainingTiles(l);
            for(int j=0; j<tempvec.size(); j++){
                occupied[tempvec.getVal(j)].add(i);
            }
        }
    }
    
    public IntVec getOccupationTile(int index){
        return occupied[index];
    }
    
    public void addMover(Mover m){
        movers[numMovers++] = m;
    }
    public void removeMover(int index){
        movers[index] = movers[--numMovers];
    }
    
    public int getNumMovers(){
        return numMovers;
    }
    
    public Mover getMover(int index){
        return movers[index];
    }
    
    public void actMovers(Level l){
        updateOccupationMap(l);
        for(int i=0; i<numMovers; i++){
            movers[i].act(l,this);
        }
    }
    
    public void drawMovers(Level l){
        for(int i=0; i<numMovers; i++){
            movers[i].draw(l);
        }
    }
}


Level l;
boolean Keys[256] = {0};
MoverHandler m;


void setup(){
    size(600, 400);
    
    m = new MoverHandler();
    m.addMover(new Player());
    
    PImage img = loadImage(MAP1);
    img.loadPixels();
    
    l = new Level(img.width, img.height);
    
    color cblack = color(0, 0, 0);
    for(int j=0; j<img.height; j++){
        for(int i=0; i<img.width; i++){
            l.getTile(i*TILE_SIZE,j*TILE_SIZE).type=(img.get(i,j) == cblack);
        }
    }
    background(0,70,70);    
    
    smooth();
    
    frameRate(30);
} 
 
void draw() {  
 
    background(0,70,70);
    
    m.actMovers(l);
    l.Draw();
    m.drawMovers(l);
 
}
 
void keyPressed() {
    Keys[keyCode] = true;
}
void keyReleased() {
    Keys[keyCode] = false;
}
 
void mouseDragged() {
  l.getTile((double)mouseX/(double)height+l.getOffsetX(),(double)mouseY/(double)height+l.getOffsetY()).type=TILE_SOLID;
  if(mouseButton == RIGHT){
      l.getTile((double)mouseX/(double)height+l.getOffsetX(),(double)mouseY/(double)height+l.getOffsetY()).type=TILE_NONE;
      }
}