for Proce55ing
Pgk - Source code

Pgk.java

/**
 * PGK wars.
 *
 * Copyright (C) 2004 Kenta Cho. Some rights reserved.
 * 
 * @p5 description
 * - 画面下の青い自機をマウスで操作してください
 * - マウスボタンを押すと弾を発射します
 * - 弾を画面中央のひし形に当てて画面上に押しやってください
 * - すべてのひし形を中央より上に押しやるとあなたの勝ちです
 * - 逆にすべてのひし形が中央より下にくると負けです
 * - 弾に接触すると一定時間自機が操作できなくなります
 */
public class Pgk extends BApplet {
  float BATTERY_SIZE = 10;
  float BATTERY_FIRE_INTERVAL = 50;
  Ship[] ship = new Ship[2];
  int PLAYER = 0;
  int COMPUTER = 1;
  Battery[] battery = new Battery[10];
  Bullet[] bullet = new Bullet[64];
  Particle[] particle = new Particle[32];
  BFont font;
  int TITLE = 0;
  int IN_GAME = 1;
  int GAMEOVER = 2;
  int state;
  int winner;
  Title title;
  Gameover gameover;

  void setup() {
    size(200, 400);
    framerate(30);
    smooth();
    cursor();
    font = loadFont("OCR-A.vlw.gz");
    textFont(font);
    for (int i = 0; i < ship.length; i++)
      ship[i] = new Ship();
    ship[0].type = PLAYER;
    ship[1].type = COMPUTER;
    for (int i = 0; i < battery.length; i++)
      battery[i] = new Battery();
    for (int i = 0; i < bullet.length; i++)
      bullet[i] = new Bullet();
    for (int i = 0; i < particle.length; i++)
      particle[i] = new Particle();
    title = new Title();
    gameover = new Gameover();
    startTitle();
  }

  void startTitle() {
    state = TITLE;
    for (int i = 0; i < battery.length; i++) {
      Battery bt = battery[i];
      bt.start((int) random(BATTERY_FIRE_INTERVAL));
      bt.pos.x = (i % 5) * (width / 5) + width / 10;
      bt.pos.y = (i / 5) * (height / 4) + height / 8 * 3;
    }
  }

  void startInGame() {
    state = IN_GAME;
    for (int i = 0; i < bullet.length; i++)
      bullet[i].start();
    for (int i = 0; i < ship.length; i++)
      ship[i].start();
    for (int i = 0; i < particle.length; i++)
      particle[i].start();
  }

  void startGameover() {
    state = GAMEOVER;
    gameover.start();
  }

  void loop() {
    background(80, 100, 50);
    stroke(200, 255, 200);
    line(0, height / 2, width, height / 2);
    if (state == TITLE) {
      for (int i = 0; i < battery.length; i++)
        battery[i].loop();
      title.loop();
    } else if (state == IN_GAME) {
      for (int i = 0; i < ship.length; i++)
        ship[i].loop();      
      for (int i = 0; i < battery.length; i++)
        battery[i].loop();
      for (int i = 0; i < bullet.length; i++)
        if (bullet[i].isExist)
          bullet[i].loop();
      for (int i = 0; i < particle.length; i++)
        if (particle[i].cnt >= 0)
          particle[i].loop();
      if (batteriesCheckGameSet())
        startGameover();
    } else if (state == GAMEOVER) {
      for (int i = 0; i < battery.length; i++)
        battery[i].loop();
      gameover.loop();
    }
  }

  /**
   * My ship and enemy's ship
   */
  class Ship {
    int RESPAWN_CNT = 240;
    int INVINSIBLE_CNT = 120;
    Vector pos = new Vector();
    Vector vel = new Vector();
    float MAX_SPEED = 8;
    float speed;
    Shot shot = new Shot();
    int type;
    Vector target = new Vector();
    int cnt;
    
