/**
* Game 09: "Squidfold"
* Arrows move
* Z to brighten
* X to darken
*
* (m = mute)
* 
This work by Nathan McCoy is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License.
*/
import ddf.minim.*;
import ddf.minim.effects.*;
import ddf.minim.signals.*;
AudioOutput out;
Minim minim;
BandPass bpf;
PinkNoise pn;
ArrayList stuff;
boolean[] keys = new boolean[256];
int MAXDAM = 40;
int BREAKDAM = 30;
int breaks;
int RECOVER = 8;
float PACE = 2;
float BASE_RATE = 0.03;
float ACCEL_FACTOR = 0.000002 * PACE;
float SPEED_BASE = 1.5;
float SPEED_RATE = 0.0005 * PACE;
float SIZE_BASE = 70;
float SIZE_RATE = 0.006 * PACE;
float WORD_CHANCE = 0.04;
String[] WORDS = {"no", "stop", "don't", "betrayed", "forgotten", "alone", "empty", "cold", "trapped", "ashamed", "sorry", "afraid", "lost"};
String randomWord() { return WORDS[(int)random(WORDS.length)];}
float stuff_clock;
int ticks;
int damage;
int x = 0xDEADBEEF;
PVector playerPos;
float CEIL = 10;
float FLOOR = 470;
int COLOR_SPREAD = 96;
float lastbg;
boolean muted;
boolean playing;
boolean gameover;
boolean titlescreen;
PImage snapshot;
PFont font;
class WordThing extends Thing
{
String word;
WordThing(color col, float rad, PVector p, PVector v)
{
super(col, rad, p, v);
word = randomWord();
}
void draw()
{
fill(c);
textAlign(CENTER, CENTER);
textFont(font, 64);
text(word, pos.x, pos.y);
}
void perturb()
{
word = randomWord();
}
}
class Thing
{
ArrayList vertices;
color c;
float r;
PVector pos;
PVector vel;
Thing(color col, float rad, PVector p, PVector v)
{
c= col;
r = rad;
pos = p;
vel = v;
vertices = new ArrayList();
int SIDES = 13;
for(int i = 0; i < SIDES; i++)
{
vertices.add(new PVector(cos(TWO_PI / SIDES * i) * r, sin(TWO_PI / SIDES * i) * r));
}
}
void draw()
{
fill(c);
noStroke();
// ellipse(pos.x, pos.y, r, r);
beginShape();
for(int i = 0; i < vertices.size(); i++)
vertex(pos.x + ((PVector)vertices.get(i)).x, pos.y + ((PVector)vertices.get(i)).y);
endShape(CLOSE);
}
void perturb()
{
float amt = r / 30;
for(int i = 0; i < vertices.size(); i++)
{
((PVector)vertices.get(i)).x += random(amt*2) - amt;
((PVector)vertices.get(i)).y += random(amt*2) - amt;
}
}
}
void startGame()
{
damage = 0;
ticks = 0;
lastbg = 128;
breaks = 0;
if(!muted) out.unmute();
playerPos = new PVector(width/2, height * 0.8);
stuff = new ArrayList();
stuff_clock = 0;
playing = true;
gameover = false;
titlescreen = false;
}
void titleScreen()
{
damage = 0;
playing = false;
gameover = false;
titlescreen = true;
}
void gameOver()
{
snapshot = createImage(width, height, RGB);
snapshot.loadPixels();
loadPixels();
for(int i = 0; i < pixels.length; i++) snapshot.pixels[i] = pixels[i];
snapshot.updatePixels();
//snapshot.filter(BLUR);
playing = false;
gameover = true;
out.mute();
}
void setup()
{
size(480, 480, P2D);
frameRate(60);
ellipseMode(RADIUS);
font = loadFont("Garamond-64.vlw");
minim = new Minim(this);
out = minim.getLineOut();
pn = new PinkNoise(1.0);
out.addSignal(pn);
bpf = new BandPass(440, 200, out.sampleRate());
out.addEffect(bpf);
out.mute();
titleScreen();
}
void stop()
{
out.close();
minim.stop();
super.stop();
}
void addThing()
{
float difficulty = SPEED_BASE + ticks * SPEED_RATE;
color c = color(128 + COLOR_SPREAD*((int)random(3)-1));
float radius = SIZE_BASE + ticks * SIZE_RATE;
PVector pos = new PVector(random(width), -radius);
PVector vel = new PVector(random(difficulty) - difficulty/2, difficulty);
if(random(1) < WORD_CHANCE)
{
pos.x = width/2;
difficulty = SPEED_BASE;
radius = SIZE_BASE;
vel = new PVector(random(difficulty) - difficulty/2, difficulty);
stuff.add(0, new WordThing(color(lastbg), radius, pos, vel));
}
else
stuff.add(0, new Thing(c, radius, pos, vel));
}
void noiseShader()
{
int ratio = 256/MAXDAM;
loadPixels();
for(int i = 0; i < pixels.length; i++)
{
x = ((x << 1) + (((x >> 27) ^ (x >> 30)) & 1));
if((x & 0xFF) <= red(pixels[i])) pixels[i] = color(255, 255-damage*ratio, 255-damage*ratio);
else pixels[i] = color(0);
}
updatePixels();
}
void controls()
{
float SPEED = 3;
if(keys[UP]) playerPos.y -= SPEED;
if(keys[DOWN]) playerPos.y += SPEED;
if(keys[LEFT]) playerPos.x -= SPEED;
if(keys[RIGHT]) playerPos.x += SPEED;
if(playerPos.x < 0) playerPos.x = 0;
if(playerPos.x >= width) playerPos.x = width - 1;
if(playerPos.y < CEIL) playerPos.y = CEIL;
if(playerPos.y > FLOOR) playerPos.y = FLOOR;
}
void draw()
{
if(titlescreen)
{
background(5);
textFont(font, 64);
textAlign(CENTER, CENTER);
fill(128);
text("squidfold", width/2, height/2);
textFont(font, 32);
text("[space]", width/2, height * 0.75);
noiseShader();
}
else if(gameover)
{
// snapshot.filter(BLUR);
snapshot.loadPixels();
for(int melt = 0; melt < 1; melt++)
for(int i = snapshot.pixels.length - 2; i > width; i--)
{
x = ((x << 1) + (((x >> 27) ^ (x >> 30)) & 1));
color c;
int fuzz = (x&0xFF)%3 - 1;
x = ((x << 1) + (((x >> 27) ^ (x >> 30)) & 1));
if(red(snapshot.pixels[i-fuzz]) < red(snapshot.pixels[i-width]) && x % 2 == 0)
{
c = snapshot.pixels[i-fuzz];
snapshot.pixels[i-fuzz] = snapshot.pixels[i - width];
snapshot.pixels[i - width] = c;
}
}
snapshot.updatePixels();
background(0);
image(snapshot, 0, 0);
fill(255);
textFont(font, 64);
textAlign(CENTER, CENTER);
text("end", width/2, height/2);
textFont(font, 32);
text("result: "+(ticks/6)+"\nbroken: "+breaks, width/2, height*0.8);
loadPixels();
for(int i = 0; i < pixels.length; i++)
{
if(green(pixels[i]) > 16)
{
if(red(snapshot.pixels[i]) > 128) pixels[i] = color(0);
else pixels[i] = color(255, 0, 0);
}
}
updatePixels();
}
else
{
controls();
float bg = 128;///map(playerPos.y, FLOOR, CEIL, 0, 254);
if(keys['Z']) bg += COLOR_SPREAD;
if(keys['X']) bg -= COLOR_SPREAD;
bg = (bg+lastbg*10)/11;
lastbg = bg;
background(bg);
ticks++;
stuff_clock += BASE_RATE + ACCEL_FACTOR * ticks;
if(stuff_clock > 1)
{
stuff_clock = 0;
addThing();
}
Iterator si = stuff.iterator();
while(si.hasNext())
{
Thing t = (Thing) si.next();
t.pos.add(t.vel);
if(t.pos.y > height + t.r) si.remove();
else
{
if(abs(red(t.c)-lastbg) < COLOR_SPREAD/2) t.perturb();
t.draw();
}
}
/*
fill(128);
ellipse(width/2, height/2, 100, 100);
fill(5);
ellipse(width/2 + 100, height/2, 100, 100);
fill(250);
ellipse(width/2 - 100, height/2, 100, 100);
*/
float passBand = map(bg, 0, 255, 300, 1800);
passBand = max(passBand, 50);
bpf.setFreq(passBand);
bpf.setBandWidth(passBand * 0.2 * ((MAXDAM * 1.0 - damage) / MAXDAM) );
noStroke();
float spin = ticks / 5.0;
fill(255);
//////////////////////////////////////COLLISION DETECTION
if(abs(red(get((int)playerPos.x, (int)playerPos.y))-bg) > COLOR_SPREAD/2)
{
damage++;
if(damage > MAXDAM)
{
damage = MAXDAM;
}
}
else
{
if(damage > BREAKDAM)
{
damage = 0;
breaks++;
background(255);
bpf.setFreq(2000);
bpf.setBandWidth(1900);
}
if(ticks % RECOVER == 0) damage--;
if(damage < 0) damage = 0;
}
stroke(bg);
strokeWeight(7);
noFill();
rect(0, 0, width, height);
noStroke();
fill(255);
ellipse(playerPos.x, playerPos.y, 10, 10);
fill(0);
arc(playerPos.x, playerPos.y, 10, 10, spin, spin + HALF_PI);
arc(playerPos.x, playerPos.y, 10, 10, spin + PI, spin + HALF_PI + PI);
fill(255);
ellipse(playerPos.x, playerPos.y, 5, 5);
fill(0);
arc(playerPos.x, playerPos.y, 5, 5, -spin, -spin + HALF_PI);
arc(playerPos.x, playerPos.y, 5, 5, -spin + PI, -spin + HALF_PI + PI);
if(!keys['1']) noiseShader();
//println(frameRate);
if(damage == MAXDAM) gameOver();
fill(0, 255, 0);
textAlign(LEFT, BOTTOM);
textFont(font, 64);
//text(""+frameRate, 0, height);
}
}
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 == 'Q') ticks+=1000;
if(theKey == 'M')
{
muted = !muted;
if(muted) out.mute();
else out.unmute();
}
if((theKey == ' ') && gameover)
{
titleScreen();
}
else if((theKey == ' ') && titlescreen)
{
startGame();
}
}
void up(int theKey)
{
//println(theKey + " up");
}