/**
* Game 07: "AVOID SPIKES"
* Everyone's favorite part of Tempest.
* Mouse to move and shoot.
* P to pause, M to mute.
* (More (and better) games!)
* 
This work by Nathan McCoy is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License.
*/
import ddf.minim.*;
boolean[] keys = new boolean[256];
PFont font;
boolean gameOver;
ArrayList spikes;
ArrayList shots;
final int COLUMNS = 12;
float TUNNEL_LENGTH = 200;
float travel = 0;
float PLAYER_Y = 600;
float player_x;
int MAX_SHOTS = 5;
int SHOT_COOLDOWN = 6;
float BASE_SPEED = 3;
float SPEED_FACTOR = 0.025;
float SHOT_SPEED = 6;
int SCORE_SHOOT_SPIKE = 1;
int SCORE_KILL_SPIKE = 0;
int SCORE_AVOID_SPIKE = 0;
float SPIKE_DAMAGE_RATE = 5;
float SPIKE_GROWTH_RATE = 1;
int shot_timer;
float spike_timer;
float SPIKE_TIME = 60;
int score;
int hiscore;
float speed;
boolean muted;
boolean paused;
boolean intro = true;
Minim minim;
AudioSample tick;
AudioSample shoot;
AudioSample die;
int last_col;
ParticleList death_sparks;
class ParticleList extends ArrayList
{
void tick()
{
Iterator i = iterator();
while(i.hasNext())
{
Particle p = (Particle) i.next();
p.tick();
if(p.life <= 0) i.remove();
}
}
void draw()
{
Iterator i = iterator();
while(i.hasNext())
{
Particle p = (Particle) i.next();
p.draw();
}
}
}
abstract class Particle
{
PVector pos;
PVector vel;
PVector accel;
int life;
int maxlife;
Particle(PVector p, PVector v, int lifetime)
{
this(p, v, new PVector(0, 0), lifetime);
}
Particle(PVector p, PVector v, PVector a, int lifetime)
{
accel = new PVector(a.x, a.y);
pos = new PVector(p.x, p.y);
vel = new PVector(v.x, v.y);
life = maxlife = lifetime;
}
void tick()
{
vel.add(accel);
pos.add(vel);
life--;
}
abstract void draw();
}
class Deathspark extends Particle
{
color col;
Deathspark(PVector pos, PVector vel, color c)
{
super(pos, vel, (int)random(120));
col = c;
}
void draw()
{
fill(col);
noStroke();
rect((int)pos.x, (int)pos.y, 2 ,2);
}
}
float col2x(int column)
{
return width/COLUMNS * (0.5 + column);
}
class Spike
{
float tail;
float len;
int column;
Spike(int c, int l)
{
column = c;
len = l;
tail = -len;
}
void draw()
{
noFill();
strokeWeight(1);
stroke(0, 255, 0);
line(col2x(column), tail + len, col2x(column), tail);
stroke(255);
ellipse(col2x(column), tail + len, 5, 5);
}
}
class Shot
{
int column;
float y;
Shot(int tc)
{
column = tc;
y = PLAYER_Y;
}
void draw()
{
stroke(255, 255, 0);
noFill();
ellipse(col2x(column), y, 5+random(2), 5+random(2));
}
}
void reset()
{
score = 0;
hiscore = 0;
shot_timer = 0;
speed = BASE_SPEED;
spikes = new ArrayList();
shots = new ArrayList();
death_sparks = new ParticleList();
}
void setup()
{
size(480, 640, P3D);
frameRate(60);
reset();
minim = new Minim(this);
shoot = minim.loadSample("shot.wav", 1024);
tick = minim.loadSample("tick.wav", 1024);
die = minim.loadSample("die.wav", 1024);
font = loadFont("PressStartK-64.vlw");
textFont(font, 16);
}
void deathSparks()
{
loadPixels();
for(int x = 0; x < width; x++)
for(int y = 0; y < height; y++)
if(pixels[x+y*width] != color(0, 0, 0))
death_sparks.add(new Deathspark(new PVector(x, y), new PVector((x-mouseX)*0.05*random(1), (y-PLAYER_Y)*0.05*random(1)), pixels[x+y*width]));
}
void draw()
{
if(intro)
{
background(0);
textAlign(CENTER, CENTER);
fill(255);
textFont(font, 32);
text("AVOID SPIKES", width/2, height/2);
textFont(font, 16);
text("click to play", width/2, height/2+30);
}
else
if(!paused)
{
background(0);
player_x = mouseX;
int player_column = (int)(player_x / width * COLUMNS);
if(player_column < 0) player_column = 0;
if(player_column > COLUMNS-1) player_column = COLUMNS-1;
if(player_column != last_col && !muted) tick.trigger();
last_col = player_column;
speed = BASE_SPEED + score * SPEED_FACTOR;
spike_timer += speed;
if(spike_timer > SPIKE_TIME)
{
spike_timer = 0;
spikes.add(new Spike((int)random(COLUMNS), 80));
}
shot_timer--;
if(shot_timer <= 0 && mousePressed && shots.size() < MAX_SHOTS)
{
shots.add(new Shot(player_column));
println("shot");
shot_timer = SHOT_COOLDOWN;
if(!muted) shoot.trigger();
}
Iterator is = shots.iterator();
while(is.hasNext())
{
Shot s = (Shot) is.next();
s.draw();
s.y -= SHOT_SPEED;
if(s.y < 0) is.remove();
else
for(int k = 0; k < spikes.size(); k++)
{
Spike sk = (Spike) spikes.get(k);
if(s.y < sk.tail+sk.len && s.column == sk.column)
{
sk.len-= SPIKE_DAMAGE_RATE;
is.remove();
score += SCORE_SHOOT_SPIKE;
break;
}
}
}
boolean died = false;
Iterator ik = spikes.iterator();
while(ik.hasNext())
{
Spike k = (Spike) ik.next();
{
if(k.tail > height)
{
ik.remove();
score += SCORE_AVOID_SPIKE;
}
else if(k.len <= 0)
{
ik.remove();
score += SCORE_KILL_SPIKE;
}
else
{
k.tail += speed;
k.draw();
if(k.column == player_column && k.tail < PLAYER_Y && k.tail+k.len > PLAYER_Y)
{
score = 0;
died = true;
}
}
}
}
stroke(0, 0, 255);
for(int i = 0; i <= COLUMNS; i++)
line(i * (width-2) / COLUMNS + 1, 0, i * (width-2) / COLUMNS + 1, height);
stroke(255, 255, 0);
line(player_column * (width-2) / COLUMNS + 1, 0, player_column * (width-2) / COLUMNS + 1, height);
line((player_column+1) * (width-2) / COLUMNS + 1, 0, (player_column+1) * (width-2) / COLUMNS + 1, height);
line(player_column * (width-2) / COLUMNS + 1, PLAYER_Y, mouseX, PLAYER_Y+5);
line((player_column+1) * (width-2) / COLUMNS + 1, PLAYER_Y, mouseX, PLAYER_Y+5);
line(player_column * (width-2) / COLUMNS + 1, PLAYER_Y, mouseX, PLAYER_Y+10);
line((player_column+1) * (width-2) / COLUMNS + 1, PLAYER_Y, mouseX, PLAYER_Y+10);
line(player_column * (width-2) / COLUMNS + 1, PLAYER_Y, player_column * (width-2) / COLUMNS + 1 + 10, PLAYER_Y-5);
line((player_column+1) * (width-2) / COLUMNS + 1, PLAYER_Y, (player_column+1) * (width-2) / COLUMNS + 1 - 10, PLAYER_Y-5);
if(died)
{
deathSparks();
spikes.clear();
shots.clear();
if(!muted) die.trigger();
}
death_sparks.tick();
death_sparks.draw();
fill(255);
textAlign(LEFT, TOP);
text("Score: " +score, 8, 8);
hiscore = max(score, hiscore);
if(score == hiscore) fill(255); else fill(128);
textAlign(RIGHT, TOP);
text("Best: " +hiscore, width-8, 8);
}
}
void mousePressed()
{
intro = false;
}
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') paused = !paused;
}
void up(int theKey)
{
println(theKey + " up");
}
void stop()
{
shoot.close();
die.close();
tick.close();
minim.stop();
super.stop();
}