    void start() {
      shot.start();
      if (type == PLAYER) {
        shot.dir = -1;
      }  else if (type == COMPUTER) {
        shot.dir = 1;
        setTarget();
      }
      restart();
      cnt = INVINSIBLE_CNT;
    }
    
    void restart() {
      pos.x = width / 2;      
      if (type == PLAYER)
        pos.y = height / 6 * 5;
      else if (type == COMPUTER)
        pos.y = height / 6;
      vel.x = vel.y = 0;
      speed = 0;
      cnt = RESPAWN_CNT;
    }

    void loop() {
      if (cnt > 0)
        cnt--;
      if (shot.isExist)
        shot.loop();
      if (cnt > INVINSIBLE_CNT)
        return;
      pos.x += vel.x;
      pos.y += vel.y;
      if (type == PLAYER) {
        playerMove();
        if (pos.y < height / 2)
          pos.y = height / 2;
        else if (pos.y > height)
          pos.y = height;
      }  else if (type == COMPUTER) {
        computerMove();
        if (pos.y < 0)
          pos.y = 0;
        else if (pos.y > height / 2)
          pos.y = height / 2;
      }
      if (pos.x < 0)
        pos.x = 0;
      if (pos.x > width)
        pos.x = width;
      if (cnt == 0 && bulletsCheckHit(pos)) {
        for (int i = 0; i < 16; i++) {
          Particle pt = particlesGetInstance();
          pt.set(pos);
        }
        restart();
      }
      if (cnt == 0 || cnt % 20 > 10)
        draw();
    }
    
    void playerMove() {
      int mox = mouseX - (int) pos.x;
      int moy = mouseY - (int) pos.y;
      float mouseDst = sqrt(mox * mox + moy * moy);
      if (mouseDst > 0) {
        vel.x += (mox / mouseDst * speed - vel.x) * 0.1f;
        vel.y += (moy / mouseDst * speed - vel.y) * 0.1f;
      }
      if (mouseDst > 5) {
        speed += (MAX_SPEED - speed) * 0.3f;
      } else {
        speed *= 0.5f;
        vel.x *= 0.2f;
        vel.y *= 0.2f;
      }
      if (!shot.isExist) {
        if (mousePressed)
          shot.set(pos);
      }
    }

    void computerMove() {
      int mox = (int) (target.x - pos.x);
      int moy = (int) (target.y - pos.y);
      float mouseDst = sqrt(mox * mox + moy * moy);
      if (mouseDst > 0) {
        vel.x += (mox / mouseDst * speed - vel.x) * 0.1f;
        vel.y += (moy / mouseDst * speed - vel.y) * 0.1f;
      }
      speed = MAX_SPEED;
      if (mouseDst < 5)
        setTarget();
      if (!shot.isExist && checkBatteryFront(pos.x))
        shot.set(pos);
    }
    
    void setTarget() {
      int bi = (int) random(battery.length);
      Battery tb = null;
      for (int i = 0; i < battery.length; i++) {
        tb = battery[bi];
        if (tb.type == PLAYER)
          break;
        bi++;
        if (bi >= battery.length)
          bi = 0;
      }
      target.x = tb.pos.x;
      target.y = random(tb.pos.y / 2);
    }
    
    boolean checkBatteryFront(float x) {
      for (int i = 0; i < battery.length; i++) {
        if (abs(battery[i].pos.x - x) < BATTERY_SIZE)
          return true;
      }
      return false;
    }

    void draw() {
      push();
      translate(pos.x, pos.y);
      if (type == PLAYER) {
        fill(100, 100, 200);
        stroke(180, 190, 250);
        scale(1, -1);
      } else if (type == COMPUTER) {
        fill(200, 100, 100);
        stroke(250, 190, 180);
      }
      beginShape(TRIANGLE_STRIP);
      vertex(-6, -8);
      vertex(0, 8);
      vertex(0, -2);
      vertex(6, -8);
      endShape();
      pop();
    }
  }

