import ddf.minim.*;
/**
* Game 16: "Warped!"
*
*
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;
AudioSample shotSound;
AudioSample exploSound;
AudioSample dieSound;
AudioSample warpSound;
AudioSample dinkSound;
PFont font;
int NUM_STARS = 50;
float MOVEMENT_SPEED = 1.5;
float SHOT_SPEED = 6;
float SHIPSIZE_X = 30;
float SHIPSIZE_Y = 5;
float WARN_TIME = 360*2;
float WAVE_TIME = 360*5;
float LEVEL_GAP_TIME = WAVE_TIME * 3;
float wave_timer;
int level;
int wave;
int score;
int top;
PVector player_pos;
float warp_speed;
float START_SPEED = 2;
ArrayList stars;
ArrayList enemies;
PVector bullet;
int drawing_frame;
ParticleList explosions;
boolean dead;
abstract class Enemy
{
PVector pos;
float timer;
float radius;
abstract void draw();
abstract boolean tick();
}
void setup()
{
ellipseMode(RADIUS);
size(720, 480);
frameRate(600);
minim = new Minim(this);
shotSound = minim.loadSample("shot.wav", 1024);
exploSound = minim.loadSample("explo.wav", 1024);
dieSound = minim.loadSample("die.wav", 1024);
warpSound = minim.loadSample("warp.wav", 1024);
dinkSound = minim.loadSample("dink.wav", 1024);
font = loadFont("PressStartK-32.vlw");
title = true;
}
void newGame()
{
enemies = new ArrayList();
spawnWave();
player_pos = new PVector(width/4, height/2);
stars = new ArrayList();
for(int i = 0; i < NUM_STARS; i++)
{
stars.add(new PVector(random(width), random(height)));
}
warp_speed = START_SPEED;
wave_timer = WAVE_TIME;
explosions = new ParticleList();
score = 0;
level = 0;
wave = 0;
dead = false;
title = false;
}
void spawnWave()
{
float y;
switch((int)random(7))
{
case 0:
//spinners down
y = random(height/2);
for(int i = 0; i < 5; i++)
{
enemies.add(new Spinner(new PVector(width+20, y+40*i), WARN_TIME + 180*i));
}
break;
case 1:
//spinners up
y = random(height/2)+height/2;
for(int i = 0; i < 5; i++)
{
enemies.add(new Spinner(new PVector(width+20, y-40*i), WARN_TIME + 180*i));
}
break;
case 2:
//spinners level
y = random(height);
for(int i = 0; i < 5; i++)
{
enemies.add(new Spinner(new PVector(width+20, y), WARN_TIME + 180*i));
}
break;
case 3:
//rocks
for(int i = 0; i < 10; i++)
{
enemies.add(new Asteroid(new PVector(width+40, random(height)), WARN_TIME + 90*i));
}
break;
case 4:
//metal rocks
for(int i = 0; i < 3; i++)
{
enemies.add(new MetalAsteroid(new PVector(width+40, random(height)), WARN_TIME + 270*i));
}
break;
case 5:
//wavers up
y = random(height);
for(int i = 0; i < 5; i++)
{
enemies.add(new Waver(new PVector(width+20, y), WARN_TIME + 180*i, -70));
}
break;
case 6:
//wavers down
y = random(height);
for(int i = 0; i < 5; i++)
{
enemies.add(new Waver(new PVector(width+20, y), WARN_TIME + 180*i, 70));
}
break;
// case default:
}
}
void stop()
{
exploSound.close();
shotSound.close();
dieSound.close();
warpSound.close();
dinkSound.close();
minim.stop();
super.stop();
}
void physicsStep()
{
explosions.tick();
wave_timer-= warp_speed;
if(wave_timer <= 0)
{
if(wave >= 5 + level)
{
if(enemies.isEmpty())
{
wave_timer = LEVEL_GAP_TIME;
wave = 0;
if(level == 0) warp_speed += 3;
else warp_speed += 1;
level++;
if(!muted) warpSound.trigger();
}
}
else
{
wave++;
wave_timer = WAVE_TIME;
spawnWave();
}
}
Iterator ei = enemies.iterator();
while(ei.hasNext())
{
Enemy e = (Enemy) ei.next();
if(inRadius(e.pos, player_pos, e.radius) && !dead)
{
if(!muted) dieSound.trigger();
dead = true;
warp_speed = 0;//-warp_speed;
explosions.add(new Explosion (player_pos, 200));
// warp_speed = 0;
}
if(e.tick()) ei.remove();
}
for(int i = 0; i < stars.size(); i++)
{
PVector star = (PVector) stars.get(i);
star.x -= warp_speed/NUM_STARS*i;
if(star.x < -5*warp_speed)
{
star.x = width;
star.y = random(height);
}
}
if(!dead)
{
if(keys[UP])player_pos.y -= MOVEMENT_SPEED + level/3.0;
if(keys[DOWN])player_pos.y += MOVEMENT_SPEED + level/3.0;
if(keys[LEFT])player_pos.x -= MOVEMENT_SPEED + level/3.0;
if(keys[RIGHT])player_pos.x += MOVEMENT_SPEED + level/3.0;
if(player_pos.x < SHIPSIZE_X) player_pos.x = SHIPSIZE_X;
if(player_pos.x > width-SHIPSIZE_X) player_pos.x = width-SHIPSIZE_X;
if(player_pos.y < SHIPSIZE_Y) player_pos.y = SHIPSIZE_Y;
if(player_pos.y > height - SHIPSIZE_Y) player_pos.y = height - SHIPSIZE_Y;
if(bullet != null)
{
bullet.x += SHOT_SPEED;
if(bullet.x > width) bullet = null;
}
if(keys['Z'] && bullet == null)
{
if(!muted) shotSound.trigger();
bullet = new PVector(player_pos.x, player_pos.y);
}
}
}
void drawingStep()
{
if(title)
{
background(0);
textFont(font, 32);
fill(255);
textAlign(CENTER, CENTER);
text("Warped!\npress [space] to play", width/2, height/2);
}
else
{
if(!paused) drawing_frame++;
if(warp_speed == START_SPEED)
background(0);
else{
fill(0, 96);
rect(0, 0, width, height);
}
for(int i = 0; i < stars.size(); i++)
{
PVector star = (PVector) stars.get(i);
noFill();
strokeWeight(1);
stroke(255.0/NUM_STARS*i, 64+255.0/NUM_STARS*i, 255);
line(star.x, star.y, star.x + 1 + 4*warp_speed/NUM_STARS*i, star.y);
}
for(int i = 0; i < enemies.size(); i++)
{
Enemy e = (Enemy) enemies.get(i);
e.draw();
}
if(bullet != null)
{
stroke(255, 230, 192);
fill(255);
strokeWeight(2);
ellipse(bullet.x, bullet.y, 10, 3);
}
if(!dead)
{
fill(192);
noStroke();
strokeWeight(1);
ellipse(player_pos.x, player_pos.y, SHIPSIZE_X, SHIPSIZE_Y);
fill(sin(drawing_frame)*255+128, 255*sin(drawing_frame)+360, 255);
triangle(player_pos.x - SHIPSIZE_X*1.3, player_pos.y, player_pos.x - SHIPSIZE_X/2, player_pos.y - SHIPSIZE_Y,player_pos.x - SHIPSIZE_X/2, player_pos.y + SHIPSIZE_Y);
fill(192);
triangle(player_pos.x + SHIPSIZE_X/2, player_pos.y, player_pos.x - SHIPSIZE_X/2, player_pos.y, player_pos.x - SHIPSIZE_X/2, player_pos.y - SHIPSIZE_X/2);
triangle(player_pos.x + SHIPSIZE_X, player_pos.y, player_pos.x - SHIPSIZE_X/2, player_pos.y, player_pos.x - SHIPSIZE_X, player_pos.y + SHIPSIZE_X/3);
}
explosions.draw();
textFont(font, 16);
textAlign(LEFT, TOP);
fill(255);
top = max(top, score);
text("Score: "+score+"\nTop: "+top, 8, 8);
if(dead)
{
textAlign(CENTER, CENTER);
textFont(font, 16);
text("GAME OVER\n press [space] to play again", width/2, height/2);
}
if(level > 0 && wave == 0 && wave_timer > WAVE_TIME)
{
textAlign(CENTER, CENTER);
textFont(font, 32);
text("Sector Clear!\nwarp to next sector", width/2, height/2);
}
if(level > 0 && wave == 0 && wave_timer < WAVE_TIME)
{
fill(255, 0, 0);
textAlign(CENTER, CENTER);
textFont(font, 32);
text("Error!\nwarp malfunction", width/2, height/2);
}
}
}
void draw()
{
while(physics_ms_last + PHYS_MS < millis())
{
if(!paused && !title)
{
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 == ' ' && (dead || title)) newGame();
if(theKey == 'P' && !dead && !title) paused = !paused;
if(theKey == 'M') muted = !muted;
}
void up(int theKey)
{
println(theKey + " up");
}