Logo Search packages:      
Sourcecode: val-and-rick version File versions  Download package

ship.d

/*
 * $Id: ship.d,v 1.1.1.1 2005/03/13 16:15:04 kenta Exp $
 *
 * Copyright 2005 Kenta Cho. Some rights reserved.
 */
module abagames.vr.ship;

private import std.math;
private import opengl;
private import abagames.util.vector;
private import abagames.util.rand;
private import abagames.util.math;
private import abagames.util.sdl.pad;
private import abagames.util.sdl.recordableinput;
private import abagames.util.bulletml.bullet;
private import abagames.vr.field;
private import abagames.vr.gamemanager;
private import abagames.vr.screen;
private import abagames.vr.shape;
private import abagames.vr.bullettarget;
private import abagames.vr.particle;
private import abagames.vr.explosion;
private import abagames.vr.letter;
private import abagames.vr.shot;
private import abagames.vr.enemy;
private import abagames.vr.stagemanager;
private import abagames.vr.soundmanager;
private import abagames.vr.crystal;
private import abagames.vr.prefmanager;

/**
 * Player's ship.
 */
00034 public class Ship: BulletTarget {
 public:
  static bool replayMode;
 private:
  static const float SIGHT_RANGE_BASE = 7;
  static const float SIGHT_RANGE_MOVE = 5;
  static const float SIGHT_RANGE_VEL = 0.1f;
  static const int RESTART_CNT = 300;
  static const int INVINCIBLE_CNT = 228;
  static const float HIT_WIDTH = 0.02f;
  static const int FIRE_INTERVAL = 3;
  static const float SCROLL_SPEED_BASE = 0.01f;
  static const float SCROLL_SPEED_MAX = 0.1f;
  static const float SCROLL_SPEED_AIR = 0.3f;
  static const float SCROLL_START_Y = 3;
  static const float SPEED_BASE = 0.15f;
  static const int SHADOW_NUM = 100;
  static const int SHADOW_STEP = 10;
  static Rand rand;
  RecordablePad pad;
  Field field;
  Screen screen;
  ShotPool shots;
  ParticlePool particles;
  ExplosionPool explosions;
  EnemyPool enemies;
  StageManager stageManager;
  InGameState gameState;
  Vector _pos;
  float deg;
  float speed;
  BitmapShape _shape;
  int fireCnt;
  int fireSprCnt;
  float fireSprDeg;
  int cnt;
  float sightRange;
  Blaster blaster;
  BitmapShape sightShape;
  bool bBtnPressed;
  float scrollSpeed, _scrollSpeedBase;
  bool _airMode, _toAir, _toGround, opening;
  int modeChangeCnt;
  float crystalNum, purserAppCrystalNum;
  int purserGetCnt;
  bool _purserReady;
  bool crystalFull;
  Purser purser;
  ShipShadow[SHADOW_NUM] shadow;
  int shadowIdx, shadowNum;
  bool onBlock;

  public static void setRandSeed(long seed) {
    rand.setSeed(seed);
  }

  public this(Pad pad, Field field, Screen screen) {
    rand = new Rand;
    this.pad = cast(RecordablePad) pad;
    this.field = field;
    this.screen = screen;
    _pos = new Vector;
    _shape = new BitmapShape(BitmapShape.ship, 0);
    sightRange = SIGHT_RANGE_BASE;
    sightShape = new BitmapShape(BitmapShape.sight, 0);
    purser = new Purser(field);
  }

  public void setParticles(ParticlePool particles) {
    this.particles = particles;
  }

  public void setExplosions(ExplosionPool explosions) {
    this.explosions = explosions;
  }

  public void setShots(ShotPool shots) {
    this.shots = shots;
    foreach (inout ShipShadow ss; shadow)
      ss = new ShipShadow(_shape, shots);
  }

  public void setEnemies(EnemyPool enemies) {
    this.enemies = enemies;
    blaster = new Blaster(field, enemies);
  }

  public void setStageManager(StageManager stageManager) {
    this.stageManager = stageManager;
  }

  public void setGameState(InGameState gameState) {
    this.gameState = gameState;
  }

  public void start() {
    _pos.x = 0;
    _pos.y = -field.size.y;
    deg = 0;
    speed = SPEED_BASE;
    cnt = -INVINCIBLE_CNT;
    fireSprDeg = 0.5f;
    bBtnPressed = true;
    _scrollSpeedBase = SCROLL_SPEED_BASE;
    purserGetCnt = -1;
    purser.exists = false;
    blaster.exists = false;
    setNextCrystalNum();
    _airMode = true;
    _toAir = false;
    _toGround = true;
    modeChangeCnt = 60;
    shadowNum = 0;
    opening = true;
    restart();
  }

  private void setNextCrystalNum() {
    purserGetCnt++;
    crystalNum = 0;
    purserAppCrystalNum = (purserGetCnt + 1) * 100;
    _purserReady = crystalFull = false;
  }

  public void restart() {
    fireCnt = fireSprCnt = 0;
    sightRange = SIGHT_RANGE_BASE;
    scrollSpeed = _scrollSpeedBase;
    foreach (ShipShadow ss; shadow)
      ss.set(_pos, deg);
    shadowIdx = shadow.length - 1;
    if (field.getBlock(_pos) >= 0)
      onBlock = true;
    else
      onBlock = false;
  }

  public void move() {
    float px = _pos.x, py = _pos.y;
    field.scroll(scrollSpeed);
    addDistance(scrollSpeed * 10);
    cnt++;
    PadState input;
    if (!replayMode) {
      input = pad.getState();
    } else {
      try {
        input = pad.replay();
      } catch (NoRecordDataException e) {
        gameState.isGameOver = true;
        input = pad.getNullState();
      }
    }
    if (gameState.isGameOver) {
      input.clear();
      clearVisibleBullets();
      if (cnt < -INVINCIBLE_CNT)
        cnt = -RESTART_CNT;
    } else if (cnt < -INVINCIBLE_CNT) {
      input.clear();
      clearVisibleBullets();
    } else if (_toAir || _toGround) {
      input.clear();
      clearVisibleBullets();
    }
    float vx = 0, vy = 0;
    if (input.dir & PadState.Dir.UP)
      vy = 1;
    if (input.dir & PadState.Dir.DOWN)
      vy = -1;
    if (input.dir & PadState.Dir.RIGHT)
      vx = 1;
    if (input.dir & PadState.Dir.LEFT)
      vx = -1;
    if (vx != 0 && vy != 0) {
      vx *= 0.7f;
      vy *= 0.7f;
    }
    if (!_airMode && (vx != 0 || vy != 0) && !(input.button & PadState.Button.B)) {
      float ad = atan2(vx, vy);
      Math.normalizeDeg(ad);
      ad -= deg;
      if (ad > PI)
        ad -= PI * 2;
      else if (ad < -PI)
        ad += PI * 2;
      deg += ad * 0.15f;
      Math.normalizeDeg(deg);
    }
    vx *= speed;
    vy *= speed;
    if (!_airMode && field.checkInField(_pos.x, _pos.y - field.lastScrollY))
      _pos.y -= field.lastScrollY;
    if ((_airMode || onBlock || field.getBlock(_pos.x + vx, _pos.y) < 0) &&
        field.checkInField(_pos.x + vx, _pos.y))
      _pos.x += vx;
    bool srf = false;
    if ((_airMode || onBlock || field.getBlock(px, _pos.y + vy) < 0) &&
        field.checkInField(_pos.x, _pos.y + vy)) {
      _pos.y += vy;
    } else {
      if (input.dir & PadState.Dir.UP) {
        if (sightRange < SIGHT_RANGE_BASE + SIGHT_RANGE_MOVE)
          sightRange += SIGHT_RANGE_VEL;
        srf = true;
      }
      if (input.dir & PadState.Dir.DOWN) {
        if (sightRange > SIGHT_RANGE_BASE - SIGHT_RANGE_MOVE)
          sightRange -= SIGHT_RANGE_VEL;
        srf = true;
      }
    }
    if (!srf) {
      sightRange += (SIGHT_RANGE_BASE - sightRange) * 0.1f;
    }
    if (!_airMode && field.getBlock(_pos.x, _pos.y) >= 0) {
      if (!onBlock)
        if (cnt <= 0)
          onBlock = true;
        else {
          if (field.checkInField(_pos.x, _pos.y - field.lastScrollY)) {
            _pos.x = px;
            _pos.y = py;
          } else {
            destroyed();
          }
        }
    } else {
      onBlock = false;
    }
    if (_airMode) {
      if (_toAir) {
        deg *= 0.95f;
        if (modeChangeCnt == 1)
          SoundManager.fadeBgm();
        else if (modeChangeCnt == 40)
          SoundManager.playBgm("sky.ogg");
        if (modeChangeCnt < 100) {
          pos.y += (field.size.y * -0.8f - pos.y) * 0.02f;
          if (scrollSpeed < SCROLL_SPEED_AIR)
            scrollSpeed += SCROLL_SPEED_AIR * 0.002f;
        } else {
          if (modeChangeCnt == 100)
            addRocketParticles();
          pos.y += (field.size.y * -0.1f - pos.y) * 0.04f;
          if (scrollSpeed < SCROLL_SPEED_AIR)
            scrollSpeed += SCROLL_SPEED_AIR * 0.05f;
        }
        modeChangeCnt++;
        if (modeChangeCnt > 120)
          _toAir = false;
        stageManager.changeZakoForced();
      } else if (_toGround) {
        pos.y += (field.size.y * -0.5f - pos.y) * 0.02f;
        if (modeChangeCnt == 1)
          SoundManager.fadeBgm();
        else if (modeChangeCnt == 120 && !opening)
          SoundManager.playBgm("ground.ogg");
        modeChangeCnt++;
        if (modeChangeCnt > 120) {
          _toGround = false;
          _airMode = false;
          opening = false;
          stageManager.changeZakoForced();
        } else if (modeChangeCnt > 110) {
          scrollSpeed *= 0.95f;
        }
      } else {
        scrollSpeed += (SCROLL_SPEED_AIR - scrollSpeed) * 0.1f;
      }
    } else if (_pos.y >= SCROLL_START_Y) {
      scrollSpeed += (SCROLL_SPEED_MAX - scrollSpeed) * 0.1f;
    } else {
      scrollSpeed += (_scrollSpeedBase - scrollSpeed) * 0.1f;
    }
    bool fireEvenShadow = false, fireOddShadow = false;
    if (input.button & PadState.Button.A) {
      if (fireCnt <= 0) {
        SoundManager.playSe("shot.wav");
        Shot s = shots.getInstance();
        if (s)
          s.set(_pos, deg);
        fireCnt = FIRE_INTERVAL;
        float td;
        switch (fireSprCnt % 2) {
        case 0:
          td = fireSprDeg * (fireSprCnt / 2 % 4 + 1) * 0.2f;
          fireEvenShadow = true;
          break;
        case 1:
          td = - fireSprDeg * (fireSprCnt / 2 % 4 + 1) * 0.2f;
          fireOddShadow = true;
          break;
        }
        fireSprCnt++;
        s = shots.getInstance();
        if (s)
          s.set(pos, deg + td);
      }
    }
    fireCnt--;
    if (fireEvenShadow || fireOddShadow) {
      int si = shadowIdx;
      for (int i = 0; i < shadowNum; i++) {
        si -= SHADOW_STEP;
        if (si < 0)
          si += shadow.length;
        if ((i % 2 == 0 && fireEvenShadow) || (i % 2 == 1 && fireOddShadow))
          shadow[si].fire();
      }
    }
    if (input.button & PadState.Button.B) {
      foreach (ShipShadow ss; shadow)
        ss.slide(_pos.x - px, _pos.y - py);
    } else {
      foreach (ShipShadow ss; shadow)
        ss.slide(0, -scrollSpeed);
      if (input.dir != 0 || _airMode) {
        shadow[shadowIdx].set(_pos, deg);
        shadowIdx++;
        if (shadowIdx >= shadow.length)
          shadowIdx = 0;
      }
    }
    if (!airMode) {
      if (input.button & PadState.Button.B) {
        if (!bBtnPressed && !blaster.exists) {
          blaster.set(_pos, sightRange);
        }
        bBtnPressed = true;
      } else {
        bBtnPressed = false;
      }
      speed = SPEED_BASE;
    } else {
      if (input.button & PadState.Button.B)
        speed += (SPEED_BASE * 0.9f - speed) * 0.1f;
      else
        speed += (SPEED_BASE * 1.5f - speed) * 0.1f;
    }
    if (blaster.exists)
      blaster.move();
    if (purser.exists) {
      purser.move();
      if (!purser.exists) {
        setNextCrystalNum();
      } else if (purser.pos.dist(_pos) < 1) {
        SoundManager.playSe("large_dest.wav");
        purser.exists = false;
        _airMode = true;
        _toAir = true;
        scrollSpeed = 0;
        modeChangeCnt = 0;
        enemies.destroy();
      }
    }
    if (_airMode && !_toAir && !_toGround) {
      crystalNum -= 0.15f;
      if (crystalNum <= 0)
        destroyed(true);
    }
    _scrollSpeedBase += (SCROLL_SPEED_MAX - _scrollSpeedBase) * 0.00001f;
  }

  public Vector getTargetPos() {
    return _pos;
  }

  public bool checkBulletHit(Vector p, Vector pp) {
    if (cnt <= 0 || _toAir || _toGround)
      return false;
    float bmvx, bmvy, inaa;
    bmvx = pp.x;
    bmvy = pp.y;
    bmvx -= p.x;
    bmvy -= p.y;
    inaa = bmvx * bmvx + bmvy * bmvy;
    if (inaa > 0.00001) {
      float sofsx, sofsy, inab, hd;
      sofsx = _pos.x;
      sofsy = _pos.y;
      sofsx -= p.x;
      sofsy -= p.y;
      inab = bmvx * sofsx + bmvy * sofsy;
      if (inab >= 0 && inab <= inaa) {
        hd = sofsx * sofsx + sofsy * sofsy - inab * inab / inaa;
        if (hd >= 0 && hd <= HIT_WIDTH) {
          destroyed();
          return true;
        }
      }
    }
    return false;
  }

  public bool checkEnemyHit(EnemySpec spec, EnemyState state) {
    if (_toAir || _toGround)
      return false;
    bool hit = spec.checkCollision(fabs(_pos.x - state.pos.x) * 1.5f, fabs(_pos.y - state.pos.y) * 1.5f, _shape);
    if (hit)
      destroyed();
    return hit;
  }

  private void destroyed(bool outOfCrystal = false) {
    if (cnt <= 0)
      return;
    Explosion ep = explosions.getInstance();
    if (ep)
      ep.set(pos, 0, 0, 40, 3);
    if (!outOfCrystal) {
      for (int i = 0; i < 128; i++) {
        Particle pt = particles.getInstanceForced();
        pt.set(pos, rand.nextSignedFloat(1), rand.nextSignedFloat(1),
               rand.nextFloat(0.25f) + 0.75f, rand.nextFloat(0.25f) + 0.75f, 0.33f, 32 + rand.nextInt(64));
      }
      SoundManager.playSe("ship_dest.wav");
      screen.setScreenShake(60, 0.05f);
    } else {
      SoundManager.playSe("large_dest.wav");
    }
    if (_airMode) {
      _toGround = true;
      modeChangeCnt = 0;
      setNextCrystalNum();
      enemies.destroy();
    } else {
      gameState.shipDestroyed();
      shadowNum++;
      restart();
      cnt = -RESTART_CNT;
    }
  }

  private void addRocketParticles() {
    SoundManager.playSe("rocket.wav");
    for (int i = 0; i < 64; i++) {
      Particle pt = particles.getInstanceForced();
      float d = rand.nextSignedFloat(PI);
      pt.set(pos, sin(d) * 0.8f, cos(d) * 0.2f,
             0.3 + rand.nextFloat(0.2f), 0.3 + rand.nextFloat(0.2f), 1, 32);
    }
    for (int i = 0; i < 64; i++) {
      Particle pt = particles.getInstanceForced();
      pt.set(pos, rand.nextSignedFloat(0.2f), - 0.8f - rand.nextSignedFloat(0.3f),
             0.3 + rand.nextFloat(0.2f), 0.3 + rand.nextFloat(0.2f), 1, 32 + rand.nextInt(16));
    }
  }

  public bool hasCollision() {
    if (cnt < -INVINCIBLE_CNT)
      return false;
    else
      return true;
  }

  private void addDistance(float dist) {
    gameState.addDistance(dist);
  }

  public void getCrystal() {
    SoundManager.playSe("crystal.wav");
    crystalNum++;
    if (crystalNum >= purserAppCrystalNum) {
      crystalNum = purserAppCrystalNum;
      if (!crystalFull) {
        _purserReady = true;
        crystalFull = true;
      }
    }
  }

  public void setPurser(float x, float y) {
    purser.set(x, y);
    _purserReady = false;
  }

  public void clearVisibleBullets() {
    gameState.clearVisibleBullets();
  }

  public void draw() {
    if (blaster.exists)
      blaster.draw();
    if (purser.exists)
      purser.draw();
    if (cnt < -INVINCIBLE_CNT || (cnt < 0 && (-cnt % 32) < 16))
      return;
    Screen.setColor(1, 1, 1);
    glPushMatrix();
    Screen.glTranslate(pos);
    glRotatef(-deg * 180 / PI, 0, 0, 1);
    if (airMode && !_toGround) {
      glPushMatrix();
      glTranslatef(0, -0.5f, 0);
      purser.shape.draw();
      glPopMatrix();
    }
    if (!airMode) {
      glPushMatrix();
      glTranslatef(0.3f, -0.2f, 0);
      _shape.drawAlpha();
      glPopMatrix();
    } else if (_toAir && modeChangeCnt < 60) {
      glPushMatrix();
      glTranslatef(0.3f * (1 + modeChangeCnt * 0.2f), -0.2f * (1 + modeChangeCnt * 0.2f), 0);
      _shape.drawAlpha((1 - cast(float) modeChangeCnt / 60) * 0.5f);
      glPopMatrix();
    } else if (_toGround && modeChangeCnt > 60) {
      float rc = (120 - modeChangeCnt) * 0.2f + 1;
      glPushMatrix();
      glTranslatef(0.3f * rc, -0.2f * rc, 0);
      _shape.drawAlpha((1 - cast(float) (120 - modeChangeCnt) / 60) * 0.5f);
      glPopMatrix();
    }
    _shape.draw();
    glPopMatrix();
    if (!airMode) {
      glPushMatrix();
      glTranslatef(pos.x, pos.y + sightRange, 0);
      sightShape.draw();
      glPopMatrix();
    }
    int si = shadowIdx;
    for (int i = 0; i < shadowNum; i++) {
      si -= SHADOW_STEP;
      if (si < 0)
        si += shadow.length;
      shadow[si].draw();
    }
  }

  public void drawState() {
    glPushMatrix();
    glTranslatef(-8.8f, -10, 0);
    glScalef(0.75f, 0.75f, 1);
    Crystal.shape.draw();
    glPopMatrix();
    Letter.drawNum(cast(int) crystalNum, -6.8f, -10.3f, 1);
    Letter.drawNum(cast(int) purserAppCrystalNum, -4.6f, -10.3f, 1,
                   Letter.Direction.TO_RIGHT, 1);
  }

  public Vector pos() {
    return _pos;
  }

  public BitmapShape shape() {
    return _shape;
  }

  public float scrollSpeedBase() {
    return _scrollSpeedBase;
  }

  public bool airMode() {
    return _airMode;
  }

  public bool toAir() {
    return _toAir;
  }

  public bool toGround() {
    return _toGround;
  }

  public bool purserReady() {
    return _purserReady;
  }
}

