for Proce55ing
La - Source code

La.java

/**
 * L.A.<br>
 * Life game meets shmup game.<br><br>
 *
 * Copyright (C) 2004 Kenta Cho. Some rights reserved.
 * 
 * @p5 description
 * - 画面中央の自機をマウスで操作してください
 * - マウスボタンを押すと弾を発射します
 * - 前方に発射される赤弾はセルを破壊します
 * - 後方に発射される緑弾はセルを生成します
 * - セルに接触するとゲームオーバーです
 */
public class La extends BApplet {
  Ship ship;
  Shot[] shot = new Shot[6];
  Field field;
  BFont font;
  int TITLE = 0;
  int IN_GAME = 1;
  int GAMEOVER = 2;
  int state;
  Title title;
  Gameover gameover;
  int score;

  void setup() {
    size(300, 300);
    framerate(30);
    smooth();
    cursor();
    font = loadFont("OCR-A.vlw.gz");
    textFont(font);
    field = new Field();
    for (int i = 0; i < shot.length; i++)
      shot[i] = new Shot();
    ship = new Ship();
    title = new Title();
    gameover = new Gameover();
    startTitle();
  }

  void startTitle() {
    state = TITLE;
    field.start();
  }

  void startInGame() {
    state = IN_GAME;
    score = 0;
    initShots();
    ship.start();
    field.start();
  }

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

  void loop() {
    background(40, 60, 20);
    switch (state) {
      case 0 : // TITLE
        field.move();
        field.draw();
        title.loop();
        break;
      case 1 : // IN_GAME
        moveShots();
        ship.move();
        field.move();
        drawShots();
        ship.draw();
        field.draw();
        break;
      case 2 : // GAMEOVER
        ship.draw();
        field.draw();
        gameover.loop();
        break;
    }
    drawStatus();
  }

  void drawStatus() {
    push();
    translate(0, 0, 30);
    textSize(26);
    textMode(ALIGN_RIGHT);
    fill(255, 255, 255);
    text((int) score, width - 18, 36);
    pop();
  }

  float adjustDeg(float d) {
    if (d > PI)
      return d - PI * 2;
    else if (d < -PI)
      return d + PI * 2;
    else
      return d;
  }

  /**
   * Vector.
   */
  class Vector {
    float x, y;
  }

  /**
   * My ship.
   */
  class Ship {
    Vector pos = new Vector();
    Vector vel = new Vector();
    int FIRE_INTERVAL = 10;
    float MAX_SPEED = 10;
    float deg;
    float speed;
    int fireCnt;

    public void start() {
      pos.x = width / 2;
      pos.y = height / 2;
      vel.x = vel.y = 0;
      restart();
    }

    public void restart() {
      deg = 0;
      speed = 0;
      fireCnt = 0;
    }

    public void move() {
      float pcx = pos.x, pcy = pos.y;
      pos.x += vel.x;
      pos.y += vel.y;
      vel.x += (sin(deg) * speed - vel.x) * 0.1f;
      vel.y += (cos(deg) * speed - vel.y) * 0.1f;
      int mox = mouseX - (int) pos.x;
      int moy = mouseY - (int) pos.y;
      float mouseDst = sqrt(mox * mox + moy * moy);
      float sd = mouseDst / (width / 2);
      if (sd > 0.05f)
        speed += (MAX_SPEED * sd - speed) * 0.3f;
      else
        speed *= 0.3f;
      float md = adjustDeg(atan2(mox, moy) - deg);
      if (sd > 0.05f)
        deg += md * 0.2f;
      deg = adjustDeg(deg);
      if (mousePressed) {
        if (fireCnt <= 0) {
          fireCnt = FIRE_INTERVAL;
          add2Shot(pos, -deg);
        }
      }
      fireCnt--;
      if (field.checkHit(pos, null, -1))
        startGameover();
    }

    public void draw() {
      fill(100, 100, 200);
      stroke(180, 190, 250);
      push();
      translate(pos.x, pos.y, 0);
      rotate(-deg, 0, 0, 1);
      beginShape(TRIANGLE_STRIP);
      vertex(-6, -8, 0);
      vertex(0, 8, 0);
      vertex(0, -2, 0);
      vertex(6, -8, 0);
      endShape();
      pop();
    }
  }

  /**
   * My shot.
   */
  class Shot {
    int REMOVE = 0;
    int CREATE = 1;
    float SPEED = 6;
    Vector pos = new Vector();
    Vector ppos = new Vector();
    float deg;
    int type;
    boolean isExist = false;

    void set(Vector p, float d, int t) {
      pos.x = p.x;
      pos.y = p.y;
      deg = d;
      type = t;
      isExist = true;
    }

    void remove() {
      isExist = false;
    }

    void move() {
      ppos.x = pos.x;
      ppos.y = pos.y;
      pos.x -= sin(deg) * SPEED;
      pos.y += cos(deg) * SPEED;
      if (field.checkHit(pos, ppos, type))
        isExist = false;
    }

    public void draw() {
      push();
      translate(pos.x, pos.y, 0);
      rotate(deg, 0, 0, 1);
      if (type == REMOVE) {
        fill(200, 100, 100);
        stroke(250, 150, 150);
      } else {
        fill(100, 200, 100);
        stroke(150, 250, 150);
      }
      beginShape(TRIANGLE_STRIP);
      vertex(0, 5, 0);
      vertex(-2, -5, 0);
      vertex(2, -5, 0);
      endShape();
      pop();
    }
  }

