import ddf.minim.*;
boolean cheats = false;
/**
* Game 23: "No more spheres!" - title by Pierrec
* Right-click to move, left-click to shoot.
*
* [attribution: bell sound by sandyrb]
*
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;
boolean l_down;
boolean r_down;
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;
PFont font;
AudioPlayer bgm;
AudioSample burstSound;
AudioSample shotSound;
AudioSample dieSound;
AudioSample dingSound;
void audioInit()
{
minim = new Minim(this);
burstSound = minim.loadSample("burst.wav", 1024);
shotSound = minim.loadSample("shot.wav", 1024);
dieSound = minim.loadSample("die.wav", 1024);
dingSound = minim.loadSample("ding.wav", 1024);
bgm = minim.loadFile("deathtoll.mp3", 2048);
bgm.loop();
}
void audioClose()
{
burstSound.close();
shotSound.close();
dieSound.close();
dingSound.close();
bgm.close();
minim.stop();
}
color TEXT_COLOR = color(128, 32, 0);
float DEFAULT_SPHERE_RADIUS = 25; float DSR = DEFAULT_SPHERE_RADIUS;
int SPHERE_HP = 10;
int BLAST_LIFE = 60;
float SPHERE_SPIN_RATE = 0.005;
float SHOT_KICK = DEFAULT_SPHERE_RADIUS * 3 / SPHERE_HP;
float PLAYER_ACCEL = 0.05;
float PLAYER_TOP_SPEED = 3;
float CONTROL_INNER_RADIUS = 10;
float CONTROL_OUTER_RADIUS = 100;
float PLAYER_TURN_RATE = 0.05;
float SHOT_SPEED = 6;
int AUTOFIRE_RATE = 15;
int DEATH_DURATION = PHYSICS_FPS * 2;
int CLEAR_DURATION = PHYSICS_FPS * 3;
int INTRO_DURATION = PHYSICS_FPS * 1;
ParticleList shrapnel;
ArrayList bullets;
ArrayList spheres;
PVector player_pos;
float player_speed;
float player_angle;
int autofire_timer;
boolean player_alive;
int level;
int level_time;
int game_time;
int best_time;
boolean time_counting;
boolean victory;
int death_countdown;
int clear_countdown;
int intro_countdown;
void setup()
{
ellipseMode(RADIUS);
size(600, 600);
frameRate(600);
audioInit();
font = loadFont("PressStartK-64.vlw");
loadLevel(0);
}
void loadLevel(int lev)
{
level = lev;
shrapnel = new ParticleList();
bullets = new ArrayList();
spheres = new ArrayList();
player_pos = new PVector(width/2, height/2);
player_angle = -HALF_PI;
player_speed = 0;
player_alive = true;
time_counting = false;
if(level > 0) time_counting = true;
else game_time = 0;
intro_countdown = INTRO_DURATION;
level_time = 0;
clear_countdown = 0;
death_countdown = 0;
victory = false;
switch(level)
{
case 0:
spheres.add(new Sphere(width*3/4, height/4));
break;
case 1:
spheres.add(new Sphere(width/2, height/4));
spheres.add(new YSphere(width/2, height*3/4));
spheres.add(new XSphere(width*3/4, height*3/4));
spheres.add(new RSphere(width*1/4, height*3/4));
break;
case 2:
spheres.add(new XSphere(width/2, height/2));
spheres.add(new YSphere(DSR, height/2));
spheres.add(new YSphere(width-DSR, height/2));
spheres.add(new RSphere(DSR, DSR));
spheres.add(new RSphere(width-DSR, DSR));
spheres.add(new RSphere(DSR, height-DSR));
spheres.add(new RSphere(width-DSR, height-DSR));
break;
case 3:
spheres.add(new RSphere(DSR, DSR));
spheres.add(new RSphere(width-DSR, DSR));
spheres.add(new RSphere(DSR, height-DSR));
spheres.add(new RSphere(width-DSR, height-DSR));
spheres.add(new RSphere(width/2, DSR));
spheres.add(new RSphere(DSR, height/2));
spheres.add(new RSphere(width/2, height-DSR));
spheres.add(new RSphere(width-DSR, height/2));
spheres.add(new RSphere(width/2, height/2));
break;
case 4:
Sphere cs = new CrossSphere(width/2, height/2);
//cs.radius *= 1.3;
spheres.add(cs);
spheres.add(new YSphere(width*3/4, height/2));
spheres.add(new YSphere(width/4, height/2));
spheres.add(new XSphere(width/2, height*3/4));
spheres.add(new XSphere(width/2, height/4));
spheres.add(new Sphere(width-DSR, height/2));
spheres.add(new Sphere(DSR, height/2));
spheres.add(new Sphere(width/2, height-DSR));
spheres.add(new Sphere(width/2, DSR));
break;
case 5:
float DSR2 = DSR * 2;
spheres.add(new CrossSphere(DSR2, DSR2));
spheres.add(new CrossSphere(width-DSR2, DSR2));
spheres.add(new CrossSphere(DSR2, height-DSR2));
spheres.add(new CrossSphere(width-DSR2, height-DSR2));
spheres.add(new XSphere(width/2, DSR2));
spheres.add(new YSphere(DSR2, height/2));
spheres.add(new XSphere(width/2, height-DSR2));
spheres.add(new YSphere(width-DSR2, height/2));
spheres.add(new RSphere(width/2, height/2));
break;
case 6:
for(int i = 0; i <= 12; i++)
{
spheres.add(new XSphere(DSR, height*i/12));
spheres.add(new XSphere(width-DSR, height*i/12));
}
break;
case 7:
Sphere rs = new RSphere(width/2, height/2);
rs.hp = 1;
spheres.add(rs);
spheres.add(new CrossSphere(width*3/4, height*1/3));
spheres.add(new CrossSphere(width/4, height*2/3));
spheres.add(new CrossSphere(width*2/3, height*3/4));
spheres.add(new CrossSphere(width/3, height/4));
spheres.add(new Sphere(width-DSR, height-DSR));
spheres.add(new Sphere(DSR, height-DSR));
spheres.add(new Sphere(width-DSR, DSR));
spheres.add(new Sphere(DSR, DSR));
break;
default:
victory = true;
break;
}
}
void stop()
{
audioClose();
super.stop();
}
void physicsStep()
{
if(intro_countdown > 0) intro_countdown --;
if(time_counting && intro_countdown == 0 && !victory)
{
level_time ++;
game_time ++;
}
if(!player_alive)
{
death_countdown --;
time_counting = false;
if(death_countdown <= 0) loadLevel(level);
}
if(spheres.isEmpty() && shrapnel.isEmpty() && player_alive && clear_countdown == 0 && !victory)
{
clear_countdown = CLEAR_DURATION;
if(!muted) dingSound.trigger();
}
if(clear_countdown > 0)
{
time_counting = false;
clear_countdown --;
if(clear_countdown <= 0) loadLevel(level+1);
}
//do controls
if(player_alive && intro_countdown == 0 && !victory)
{
PVector mouse_pos = new PVector(mouseX, mouseY);
PVector steer_delta = PVector.sub(mouse_pos, player_pos);
float to_angle = atan2(steer_delta.y, steer_delta.x);
player_angle = angleTowards(player_angle, to_angle, PLAYER_TURN_RATE);
float distan = steer_delta.mag();
float target_speed = map(distan, CONTROL_INNER_RADIUS, CONTROL_OUTER_RADIUS, 0, PLAYER_TOP_SPEED);
target_speed = max(0, target_speed);
target_speed = min(PLAYER_TOP_SPEED, target_speed);
if(r_down && distan > CONTROL_INNER_RADIUS) ;//target_speed = PLAYER_TOP_SPEED;
else target_speed = 0;
if(abs(target_speed - player_speed) < PLAYER_ACCEL) player_speed = target_speed;
else if(player_speed < target_speed) player_speed += PLAYER_ACCEL;
else if(player_speed > target_speed) player_speed -= PLAYER_ACCEL;
player_pos.x += cos(player_angle) * player_speed;
player_pos.y += sin(player_angle) * player_speed;
if(player_pos.x < 0) player_pos.x = 0;
if(player_pos.y < 0) player_pos.y = 0;
if(player_pos.x > width) player_pos.x = width;
if(player_pos.y > height) player_pos.y = height;
autofire_timer--;
if(l_down && autofire_timer <= 0)
{
bullets.add(new Shot(player_pos, player_angle));
autofire_timer = AUTOFIRE_RATE;
if(!muted) shotSound.trigger();
}
}
Iterator bi = bullets.iterator();
while(bi.hasNext())
{
Shot b = (Shot) bi.next();
b.tick();
if(b.out()) bi.remove();
else
{
Iterator si = spheres.iterator();
while(si.hasNext())
{
Sphere s = (Sphere) si.next();
if(inRadius(s.pos, b.pos, s.radius))
{
s.hp --;
if(s.hp > 0)
{
s.pos.x += cos(b.angle)* SHOT_KICK;
s.pos.y += sin(b.angle)* SHOT_KICK;
}
bi.remove();
//TODO hit sound
break;
}
}
}
//TODO collision
}
Iterator si = spheres.iterator();
while(si.hasNext())
{
Sphere s = (Sphere) si.next();
s.tick();
if(s.hp <= 0)
{
si.remove();
s.die();
if(!muted) burstSound.trigger();
}
}
shrapnel.tick();
}
void drawingStep()
{
if(death_countdown > 0) //camera shake
{
float SHAKE_SCALE = 0.1;
translate(SHAKE_SCALE*(random(death_countdown)-death_countdown/2), SHAKE_SCALE*(random(death_countdown)-death_countdown/2));
}
background(blendColors(color(48, 0, 0), TEXT_COLOR, intro_countdown * 1.0/INTRO_DURATION));
noFill();
stroke(blendColors(color(0), TEXT_COLOR, intro_countdown * 1.0/INTRO_DURATION));
int GRIDSIZE = 50;
for(int x = 0; x < width; x+=GRIDSIZE)
line(x, 0, x, height);
for(int y = 0; y < height; y+=GRIDSIZE)
line(0, y, width, y);
String more_spheres;
int count = spheres.size();
if(count == 0) more_spheres = "No more spheres!";
else if(count == 1) more_spheres = "1 more sphere";
else more_spheres = ""+count+" more spheres";
if(!player_alive) more_spheres = "...";
textFont(font, 32);
textAlign(CENTER, CENTER);
fill(blendColors(TEXT_COLOR, color(255), clear_countdown * 1.0/ CLEAR_DURATION));
text(more_spheres, width/2, height/2);
if(level > 0)
{
textAlign(CENTER, TOP);
String time_text = "";
if(victory) time_text = "Final Time: ";
time_text += game_time / PHYSICS_FPS;
time_text += ".";
String decimal = ""+(game_time * 100 / PHYSICS_FPS)%100;
if(decimal.length() < 2) decimal += "0";
time_text += decimal;
text(time_text, width/2, 8);
textAlign(LEFT, TOP);
textFont(font, 16);
if(!victory) text("Level "+level, 8, 8);
else
{
textAlign(CENTER, TOP);
text("click to play again", width/2, 48);
textAlign(RIGHT, BOTTOM);
text("v1.0", width-8, height-8);
}
}
else
{
textAlign(RIGHT, CENTER);
textFont(font, 16);
fill(blendColors(TEXT_COLOR, color(255), clear_countdown * 1.0/ CLEAR_DURATION));
if(clear_countdown == 0)
text("shoot this ", width*3/4 - 30, height/4);
else
text("begin", width*3/4 - 30, height/4);
}
//draw blasts
shrapnel.draw();
//draw spheres
Iterator si = spheres.iterator();
while(si.hasNext())
{
Sphere s = (Sphere) si.next();
s.draw();
}
//draw bullets
Iterator bi = bullets.iterator();
while(bi.hasNext())
{
Shot b = (Shot) bi.next();
b.draw();
}
//draw player
if(player_alive && intro_countdown == 0 && !victory)
{
strokeWeight(1);
fill(255, 255, 96);
noStroke();
float pradius = 17;
pushMatrix();
translate(player_pos.x, player_pos.y);
beginShape();
vertex(cos(player_angle)*pradius, sin(player_angle)*pradius);
vertex(cos(player_angle+TWO_PI*2/5)*pradius, sin(player_angle+TWO_PI*2/5)*pradius);
vertex(0, 0);
vertex(cos(player_angle-TWO_PI*2/5)*pradius, sin(player_angle-TWO_PI*2/5)*pradius);
endShape(CLOSE);
popMatrix();
}
}
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 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(muted) bgm.mute();
else bgm.unmute();
}
if(theKey == 'P' && !title) paused = !paused;
if(cheats && theKey == '=') loadLevel(level+1);
}
void up(int theKey)
{
println(theKey + " up");
}
void mousePressed()
{
if(mouseButton == LEFT) l_down = true;
if(mouseButton == RIGHT) r_down = true;
if(victory && intro_countdown == 0) loadLevel(0);
}
void mouseReleased()
{
if(mouseButton == LEFT) l_down = false;
if(mouseButton == RIGHT) r_down = false;
}