for Proce55ing
EDB0 - Source code

EDB0.java

import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

/**
 * 0xEDB0.
 *
 * Copyright (C) Kenta Cho. Some rights reserved.
 *
 * @p5 description
 * - [W][A][S][D]いずれかのキーを押してゲームを始めてください
 * - キーが効かない場合はゲーム画面を一回クリックしてください
 * - [W][A][S][D]で赤い数を操作して緑の数を倒してください
 * - 白い数または緑の数に接触すると赤い数は破壊されます
 * - 赤い数が3回破壊されるとゲームオーバーです
 * - 左下に赤い数の残り数、右下にスコアが表示されます
 * - 数の位置はすべて16進数で表されており、2進数に変換したビットが位置に相当します
 * - 例) 4 -> 0100 B -> 1011
 */
public class EDB0 extends BApplet {
  Pad pad;
  HexadecimalScreen backScreen, bulletScreen, shipScreen, enemyScreen;
  Ship ship;
  Shot[] shot = new Shot[32];
  Enemy[] enemy = new Enemy[128];
  Bullet[] bullet = new Bullet[256];
  BFont font;
  int TITLE = 0;
  int IN_GAME = 1;
  int GAMEOVER = 2;
  int state;
  Title title;
  Gameover gameover;
  int aimEnemyAppInterval;
  int laserEnemyAppInterval;
  float rank;
  int score;
  int left;
  boolean padPressed;

  void setup() {
    size(240, 480);
    framerate(30);
    smooth();
    cursor();
    font = loadFont("OCR-A.vlw.gz");
    textFont(font);
    pad = new Pad();
    addKeyListener(pad);
    backScreen = new HexadecimalScreen(8, 48, true);
    bulletScreen = new HexadecimalScreen(8, 48, false);
    shipScreen = new HexadecimalScreen(8, 48, false);
    enemyScreen = new HexadecimalScreen(8, 48, false);
    ship = new Ship();
    for (int i = 0; i < shot.length; i++)
      shot[i] = new Shot();
    for (int i = 0; i < enemy.length; i++)
      enemy[i] = new Enemy();
    for (int i = 0; i < bullet.length; i++)
      bullet[i] = new Bullet();
    title = new Title();
    gameover = new Gameover();
    startTitle();
  }

  void startTitle() {
    state = TITLE;
    left = -1;
    padPressed = true;
  }

  void startInGame() {
    state = IN_GAME;
    ship.init();
    for (int i = 0; i < shot.length; i++)
      shot[i].init();
    for (int i = 0; i < enemy.length; i++)
      enemy[i].init();
    for (int i = 0; i < bullet.length; i++)
      bullet[i].init();
    aimEnemyAppInterval = laserEnemyAppInterval = 0;
    rank = 1;
    score = 0;
    left = 2;
  }

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

  void loop() {
    background(30, 40, 50);
    fill(100, 100, 100);
    backScreen.draw();
    if (state == TITLE) {
      title.loop();
    } else if (state == IN_GAME) {
      bulletScreen.clear();
      shipScreen.clear();
      enemyScreen.clear();
      rank += 0.0025f;
      aimEnemyAppInterval--;
      if (aimEnemyAppInterval < 0) {
        Enemy en = getEnemyInstance();
        if (en != null)
          en.set(random(enemyScreen.scDightWidth), en.TYPE_AIM);
        aimEnemyAppInterval = (int) ((8 + random(3)) / (rank * 0.5f));
      }
      laserEnemyAppInterval--;
      if (laserEnemyAppInterval < 0) {
        for (int i = 0; i < (3 + random(2)) * rank; i++) {
          Enemy en = getEnemyInstance();
          if (en != null)
            en.set(random(enemyScreen.scDightWidth), en.TYPE_LASER);
        }
        laserEnemyAppInterval = (int) (20 + random(5));
      }
      ship.loop();
      for (int i = 0; i < shot.length; i++)
        if (shot[i].isExist)
          shot[i].loop();
      for (int i = 0; i < enemy.length; i++)
        if (enemy[i].isExist)
          enemy[i].loop();
      for (int i = 0; i < bullet.length; i++)
        if (bullet[i].isExist)
          bullet[i].loop();
      fill(200, 255, 200);
      enemyScreen.draw();
      fill(255, 200, 200);
      shipScreen.draw();
      fill(255, 255, 255);
      bulletScreen.draw();
    } else if (state == GAMEOVER) {
      gameover.loop();
    }
    textSize(24);
    textMode(ALIGN_RIGHT);
    text("0x" + Integer.toString(score, 16).toUpperCase(), width, height);
    if (left >= 0) {
      textMode(ALIGN_LEFT);
      text("0x" + Integer.toString(left, 16).toUpperCase(), 0, height);
    }
  }

  /**
   * My ship.
   */
  class Ship {
    float SPEED = 1;
    Vector pos = new Vector();
    Vector ppos = new Vector();
    int SHOT_INTERVAL = 6;
    int shotCnt;
    int cnt;
    int RESTART_CNT = 100;
    int INVINCIBLE_CNT = 75;