  /**
   * Ship's shot.
   */
  class Shot {
    float SPEED = 10;
    Vector pos = new Vector();
    float dir;
    boolean isExist;

    void start() {
      isExist = false;
    }

    void setDir(float d) {
      dir = d;
    }
    
    void set(Vector p) {
      pos.x = p.x;
      pos.y = p.y;
      isExist = true;
    }
    
    void loop() {
      pos.y += SPEED * dir;
      if (pos.y < 0 || pos.y >= height || batteriesCheckHit(pos, dir))
        isExist = false;
      push();
      translate(pos.x, pos.y);
      fill(200, 200, 200);
      stroke(250, 250, 250);
      beginShape(QUADS);
      vertex(-2, -5);      
      vertex(2, -5);      
      vertex(2, 5);      
      vertex(-2, 5);      
      endShape();
      pop();
    }
  }

  /**
   * Battery.
   */
  class Battery {
    float SIZE = BATTERY_SIZE;
    float FLIP_MOVE = 3;
    Vector pos = new Vector();
    Vector vel = new Vector();
    int cnt;
    float fireInterval;
    int type;
    
    void start(int c) {
      vel.x = vel.y = 0;
      cnt = c;
      fireInterval = BATTERY_FIRE_INTERVAL;
    }
    
    boolean checkHit(Vector p, float d) {
      if (p.dist(pos) < SIZE) {
        vel.x += pos.x - p.x;
        vel.y += FLIP_MOVE * d;
        return true;
      }
      return false;
    }
    
    void loop() {
      pos.x += vel.x;
      if (pos.x < 0 || pos.x >= width) {
        vel.x = -vel.x;
        pos.x += vel.x * 2;
      }
      pos.y += vel.y;
      if (pos.y < 50)
        pos.y = 50;
      if (pos.y > height - 50)
        pos.y = height - 50;
      if (pos.y > height / 2)
         type = COMPUTER;
      else
        type = PLAYER;
      vel.x *= 0.95f;
      vel.y *= 0.95f;
      cnt--;
      if (cnt < 0) {
        cnt = (int) fireInterval;
        Bullet b = bulletsGetInstance();
        if (b != null) {
          Vector ap;
          if (type == COMPUTER)
            ap = ship[1].pos;
          else
            ap = ship[0].pos;
          b.set(pos, atan2(ap.x - pos.x, ap.y - pos.y), 3);
        }
      }
      if (fireInterval > 10)
        fireInterval -= 0.01;
      push();
      translate(pos.x, pos.y);
      if (type == COMPUTER) {
        fill(200, 100, 100);
        stroke(220, 200, 200);
      } else {
        fill(100, 100, 200);
        stroke(200, 200, 220);
      }
      beginShape(QUADS);
      vertex(SIZE, 0);
      vertex(0, SIZE);
      vertex(-SIZE, 0);
      vertex(0, -SIZE);
      endShape();
      pop();
    }
  }

  boolean batteriesCheckHit(Vector p, float d) {
    boolean r = false;
    for (int i = 0; i < battery.length; i++)
      if (battery[i].checkHit(p, d))
        r = true;
    return r;
  }

  boolean batteriesCheckGameSet() {
    int tp = battery[0].type;
    for (int i = 1; i < battery.length; i++) {
      if (battery[i].type != tp)
        return false;
    }
    winner = tp;
    return true;
  }

  /**
   * Bullets generated from Battery.
   */
  class Bullet {
    float SIZE = 4;
    Vector pos = new Vector();
    float deg;
    float speed;
    int cnt;
    boolean isExist;
    
    void start() {
      isExist = false;
    }
    
    void set(Vector p, float d, float s) {
      pos.x = p.x;
      pos.y = p.y;
      deg = d;
      speed = s;
      cnt = 0;
      isExist = true;
    }
    
