import ddf.minim.*; /** * Game 11: "Waterfall Bomb"
* Avoid rocks and bombs. Use rocks as shelter from explosions.
* P to pause, M to mute.
*
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; boolean game_over; int drawing_ms_last; int physics_ms_last; int CORE_FPS = 300; int DRAWING_FPS = 60; int PHYSICS_FPS = 180; float DRAW_MS = 1000.0 / DRAWING_FPS; float PHYS_MS = 1000.0 / PHYSICS_FPS; int physframe; int drawframe; Minim minim; ParticleList particles; float WATER_ACCEL = 0.03; int WATER_LIFE = 200; float WATER_SW = 30; float WATER_EW = 10; float ROCK_RADIUS = 30; float ROCK_ADD_RADIUS = 20; float BOMB_RADIUS = 15; float MARGIN = 50; int EXPLOSIONS_PER_BLAST = 50; float EXPL_SPEED = 2; float EXPL_SIZE = 10; float EXPL_GROWTH = 0.2; int BOMB_START_FRAMES = 500; int BOMB_INCREASE_RATE = 5; int BOMB_MAX_RATE = 150; int ROCK_FRAMES = 50; int LIFE_FRAMES = 4000; int STUN_TIME = PHYSICS_FPS * 3; int START_LIVES = 5; float GRAVITY = 0.0015; float INITIAL_VEL = 0.5; ArrayList falling_stuff; ParticleList explosions; PVector player; int score; int lives; float drop_offset; int stun_timer; int extra_lives; int bomb_counter; int rock_counter; int life_counter; AudioSample bombSound; AudioSample hurtSound; AudioSample lifeSound; AudioPlayer bgm; PFont font; void setup() { size(720, 480, P2D); frameRate(600); minim = new Minim(this); bombSound = minim.loadSample("explo1.wav", 1024); bombSound.setGain(-10); hurtSound = minim.loadSample("hurt.wav", 1024); hurtSound.setGain(-10); lifeSound = minim.loadSample("1up.wav", 1024); lifeSound.setGain(-10); bgm = minim.loadFile("dripblop.mp3", 2048); bgm.loop(); reset(); cursor(CROSS); font = loadFont("ARBERKLEY-48.vlw"); title = true; game_over = true; particles = new ParticleList(); } void stop() { bombSound.close(); hurtSound.close(); lifeSound.close(); bgm.close(); minim.stop(); super.stop(); } void reset() { title = false; score = life_counter = bomb_counter = rock_counter = 0; game_over = false; stun_timer = STUN_TIME; lives = START_LIVES; player = new PVector(mouseX, mouseY); falling_stuff = new ArrayList(); explosions = new ParticleList(); } void loseLife() { lives--; stun_timer = STUN_TIME; if(!muted) hurtSound.trigger(); if(lives == 0) game_over = true; } void drawPlayer() { ellipseMode(RADIUS); strokeWeight(2); fill(255 - (drawframe % 5) * 64, 255, 0); stroke(0, 255, 255 - ((drawframe + 2) % 5) * 64); if(stun_timer == 0) { ellipse(player.x, player.y, 9, 9); } else { if((drawframe / 4) % 2 == 0) ellipse(player.x, player.y, 9, 9); noFill(); ellipse(player.x, player.y, stun_timer * 2, stun_timer * 2); } } void drawHud() { fill(0); textFont(font, 24); textAlign(LEFT, TOP); if(!title) text("Score: "+score + "\nLives: " + lives, 8, 8); textFont(font, 48); textAlign(CENTER, CENTER); if(game_over) { if(title)text("Waterfall Bomb\nclick to play", width/2, height/2); else text("Game Over", width/2, height/2); } else if(paused) { text("Paused\nclick dot to resume", width/2, height/2); } } void mousePressed() { if(game_over) reset(); else if(paused) { if(PVector.dist(new PVector(mouseX, mouseY), player) < 10) paused = false; } } class Explosion extends Particle { float radius; Explosion(PVector p, int num) { super(p, new PVector(cos(TWO_PI / EXPLOSIONS_PER_BLAST * num) * EXPL_SPEED, sin(TWO_PI / EXPLOSIONS_PER_BLAST * num) * EXPL_SPEED), 0); radius = EXPL_SIZE; // float angle = TWO_PI / EXPLOSIONS_PER_BLAST * num; } void tick() { radius += EXPL_GROWTH; super.tick(); } void draw() { fill(300+life/2, 255 + life, 255 + life*2); noStroke(); ellipseMode(RADIUS); ellipse(pos.x, pos.y, radius, radius); } } abstract class FallingThing { PVector pos; PVector vel; float radius; abstract void draw(); abstract void die(); } class Rock extends FallingThing { Rock(PVector p) { pos = p; vel = new PVector(0, INITIAL_VEL); radius = ROCK_RADIUS + random(ROCK_ADD_RADIUS); } void draw() { ellipseMode(RADIUS); fill(192, 128, 64); stroke(128, 64, 0); strokeWeight(2); ellipse(pos.x, pos.y, radius, radius); } void die() { } } class ExtraLife extends FallingThing { ExtraLife(PVector p) { pos = p; vel = new PVector(0, INITIAL_VEL); radius = 15; } void draw() { ellipseMode(RADIUS); fill(255 - (drawframe % 5) * 64, 255, 0); stroke(0, 255, 255 - ((drawframe + 2) % 5) * 64); strokeWeight(2); ellipse(pos.x, pos.y, radius, radius); noFill(); ellipse(pos.x, pos.y, radius * 2 - radius * sin(drawframe * 0.3), radius* 2 + radius * cos(drawframe * 0.3)); } void die() { } } class Bomb extends FallingThing { Bomb(PVector p) { pos = p; vel = new PVector(0, INITIAL_VEL); radius = BOMB_RADIUS; } void draw() { ellipseMode(RADIUS); fill(64, 64, 64); stroke(255 - 15*(drawframe%16), 0, 0); strokeWeight(2); ellipse(pos.x, pos.y, radius, radius); } void die() { if(!game_over) score++; if(!muted) bombSound.trigger(); for(int i = 0; i < EXPLOSIONS_PER_BLAST; i++) { explosions.add(new Explosion(pos, i)); } } } void physicsStep() { player.x = mouseX; if(player.x < 0) player.x = 0; if(player.x > width) player.x = width; player.y = mouseY; if(player.y < 0) player.y = 0; if(player.y > height) player.y = height; if(physframe % 1 == 0) particles.add(new WaterDrop(new PVector(random(width), 0))); if(!game_over) { bomb_counter++; rock_counter++; life_counter++; } stun_timer = max(stun_timer - 1, 0); if(bomb_counter > max(BOMB_START_FRAMES - BOMB_INCREASE_RATE * score, BOMB_MAX_RATE)) { drop_offset += width/4 + random(width/2); while(drop_offset > width) drop_offset -= width; falling_stuff.add(new Bomb(new PVector(drop_offset, -MARGIN))); bomb_counter = 0; } if(rock_counter > ROCK_FRAMES) { drop_offset += width/4 + random(width/2); while(drop_offset > width) drop_offset -= width; falling_stuff.add(new Rock(new PVector(drop_offset, -MARGIN))); rock_counter = 0; } if(life_counter > LIFE_FRAMES) { drop_offset += width/4 + random(width/2); while(drop_offset > width) drop_offset -= width; falling_stuff.add(new ExtraLife(new PVector(drop_offset, -MARGIN))); life_counter = 0; } explosions.tick(); Iterator exi = explosions.iterator(); ArrayList blasts = new ArrayList(); while(exi.hasNext()) { Explosion ex = (Explosion) exi.next(); if(ex.pos.x < -MARGIN || ex.pos.x > width+MARGIN || ex.pos.y < -MARGIN || ex.pos.y > height+MARGIN) exi.remove(); else { Iterator fsi = falling_stuff.iterator(); while(fsi.hasNext()) { FallingThing ft = (FallingThing)fsi.next(); if(inRadius(ft.pos, ex.pos, ft.radius + ex.radius)) { if(ft instanceof Bomb) { blasts.add(ft); fsi.remove(); } else { float distance = PVector.dist(ft.pos, ex.pos); ex.radius = distance - ft.radius; } } } if(ex.radius < 0) exi.remove(); else if(inRadius(player, ex.pos, ex.radius) && !game_over) { if(stun_timer == 0) loseLife(); } } } for(int i = 0; i < blasts.size(); i++) { Bomb b = (Bomb) blasts.get(i); b.die(); } particles.tick(); Iterator fsi = falling_stuff.iterator(); while(fsi.hasNext()) { FallingThing ft = (FallingThing)fsi.next(); ft.pos.add(ft.vel); ft.vel.y += GRAVITY; if(ft.pos.y > height + MARGIN || (ft instanceof Bomb && ft.pos.y > height)) { ft.die(); fsi.remove(); } else if(inRadius(player, ft.pos, ft.radius) && !game_over) { if(ft instanceof ExtraLife) { lives++; if(!muted) lifeSound.trigger(); fsi.remove(); } else if(stun_timer == 0) loseLife(); } } physframe++; } void drawingStep() { background(0, 128, 255); particles.draw(); explosions.draw(); Iterator fsi = falling_stuff.iterator(); while(fsi.hasNext()) { FallingThing ft = (FallingThing)fsi.next(); ft.draw(); } if(!game_over) drawPlayer(); drawHud(); drawframe++; } class WaterDrop extends Particle { WaterDrop(PVector p) { super(p, new PVector(0, 0), new PVector(0, WATER_ACCEL), WATER_LIFE); } void draw() { //strokeWeight(1); ellipseMode(CORNERS); rectMode(CORNERS); float lp = ((float)life)/maxlife; fill(lp*255, lp*128+127, 255); noStroke(); rect((int)pos.x - WATER_SW*lp - WATER_EW, 0, (int)pos.x + WATER_SW*lp + WATER_EW, pos.y); } } 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 == 'P' && !game_over) paused = true; if(theKey == 'M') { muted = !muted; if(muted) bgm.mute(); else bgm.unmute(); } } void up(int theKey) { println(theKey + " up"); }