import ddf.minim.*;
/**
* Game 21: "Much Worse"
* A/D or arrows to move left/right
* Click to throw grenade
*
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 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;
int ISLAND_TOP = 320;
int ISLAND_SIDES = 0;
float ISLAND_DOME = 0.0018;
int LAVA_LEVEL = 20;
int GRENADE_LIFE = PHYSICS_FPS*2/3;
float GRENADE_GRAVITY = 0.03;
float GRENADE_LAUNCH_KICK = GRENADE_LIFE * GRENADE_GRAVITY / 2;
int MAX_GRENADES = 2;
int EXPLOSION_LIFE = 60;
int GRENADE_EXPLO_RADIUS = 30;
int METEOR_TIME = 20 * PHYSICS_FPS;
int START_METEORS_PER_TIME = 7;
float METEOR_SPEED = 5;
int METEOR_WARN = GRENADE_LIFE * 3;
int METEOR_SPAWN_AHEAD = PHYSICS_FPS*3;
float METEOR_BLAST_RADIUS = 50;
int METEOR_HIT_SCORE = 50;
int METEOR_SURVIVE_SCORE = 10;
int physics_frame;
ArrayList grenades;
ArrayList meteors;
ArrayList explosions;
ParticleList particles;
PVector player_pos;
int[][] ground;
int meteor_timer;
int meteors_per_time;
int score;
int topscore;
boolean gameover;
String gameover_text;
PFont font;
AudioSample grenadeToss;
AudioSample grenadeBoom;
AudioSample meteorBoom;
AudioSample meteorWarn;
AudioSample meteorFall;
void setup()
{
size(720, 480);
frameRate(600);
font = loadFont("PressStartK-16.vlw");
audioInit();
newGame();
gameover = true;
gameover_text = "MUCH WORSE";
}
void audioInit()
{
minim = new Minim(this);
grenadeToss = minim.loadSample("grenade_toss.wav", 1024);
grenadeBoom = minim.loadSample("grenade_hit.wav", 1024);
meteorBoom = minim.loadSample("meteor_hit.wav", 1024);
meteorWarn = minim.loadSample("meteor_warn.wav", 1024);
meteorFall = minim.loadSample("meteor_fall.wav", 1024);
}
void audioClose()
{
grenadeToss.close();
grenadeBoom.close();
meteorBoom.close();
meteorWarn.close();
meteorFall.close();
minim.stop();
}
boolean worldIsSolid(int x, int y)
{
if(x < 0 || x >= width) return false;
if(y < 0 || y >= height) return false;
return ground[x][y] != 0;
}
void newGame()
{
ground = new int[width][height];
for(int x = 0; x < width; x++)
for(int y = 0; y < height; y++)
{
if(y > (ISLAND_TOP + (x-width/2)*(x-width/2)*ISLAND_DOME) && x >= ISLAND_SIDES && x < width-ISLAND_SIDES) ground[x][y] = 1;
}
grenades = new ArrayList();
meteors = new ArrayList();
explosions = new ArrayList();
particles = new ParticleList();
player_pos = new PVector(width/2, ISLAND_TOP-1);
meteor_timer = 0;
physics_frame = 0;
meteors_per_time = START_METEORS_PER_TIME;
score = 0;
gameover = false;
}
void stop()
{
audioClose();
super.stop();
}
void physicsStep()
{
physics_frame++;
particles.tick();
if(!gameover)
{
meteor_timer ++;
if(meteor_timer % (METEOR_TIME / meteors_per_time + 1) == 0) meteors.add(new Meteor());
if(meteor_timer >= METEOR_TIME)
{
meteor_timer = 0;
meteors_per_time ++;
}
}
Iterator gi = grenades.iterator();
while(gi.hasNext())
{
Grenade g = (Grenade) gi.next();
g.tick();
if(worldIsSolid((int)g.pos.x, (int)g.pos.y)) g.life = 0;
if(g.life <= 0)
{
//TODO blow up
if(!muted) grenadeBoom.trigger();
explosions.add(new Explosion(g.pos, GRENADE_EXPLO_RADIUS));
gi.remove();
}
}
Iterator ei = explosions.iterator();
while(ei.hasNext())
{
Explosion e = (Explosion) ei.next();
if(e.life == EXPLOSION_LIFE)
{
e.eatDirt();
}
e.tick();
if(e.life <= 0)
{
ei.remove();
}
}
Iterator mi = meteors.iterator();
while(mi.hasNext())
{
boolean blewup = false;
Meteor m = (Meteor) mi.next();
m.tick();
if(worldIsSolid((int)m.pos.x, (int)m.pos.y) || m.pos.y >= height)
{
//TODO blow up
if(!gameover) score += METEOR_SURVIVE_SCORE;
blewup = true;
mi.remove();
}
else
{
ei = explosions.iterator();
while(ei.hasNext())
{
Explosion e = (Explosion) ei.next();
if(inRadius(e.pos, m.pos, e.radius))
{
//TODO blow up
blewup = true;
mi.remove();
if(!gameover) score+= METEOR_HIT_SCORE;
break;
}
}
}
if(blewup)
{
explosions.add(new Explosion(m.pos, METEOR_BLAST_RADIUS));
if(!muted) meteorBoom.trigger();
}
}
// boolean bias = false;
//if(random(2) > 1) bias = true;
for(int x = 1; x < width - 1; x++)
for(int y = height-2; y >= 0; y--)
{
if(ground[x][y] != 0)
{
// bias = !bias;
if(ground[x][y+1] == 0) {ground[x][y] = 0; ground[x][y+1] = 2;}
else if(ground[x][y] == 2)
{
if(ground[x+1][y+1] == 0) {ground[x][y] = 0; ground[x+1][y+1] = 2;}// bias = !bias;}
else if(ground[x-1][y+1] == 0) {ground[x][y] = 0; ground[x-1][y+1] = 2;}// bias = !bias;}
}
}
}
if(!gameover)
{
if(!worldIsSolid((int)player_pos.x, (int)player_pos.y+1)) player_pos.y++;
if(worldIsSolid((int)player_pos.x, (int)player_pos.y)) player_pos.y--;
if(keys[LEFT] || keys['A'])
if(!worldIsSolid((int)player_pos.x-1, (int)player_pos.y-2)) player_pos.x--;
if(keys[RIGHT] || keys['D'])
if(!worldIsSolid((int)player_pos.x+1, (int)player_pos.y-2)) player_pos.x++;
if(player_pos.y > height - LAVA_LEVEL)
{
gameover = true;
gameover_text = "GAME OVER:\nfell in lava";
}
}
}
void drawingStep()
{
ellipseMode(RADIUS);
background(64, 0, 32);
particles.draw();
// stroke(160, 160, 160);
loadPixels();
for(int x = 0; x < width; x++)
for(int y = 0; y < height; y++)
{
if(ground[x][y]==1) pixels[x+y*width] = color(190, 160, 130);
if(ground[x][y]==2) pixels[x+y*width] = color(160, 130, 100);
}
updatePixels();
noStroke();
fill(255, 150, 0);
rect(0, height-LAVA_LEVEL, width, LAVA_LEVEL);
fill(96, 255, 255);
if(!gameover) rect(player_pos.x - 2, player_pos.y - 2, 5, 5);
fill(0, 192, 32);
Iterator gi = grenades.iterator();
while(gi.hasNext())
{
Grenade g = (Grenade) gi.next();
rect(g.pos.x - 1, g.pos.y - 1, 3, 3);
}
Iterator mi = meteors.iterator();
while(mi.hasNext())
{
Meteor m = (Meteor) mi.next();
m.draw();
}
Iterator ei = explosions.iterator();
while(ei.hasNext())
{
Explosion e = (Explosion) ei.next();
e.draw();
}
textFont(font);
fill(255);
textAlign(LEFT, TOP);
topscore = max(topscore, score);
text("Score: "+score+"\nTop: "+topscore, 8, 8);
if(gameover)
{
textAlign(CENTER, CENTER);
text(gameover_text+"\n\nclick to start", width/2, height/2);
}
}
void draw()
{
cursor(CROSS);
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 mousePressed()
{
if(grenades.size() < MAX_GRENADES && !gameover)
{
grenades.add(new Grenade());
if(!muted) grenadeToss.trigger();
}
if(gameover && meteors.isEmpty() && grenades.isEmpty())
{
newGame();
}
}
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 == 'M') muted = !muted;
if(theKey == 'P' && !title) paused = !paused;
}
void up(int theKey)
{
println(theKey + " up");
}