    void init() {
      restart();
      cnt = 0;
    }

    void restart() {
      pos.x = shipScreen.scDightWidth / 2;
      pos.y = shipScreen.scHeight / 4 * 3;
      shotCnt = SHOT_INTERVAL;
      cnt = RESTART_CNT;
    }

    void loop() {
      int dir = pad.getDirState();
      if (cnt > INVINCIBLE_CNT) {
        dir = 0;
        shotCnt = SHOT_INTERVAL;
      }
      float mx = 0, my = 0;
      if ((dir & pad.DIR_UP) != 0)
        my = -1;
      if ((dir & pad.DIR_DOWN) != 0)
        my = 1;
      if ((dir & pad.DIR_LEFT) != 0)
        mx = -1;
      if ((dir & pad.DIR_RIGHT) != 0)
        mx = 1;
      if (mx != 0 && my != 0) {
        mx *= 0.71f;
        my *= 0.71f;
      }
      mx *= SPEED;
      my *= SPEED;
      ppos.x = pos.x;
      ppos.y = pos.y;
      pos.x += mx;
      pos.y += my;
      if (pos.x < 0)
        pos.x = 0;
      else if (pos.x >= shipScreen.scDightWidth)
        pos.x = shipScreen.scDightWidth - 0.01f;
      if (pos.y < 0)
        pos.y = 0;
      else if (pos.y >= shipScreen.scHeight)
        pos.y = shipScreen.scHeight - 0.01f;
      shotCnt--;
      if (shotCnt < 0) {
        shotCnt = SHOT_INTERVAL;
        Shot s = getShotInstance();
        if (s != null)
          s.set(pos, PI, 1);
      }
      if (cnt <= 0
        && (enemiesCheckHit(pos)
          || bulletsCheckHit(pos)
          || bulletsCheckHit(ppos))) {
        for (int i = 0; i < 16; i++) {
          Shot s = getShotInstance();
          if (s != null)
            s.set(pos, random(PI * 2), 0.5f + random(1.5f));
        }
        left--;
        restart();
      }
      if (cnt <= 0 || (cnt < INVINCIBLE_CNT && cnt % 16 >= 8))
        shipScreen.pset((int) pos.x, (int) pos.y);
      if (cnt > 0) {
        if (left < 0 && cnt <= INVINCIBLE_CNT)
          startGameover();
        if (cnt > INVINCIBLE_CNT / 2)
          bulletsClear();
        cnt--;
      }
    }
  }

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

    void init() {
      isExist = false;
    }

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