    void loop() {
      pos.x += sin(deg) * speed;
      pos.y += cos(deg) * speed;
      if (pos.x < 0 || pos.x >= width || pos.y < 0 || pos.y >= height) {
        isExist = false;
        return;
      }
      cnt++;
      push();
      translate(pos.x, pos.y);
      fill(200, 200, 100);
      stroke(250, 250, 100);
      rotate(cnt * 0.11f);      
      beginShape(QUADS);
      vertex(SIZE, 0);
      vertex(0, SIZE);
      vertex(-SIZE, 0);
      vertex(0, -SIZE);
      endShape();
      pop();
    }
  }

  int bulletIdx = 0;

  Bullet bulletsGetInstance() {
    for (int i = 0; i < bullet.length; i++) {
      if (!bullet[bulletIdx].isExist)
        return bullet[bulletIdx];
      bulletIdx--;
      if (bulletIdx < 0)
        bulletIdx = bullet.length - 1;
    }
    return null;
  }

  boolean bulletsCheckHit(Vector p) {
    for (int i = 0; i < bullet.length; i++) {
      if (bullet[i].isExist) {
        if (bullet[i].pos.dist(p) < 3)
          return true;
      }
    }
    return false;
  }    

  /**
   * Particle.
   */
  class Particle {
    Vector pos = new Vector();
    Vector vel = new Vector();
    int cnt;
    int size;

    void start() {
      cnt = -1;
    }

    void set(Vector p) {
      pos.x = p.x;
      pos.y = p.y;
      vel.x = random(10) - 5;
      vel.y = random(10) - 5;
      cnt = 15 + (int) random(15);
      size = 5 + (int) random(5);
    }

    void loop() {
      pos.x += vel.x;
      pos.y += vel.y;
      vel.x *= 0.96;
      vel.y *= 0.96;
      cnt--;
      size++;
      stroke(230, 250, 230);
      noFill();
      rect(pos.x, pos.y, size, size);
    }
  }

  int particleIdx = 0;

  Particle particlesGetInstance() {
    particleIdx--;
    if (particleIdx < 0)
      particleIdx = particle.length - 1;
    return particle[particleIdx];
  }

  /**
   * Vector.
   */
  class Vector {
    float x, y;
    
    public float dist(Vector v) {
      float ax = abs(x - v.x);
      float ay = abs(y - v.y);
      if (ax > ay)
        return ax + ay / 2;
      else
        return ay + ax / 2;
    }
  }

  /**
   * Title screen.
   */
  class Title {
    boolean startPressed;

    void loop() {
      boolean onStart;
      if (mouseX > width / 2 - 60
        && mouseX < width / 2 + 60
        && mouseY > height - 100
        && mouseY < height - 60)
        onStart = true;
      else
        onStart = false;
      push();
      translate(0, 0, 30);
      textSize(32);
      textMode(ALIGN_CENTER);
      fill(255, 255, 255);
      text("PGK wars", width / 2, height / 3);
      stroke(250, 100, 50);
      if (onStart)
        fill(150, 250, 200);
      else
        fill(100, 200, 150);
      strokeWeight(2);
      rect(width / 2 - 50, height - 100, 100, 25);
      strokeWeight(1);
      fill(255, 255, 255);
      textSize(25);
      text("START", width / 2, height - 80);
      pop();
      if (onStart && mousePressed)
        startPressed = true;
      if (!mousePressed && startPressed) {
        startPressed = false;
        if (onStart)
          startInGame();
      }
    }
  }

  /**
   * Gameover screen.
   */
  class Gameover {
    int cnt;

    void start() {
      cnt = 0;
    }

    void loop() {
      push();
      translate(0, 0, 30);
      textSize(24);
      textMode(ALIGN_CENTER);
      fill(255, 255, 255);
      if (winner == PLAYER)
        text("You won", width / 2, height / 3);
      else
        text("You lost", width / 2, height / 3);
      pop();
      cnt++;
      if (cnt > 300 || (cnt > 30 && mousePressed)) {
        startTitle();
        return;
      }
    }
  }
}