/** * 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!)

* Creative Commons License
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(); }