  void initShots() {
    for (int i = 0; i < shot.length; i++)
      shot[i].isExist = false;
  }
  void moveShots() {
    for (int i = 0; i < shot.length; i++)
      if (shot[i].isExist)
        shot[i].move();
  }
  void drawShots() {
    for (int i = 0; i < shot.length; i++)
      if (shot[i].isExist)
        shot[i].draw();
  }
  int shotIdx = 0;
  Shot getShotInstance() {
    for (int i = 0; i < shot.length; i++) {
      if (!shot[shotIdx].isExist)
        return shot[shotIdx];
      shotIdx--;
      if (shotIdx < 0)
        shotIdx = shot.length - 1;
    }
    return null;
  }
  void add2Shot(Vector p, float d) {
    Shot s = getShotInstance();
    if (s == null)
      return;
    s.set(p, d, 0);
    Shot s2 = getShotInstance();
    if (s2 == null) {
      s.isExist = false;
      return;
    }
    s2.set(p, d + PI, 1);
  }

  /**
   * Life game field.
   */
  class Field {
    int EMPTY = 0;
    int EXIST = 1;
    int TAINT = 2;
    int WIDTH = 30, HEIGHT = 30;
    float SIZE = 10.0f, DSIZE = 9.0f;
    int[][] cell = new int[30][30];
    int[][] cellCnt = new int[30][30];
    boolean[][] cellTaint = new boolean[30][30];
    float interval;
    int cnt;

    public void start() {
      for (int y = 0; y < HEIGHT; y++)
        for (int x = 0; x < WIDTH; x++)
          cell[x][y] = EMPTY;
      interval = 12.0f;
    }

    public void move() {
      cnt--;
      if (cnt > 0)
        return;
      cnt = (int) interval;
      interval *= 0.998f;

      for (int y = 0; y < HEIGHT; y++) {
        for (int x = 0; x < WIDTH; x++) {
          if (x > 0 && x < WIDTH - 1 && y > 0 && y < HEIGHT - 1)
            continue;
          int c;
          if (((int) random(4)) != 0)
            c = EXIST;
          else
            c = EMPTY;
          cell[x][y] = c;
        }
      }

      for (int y = 1; y < HEIGHT - 1; y++) {
        for (int x = 1; x < WIDTH - 1; x++) {
          int c = 0;
          boolean t = true;
          for (int oy = -1; oy <= 1; oy++) {
            for (int ox = -1; ox <= 1; ox++) {
              if (cell[x + ox][y + oy] != EMPTY)
                c++;
              if (cell[x + ox][y + oy] == EXIST)
                t = false;
            }
          }
          cellCnt[x][y] = c;
          cellTaint[x][y] = t;
        }
      }
      for (int y = 1; y < HEIGHT - 1; y++) {
        for (int x = 1; x < WIDTH - 1; x++) {
          int c = cellCnt[x][y];
          if (cell[x][y] != EMPTY) {
            if (c < 3 || c > 4)
              cell[x][y] = EMPTY;
          } else {
            if (c == 3) {
              if (cellTaint[x][y])
                cell[x][y] = TAINT;
              else
                cell[x][y] = EXIST;
            }
          }
        }
      }
    }

    public boolean checkHit(Vector p, Vector pp, int t) {
      int cx = (int) (p.x / SIZE);
      int cy = (int) (p.y / SIZE);
      if (cx < 0 || cx >= WIDTH || cy < 0 || cy >= HEIGHT)
        return true;
      if (cell[cx][cy] != EMPTY) {
        if (t == 0) {
          cell[cx][cy] = EMPTY;
          score++;
        } else if (t == 1) {
          int pcx = (int) (pp.x / SIZE);
          int pcy = (int) (pp.y / SIZE);
          cell[pcx][pcy] = TAINT;
        }
        return true;
      }
      return false;
    }

    public void draw() {
      float sx, sy;
      sy = 0;
      for (int y = 0; y < HEIGHT; y++, sy += SIZE) {
        sx = 0;
        for (int x = 0; x < WIDTH; x++, sx += SIZE) {
          if (cell[x][y] != EMPTY) {
            if (cell[x][y] == TAINT) {
              stroke(150, 220, 150);
              fill(100, 150, 100);
            } else {
              stroke(180, 180, 150);
              fill(120, 120, 100);
            }
            beginShape(QUADS);
            vertex(sx, sy, 0);
            vertex(sx + DSIZE, sy, 0);
            vertex(sx + DSIZE, sy + DSIZE, 0);
            vertex(sx, sy + DSIZE, 0);
            endShape();
          }
        }
      }
    }
  }

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

    void loop() {
      boolean onStart;
      if (mouseX > width / 2 - 50
        && mouseX < width / 2 + 50
        && mouseY > height - 100
        && mouseY < height - 75)
        onStart = true;
      else
        onStart = false;
      push();
      translate(0, 0, 30);
      textSize(42);
      textMode(ALIGN_CENTER);
      fill(255, 255, 255);
      text("L.A.", width / 2, height / 3);
      stroke(250, 100, 50);
      if (onStart)
        fill(200, 250, 150);
      else
        fill(150, 200, 100);
      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();
      drawStatus();
      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(32);
      textMode(ALIGN_CENTER);
      fill(255, 255, 255);
      text("Game Over", width / 2, height / 3);
      pop();
      cnt++;
      if (cnt > 300 || (cnt > 30 && mousePressed)) {
        startTitle();
        return;
      }
    }
  }
}