import ddf.minim.*;
/**
* Game 17 - "Raintrace"
* Make the spinners spin.
*
Go play more games at NMcCoy.net!
* 
This work by Nathan McCoy is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License.
*/
boolean[] keys = new boolean[256];
boolean title;
boolean paused;
boolean muted;
int level;
boolean levelflash;
int drawing_ms_last;
int physics_ms_last;
int CORE_FPS = 540;
int DRAWING_FPS = 60;
int PHYSICS_FPS = 180;
int DRAW_MS = 1000 / DRAWING_FPS;
int PHYS_MS = 1000 / PHYSICS_FPS;
Minim minim;
float GRAVITY = 0.03;
float MAX_SPEED = 30.0;
ArrayList drops;
ArrayList colliders;
Collider dragging;
PFont font;
float SPIN_DRAIN = 0.0002;
float SPIN_BOOST = 0.003;
float SPIN_PEAK = 0.2;
color[] COLORS = {color(140, 140, 140), color(220, 40, 40), color(40, 220, 40), color (40, 70, 250)};
class Drop
{
PVector pos;
PVector vel;
PVector lastdraw;
int col;
Drop(PVector p, PVector v, int c)
{
pos = new PVector(p.x, p.y);
lastdraw = new PVector(p.x, p.y);
vel = new PVector(v.x, v.y);
col = c;
}
void tick()
{
vel.y += GRAVITY;
vel.limit(MAX_SPEED);
pos.add(vel);
}
void draw()
{
strokeWeight(1);
stroke(COLORS[col]);
line(pos.x, pos.y, lastdraw.x, lastdraw.y);
lastdraw.x = pos.x;
lastdraw.y = pos.y;
}
}
class Collider
{
boolean movable = false;
PVector pos;
float radius = 20;
void touch(Drop d)
{
PVector away = PVector.sub(d.pos, pos);
away.normalize();
if(PVector.dot(away, d.vel) < 0)
{
away.mult(PVector.dot(away, d.vel) * 2);
d.vel.sub(away);
d.vel.mult(0.5);
}
}
Collider(PVector p)
{
pos = new PVector(p.x, p.y);
}
void draw()
{
fill(160, 160, 160);
strokeWeight(1);
noStroke();
ellipseMode(RADIUS);
ellipse(pos.x, pos.y, radius, radius);
}
void tick()
{
}
}
class Filter extends Collider
{
int col;
Filter(PVector p, int c)
{
super(p);
col = c;
}
void draw()
{
noFill();
strokeWeight(2);
stroke(COLORS[col]);
ellipseMode(RADIUS);
ellipse(pos.x, pos.y, radius, radius);
}
void touch(Drop d)
{
d.col = col;
}
}
class Angler extends Collider
{
float angle;
Angler(PVector p, float a)
{
super(p);
angle = a;
}
void draw()
{
noFill();
strokeWeight(2);
stroke(COLORS[0]);
ellipseMode(RADIUS);
ellipse(pos.x, pos.y, radius, radius);
line(pos.x, pos.y, pos.x+cos(angle)*radius, pos.y+sin(angle)*radius);
}
void touch(Drop d)
{
float m = d.vel.mag();
d.vel = new PVector(cos(angle)*m, sin(angle)*m);
}
}
class Spinner extends Collider
{
float angle;
float omega;
int col;
Spinner(PVector p, int c)
{
super(p);
col = c;
}
void touch(Drop d)
{
if(d.col == col)
{
omega += SPIN_BOOST;
d.col = 0;
}
}
void tick()
{
if(omega > SPIN_PEAK * 1.2) omega = SPIN_PEAK * 1.2;
omega -= SPIN_DRAIN;
if(omega < 0) omega = 0;
angle += omega;
if(angle > TWO_PI) angle -= TWO_PI;
}
void draw()
{
fill(COLORS[col]);
noStroke();
strokeWeight(2);
if(omega > SPIN_PEAK) stroke(255);
arc(pos.x, pos.y, radius, radius, angle, angle+HALF_PI);
arc(pos.x, pos.y, radius, radius, angle+PI, angle+HALF_PI+PI);
}
}
void setup()
{
size(320, 480);
frameRate(600);
minim = new Minim(this);
drops = new ArrayList();
colliders = new ArrayList();
loadLevel(1);
font = loadFont("PressStartK-8.vlw");
}
void loadLevel(int lev)
{
level = lev;
levelflash = true;
colliders.clear();
if(level == 1)
{
colliders.add(new Filter(new PVector(width/2, height / 4), 1));
Collider thingy = new Collider(new PVector(width/2, height / 2));
thingy.movable = true;
colliders.add(thingy);
colliders.add(new Spinner(new PVector(width/2, height - height / 4), 1));
}
if(level == 2)
{
colliders.add(new Filter(new PVector(width/3, height / 4), 2));
Collider thingy = new Angler(new PVector(width/2, height / 2), 0);
thingy.movable = true;
colliders.add(thingy);
colliders.add(new Spinner(new PVector(width*2/3, height - height / 4), 2));
}
if(level == 3)
{
colliders.add(new Filter(new PVector(width/2, height / 4), 3));
colliders.add(new Collider(new PVector(width/2, height / 2)));
Collider thingy = new Angler(new PVector(width*2/3, height / 2), 0);
thingy.movable = true;
colliders.add(thingy);
Collider thingy2 = new Angler(new PVector(width/3, height / 2), PI);
thingy2.movable = true;
colliders.add(thingy2);
colliders.add(new Spinner(new PVector(width/2, height - height / 4), 3));
}
if(level == 4)
{
colliders.add(new Filter(new PVector(width/2, height / 4), 1));
colliders.add(new Collider(new PVector(width/2, height / 2)));
colliders.add(new Angler(new PVector(width/4, height * 5 / 8), 0));
Collider thingy = new Angler(new PVector(width*2/3, height / 7), TWO_PI/3);
thingy.movable = true;
colliders.add(thingy);
Collider thingy2 = new Angler(new PVector(width*5/6, height / 7), TWO_PI/3);
thingy2.movable = true;
colliders.add(thingy2);
colliders.add(new Spinner(new PVector(width/2, height - height / 4), 1));
}
if(level == 5)
{
colliders.add(new Filter(new PVector(width/2, height / 6), 2));
colliders.add(new Filter(new PVector(width/2, height / 2), 3));
colliders.add(new Spinner(new PVector(width/4, height * 5 / 6), 2));
colliders.add(new Spinner(new PVector(width*3/4, height * 5 / 6), 3));
Collider thingy = new Angler(new PVector(width*2/3, height / 2), 0 + HALF_PI/2);
thingy.movable = true;
colliders.add(thingy);
Collider thingy2 = new Angler(new PVector(width/3, height / 2), PI - HALF_PI/2);
thingy2.movable = true;
colliders.add(thingy2);
}
}
void stop()
{
minim.stop();
super.stop();
}
void mousePressed()
{
PVector pressedAt = new PVector(mouseX, mouseY);
Iterator ic = colliders.iterator();
dragging = null;
while(ic.hasNext())
{
Collider c = (Collider) ic.next();
if(inRadius(c.pos, pressedAt, c.radius) && c.movable)
{
dragging = c;
break;
}
}
}
void mouseDragged()
{
if(mouseX > 0 && mouseX < width && mouseY > 0 && mouseY < height)
if(dragging != null)
{
PVector delta = new PVector(mouseX - pmouseX, mouseY - pmouseY);
//dragging.pos.add(delta);
dragging.pos = new PVector(mouseX, mouseY);
}
}
void mouseReleased()
{
dragging = null;
}
void physicsStep()
{
drops.add(new Drop(new PVector(random(width), 0), new PVector(0, 0), (int)random(0)));
Iterator id = drops.iterator();
while(id.hasNext())
{
Drop d = (Drop) id.next();
d.tick();
Iterator ic = colliders.iterator();
while(ic.hasNext())
{
Collider c = (Collider) ic.next();
if(inRadius(c.pos, d.pos, c.radius))
{
c.touch(d);
}
}
if(d.lastdraw.y > height) id.remove();
}
Iterator ic = colliders.iterator();
boolean all_spinners_happy = true;
while(ic.hasNext())
{
Collider c = (Collider) ic.next();
c.tick();
if(c instanceof Spinner)
{
Spinner s = (Spinner) c;
if(s.omega < SPIN_PEAK) all_spinners_happy = false;
}
}
if(all_spinners_happy && !colliders.isEmpty()) loadLevel(level+1);
}
void drawingStep()
{
fill(32, 40, 54, 32);
rect(0, 0, width, height);
Iterator id = drops.iterator();
while(id.hasNext())
{
Drop d = (Drop) id.next();
d.draw();
}
Iterator ic = colliders.iterator();
while(ic.hasNext())
{
Collider c = (Collider) ic.next();
c.draw();
if(c.movable)
{
strokeWeight(1);
stroke(192, 128, 0);
noFill();
ellipse(c.pos.x, c.pos.y, c.radius+5, c.radius+5);
}
}
textFont(font);
textAlign(LEFT, BOTTOM);
fill(255);
if(!colliders.isEmpty())
text("Level "+level, 2, height-2);
else text("Game Complete", 2, height-2);
if(levelflash)
{
fill(255);
noStroke();
rect(0, 0, width, height);
levelflash = false;
}
}
void draw()
{
while(physics_ms_last + PHYS_MS < millis())
{
if(!paused)
{
physicsStep();
}
physics_ms_last += PHYS_MS;
}
if(drawing_ms_last + DRAW_MS < millis())
{
drawingStep();
drawing_ms_last = millis();
}
}
void keyPressed()
{
if(!keys[keyCode])
{
keys[keyCode] = true;
down(keyCode);
}
}
void keyReleased()
{
if(keys[keyCode])
{
keys[keyCode] = false;
up(keyCode);
}
}
void down(int theKey)
{
println(theKey + " down");
if(theKey == '=') loadLevel(level+1);
}
void up(int theKey)
{
println(theKey + " up");
}