class Bullet extends GameObject { PVector vel; int power; Bullet(PVector p, PVector v, int pwr) { pos = new PVector(p.x, p.y); vel = new PVector(v.x, v.y); power = pwr; } void tick() { pos.add(vel); if(pos.x < 0 || pos.y < 0 || pos.x > width || pos.y > width) removal_flag = true; Iterator ie = enemies.iterator(); while(ie.hasNext()) { Enemy e = (Enemy) ie.next(); if(inRadius(pos, e.pos, e.radius)) { if(!muted) hitSound.trigger(); e.life -= power; removal_flag = true; score += HIT_POINTS; } } if(inRadius(pos, player.pos, player.radius)) { if(!muted) hitSound.trigger(); player.life -= power; if(player.life < 0) player.life = 0; removal_flag = true; } } void draw() { fill(255); stroke(random(255), random(255), 255); ellipse(pos.x, pos.y, 3 + power * 2, 3 + power * 2); } } class Greenie extends GameObject { PVector vel; int life; int MAX_LIFE = PHYSICS_FPS * 6; float ATTRACT_RADIUS = 40; float ATTRACT_SPEED = 2; Greenie(PVector p) { pos = new PVector(p.x, p.y); vel = randomVector(0.1); life = MAX_LIFE; } void tick() { life--; if(player.life > 0) { if(inRadius(player.pos, pos, ATTRACT_RADIUS)) { vel = PVector.sub(player.pos, pos); vel.limit(ATTRACT_SPEED); } if(inRadius(player.pos, pos, player.radius)) { if(player.life < player.max_life) player.life++; else score+= GREENIE_POINTS; removal_flag = true; if(!muted) getSound.trigger(); } } pos.add(vel); if(life <= 0) removal_flag = true; } void draw() { float flicker = max(random(255), random(255)); fill(flicker, 255, flicker); stroke(0, 192, 0); strokeWeight(1); if(life < PHYSICS_FPS * 2) { fill(flicker, 255, flicker, 128); stroke(0, 192, 0, 128); } ellipse(pos.x, pos.y, 3, 3); } } class Tracer extends GameObject { color c; int TRAIL_LENGTH = 30; float TURN_CHANCE = 0.01; PVector vel; ArrayList poses; Tracer() { vel = new PVector(10, 0); pos = new PVector(random(width), random(height)); poses = new ArrayList(); c = color(0, 255, 255); } void tick() { if(random(1) < TURN_CHANCE) { float angle = HALF_PI * (int)random(4); float nx = cos(angle)*vel.x - sin(angle)*vel.y; float ny = cos(angle)*vel.y + sin(angle)*vel.x; vel.x = nx; vel.y = ny; } pos.add(vel); if(pos.x < -1){ pos.x = -1; vel.mult(-1); } if(pos.y < -1){ pos.y = -1; vel.mult(-1); } if(pos.x > width){ pos.x = width; vel.mult(-1); } if(pos.y > height){ pos.y = height; vel.mult(-1); } poses.add(new PVector(pos.x, pos.y)); if(poses.size() > TRAIL_LENGTH) poses.remove(0); } void draw() { for(int i =0; i < poses.size() - 1; i++) { PVector p1 = (PVector)poses.get(i); PVector p2 = (PVector)poses.get(i+1); stroke(blendColors(color(32, 64, 96), c, (i+1)/(float)poses.size())); line(p1.x, p1.y, p2.x, p2.y); } } } class SmokeTrail extends Particle { SmokeTrail(float x, float y, int r) { super(new PVector(x, y), new PVector(0, 0), r); } SmokeTrail(float x, float y, PVector v, int r) { super(new PVector(x, y), v, r); } void draw() { fill(255); noStroke(); ellipseMode(RADIUS); ellipse(pos.x, pos.y, life/2, life/2); } } abstract class Ship extends GameObject { int spawn_timer; float top_speed = 2; float traction = 0.1; int stun_timer; PVector target_vel; float angle_heading; int life; int max_life; Ship(float x, float y) { vel = new PVector(0, 0); target_vel = new PVector(0, 0); spawn_timer = ENEMY_SPAWN_TIME; stun_timer = 0; pos = new PVector(x, y); radius = 7; } void tick() { spawn_timer --; stun_timer --; if(spawn_timer <= 0) { if(this == grabbed) { vel.x = vel.y = 0; pos = PVector.add(player.pos, new PVector(cos(player.angle_heading)*(player.radius + radius),sin(player.angle_heading)*(player.radius + radius))); angle_heading = player.angle_heading; } else if(stun_timer <= 0) { if(vel.dist(target_vel) < traction) { vel.x = target_vel.x; vel.y = target_vel.y; } else { PVector steering = PVector.sub(target_vel, vel); steering.normalize(); steering.mult(traction); vel.add(steering); } } else { smoke.add(new SmokeTrail(pos.x, pos.y, (int)radius * 3)); Iterator ie = enemies.iterator(); while(ie.hasNext()) { Enemy e = (Enemy) ie.next(); if(e != this && inRadius(e.pos, pos, e.radius + radius)) { if(this != player) //hack! if(e.stun_timer <= 0) { score += HIT_POINTS; if(!muted) hitSound.trigger(); e.life -= max_life; life -= e.max_life; if(e.life <= 0) { PVector away = PVector.sub(e.pos, pos); away.normalize(); away.mult(KICK_STRENGTH); e.vel = away; e.stun_timer = KICK_STUN; } else { PVector from = PVector.sub(pos, e.pos); PVector rebound = proj(vel, from); rebound.mult(-2); vel.add(rebound); rebound.mult(-max_life / (float)(max_life + e.max_life)); e.vel.add(rebound); e.stun_timer = HIT_STUN; } } } } } if(life <= 0 && !removal_flag && stun_timer <= 0) { //TODO scoring and poof sound if(!muted) poofSound.trigger(); for(int i = 0; i < 8; i++) smoke.add(new SmokeTrail(pos.x, pos.y, randomVector(1), (int)radius * 6)); for(int i = 0; i < (max_life+1)/2; i++) { loot.add(new Greenie(pos)); } removal_flag = true; if(grabbed == this) grabbed = null; } if(vel.mag() > 0) angle_heading = vel.heading2D(); if(this != grabbed) { pos.add(vel); if(pos.x < 0) { pos.x = -pos.x; vel.x = -vel.x; if(!muted) bounceSound.trigger(); } if(pos.x > width) { pos.x = width*2 -pos.x; vel.x = -vel.x; if(!muted) bounceSound.trigger(); } if(pos.y < 0) { pos.y = -pos.y; vel.y = -vel.y; if(!muted) bounceSound.trigger(); } if(pos.y > height) { pos.y = height*2 -pos.y; vel.y = -vel.y; if(!muted) bounceSound.trigger(); } } } } } class Player extends Ship { Player(float x, float y) { super(x, y); } void draw() { if(spawn_timer > 0) { fill(255); strokeWeight(1); stroke(255); drawCharge(pos.x, pos.y, 1 - spawn_timer / (float) ENEMY_SPAWN_TIME, radius * 3, radius, 3); } else { pushMatrix(); translate(pos.x, pos.y); rotate(angle_heading); ellipseMode(RADIUS); fill(255, 192, 0); stroke(255, 255, 160); fill(blendColors(color(255, 0, 0), color(255, 192, 0), life /(float)max_life)); stroke(blendColors(color(255, 0, 0), color(255, 255, 160), life /(float)max_life)); strokeWeight(1); beginShape(); vertex(-radius, 0); vertex(0, -radius); vertex(radius, -radius/2); vertex(0, 0); vertex(radius, radius/2); vertex(0, radius); endShape(CLOSE); noFill(); stroke(160); if(grab_timer > 0) { float r = GRAB_RADIUS * grab_timer / GRAB_TIME; ellipse(GRAB_DISTANCE, 0, r, r); } popMatrix(); } } void tick() { grab_timer --; PVector aim = new PVector(0, 0); if(keys[UP]) aim.y -= 1; if(keys[DOWN]) aim.y += 1; if(keys[LEFT]) aim.x -= 1; if(keys[RIGHT]) aim.x += 1; aim.normalize(); aim.mult(top_speed); target_vel = aim; super.tick(); //collisions Iterator pie = enemies.iterator(); while(pie.hasNext()) { Enemy e = (Enemy) pie.next(); if(inRadius(pos, e.pos, radius + e.radius) && stun_timer <= 0 && e != grabbed && e.stun_timer <= 0 && e.spawn_timer <= 0) { if(!muted) hitSound.trigger(); PVector away = PVector.sub(pos, e.pos); away.normalize(); away.mult(PLAYER_HURT_KICK); vel = away; e.vel = new PVector(0, 0); stun_timer = HIT_STUN; e.stun_timer = HIT_STUN; life-= 1; //e.life -= 1; } } //grabbing detection if(grabbed == null && grab_timer > 0) { //TODO grab sound PVector grab_point = new PVector(pos.x, pos.y); grab_point.x += cos(angle_heading) * GRAB_DISTANCE; grab_point.y += sin(angle_heading) * GRAB_DISTANCE; Iterator ie = enemies.iterator(); Enemy closest = null; float d = MAX_FLOAT; while(ie.hasNext()) { Enemy e = (Enemy) ie.next(); if(inRadius(grab_point, e.pos, GRAB_RADIUS + e.radius) && e.radius <= MAX_GRAB_SIZE) { float td = PVector.dist(grab_point, e.pos); if(td < d) { closest = e; d = td; } } } if(closest != null) { grabbed = closest; if(!muted) grabSound.trigger(); grabbed.stun_timer = 0; } } } } class Enemy extends Ship { Enemy(float x, float y) { super(x, y); top_speed = 1; traction = 0.05; life = max_life = 2; } void tick() { if(life < 0) life = 0; PVector aim = PVector.sub(player.pos, pos); Iterator ie = enemies.iterator(); while(ie.hasNext()) { Enemy e = (Enemy) ie.next(); if(e != this && inRadius(e.pos, pos, ENEMY_AVOID_BUFFER + e.radius + radius)) { aim = PVector.sub(pos, e.pos); } } aim.normalize(); aim.mult(top_speed); target_vel = aim; super.tick(); } void draw() { if(spawn_timer > 0) { fill(255); strokeWeight(1); stroke(255); drawCharge(pos.x, pos.y, 1 - spawn_timer / (float) ENEMY_SPAWN_TIME, radius * 3, radius, 3); } else { pushMatrix(); translate(pos.x, pos.y); rotate(angle_heading); ellipseMode(RADIUS); fill(blendColors(color(255, 0, 0), color(64, 128, 192), life /(float)max_life)); stroke(blendColors(color(255, 0, 0), color(255), life /(float)max_life)); strokeWeight(1); ellipse(0, 0, radius, radius); ellipse(radius/2, 0, radius/2, radius/2); //line(0, 0, radius*2, 0); popMatrix(); } } } class Hunter extends Enemy { Hunter(float x, float y) { super(x, y); top_speed = 1.5; traction = 0.05; life = max_life = 3; radius = 10; } void draw() { if(spawn_timer > 0) { fill(255); strokeWeight(1); stroke(255); drawCharge(pos.x, pos.y, 1 - spawn_timer / (float) ENEMY_SPAWN_TIME, radius * 3, radius, 3); } else { pushMatrix(); translate(pos.x, pos.y); rotate(angle_heading); ellipseMode(RADIUS); fill(blendColors(color(255, 0, 0), color(160), life /(float)max_life)); stroke(blendColors(color(255, 0, 0), color(255), life /(float)max_life)); strokeWeight(1); ellipse(0, 0, radius, radius); fill(blendColors(color(255, 0, 0), color(192, 64, 64), life /(float)max_life)); triangle(-radius, -radius, radius, -radius/2, 0, -radius/2); triangle(-radius, radius, radius, radius/2, 0, radius/2); //line(0, 0, radius*2, 0); popMatrix(); } } } class Shooter extends Enemy { int shot_countdown; int SHOT_TIME = 120; Shooter(float x, float y) { super(x, y); top_speed = 0.5; traction = 0.05; life = max_life = 3; radius = 10; shot_countdown = (int)random(SHOT_TIME) + SHOT_TIME; } void draw() { if(spawn_timer > 0) { fill(255); strokeWeight(1); stroke(255); drawCharge(pos.x, pos.y, 1 - spawn_timer / (float) ENEMY_SPAWN_TIME, radius * 3, radius, 3); } else { pushMatrix(); translate(pos.x, pos.y); rotate(angle_heading); ellipseMode(RADIUS); fill(blendColors(color(255, 0, 0), color(64, 128, 192), life /(float)max_life)); stroke(blendColors(color(255, 0, 0), color(255), life /(float)max_life)); strokeWeight(1); ellipse(0, 0, radius, radius); fill(blendColors(color(255, 0, 0), color(192, 64, 64), life /(float)max_life)); triangle(-radius, -radius, radius, 0, -radius, radius); //line(0, 0, radius*2, 0); popMatrix(); } } void tick() { super.tick(); shot_countdown--; if(this == grabbed) shot_countdown-=2; if(shot_countdown <= 0 && (PVector.dist(pos, player.pos) > POINT_BLANK_LIMIT || this == grabbed) && stun_timer <= 0 && life > 0) { if(this != grabbed) angle_heading = PVector.sub(player.pos, pos).heading2D(); shot_countdown = SHOT_TIME; PVector shoot_from = new PVector(pos.x + cos(angle_heading)*radius*1.2,pos.y + sin(angle_heading)*radius*1.2); PVector shot_vel = new PVector(cos(angle_heading)*SHOT_SPEED, sin(angle_heading)*SHOT_SPEED); bullets.add(new Bullet(shoot_from, shot_vel, max_life/2)); if(!muted) shotSound.trigger(); vel.x = vel.y = 0; } } } void bigger(Enemy e) { e.life = e.life * 5 / 2; e.max_life = e.max_life * 5 / 2; e.top_speed *= 0.75; e.radius *= 2; }