import ddf.minim.*; /** * Game 17 - "Raintrace"
* Make the spinners spin.
*
Go play more games at NMcCoy.net!
* Creative Commons License
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"); }