public class Purser {
 public:
  bool exists;
 private:
  Field field;
  BitmapShape _shape;
  Vector _pos;
  int cnt;

  public this(Field field) {
    this.field = field;
    _shape = new BitmapShape(BitmapShape.purser, 0);
    _pos = new Vector;
    exists = false;
  }

  public void set(float x, float y) {
    _pos.x = x;
    _pos.y = y;
    cnt = 0;
    exists = true;
  }

  public void move() {
    cnt++;
    _pos.y -= field.lastScrollY;
    if (_pos.y <= -field.outerSize.y)
      exists = false;
  }

  public void draw() {
    glPushMatrix();
    Screen.glTranslate(_pos);
    _shape.draw();
    int c = cnt % 32;
    if (c < 16) {
      glScalef(cast(float) c / 8 + 1, cast(float) c / 8 + 1, 1);
      _shape.drawAlpha();
    }
    glPopMatrix();
  }

  public BitmapShape shape() {
    return _shape;
  }

  public Vector pos() {
    return _pos;
  }
}

/**
 * Ship's shadow works as an option.
 */
00660 public class ShipShadow {
 private:
  Vector pos;
  float deg;
  BitmapShape shape;
  ShotPool shots;

  public this(BitmapShape shape, ShotPool shots) {
    this.shape = shape;
    this.shots = shots;
    pos = new Vector;
  }

  public void set(Vector p, float d) {
    pos.x = p.x;
    pos.y = p.y;
    deg = d;
  }

  public void slide(float mx, float my) {
    pos.x += mx;
    pos.y += my;
  }

  public void fire() {
    Shot s = shots.getInstance();
    if (s)
      s.set(pos, deg);
  }

  public void draw() {
    glPushMatrix();
    Screen.glTranslate(pos);
    glRotatef(-deg * 180 / PI, 0, 0, 1);
    shape.drawAlpha();
    glPopMatrix();
  }
}

Generated by  Doxygen 1.6.0   Back to index