//Owen Canavan
//D13125137
//Menu menu; //menu object
String state = ""; //state tracker
RoidManager rm;
Player player;
ExplosionManager em;
int level = 1;
int scr_diag; //screen diagonal for range finding
void setup(){
size(800,600);
scr_diag = (int)ceil(sqrt(width*width + height*height)/2);
changeState("game");
}
void draw(){
background(0);
if(state == "game" || state == "gameOver"){
rm.update();
player.update();
em.update();
rm.draw();
player.draw();
em.draw();
}
}
void levelUp(){ //increase level and reset large rocks
level++;
rm.init();
}
void startGame(){ //start new game
level = 1;
player = new Player(width/2,height/2);
rm = new RoidManager();
rm.init();
em = new ExplosionManager();
}
void keyPressed(){ //input manager, delegate to player
if(state == "game")
player.keyPressed();
if(state == "gameOver"){
if(key == ' ')
changeState("game");
}
}
void keyReleased(){ //input manager, delegate to player
if(state == "game")
player.keyReleased();
}
void changeState(String s){ //simple state manager; change state, call enter state function
if(state != s){
state = s;
if(state == "game")
startGame();
}
}
//-------------------------
//asteroid.pde
//-------------------------
class RoidManager { //asteroid manager, 4 large, 8 medium, 16 small
Roid[] large, medium, small;
int active;
RoidManager() {
large = new Roid[4];
for (int i = 0; i < large.length; i++)
large[i] = new Roid(1);
medium = new Roid[8];
for (int i = 0; i < medium.length; i++)
medium[i] = new Roid(2);
small = new Roid[16];
for (int i = 0; i < small.length; i++)
small[i] = new Roid(3);
}
void init() { //start level with 4 large asteroids. base translation speed on game level
float speed = 0.5 + (level-1)*0.25;
//pick random starting locations within own areas, but avoid the player if they are not in the middle
large[0].spawn(player.pos.x < width/2-100 ? random(width/2,width):random(0, width/3), player.pos.y < height/2-100 ? random(height/2,height): random(0, height/3), random(-1, 1), random(-1, 1),speed, random(-1, 1));
large[1].spawn(player.pos.x < width/2-100 ? random(width/2,width):random(0, width/3), player.pos.y > height/2+100 ? random(0,height/2): random(2*height/3,height), random(-1,1),random(-1,1),speed, random(-1,1));
large[2].spawn(player.pos.x > width/2+100 ? random(0,width/2):random(2*width/3,width), player.pos.y < height/2-100 ? random(height/2,height): random(0, height/3), random(-1,1),random(-1,1),speed, random(-1,1));
large[3].spawn(player.pos.x > width/2+100 ? random(0,width/2):random(2*width/3,width), player.pos.y > height/2+100 ? random(0,height/2): random(2*height/3,height), random(-1,1),random(-1,1),speed, random(-1,1));
//track active asteroids
active = 4;
}
void checkBullet(Bullet b) { //check bullet agains every active asteroid
//check against the large asteroids
for (int i = 0; i < large.length; i++) {
Roid roid = large[i];
if (roid != null && roid.alive) {
int s = roid.containsPoint(b.x, b.y); //check point collision for each triangle segment of the asteroid
if (s > -1) { //a hit
if (roid.hp[s] < 4-roid.size) { //a hit that actually does damage (this face hasn't suffered maximum damage
roid.hits++;
roid.hp[s]++;
if (roid.hits == roid.MAX_HITS) { //maximum asteroid damage, split into 2 mediums
roid.alive = false;
PVector hit2cen = new PVector(roid.pos.x - b.x, roid.pos.y - b.y); //to determine movemnt directions. both moving away from eachother, and the player (90deg)
hit2cen.rotate(PI/2);
spawn(2, roid.pos.x, roid.pos.y, hit2cen.x, hit2cen.y);
spawn(2, roid.pos.x, roid.pos.y, -hit2cen.x, -hit2cen.y);
em.spawn(roid.pos.x,roid.pos.y,8,20,20,40,50); //big explosion
active++; //we lost one but gained two. one net gain
}
}
//bullet hit, kill and mini explosion. return
b.alive = false;
em.spawn(b.x,b.y,4,10,20,40,20);
return;
}
}
}
//havn't hit anything yet. check the mediums
for (int i = 0; i < medium.length; i++) {
Roid roid = medium[i];
if (roid != null && roid.alive) {
int s = roid.containsPoint(b.x, b.y);
if (s > -1) {
if (roid.hp[s] < 4-roid.size) {
roid.hits++;
roid.hp[s]++;
if (roid.hits == roid.MAX_HITS) {
roid.alive = false;
PVector hit2cen = new PVector(roid.pos.x - b.x, roid.pos.y - b.y);
hit2cen.rotate(PI/2);
spawn(3, roid.pos.x, roid.pos.y, hit2cen.x, hit2cen.y);
spawn(3, roid.pos.x, roid.pos.y, -hit2cen.x, -hit2cen.y);
em.spawn(roid.pos.x,roid.pos.y,8,20,20,40,50);
active++;
}
}
em.spawn(b.x,b.y,4,10,20,40,20);
b.alive = false;
return;
}
}
}
//still haven't hit anything. check the smalls
for (int i = 0; i < small.length; i++) {
Roid roid = small[i];
if (roid != null && roid.alive) {
int s = roid.containsPoint(b.x, b.y);
if (s > -1) {
if (roid.hp[s] < 4-roid.size) {
roid.hits++;
roid.hp[s]++;
if (roid.hits == roid.MAX_HITS) {
roid.alive = false;
active--;
em.spawn(roid.pos.x,roid.pos.y,8,20,20,40,50);
if(active == 0){
levelUp();
}
}
}
em.spawn(b.x,b.y,4,10,20,40,20);
b.alive = false;
return;
}
}
}
}
//create a new asteroid
void spawn(int size, float x, float y, float vx, float vy) {
Roid[] a = size == 1 ? large : ( size == 2 ? medium : small);
for (int i=0;i<a.length;i++) {
if (!a[i].alive) { //speed based on size and level. increasing speed with each level
float speed = 0.5;
if(size == 1)
speed = 0.5 + (level-1)*0.25;
else if(size == 2)
speed = 0.75 + (level-1)*0.25;
else if(size == 3)
speed = 1 + (level-1)*0.25;
a[i].spawn(x, y, vx, vy, speed, random(-1, 1));
return;
}
}
}
//update loop
void update() {
//update large, medium, small in order
for (int i = 0; i < large.length; i++) {
Roid roid = large[i];
if (roid != null && roid.alive)
roid.update();
}
for (int i = 0; i < medium.length; i++) {
Roid roid = medium[i];
if (roid != null && roid.alive)
roid.update();
}
for (int i = 0; i < small.length; i++) {
Roid roid = small[i];
if (roid != null && roid.alive)
roid.update();
}
//asteroid vs asteroid collisions
//asteroid bounce of other asteroid of greater than or equal size. don't react if hit by a small rock
//large vs large
for (int i = 0; i < large.length -1; i++) {
Roid r1 = large[i];
if (r1 != null && r1.alive) {
for (int j = i + 1; j < large.length; j++) { //ensure we double check, only loop from current position forward
Roid r2 = large[j];
if (r2 != null && r2.alive) { //check each rock is active
bounce(r1, r2, true); //bounce both large
}
}
}
}
//medium vs large
for (int i = 0; i < medium.length; i++) {
Roid r1 = medium[i];
if (r1 != null && r1.alive) {
for (int j = 0; j < large.length; j++) {
Roid r2 = large[j];
if (r2 != null && r2.alive) {
bounce(r1, r2, false); //bounce just the medium
}
}
}
}
//med vs med
for (int i = 0; i < medium.length -1; i++) {
Roid r1 = medium[i];
if (r1 != null && r1.alive) {
for (int j = i + 1; j < medium.length; j++) {
Roid r2 = medium[j];
if (r2 != null && r2.alive) {
bounce(r1, r2, true); //bounce both medium
}
}
}
}
//small vs large & medium
for (int i = 0; i < small.length; i++) {
Roid r1 = small[i];
if (r1 != null && r1.alive) {
for (int j = 0; j < large.length + medium.length; j++) { //combining both large and medium in a single loop
Roid r2 = j >= large.length ? medium[j-large.length] : large[j];
if (r2 != null && r2.alive) {
bounce(r1, r2, false); //bounce just the smalls
}
}
}
}
//small vs small
for (int i = 0; i < small.length -1; i++) {
Roid r1 = small[i];
if (r1 != null && r1.alive) {
for (int j = i + 1; j < small.length; j++) {
Roid r2 = small[j];
if (r2 != null && r2.alive) {
bounce(r1, r2, true); //bounce both small
}
}
}
}
}
void bounce(Roid r1, Roid r2, boolean both) { //if collision circle intersecting, seperate then bounce abount axis between two
float d = dist(r1.pos.x, r1.pos.y, r2.pos.x, r2.pos.y);
float RAD = (r1.radius + r2.radius)*0.75;
if (d < RAD) { //if intersecting
float _d = RAD - d;
//seperate
PVector vc = new PVector(r1.pos.x - r2.pos.x, r1.pos.y - r2.pos.y); //vector between roid centres
vc.normalize();
r1.pos.x += vc.x * _d/2;
r1.pos.y += vc.y * _d/2;
r2.pos.x -= vc.x * _d/2;
r2.pos.y -= vc.y * _d/2;
//bounce
PVector v1 = new PVector(r1.vx, r1.vy); //rock 1 velocity
PVector v2 = new PVector(r2.vx, r2.vy); //rock 2 velocity
PVector vcn = new PVector(vc.y, - vc.x); //right hand normal
//bounce r1
float dp11 = v1.dot(vcn);
float dp12 = v1.dot(vc);
PVector proj11 = vcn.mult(vcn, dp11);//project vel on the centres vector
PVector proj12 = vc.mult(vc, dp12); //project vel onto normal
proj12.x *= -1;
proj12.y *= -1;
PVector v1N = new PVector(proj11.x + proj12.x, proj11.y + proj12.y); //new bounced velocity
v1N.normalize();
r1.vx = v1N.x;
r1.vy = v1N.y;
if (both) { //do we bounce the second?
//bounce r2
float dp21 = v2.dot(vcn);
float dp22 = v2.dot(vc);
PVector proj21 = vcn.mult(vcn, dp21);
PVector proj22 = vc.mult(vc, dp22);
proj22.x *= -1;
proj22.y *= -1;
PVector v2N = new PVector(proj21.x + proj22.x, proj21.y + proj22.y);
v2N.normalize();
r2.vx = v2N.x;
r2.vy = v2N.y;
}
}
}
void draw() { //draw each rock, large, medium, small
for (int i = 0; i < large.length; i++) {
Roid roid = large[i];
if (roid != null && roid.alive)
roid.draw();
}
for (int i = 0; i < medium.length; i++) {
Roid roid = medium[i];
if (roid != null && roid.alive)
roid.draw();
}
for (int i = 0; i < small.length; i++) {
Roid roid = small[i];
if (roid != null && roid.alive)
roid.draw();
}
}
}
class Roid { //a single asteroid
int size, radius;
float rot, vx, vy, speed, rot_speed;
boolean alive;
Point pos;
Point[] points;
int[] hp;
int hits, MAX_HITS;
Roid(int _size) { //instantiate inactive
size = _size;
alive = false;
}
void spawn(float _x, float _y, float _vx, float _vy, float _speed, float r_speed) { //wake up
alive = true;
pos = new Point(_x, _y);
float vs = sqrt(_vx*_vx + _vy*_vy);
vx = _vx/vs;
vy = _vy/vs;
speed = _speed;
rot_speed = r_speed;
int min_r=1, max_r=1;
radius = size == 1 ? 100 : size == 2 ? 50 : 25;
min_r = (int)(radius * 0.75);
max_r = (int)(radius * 1.25);
int n = (int)random(8, 12);
points = new Point[n];
hp = new int[n]; //hitpoints for each facade
hits = 0;
MAX_HITS = (4-size)*floor(n/3); //bigger rocks need more hits to destroy
float _a = (2*PI)/n;
float pa = 0;
for (int i = 0; i < n; i++) { //randomised number of verticies (angles and lengths) to give lumpy surface
int px = 0;
int py = 0;
int r = (int)random(min_r, max_r);
pa += _a;
float a = random( pa - _a/2, pa + _a/2);
px = (int)(cos(a) * r);
py = (int)(sin(a) * r);
points[i] = new Point(px, py);
}
}
//we are rotating each point here instead of just doing a global translate/rotate, as we need to determine the proximity of each face to the player for colouring
void rotatePoint(Point p, float r) {
float a = atan2(p.y, p.x);
float d = dist(0, 0, p.x, p.y);
a += r;
//bounds
if (a > 2*PI)
a -= 2*PI;
if (a < 0)
a += 2*PI;
p.x = cos(a)*d;
p.y = sin(a)*d;
}
void update() {
if (!alive)
return;
rot += 0.01 * rot_speed; //global rotate. don't think it's needed anymore
//bounds
if (rot > 2*PI)
rot -= 2*PI;
if (rot < 0)
rot += 2*PI;
//rotate each point
for (int i=0;i<points.length;i++) {
rotatePoint(points[i], 0.01*rot_speed);
}
//move
pos.x += vx * speed;
pos.y += vy * speed;
//edge warp. wait til almost fully off screen
if (vx > 0 && pos.x > width + 100)
pos.x += -width-(2*100);
if (vx < 0 && pos.x < -100)
pos.x += width+(2*100);
if (vy > 0 && pos.y > height + 100)
pos.y += -height-(2*100);
if (vy < 0 && pos.y < -100)
pos.y += height+(2*100);
//collision check again player
if (player.alive && !player.dying) {
float playerDist = dist(pos.x, pos.y, player.pos.x, player.pos.y);
if (playerDist < radius)
player.hit();
}
}
void draw() {
if (!alive)
return;
fill(255);
//ellipse(pos.x,pos.y,radius*2,radius*2);
translate(pos.x, pos.y);
//rotate(rot);
stroke(0);
fill(1);
beginShape();
for (int i=0; i < points.length; i++) { //fientest of fill color
Point cur_point = points[i];
vertex(cur_point.x, cur_point.y);
}
endShape(CLOSE);
noFill();
//face shading
for (int i=0; i < points.length; i++) {
Point p1 = points[i];
Point p2 = (i == points.length-1 ? points[0] : points[i+1]); //get next point, or the first if we hit the end
color c = 0;
if (hp[i] > 0) { //show red if this face has already been hit
int p = (4-size) - hp[i];
strokeWeight(4-p);
c = color(200-(p*32), 0, 0);
}
else { //otherwise on show as grey if either vertex close to the player
float d = min(dist(pos.x+p1.x, pos.y+p1.y, player.pos.x, player.pos.y), dist(pos.x+p2.x, pos.y+p2.y, player.pos.x, player.pos.y)); //get nearest
if ( d < 2*scr_diag/3) {
PVector av = new PVector(p2.x-p1.x, p2.y - p1.y);
PVector arv = new PVector(av.y, -av.x);
if (arv.dot(new PVector(player.pos.x - pos.x, player.pos.y - pos.y)) > 0) //is this facing towards the player?
c = (int)map(d, 0, 2*scr_diag/3, 200, 0);
}
//c = 255;
}
stroke(c);
strokeWeight(2);
line(p1.x, p1.y, p2.x, p2.y);
}
//rotate(-rot);
translate(-pos.x, -pos.y);
}
//check if point is within roid shape. checking triangle from centre to each face
int containsPoint(float x, float y) {
x -= pos.x;
y -= pos.y;
Point last_point = points[0];
Point centre = new Point(0, 0);
for (int i = 1; i < points.length; i++) {
Point cur_point = points[i];
if (pointInTriangle(centre, last_point, cur_point, x, y))
return i-1;
last_point = cur_point;
}
if (pointInTriangle(centre, last_point, points[0], x, y))
return points.length-1;
return -1;
}
//braycentric coordinate point in triangle check
boolean pointInTriangle(Point p1, Point p2, Point p3, float x, float y)
{
float d = ((p2.y - p3.y)*(p1.x - p3.x) + (p3.x - p2.x)*(p1.y - p3.y));
float a = ((p2.y - p3.y)*(x - p3.x) + (p3.x - p2.x)*(y - p3.y)) / d;
float b = ((p3.y - p1.y)*(x - p3.x) + (p1.x - p3.x)*(y - p3.y)) / d;
float c = 1 - a - b;
return 0 <= a && a <= 1 && 0 <= b && b <= 1 && 0 <= c && c <= 1; //if a,b, & c all 0-1, point inside
}
}
//-------------------------
//Bullet.pde
//-------------------------
class Bullet{
float x, y, vx, vy, speed;
int lifetime, life;
boolean alive;
//A simple bullet
Bullet(){
alive = false;
}
void spawn(float _x, float _y, float _vx, float _vy, int _life, float _s){
x = _x;
y = _y;
vx = _vx;
vy = _vy;
lifetime = _life;
life = 0;
speed = _s;
alive = true;
}
void update(){
if(!alive)
return;
//move
x += vx * speed;
y += vy * speed;
//asteroid manager collision check (check against every active Roid)
rm.checkBullet(this);
//lifetime
if(++life > lifetime)
alive = false;
}
void draw(){
if(!alive)
return;
stroke(255);
fill(255);
ellipse(x,y,5,5);
}
}
//------------------------------
//Geom.pde
//------------------------------
//simple class for x,y coordinates
class Point {
float x, y;
Point(float _x, float _y) {
x = _x;
y = _y;
}
Point(Point p) {
x = p.x;
y = p.y;
}
void offset(float dx, float dy) {
x += dx;
y += dy;
}
void offset(Point p) {
x += p.x;
y += p.y;
}
String toString() {
return "(x="+x+",y="+y+")";
}
}
//an updateablt/moveable polygon. only used as triangles
class MobilePoly {
Point pos;
float vx, vy, speed, rot, rot_speed;
Point[] points;
MobilePoly(float x, float y, float _vx, float _vy, float _speed, float _rot, float _rotSpeed, Point[] _p) {
vx = _vx;
vy = _vy;
speed = _speed;
Point offset = findCentre(_p); //find centre point of triangle for rotation
points = _p;
for(int i=0; i < points.length; i++){ //reposition each point, ship ship local coordinates to poly local coords.
points[i].offset(-offset.x, -offset.y);
}
pos = new Point(x, y);
pos.offset(offset.x,offset.y);
rot = _rot;
rot_speed = _rotSpeed;
}
void update() {
//move
pos.x += vx * speed;
pos.y += vy * speed;
//rotate
rot += 0.03 * rot_speed;
//bounds
if (rot > 2*PI)
rot -= 2*PI;
if (rot < 0)
rot += 2*PI;
//edge warp
if (vx > 0 && pos.x > width)
pos.x += -width;
if (vx < 0 && pos.x < 0)
pos.x += width;
if (vy > 0 && pos.y > height)
pos.y += -height;
if (vy < 0 && pos.y < 0)
pos.y += height;
}
void draw() {
//draw triangle
translate(pos.x, pos.y);
rotate(rot);
fill(255);
stroke(255);
beginShape();
vertex(points[0].x, points[0].y);
vertex(points[1].x, points[1].y);
vertex(points[2].x, points[2].y);
endShape(CLOSE);
rotate(-rot);
translate(-pos.x, -pos.y);
}
//simple centre point calc, average sum of each point
Point findCentre(Point[] p) {
float x = 0, y = 0;
for (int i = 0; i < p.length; i++) {
if (p[i] != null) {
x += p[i].x;
y += p[i].y;
}
}
return new Point(x/3, y/3);
}
}
//---------------------------
//Particle.pde
//---------------------------
class ExplosionManager {
Explosion[] exps; //explosion pool
ExplosionManager() {
exps = new Explosion[10]; //prepare explosions;
for (int i = 0; i<exps.length;i++) {
exps[i] = new Explosion();
}
}
//start a new explosion
void spawn(float x, float y, int c, int spread, int min_life, int max_life, int size) {
for (int i = 0; i<exps.length;i++) { //search pool, find inactive
if (!exps[i].alive) {
exps[i].spawn(x, y, c, spread, min_life, max_life, size);
break;
}
}
}
void update() { //update loop
for (int i = 0; i<exps.length;i++) {
exps[i].update();
}
}
void draw() { //draw loop
for (int i = 0; i<exps.length;i++) {
exps[i].draw();
}
}
}
class Explosion { //a collection of particles
boolean alive;
Particle[] parts;
int dead_count;
Explosion() {
alive = false;
}
void spawn(float x, float y, int c, int spread, int min_life, int max_life, int size) { //create new particles
parts = new Particle[c];
for (int i=0; i < c; i++) {
parts[i] = new Particle(this, x + random(-spread, spread), y + random(-spread, spread), (int)random(min_life, max_life), size, (int)random(0, min_life/4));
}
dead_count = 0;
alive = true;
}
void update() { //update loop
if (!alive)
return;
for (int i=0;i<parts.length;i++) {
if (parts[i] != null)
parts[i].update();
}
}
void draw() { //draw loop
if (!alive)
return;
for (int i=0;i<parts.length;i++) {
if (parts[i] != null)
parts[i].draw();
}
}
void dead(Particle p) { //listen for dead particles, die if all dead
if (++dead_count == parts.length)
alive = false;
}
}
//lifetime base particle
class Particle {
Explosion ex;
float x, y;
int lifetime, life, size, delay;
boolean alive;
//instantiate and initiate
Particle(Explosion e, float _x, float _y, int _life, int _s, int _delay) {
ex = e;
x = _x;
y = _y;
size = _s;
lifetime = _life;
alive = true;
}
void update() { //update loop
if (!alive)
return;
if (delay > 0)
delay--;
else if (++life > lifetime) { //if life over, report death to parent explosion;
alive = false;
ex.dead(this);
}
}
void draw() {
if (!alive)
return;
if (delay <= 0) {
noStroke();
float p = life/(lifetime*1.0f); //determine percentage of life used. casting as float (was going int otherwise?)
int c = (int)map(p, 0, 1, 255, 0); //clamp red colour value;
fill(c, 0, 0);
ellipse(x, y, size*p, size*p); //draw, size base on life
}
}
}
//----------------------------
//Player.pde
//----------------------------
class Player {
Point pos;
float rot, vx, vy, speed=0, accel;
int w, h;
boolean keyForward, keyLeft, keyRight, firing, alive, dying;
Bullet[] bullets;
int bullet_interval = 10, bullet_cooldown;
int dying_time, dying_duration=60, tail_flick;
MobilePoly[] fragments;
int lives;
Player(float x, float y) {
alive = true;
dying = false;
lives = 3;
pos = new Point(x, y);
w = 20;
h = 40;
accel = 0;
//prepare 15 bullets
bullets = new Bullet[15];
for (int i = 0; i < bullets.length; i++)
bullets[i] = new Bullet();
}
void reset(){
//reset player for new life, same game
alive = true;
dying = false;
//position in centre
pos.x = width/2;
pos.y = height/2;
//kill velocity
vx = 0;
vy = 0;
}
void update() {
//update loop
if (!alive)
return;
if (dying) {
//ship has been hit, scatter the seperate fragments of the ship
for (int i = 0; i < fragments.length; i++)
if (fragments[i] != null)
fragments[i].update();
if (dying_time++ > dying_duration) {
//Time to die
die();
}
}
else {
if (keyForward) { //if up key is held down, alter velocity along direction of rotation
vx += sin(rot) * 0.1;
vy += -cos(rot) * 0.1;
speed += 0.1;
speed = speed > 5 ? 5 : speed;
}
//if left/right keys held down, rotate ship
rot += 0.06 * (keyLeft?-1:(keyRight?1:0));
//rotation bounds check
if (rot > 2*PI)
rot -= 2*PI;
if (rot < 0)
rot += 2*PI;
//move
pos.x += vx;
pos.y += vy;
//friction
vx *= 0.98;
vy *= 0.98;
//edge warp
if (vx > 0 && pos.x > width + h)
pos.x += -width-(2*h);
if (vx < 0 && pos.x < -h)
pos.x += width+(2*h);
if (vy > 0 && pos.y > height + h)
pos.y += -height-(2*h);
if (vy < 0 && pos.y < -h)
pos.y += height+(2*h);
//if space held down, shoot
if (firing) {
shoot();
}
//update all bullets
for (int i = 0; i < bullets.length; i++)
bullets[i].update();
}
}
void die() {
//fragment animation over, actually die
alive = false;
lives--;
em.spawn(pos.x,pos.y,16,20,20,40,150); //final big explosion. this isn't always where the fragments end up
if(lives < 0) //all lives lost, end the game
changeState("gameOver");
}
void hit() {
//hit by an asteroid
dying = true;
dying_time = 0;
//create ship fragments
fragments = new MobilePoly[4];
//create a MobilePoly triangle object to represent 4 different parts of the ship
Point[] p0 = {
new Point(0, -h/2), new Point(w/4, 0), new Point(0, h/4)
};
fragments[0] = new MobilePoly(pos.x, pos.y, vx, vy, speed, rot, random(-1, 1), p0);
Point[] p1 = {
new Point(w/4, 0), new Point(w/2, h/2), new Point(0, h/4)
};
fragments[1] = new MobilePoly(pos.x, pos.y, vx, vy, speed, rot, random(-1, 1), p1);
Point[] p2 = {
new Point(0, -h/2), new Point(0, h/4), new Point(-w/4, 0)
};
fragments[2] = new MobilePoly(pos.x, pos.y, vx, vy, speed, rot, random(-1, 1), p2);
Point[] p3 = {
new Point(0, 0+h/4), new Point(-w/2, +h/2), new Point(-w/4, 0)
};
fragments[3] = new MobilePoly(pos.x, pos.y, vx, vy, speed, rot, random(-1, 1), p3);
//mini explosion
em.spawn(pos.x,pos.y,4,15,5,10,10);
}
void shoot() {
if (!alive || dying)
return;
if (--bullet_cooldown > 0) //wait for cooldown before firing again
return;
PVector heading = PVector.fromAngle(rot-PI/2); //shoot base on rotation
for (int i = 0; i < bullets.length; i++) {
if (!bullets[i].alive) { //find a dead bullet and start it
bullets[i].spawn(pos.x, pos.y, heading.x, heading.y, 100, 10);
break;
}
}
bullet_cooldown = bullet_interval; //start new cooldown wait
}
void draw() {
if (!alive)
return;
for (int i = 0; i < bullets.length; i ++) //draw all the bullets
bullets[i].draw();
if (dying) {
for (int i = 0; i < fragments.length; i++) //draw all the fragments
if (fragments[i] != null)
fragments[i].draw();
}
else { //normal play, draw the ship
translate(pos.x, pos.y);
rotate(rot);
//ship
fill(255);
strokeWeight(1);
stroke(255);
beginShape();
vertex(0, -h/2);
vertex(w/2, h/2);
vertex(0, h/4);
vertex(-w/2, h/2);
endShape(CLOSE);
if(keyForward){ //draw flickering exhaust tail
tail_flick++;
if(tail_flick%4 == 0){
stroke(255);
strokeWeight(2);
float sp = speed/5;
line(-3*w/8, 0.7*h, 0, 0.7*h+(h*sp));
line( 3*w/8, 0.7*h, 0, 0.7*h+(h*sp));
if(sp > 0.3){
line(-3*w/8, 0.7*h, 0, 0.7*h+(h*sp*0.3));
line( 3*w/8, 0.7*h, 0, 0.7*h+(h*sp*0.3));
}
if(sp > 0.6){
line(-3*w/8, 0.7*h, 0, 0.7*h+(h*sp*0.6));
line( 3*w/8, 0.7*h, 0, 0.7*h+(h*sp*0.6));
}
}
}
rotate(-rot);
translate(-pos.x, -pos.y);
}
}
void keyPressed() { //input management
if (key == 'w' || key == CODED && keyCode == UP)
keyForward = true;
if (key == 'a' || key == CODED && keyCode == LEFT)
keyLeft = true;
if (key == 'd' || key == CODED && keyCode == RIGHT)
keyRight = true;
if (key == ' ') {
if(alive){
shoot();
firing = true;
} else {
reset();
}
}
}
void keyReleased() { //input management
if (key == 'w' || key == CODED && keyCode == UP) { //forward
keyForward = false;
accel = 0;
tail_flick = 0;
speed = 0;
}
if (key == 'a' || key == CODED && keyCode == LEFT) //rotate counter-clockwise
keyLeft = false;
if (key == 'd' || key == CODED && keyCode == RIGHT) //rotate clockwise
keyRight = false;
if (key == ' ') //shoot
firing = false;
}
}
|