    void loop() {
      if (deg != PI)
        pos.x += sin(deg) * speed;
      pos.y += cos(deg) * speed;
      if (pos.x < 0
        || pos.x >= bulletScreen.scDightWidth
        || pos.y < 0
        || pos.y >= bulletScreen.scHeight
        || enemiesCheckHit(pos)) {
        isExist = false;
        return;
      }
      shipScreen.pset((int) pos.x, (int) pos.y);
    }
  }

  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;
  }

  /**
   * Enemy.
   */
  class Enemy {
    Vector pos = new Vector();
    Vector vel = new Vector();
    boolean isExist;
    int TYPE_AIM = 0;
    int TYPE_LASER = 1;
    int type;
    boolean loaded;

    void init() {
      isExist = false;
    }

    void set(float x, int t) {
      pos.x = x;
      pos.y = 0;
      type = t;
      vel.x = random(1.0f) - 0.5f;
      switch (type) {
        case 0 : //TYPE_AIM:
          vel.y = 0.7f + random(0.3f);
          break;
        case 1 : //TYPE_LASER:
          vel.y = 0.5f;
          break;
      }
      isExist = true;
      loaded = true;
    }

    void loop() {
      pos.x += vel.x;
      pos.y += vel.y;
      vel.y -= 0.03f;
      if (pos.x < 0
        || pos.x >= bulletScreen.scDightWidth
        || pos.y < 0
        || pos.y >= bulletScreen.scHeight) {
        isExist = false;
        return;
      }
      if (loaded && vel.y <= 0) {
        loaded = false;
        Bullet b = getBulletInstance();
        if (b != null) {
          if (type == TYPE_AIM)
            b.set(
              pos,
              atan2(ship.pos.x - pos.x, ship.pos.y - pos.y),
              0.5f);
          else
            b.set(pos, 0, 0.5f);
        }
      }
      enemyScreen.pset((int) pos.x, (int) pos.y);
    }

    boolean checkHit(Vector p) {
      if ((int) p.x == (int) pos.x && (int) p.y == (int) pos.y) {
        isExist = false;
        score++;
        return true;
      }
      return false;
    }
  }

  int enemyIdx = 0;

  Enemy getEnemyInstance() {
    for (int i = 0; i < enemy.length; i++) {
      if (!enemy[enemyIdx].isExist)
        return enemy[enemyIdx];
      enemyIdx--;
      if (enemyIdx < 0)
        enemyIdx = enemy.length - 1;
    }
    return null;
  }

  boolean enemiesCheckHit(Vector p) {
    boolean r = false;
    for (int i = 0; i < enemy.length; i++)
      if (enemy[i].isExist && enemy[i].checkHit(p))
        r = true;
    return r;
  }

  /**
   * Bullets.
   */
  class Bullet {
    float SIZE = 4;
    Vector pos = new Vector();
    float deg;
    float speed;
    int cnt;
    boolean isExist;

    void init() {
      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 >= bulletScreen.scDightWidth
        || pos.y < 0
        || pos.y >= bulletScreen.scHeight) {
        isExist = false;
        return;
      }
      cnt++;
      bulletScreen.pset((int) pos.x, (int) pos.y);
    }

    boolean checkHit(Vector p) {
      if ((int) p.x == (int) pos.x && (int) p.y == (int) pos.y) {
        isExist = false;
        return true;
      }
      return false;
    }
  }

  int bulletIdx = 0;

  Bullet getBulletInstance() {
    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) {
    boolean r = false;
    for (int i = 0; i < bullet.length; i++)
      if (bullet[i].isExist && bullet[i].checkHit(p))
        r = true;
    return r;
  }

  void bulletsClear() {
    for (int i = 0; i < bullet.length; i++)
      bullet[i].isExist = false;
  }

  /**
   * 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 handler.
   */
  class Title {
    void loop() {
      textSize(42);
      textMode(ALIGN_CENTER);
      fill(255, 255, 255);
      text("0xEDB0", width / 2, height / 3);
      if (pad.getDirState() != 0) {
        if (!padPressed) {
          startInGame();
        }
        padPressed = true;
      } else {
        padPressed = false;
      }
    }
  }

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

    void start() {
      cnt = 0;
    }

    void loop() {
      textSize(32);
      textMode(ALIGN_CENTER);
      fill(255, 255, 255);
      text("0x76", width / 2, height / 3);
      cnt++;
      if (cnt > 300)
        startTitle();
      if (pad.getDirState() != 0) {
        if (!padPressed && cnt > 30) {
          startTitle();
        }
        padPressed = true;
      } else {
        padPressed = false;
      }
    }
  }

  /**
   * Screen for drawing dots in a hexadecimal form.
   */
  class HexadecimalScreen {
    int[][] v;
    int scWidth, scHeight, scDightWidth;
    boolean drawZero;
    float ox, oy, xscl;
    float tSize;

    HexadecimalScreen(int w, int h, boolean dz) {
      scWidth = w;
      scHeight = h;
      scDightWidth = w * 4;
      tSize = height / scHeight * 1.5f;
      ox = width / scWidth;
      oy = height / scHeight;
      xscl = ox / oy * 1.25f;
      ox /= xscl;
      drawZero = dz;
      if (!drawZero)
        v = new int[w][h];
    }

    void clear() {
      for (int x = 0; x < scWidth; x++)
        for (int y = 0; y < scHeight; y++)
          v[x][y] = 0;
    }

    void pset(int x, int y) {
      v[x / 4][y] |= 8 >> (x % 4);
    }

    void draw() {
      textMode(ALIGN_LEFT);
      textSize(tSize);
      float sx, sy;
      push();
      scale(xscl, 1);
      sx = 0;
      if (drawZero) {
        for (int x = 0; x < scWidth; x++, sx += ox) {
          sy = oy;
          for (int y = 0; y < scHeight; y++, sy += oy) {
            text("0", sx, sy);
          }
        }
      } else {
        for (int x = 0; x < scWidth; x++, sx += ox) {
          sy = oy;
          for (int y = 0; y < scHeight; y++, sy += oy) {
            if (v[x][y] == 0)
              continue;
            text("0123456789ABCDEF".charAt(v[x][y]), sx, sy);
          }
        }
      }
      pop();
    }
  }

  /**
   * For Handling a simultaneous key input.
   */
  class Pad implements KeyListener {
    int DIR_UP = 1, DIR_DOWN = 2, DIR_LEFT = 4, DIR_RIGHT = 8;
    int dir = 0;

    public void keyPressed(KeyEvent e) {
      int kc = e.getKeyCode();
      if (kc == KeyEvent.VK_W)
        dir |= DIR_UP;
      if (kc == KeyEvent.VK_S)
        dir |= DIR_DOWN;
      if (kc == KeyEvent.VK_A)
        dir |= DIR_LEFT;
      if (kc == KeyEvent.VK_D)
        dir |= DIR_RIGHT;
    }

    public void keyReleased(KeyEvent e) {
      int kc = e.getKeyCode();
      if (kc == KeyEvent.VK_W)
        dir &= (0xFFFF ^ DIR_UP);
      if (kc == KeyEvent.VK_S)
        dir &= (0xFFFF ^ DIR_DOWN);
      if (kc == KeyEvent.VK_A)
        dir &= (0xFFFF ^ DIR_LEFT);
      if (kc == KeyEvent.VK_D)
        dir &= (0xFFFF ^ DIR_RIGHT);
    }

    public void keyTyped(KeyEvent e) {
    }

    public int getDirState() {
      return dir;
    }
  }
}