Beautifl - Flash Gallery

Thumbnail : 俺の弾幕を越えていけ(Shooting AI)
俺の弾幕を越えていけ(Shooting AI)
heriet 2009-09-29 MIT License

再生するにはFlash Playerが必要です。デスクトップのブラウザでご覧ください。

// <Control>
//   Start: click or key 'x'.
//   Move: arrow key.
//   Shot: key 'z'.
//
// 弾幕を避けるAIを作ろう企画
// 入力をフックして弾幕を避けるAIを作ってみてください (ゲーム自体は弄らず、inputAuto関数あたりを書き換える)
// 最終的な被弾数が一番少ない美しいAIを作った人が勝ち
// 
// 敵(青・赤・紫)に当たり判定ないです。弾(白・黄色)だけ当たります
// スコアはついで。約1分半。
//
// 普通に遊びたい人はクリック。
//
// _skipFrameの値を多くするとフレームスキップします。さっさと動かしたい人向け

package {
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.BlendMode;
import flash.display.Sprite;
import flash.geom.ColorTransform;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.events.KeyboardEvent;
import flash.text.TextField;
import flash.text.TextFormat;
import net.hires.debug.Stats;

[SWF(width = "465", height = "465", frameRate = "60", backgroundColor = "0x202030")]
public class Main extends Sprite
{
  public static var root:Main;
  private static const END_FRAME:int = 4600;
  public static const SCREEN_WIDTH:int = 465;
  public static const SCREEN_HEIGHT:int = 465;
  public static const BACKGROUND_COLOR:uint = 0x202030;
  private static const ENEMY_COLOR_TRANSFORMS:Vector.<ColorTransform> = Vector.<ColorTransform>([
    new ColorTransform(1.0, 1.0, 1.0, 1.0, -32, -48, -48), new ColorTransform(1.25, 1.0, 1.0, 1.0, -12, -24, -32),
    new ColorTransform(1.0, 1.0, 1.0, 1.0, -24, -24, -24), new ColorTransform(1.0, 1.0, 1.0, 1.0, -24, -24, -24),
  ]);
  private static const NONENEMY_COLOR_TRANSFORMS:Vector.<ColorTransform> = Vector.<ColorTransform>([
    new ColorTransform(1.0, 1.0, 1.0, 1.25, -32, -16, 0), new ColorTransform(1.0, 1.0, 1.0, 1.0, -16, -8, -16),
    new ColorTransform(1.0, 1.0, 1.0, 1.0, -12, -8, -12), new ColorTransform(1.0, 1.0, 1.0, 1.0, -24, -24, -8),
  ]);
  
  private static const MOTION_DATA:String =
    "2***y00|4***b00|4***w00|g***000,3***J00|3***r00|4***w00|g***000,"
  + "0 g* ** 00|Y ** 1* 10, 0 M* ** 00|Y ** 1* 10, 0000000, 0000000, 0000000, 0000000, 0000000," 
  + "4 ** *A 00|4 ** *a 00|0 ** *0 00, 4 ** *w 00|4 ** *2 00|0 ** *0 00, 4 ** ** 00|4 ** *a 00|0 ** *0 00,"
  + "4 ** ** 00|4 ** *2 00|0 ** *0 00, 0000000, 0000000, 0000000,"
  + "0000000, 0000000, 0000000, 0000000, 0000000, 0000000, 0000000,"
  + "0000000, 0000000, 0000000, 0000000, 0000000, 0000000, 0000000,"
  + "0000000, 0000000, 0000000, 0000000, 0000000,"
  + "8 ** *2 00|8 ** *F 00|Y *8 w0 00|u ** *A 00,"
  + "8 ** *2 00|8 ** *A 00|Y *4 *0 00,"
  + "8 ** *4 00|8 ** *F 00|Y *g z0 00|Y *g z0 00|u ** *w 00,"
  + "g ** *2 01|g ** *0 01|u ** *y 01|u ** 11 00|0 ** *1 00,"
  + "_ ** ** 00|u ** 1* 00|u ** 0* 00|g ** 12 00|u ** 00 00,"
  + "_ ** ** 00|4 ** w* 00|_ ** *1 00,"
  + "a ** *1 01|8 ** *0 01|g ** *w 01|g ** 11 00|0 ** *2 00,"
  + "8 ** *2 00|8 ** *A 00|Y *0 *0 00|Y ** ** 00|Y ** *2 00,"
  + "2 ** *2 00|2 ** *A 00|Y *8 10 00|Y *8 10 00|Y *8 10 00|Y 0* *2 00";
  
  private static const SHOT_DATA:String = 
   "90a0 000k 1000 0010 313, 8000 000h 1100 0010 223, 90a0 000o 1a00 0010 1t3,"
  +"91b0 000a 2000 171a 1E5, 8000 000h 1200 00b0 3A6, 82b0 000a 1000 0712 2Y8,"
  +"91b0 00w4 0000 251a 515, 95c0 000v 1600 a100 081, 90a0 000o 1100 0030 0a1,"
  +"91b0 0004 1500 1538 3A6, 91b0 0002 1500 1548 3A6, 91b0 0002 1100 1536 3Z8,"
  +"80a0 000v 15v0 0020 029, 84b0 000o 15v0 0g0v 0vg, 90a0 Z00f 131f 0090 09o|90b0 f00a 13zn 0090 0ao,"
  +"84b0 000o 15v0 090g 0_g, 93c0 0006 1000 070k 616, 95d0 000a 1700 fi00 2_6";
  
  private static const STAGE_DATA:String
  = "0A 23a a001|1A 23b A001|30 25a a001|40 25b A001|3A 85c 8000|4A 15d L000|5A 15e 8000|6A 15f L000|"
  + "8A k3l v000|2A _3g v000|90 26i d00z|cA 23j g00z|eA 23k A00z|"
  + "h0 26i d00z|iA 23a A001|jA 36l g000|mA 36m _000|"
  + "rA 38n T101|tA 38o c101|vA 2an T101|xA 2ao c101|yA 28n T101|y_ 2go c101|zA 28n T101|zA 28o c101|"
  + "B0 1gp H101|BA 1gp 6101|DA 1gp H101|D_ 1gp 6101|EA 1gp H101|E_ 1gp 6101|"
  + "GA 42q px0H|I_ 42r _x0H|M0 g7v a001|MA g4w A001|"
  + "TA 11s 0000|X_ 11t 0000|XA 11u 0000|_A 11s 0000|__ 11t 0000|__ 11u 0000";
  
  private static const ENEMY_DATA:String = 
   "a 12 0a 300, a wx 0a 400, a 12 0a 300|a wx 0a 400, a 22 00 070|a x2 00 070, a 12 0g 3d0|a wx 0g 4d0,  b 44 00 040|b z4 00 040, 00000000, 00000000, 00000000,"
  +"b o2 Ea 022, b 82 ma 022, b 21 wa A10, b t1 wa A10, b 51 wk Ah0, b q1 wk Ah0,"
  +"c 61 wa Ba5, c 61 wa Ba0, a 21 wa D30, b 21 ta E43, b t2 Ja F52, b 32 w3 064, b r2 w3 064,"
  +"a t1 wa G80, a 21 wa G80, a f1 wa G90, c 41 wg He6, c p1 wg He6, d g1 wg If0, c p1 wg Hg0, c 41 wg Hg0,"
  + "b o2 Ja 0i1, b 82 ma 0i1";
  
  private var _resultTextField:TextField = new TextField();
  private var _stats:Stats = new Stats();
  private var _enemyLayer:BitmapData = new BitmapData(SCREEN_WIDTH, SCREEN_HEIGHT, false, 0);
  private var _enemyScreen:Bitmap = new Bitmap(_enemyLayer);
  private var _nonEnemyLayer:BitmapData = new BitmapData(SCREEN_WIDTH, SCREEN_HEIGHT, false, 0);
  private var _nonEnemyScreen:Bitmap = new Bitmap(_nonEnemyLayer);
  public var motionManager:DataManager = new DataManager(MOTION_DATA, MotionData);
  public var shotManager:ShotManager = new ShotManager(SHOT_DATA);
  private var _stageManager:StageManager = new StageManager(STAGE_DATA, ENEMY_DATA);
  private var _bullets:TokenContainer = new TokenContainer(Bullet, _enemyLayer);
  private var _enemys:TokenContainer = new TokenContainer(Enemy, _enemyLayer);
  private var _shots:TokenContainer = new TokenContainer(Bullet, _nonEnemyLayer);
  private var _effects:TokenContainer = new TokenContainer(Effect, _nonEnemyLayer);
  public var player:Player = new Player();
  
  private var _skipFrame:int = 0;
  private var _isAuto:Boolean = true;
  private var _gameFrame:int, _hitCount:int, _score:int;
  public var inputKey:InputKey = new InputKey();
  private var _lcg:LinearCongruentialGenerator = new LinearCongruentialGenerator();
  
  public function Main() {
    Main.root = this;
    stage.addEventListener(MouseEvent.CLICK, startPlayer);
    stage.addEventListener(KeyboardEvent.KEY_DOWN, checkStartPlayerKey);
    _enemyScreen.blendMode = BlendMode.ADD;
    _nonEnemyScreen.blendMode = BlendMode.ADD;
    
    _resultTextField.defaultTextFormat = new TextFormat(null, 10, 0xFFFFFF);
    _resultTextField.x = SCREEN_WIDTH - 128;
    _resultTextField.selectable = false;
    
		graphics.beginFill(0x202030);
		graphics.drawRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
		
    startGame();
  }
  private function startPlayer(event:Event = null):void {
    _isAuto = false;
    stage.focus = this;
    stage.removeEventListener(MouseEvent.CLICK, startPlayer);
    stage.removeEventListener(KeyboardEvent.KEY_DOWN, checkStartPlayerKey);
    startGame();
  }
  private function startGame(isFirst:Boolean = true):void {
    
    while (numChildren > 0) { removeChildAt(0) }
    
    if (!_isAuto) {
      stage.addEventListener(KeyboardEvent.KEY_DOWN, inputKey.onKeyDown);
      stage.addEventListener(KeyboardEvent.KEY_UP, inputKey.onKeyUp);
    }
    else{
      if(!isFirst)
        addMessage("Your Result\n\nDamaged: " + String(_hitCount) + "\nScore : " + String(_score) + "\n\n click or key 'x'  to start");
      else
        addMessage("click to start");
    }
    
    _gameFrame = 0, _score = 0, _hitCount = 0;
    inputKey.initialize();
    _lcg.initialize();
    _stageManager.initialize();
    
    if(_isAuto)
      initializeInputAuto();
    
    _nonEnemyLayer.fillRect(_nonEnemyLayer.rect, 0);
    addChild(_nonEnemyScreen);
    _enemyLayer.fillRect(_enemyLayer.rect, 0);
    addChild(_enemyScreen);
    
    _bullets.clear(); _enemys.clear();
    _shots.clear(); _effects.clear();
    player.initialize(1, (SCREEN_WIDTH << Token.FIELD_SCALE) / 2, ((SCREEN_HEIGHT - 32) << Token.FIELD_SCALE));
    
    updateResult();
    addChild(_resultTextField);
    addChild(_stats);
    addEventListener(Event.ENTER_FRAME, enterFrameHandler);
  }
  private function enterFrameHandler(event:Event):void {
    _enemyLayer.lock(); _nonEnemyLayer.lock();
    
    for (var i:int = 0; i <= _skipFrame; i++) {
      _gameFrame++;
      _enemyLayer.colorTransform(_enemyLayer.rect, ENEMY_COLOR_TRANSFORMS[_gameFrame % 4]);
      _nonEnemyLayer.colorTransform(_nonEnemyLayer.rect, NONENEMY_COLOR_TRANSFORMS[_gameFrame % 4]);
      
      _stageManager.update(_gameFrame);
      _enemys.update(); _bullets.update();
      _shots.update(); _effects.update();
      
      checkAttackEnemy();
      if(!player.isInvincible)
        checkAttackPlayer();
      
      if (_isAuto)
        inputKey.pushed = inputAuto(_gameFrame, player, _enemys.tokens, _bullets.tokens, _enemyLayer)
      
      player.update();
      player.draw(_nonEnemyLayer);
    }
    
    _enemyLayer.unlock(); _nonEnemyLayer.unlock();
    
    if (_gameFrame == END_FRAME) {
      endGame();
    }
  }
  private function endGame():void {
    removeEventListener(Event.ENTER_FRAME, enterFrameHandler);
    if (!_isAuto){
      _isAuto = true;
      stage.removeEventListener(KeyboardEvent.KEY_DOWN, inputKey.onKeyDown);
      stage.removeEventListener(KeyboardEvent.KEY_UP, inputKey.onKeyUp);
      stage.addEventListener(MouseEvent.CLICK, startPlayer);
      stage.addEventListener(KeyboardEvent.KEY_DOWN, checkStartPlayerKey);
    }
    addEventListener(Event.ENTER_FRAME, restart);
  }
  private function restart(event:Event):void {
    removeEventListener(Event.ENTER_FRAME, restart);
    startGame(false);
  }
  private function checkStartPlayerKey(event:KeyboardEvent):void
  {
    if (event.keyCode == 88) // keydown 'x'
      startPlayer();
  }
  private function checkAttackPlayer():void {
    var lx:int = player.x >> Token.FIELD_SCALE;
    var ly:int = player.y >> Token.FIELD_SCALE;
    if ((_enemyLayer.getPixel(lx, ly) & 0xFF0000) == 0) // rough check
      return;
    
    if (checkCollisionPoint(player.x, player.y, _bullets.tokens)) {
      _hitCount++; updateResult();
      player.damage();
    }
  }
  private function checkAttackEnemy():void {
    var tokens:Vector.<IToken> = _shots.tokens;
    var n:int = tokens.length;
    for (var i:int = 0; i < n; i++ ) {
      var token:Token = tokens[i] as Token;
      var lx:int = token.x >> Token.FIELD_SCALE;
      var ly:int = token.y >> Token.FIELD_SCALE;
      if ((_enemyLayer.getPixel(lx, ly) & 0xFF0000) != 0 && checkCollisionEnemy(token))
        token.vanish();
    }
  }
  private function checkCollisionEnemy(shot:IToken):Boolean {
    var tokens:Vector.<IToken> = _enemys.tokens;
    var n:int = tokens.length;
    for (var i:int = 0; i < n; i++ ) {
      var enemy:Token = tokens[i] as Token;
      if (enemy.isCollision(shot.x, shot.y)) {
        enemy.damage();
        return true;
      }
    }
    return false;
  }
  private function checkCollisionPoint(x:int, y:int, tokens:Vector.<IToken>):Boolean {
    var n:int = tokens.length;
    for (var i:int = 0; i < n; i++ ) {
      var token:Token = tokens[i] as Token;
      if (token.isCollision(x, y)) {
        return true;
      }
    }
    return false;
  }
  private function addMessage(message:String):void {
    var textField:TextField = new TextField();
    var textFormat:TextFormat = new TextFormat(null, 20, 0xFFFFFF, true);
    textField.defaultTextFormat = textFormat;
    textField.selectable = false;
    textField.width = SCREEN_WIDTH;
    textField.height = 256;
    textField.text = message;
    addChild(textField);
    
    textField.x = (SCREEN_WIDTH - textField.textWidth) / 2;
    textField.y = (SCREEN_HEIGHT - textField.textHeight) / 2;
  }
  public function addBullet(id:int=0, x:int=0, y:int=0, direction:int=0, speed:int=0, motionID:int=0, shotID:int=0):Token {
    return _bullets.addToken(id, x, y, direction, speed, motionID, shotID);
  }
  public function addEnemy(id:int=0, x:int=0, y:int=0, direction:int=0, speed:int=0, motionID:int=0, shotID:int=0):Token {
    return _enemys.addToken(id, x, y, direction, speed, motionID, shotID);
  }
  public function addShot(id:int=0, x:int=0, y:int=0, direction:int=0, speed:int=0, motionID:int=0, shotID:int=0):Token {
    return _shots.addToken(id, x, y, direction, speed, motionID, shotID);
  }
  public function addEffect(id:int=0, x:int=0, y:int=0, direction:int=0, speed:int=0, motionID:int=0, shotID:int=0):Token {
    return _effects.addToken(id, x, y, direction, speed, motionID, shotID);
  }
  public function random():uint {
    return _lcg.random()
  }
  public function addScore(value:int):void {
    _score += value; updateResult();
  }
  private function updateResult():void {
    _resultTextField.text = "Damaged: " + String(_hitCount) + "\nScore : " + String(_score);
  }
}
}
import flash.display.BitmapData;
import flash.geom.Point;
import flash.events.KeyboardEvent;

// Enemy shoot bullet. 
// When bullet collides with player, player is damaged.
// However, player is not damaged when enemy collides with player.

// Player moveable x is [8*16, 465-8*16] and y is [8*16, 465-8*16].
// Player's hit rect size is Rectangle(0, 0, 1*16, 1*16). It means a dot of screen.
// Player's hit rect area is Rectangle(token.x, token.y, 1*16, 1*16) on game field 
// and Rectangle(token.x, token.y, 1, 1) on screen bitmap.

// Bullet's hit rect size is Rectangle(0, 0, 16*16, 16*16).
// Center of hit rect is Point(token.x, token.y).
// So, Bullet's hit rect area is Rectangle(token.x - 8*16, token.y - 8*16, 16*16, 16*16) on game field
// and Rectangle(token.x / 8 - 8, token.y / 8 - 8, 16, 16) and on screen bitmap. 

function inputAuto(gameFrame:int, player:IToken, enemys:Vector.<IToken>, bullets:Vector.<IToken>, enemyScreen:BitmapData):int
{
  return InputAutoNearestBulletOtherSide(gameFrame, player, enemys, bullets, enemyScreen)
}

var centeringFlag:Boolean;

function initializeInputAuto():void
{
  centeringFlag = false;
}
// tekito-
function InputAutoNearestBulletOtherSide(gameFrame:int, player:IToken, enemys:Vector.<IToken>, bullets:Vector.<IToken>, enemyScreen:BitmapData):int
{
  var pushedKey:int = InputKey.KEY_SHOT; // always shoot
  
  if (bullets.length == 0)
    return pushedKey;
  
  if (centeringFlag) {
    if ((465/2-100)*16 < player.x && player.x < (465/2+100)*16) {
      centeringFlag = false;
      return pushedKey;
    }
    else if (player.x < 465 / 2 * 16)
      return pushedKey | InputKey.KEY_RIGHT;
    else
      return pushedKey | InputKey.KEY_LEFT;
  }
  
  if (player.x < Player.MIN_X + 30 * 16) {
    centeringFlag = true;
    return pushedKey | InputKey.KEY_RIGHT;
  }
  if (player.x > Player.MAX_X - 30 * 16) {
    centeringFlag = true;
    return pushedKey | InputKey.KEY_LEFT;
  }
  
  var nearestIndex:int = 0;
  var nearestLength:Number = Math.pow(player.x - bullets[0].x, 2) + Math.pow(player.y - bullets[0].y, 2)
  var n:int = bullets.length;
  
  for (var i:int = 1; i < n; i++ ) {
    var bullet:IToken = bullets[i];
    var len:Number = Math.pow(player.x - bullet.x, 2) + Math.pow(player.y - bullet.y, 2);
    
    if (len < nearestLength) {
      nearestLength = len;
      nearestIndex = i;
    }
  }
  
  bullet = bullets[nearestIndex];
  if (player.x < bullet.x)
    pushedKey |= InputKey.KEY_LEFT;
  else
    pushedKey |= InputKey.KEY_RIGHT;
  
  return pushedKey;
}

interface IToken
{
  function get id():int; // 0: null, 1: player, 2:player's Shot, 3-5: effect, 6-9: enemy's bullet, 10-:enemy
  function get x():int;  // screen x * 16, [0, 465 * 16] 
  function get y():int;  // screen y * 16, [0, 465 * 16] 
  function get vx():int; // velocity x
  function get vy():int; // velocity y
  function get direction():int; // degree, [0, 360) 
  function get speed():int;
  function get accelDirection():int; // degree, [0, 360)
  function get accelSpeed():int;
  function get frame():int; // alive frame count
  function get isVanished():Boolean; // When isVanished is true, token will remove 
}
class InputKey {
  public static const KEY_UP:int = 1;
  public static const KEY_RIGHT:int = 1 << 1;
  public static const KEY_DOWN:int = 1 << 2;
  public static const KEY_LEFT:int = 1 << 3;
  public static const KEY_SHOT:int = 1 << 4;
  
  private static const KEYS:Object = { up:KEY_UP, right:KEY_RIGHT, down:KEY_DOWN, left:KEY_LEFT, shot:KEY_SHOT }
  private static const KEY_CODE:Object = { up:38, right:39, down:40, left:37, shot:90 };
  
  public var pushed:int;
  
  public function initialize():void {
    pushed = 0;
  }
  private function readKeyFlag(event:KeyboardEvent):int {
    for(var key:String in KEY_CODE)
      if (event.keyCode == KEY_CODE[key])
        return KEYS[key];
    return 0;
  }
  public function onKeyDown(event:KeyboardEvent):void {
    var changedKeyFlag:int = readKeyFlag(event);
    pushed |= changedKeyFlag;
  }
  public function onKeyUp(event:KeyboardEvent):void {
    var changedKeyFlag:int = readKeyFlag(event);
    pushed ^= changedKeyFlag;
  }
}

class Token implements IToken {
  public function get id():int { return _id }
  public function get x():int { return _parent == null ? _x : _x + _parent.x }
  public function set x(value:int):void { _x = value }
  public function get y():int { return _parent == null ? _y : _y + _parent.y }
  public function set y(value:int):void { _y = value }
  public function get vx():int { return _vx }
  public function get vy():int { return _vy }
  public function get direction():int { return _direction }
  public function get speed():int { return _speed }
  public function get accelDirection():int { return _accelDirection }
  public function set accelDirection(value:int):void { _accelDirection = value }
  public function get accelSpeed():int { return _accelSpeed }
  public function set accelSpeed(value:int):void { _accelSpeed = value}
  public function get frame():int { return _frame }
  public function get isVanished():Boolean {return _isVanished}
  
  protected var _id:int, _frame:int;
  protected var _x:int, _y:int, _vx:int, _vy:int;
  protected var _direction:int, _speed:int, _accelDirection:int, _accelSpeed:int;
  protected var _isVanished:Boolean;
  protected var _parent:IToken;
  public function set parent(value:IToken):void { _parent = value }
  private var _motionGroup:Array; /* Array of MotionData */
  private var _currentMotion:MotionData;
  private var _motionFrame:int, _motionState:int;
  private var _shotGroup:Array; /* Array of ShotData */
  
  private static var tmpPoint:Point = new Point();
  public static const FIELD_SCALE:int = 4;
  private static const FIELD_WIDTH:int = Main.SCREEN_WIDTH << FIELD_SCALE;
  private static const FIELD_HEIGHT:int = Main.SCREEN_HEIGHT << FIELD_SCALE;
  public static const DEG_CIRCLE:int = 360;
  private static const DEG_HALF:int = DEG_CIRCLE / 2;
  private static const DEG_SCALE:int = 100;
  private static const PI_DEG_HALF:Number = Math.PI / DEG_HALF;
  private static const PI_HALF:Number = Math.PI / 2;
  protected static const bitmapDataTable:Vector.<BitmapData> = createBitmapTable();
  
  public static function createBitmapTable():Vector.<BitmapData> {
    var table:Vector.<BitmapData> = new Vector.<BitmapData>();
    table.push(new BitmapData(1, 1, true, 0)); // null
    table.push(new BitmapData(16, 16, true, 0xFFFF4500)); // player
    table.push(new BitmapData(16, 16, true, 0xFF606000)); // shot
    table.push(new BitmapData(6, 6, true, 0xFFFFD700)); // effect1
    table.push(new BitmapData(6, 6, true, 0xFF88672C)); // effect2
    table.push(new BitmapData(12, 12, true, 0xFFD040A0)); // effect3
    table.push(new BitmapData(16, 16, true, 0xFFFFFFFF)); // bullet1 (no use)
    table.push(new BitmapData(16, 16, true, 0xFFFF5050)); // bullet2 (no use)
    table.push(new BitmapData(16, 16, true, 0xFFFFDD50)); // bullet3
    table.push(new BitmapData(16, 16, true, 0xFFFFEEEE)); // bullut4
    table.push(new BitmapData(20, 20, true, 0xFFEE2050)); // child enemy1
    table.push(new BitmapData(32, 32, true, 0xFF5050FF)); // enemy1
    table.push(new BitmapData(80, 64, true, 0xFF50AAFF)); // enemy2
    table.push(new BitmapData(160, 80, true, 0xFFD050FF)); // enemy3
    return table;
  }
  public function initialize(id:int=0, x:int=0, y:int=0, direction:int=0, speed:int=0, motionID:int=0, shotID:int=0):void {
    _id = id; _x = x; _y = y; _direction = direction; _speed = speed;
    _frame = 0; _isVanished = false; _accelDirection = 0; _accelSpeed = 0;
    setVelocity();
    
    _currentMotion = null; _motionGroup = null;
    _motionFrame = 0; _motionState = 0; _shotGroup = null; _parent = null;
    
    if (motionID != 0){
      _motionGroup = Main.root.motionManager.getDataGroup(motionID);
      setMotion();
    }
    if (shotID != 0){
      _shotGroup = Main.root.shotManager.getDataGroup(shotID);
    }
  }
  public function update():void {
    _frame++;
    motion();
    
    if (_accelSpeed != 0 || _accelDirection != 0) {
      setMovement(_direction + _accelDirection, _speed + _accelSpeed);
    }
    _x += _vx; _y += _vy;
    
    if ((_frame > 8 && _parent == null && (_x < 0 || _x >= FIELD_WIDTH || _y < 0 || _y >= FIELD_HEIGHT)) // out of field
    || (_parent != null && _parent.isVanished)) // parent vanished
      _isVanished = true;
    else
      shot();
  }
  public function draw(buffer:BitmapData):void {
    var bitmapData:BitmapData = bitmapDataTable[_id];
    tmpPoint.x = (_x >> FIELD_SCALE) - (bitmapData.width >> 1);
    tmpPoint.y = (_y >> FIELD_SCALE) - (bitmapData.height >> 1);
    
    if (_parent != null)
    {
      tmpPoint.x += _parent.x >> FIELD_SCALE;
      tmpPoint.y += _parent.y >> FIELD_SCALE;
    }
    
    buffer.copyPixels(bitmapData, bitmapData.rect , tmpPoint, null, null, true);
  }
  public function setMovement(d:int, s:int) :void {
    _direction = d % DEG_CIRCLE;
    _speed    = s;
    setVelocity();
  }
  public function set direction(d:int) :void {
    _direction = d % DEG_CIRCLE;
    setVelocity();
  }
  public function set speed(s:int) :void {
    _speed = s;
    setVelocity();
  }
  protected function setVelocity():void {
    _vx = (Math.floor(Math.sin(_direction * PI_DEG_HALF) * DEG_SCALE) / DEG_SCALE) * _speed;
    _vy = ( -Math.floor(Math.sin(_direction * PI_DEG_HALF + PI_HALF) * DEG_SCALE) / DEG_SCALE) * _speed;
  }
  protected function setMotion():void {
    _currentMotion = _motionGroup[_motionState];
    _accelDirection = _currentMotion.accelDirection != int.MAX_VALUE ? _currentMotion.accelDirection : 0;
    _accelSpeed = _currentMotion.accelSpeed != int.MAX_VALUE ? _currentMotion.accelSpeed : 0;
    
    if (_currentMotion.direction != int.MAX_VALUE)
      _direction = _currentMotion.target ? _currentMotion.direction : _currentMotion.direction + getAimDirection();
    if (_currentMotion.speed != int.MAX_VALUE)
      _speed = _currentMotion.speed;
    setVelocity();
  }
  protected function motion():void {
    if (_currentMotion != null) {
      if (_currentMotion.frame <= _motionFrame) {
        if (_motionState < _motionGroup.length - 1) {
          _motionState = _currentMotion.goto == 0 ? _motionState+1 : _currentMotion.goto-1;
          setMotion();
          _motionFrame = 0;
        }
        else {
          _currentMotion = null;
        }
      }
      else if (_currentMotion.target) {
        direction = getAimDirection();
      }
    }
    _motionFrame++;
  }
  protected function shot():void {
    if (_shotGroup != null) {
      var startFrame:int = 0;
      var n:int = _shotGroup.length;
      for (var i:int = 0; i < n; i++ ) {
        var shotData:ShotData = _shotGroup[i];
        var f:int = _frame - shotData.startFrame + startFrame;
        if ((f >= 0) && (_frame <= startFrame + shotData.endFrame) && (f % shotData.stepFrame == 0))
          Main.root.shotManager.shot(this, shotData);
        startFrame += shotData.startFrame;
      }
    }
  }
  protected function getAimDirection():int {
    return ShotManager.getAimDirection(x, y);
  }
  public function damage():void {
  }
  public function vanish():void {
    _isVanished = true;
  }
  public function isCollision(x:int, y:int):Boolean {
    return false;
  }
}
class Effect extends Token {
  private var _gravity:int = 0;
  private var _aliveFrame:int = 0;
  
  override public function initialize(id:int = 0, x:int = 0, y:int = 0, direction:int = 0, speed:int = 0, motionID:int = 0, shotID:int = 0):void {
    super.initialize(id, x, y, direction, speed, motionID, shotID);
    
    if (id == 3) { _gravity = 4; _aliveFrame = 90 }
    else if (id == 4) { _gravity = 2; _aliveFrame = 15 }
    else if (id == 5) { _gravity = 4; _aliveFrame = 90 }
  }
  override public function update():void {
    super.update();
    if (_frame > _aliveFrame)
      vanish();
    _y += _frame * _gravity;
    
  }
}
class Bullet extends Token {
  protected var _hitTopX:int;
  protected var _hitTopY:int;
  protected var _hitBottomX:int;
  protected var _hitBottomY:int;
  
  override public function initialize(id:int = 0, x:int = 0, y:int = 0, direction:int = 0, speed:int = 0, motionID:int = 0, shotID:int = 0):void  {
    super.initialize(id, x, y, direction, speed, motionID, shotID);
    
    var bitmapData:BitmapData = bitmapDataTable[_id];
    _hitTopX = -(bitmapData.width >> 1) << Token.FIELD_SCALE;
    _hitTopY = -(bitmapData.height >> 1) << Token.FIELD_SCALE;
    _hitBottomX = (bitmapData.width >> 1) << Token.FIELD_SCALE;
    _hitBottomY = (bitmapData.height >> 1) << Token.FIELD_SCALE;
  }
  override public function isCollision(cx:int, cy:int):Boolean  {
    return (x + _hitTopX < cx) && (y + _hitTopY < cy) && (x + _hitBottomX > cx) && (y + _hitBottomY > cy);
  }
}
class Enemy extends Bullet {
  private var _life:int = 0;
  
  override public function initialize(id:int = 0, x:int = 0, y:int = 0, direction:int = 0, speed:int = 0, motionID:int = 0, shotID:int = 0):void {
    super.initialize(id, x, y, direction, speed, motionID, shotID);
    _life = 3;
    
    if (id == 12)
      _life += 10;
    else if (id == 10)
      _life = 1;
    else if (id == 13)
      _life += 40;
    
    _hitTopX -= 16 << Token.FIELD_SCALE; _hitTopY -= 16 << Token.FIELD_SCALE;
    _hitBottomX += 16 << Token.FIELD_SCALE; _hitBottomY += 16 << Token.FIELD_SCALE;
  }
  override public function damage():void {
    _life--;
    Main.root.addScore(10);
    
    if (_life == 0) {
      n = _parent == null ? 12 + (id - 11) * 8 : 4;
      for (i = 0; i < n; i++ ) {
        Main.root.addEffect(5, x, y, 240/n*i + int(Main.root.random() % 30) + 210, 64 + int(Main.root.random() % 64), 2)
      }
      Main.root.addScore(_parent == null ? (id - 10) * 1000 : 5000);
      vanish();
    }
    else
    {
      var n:int = 4;
      for (var i:int = 0; i < n; i++ ) {
        Main.root.addEffect(4, x, y, 120/n*i + int(Main.root.random() % 60) + 120, 8 + int(Main.root.random() % 64), 1)
      }
    }
  }
}
class Player extends Token {
  public static const MAX_X:int = (Main.SCREEN_WIDTH - 8) << Token.FIELD_SCALE;
  public static const MIN_X:int = 8  << Token.FIELD_SCALE;
  public static const MAX_Y:int = (Main.SCREEN_HEIGHT - 8) << Token.FIELD_SCALE;
  public static const MIN_Y:int = 8  << Token.FIELD_SCALE;
  private static const COMMAND_DIRECTIONS:Object = { 1:0, 2:90, 4:180, 8:270, 3:45, 6:135, 9:315, 12:225 }
  private static const MOVE_SPEED:int = 32;
  private static const MOVE_MAXSPEED:int = 96;
  private static const MOVE_BRAKE:int = 32;
  private static const SHOT_SPEED:int = 196;
  private static const DAMAGED_INVINCIBLE_FRAME:int = 30;
  public function get isInvincible():Boolean { return _invincibleFrame > 0 }
  private var _invincibleFrame:int;  
  private var _shotFrame:int;
  private var _shotFlag:Boolean;
  
  override public function initialize(id:int = 0, x:int = 0, y:int = 0, direction:int = 0, speed:int = 0, motionID:int = 0, shotID:int = 0):void 
  {
    super.initialize(id, x, y, direction, speed, motionID, shotID);
    _invincibleFrame = 0;
    _shotFrame = 0;
    _shotFlag = false;
  }
  override public function update():void {
    super.update();
    command();
    
    if (_invincibleFrame > 0) _invincibleFrame--;
  }
  override public function draw(buffer:BitmapData):void {
    if(_invincibleFrame == 0 || _invincibleFrame % 4 == 0)
      super.draw(buffer);
  }
  override protected function shot():void {
    if (_shotFrame != 0) {
      if (_shotFrame > 0) {
        if (_shotFrame > 12) { _shotFrame = -12; _shotFlag = false; }
        else if(_shotFrame % 4 == 1){
          Main.root.addShot(2, _x - 256, _y - 32, 0, SHOT_SPEED);
          Main.root.addShot(2, _x + 256, _y - 32, 0, SHOT_SPEED);
        }
      }
      _shotFrame++;
      
      if (_shotFrame == 0){
        if(_shotFlag) {
          _shotFrame = 1; _shotFlag = false;
        }
        else if (((Main.root.inputKey.pushed & InputKey.KEY_SHOT) != 0)) {
          _shotFrame = 1; _shotFlag = false;
        }
      }
    }
  }
  private function command():void {
    var pushedKey:int = Main.root.inputKey.pushed;
    var p:int = pushedKey & (InputKey.KEY_UP | InputKey.KEY_RIGHT | InputKey.KEY_DOWN | InputKey.KEY_LEFT)
    
    if (p in COMMAND_DIRECTIONS) {
      if (_speed < MOVE_MAXSPEED) _speed += MOVE_SPEED;
      
      direction = COMMAND_DIRECTIONS[p];
      setVelocity();
    }
    else if (_speed > 0) {
      var b:int = MOVE_BRAKE;
      _speed -= b;
      if (_speed < 0)
        _speed = 0;
      setVelocity();
    }
    
    if (_x < MIN_X) { _x = MIN_X }
    else if (_x > MAX_X) { _x = MAX_X }
    
    if (_y < MIN_Y) {_y = MIN_Y}
    else if (_y > MAX_Y) { _y = MAX_Y }
    
    if ((pushedKey & InputKey.KEY_SHOT) != 0) {
      if (_shotFrame == 0)
        _shotFrame++;
      else
        _shotFlag = true;
    }
  }
  override public function damage():void  {
    _invincibleFrame = DAMAGED_INVINCIBLE_FRAME;
    var n:int = 16;
    for (var i:int = 0; i < n; i++ ) {
      Main.root.addEffect(3, _x, _y, 90/n*i + int(Main.root.random() % 30) + 315, 16 + int(Main.root.random() % 96), 1)
    }
  }
}
class DataContainer{
  protected static const VALUE_TABLE:Object = createValueTable();
  protected static function createValueTable():Object {
    var i:int; var table:Object = { };
    var ca:int = "a".charCodeAt(0); var cA:int = "A".charCodeAt(0);
    for (i = 0; i < 10; i++) { table[String(i)] = i }
    for (i = 0; i < 26; i++ ) { table[String.fromCharCode(ca + i)] = 10 + i}
    for (i = 0; i < 26; i++ ) { table[String.fromCharCode(cA + i)] = 36 + i}
    table['-'] = 62; table['_'] = 63; table['*'] = int.MAX_VALUE;
    return table;
  }
  protected function getDeg(value:int):int{return value != int.MAX_VALUE ? Math.floor(value * Token.DEG_CIRCLE / 64) : value}
  protected function getInteger(value:int):int {return value != int.MAX_VALUE ? (value < 32 ? value : -(value - 31)) : value}
  protected function getInteger2(value:int):int { return value != int.MAX_VALUE ? (value < 32 ? value * 2: -(value - 31)) * 2 : value }
  public function setData(data:String):void { }
}
class DataManager {
  private var _dataClass:Class;
  protected var _dataList:Array = [];
  
  public function DataManager(data:String, dataClass:Class) {
    _dataClass = dataClass;
    var groupTable:Array = data.replace(/\s/g, "").split(/,/);
    var n:int = groupTable.length;
    
    for (var i:int = 0; i < n; i++) {
      var elementList:Array = [];
      var elementDataList:Array = String(groupTable[i]).split(/\|/);
      var m:int = elementDataList.length;
      for (var j:int = 0; j < m; j++ ) {
        var element:DataContainer = new dataClass();
        element.setData(elementDataList[j]);
        elementList.push(element);
      }
      _dataList[i] = elementList;
    }
  }
  public function getDataGroup(dataID:int):Array {
    return _dataList[dataID-1];
  }
}

class StageEnemyData extends DataContainer{
  public var id:int;
  public var x:int, y:int;
  public var direction:int, speed:int;
  public var motion:int, shot:int;
  public var children:int;
  
  override public function setData(data:String):void {
    id = VALUE_TABLE[data.charAt(0)];
    x = Math.floor(getInteger(VALUE_TABLE[data.charAt(1)]) * Main.SCREEN_WIDTH / 32) << Token.FIELD_SCALE;
    y = Math.floor(getInteger(VALUE_TABLE[data.charAt(2)]) * Main.SCREEN_HEIGHT / 32) << Token.FIELD_SCALE;
    direction = getDeg(VALUE_TABLE[data.charAt(3)]); speed = getInteger2(VALUE_TABLE[data.charAt(4)]);
    motion = VALUE_TABLE[data.charAt(5)]; shot = VALUE_TABLE[data.charAt(6)];
    children = VALUE_TABLE[data.charAt(7)];
  }
}
class StageEnemyFormationData extends DataContainer {
  public var startFrame:int, endFrame:int;
  public var stepFrame:int, num:int;
  public var id:int;
  public var stepX:int, stepY:int;
  public var stepDirection:int, stepSpeed:int;
  
  override public function setData(data:String):void {
    startFrame = VALUE_TABLE[data.charAt(0)] * 64 + VALUE_TABLE[data.charAt(1)];
    stepFrame = VALUE_TABLE[data.charAt(2)] * 8;
    num = VALUE_TABLE[data.charAt(3)];
    endFrame = startFrame + stepFrame * num;
    id = VALUE_TABLE[data.charAt(4)];
    stepX = getInteger2(VALUE_TABLE[data.charAt(5)]) << Token.FIELD_SCALE;
    stepY = getInteger2(VALUE_TABLE[data.charAt(6)]) << Token.FIELD_SCALE;
    stepDirection = getDeg(VALUE_TABLE[data.charAt(7)]);
    stepSpeed = getInteger2(VALUE_TABLE[data.charAt(8)]);
  }
}
class StageManager extends DataManager {
  private var _stageEnemyTable:Array = [];
  private var _enemyManager:DataManager;
  private var _currentStage:int = 0;
  private var _activeFormations:Array = [];
  
  public function StageManager(data:String, enemyData:String) {
    super(data, StageEnemyFormationData);
    _enemyManager = new DataManager(enemyData, StageEnemyData);
    
    var n:int = _dataList.length;
    for (var i:int = 0; i < n; i++ ) {
      var frameEnemyTable:Object = { };
      var m:int = _dataList[i].length
      for (var j:int = 0; j < m; j++ ) {
        var formation:StageEnemyFormationData = _dataList[i][j];
        if (!(formation.startFrame in frameEnemyTable)) {
          frameEnemyTable[formation.startFrame] = [];
        }
        (frameEnemyTable[formation.startFrame] as Array).push(formation);
      }
      _stageEnemyTable[i] = frameEnemyTable;
    }
  }
  public function initialize():void {
    _activeFormations = [];
  }
  public function update(frame:int):void {
    var stageEnemy:Object = _stageEnemyTable[_currentStage];
    if (frame in stageEnemy) {
      var formations:Array = stageEnemy[frame];
      var n:int = formations.length;
      for (var i:int = 0; i < n; i++ ) {
        _activeFormations.push(formations[i]);
      }
    }
    n = _activeFormations.length;
    for (i = 0; i < n; i++ ) {
      var formation:StageEnemyFormationData = _activeFormations[i];
      
      if (frame >= formation.endFrame){
        _activeFormations.splice(i, 1); n--; i--;
      }
      else if ((frame - formation.startFrame) % formation.stepFrame == 0){
        createFormationEnemy(formation, int((frame - formation.startFrame) / formation.stepFrame));
      }
    }
  }
  private function createFormationEnemy(formation:StageEnemyFormationData, step:int):void {
    var enemys:Array = _enemyManager.getDataGroup(formation.id);
    var n:int = enemys.length;
    for (var i:int = 0; i < n; i++ ){
      var enemy:StageEnemyData = enemys[i];
      var enemyToken:Token = createEnemy(enemy);
      enemyToken.x += formation.stepX * step; enemyToken.y += formation.stepY * step;
      enemyToken.direction += formation.stepDirection * step; enemyToken.speed += formation.stepSpeed * step;
    }
  }
  private function createEnemy(enemy:StageEnemyData, parent:IToken = null):Token {
    var token:Token = Main.root.addEnemy(enemy.id, enemy.x, enemy.y, enemy.direction, enemy.speed, enemy.motion, enemy.shot);
    if (parent != null)
      token.parent = parent;
    if (enemy.children != 0) {
      var children:Array = _enemyManager.getDataGroup(enemy.children);
      var n:int = children.length;
      for (var i:int = 0; i < n; i++ )
        createEnemy(children[i], token);
    }
    return token;
  }
}
class MotionData extends DataContainer
{
  public var frame:int;
  public var direction:int, speed:int;
  public var accelDirection:int, accelSpeed:int;
  public var goto:int, target:Boolean;
  
  override public function setData(data:String):void {
    frame = VALUE_TABLE[data.charAt(0)] * 2;
    direction = getDeg(VALUE_TABLE[data.charAt(1)]);
    speed = getInteger2(VALUE_TABLE[data.charAt(2)]);
    accelDirection = getDeg(VALUE_TABLE[data.charAt(3)]);
    accelSpeed = getInteger(VALUE_TABLE[data.charAt(4)]);
    goto = VALUE_TABLE[data.charAt(5)];
    target = VALUE_TABLE[data.charAt(6)] != 0;
  }
}
class ShotData extends DataContainer
{
  public var id:int, func:int, motion:int, shot:int;
  public var x:int, y:int, direction:int, speed:int;
  public var aim:Boolean, deg:int, accelDeg:int, frame:int;
  public var len:int, num:int, way:int, wide:int;
  public var startFrame:int, stepFrame:int, endFrame:int;
  
  override public function setData(data:String):void {
    id = VALUE_TABLE[data.charAt(0)];
    func = VALUE_TABLE[data.charAt(1)];
    motion = VALUE_TABLE[data.charAt(2)]; shot = VALUE_TABLE[data.charAt(3)];
    x = getInteger(VALUE_TABLE[data.charAt(4)]) << Token.FIELD_SCALE;
    y = getInteger(VALUE_TABLE[data.charAt(5)]) << Token.FIELD_SCALE;
    direction = getDeg(VALUE_TABLE[data.charAt(6)]);
    speed = getInteger2(VALUE_TABLE[data.charAt(7)]);
    aim = VALUE_TABLE[data.charAt(8)] != 0;
    deg = getDeg(VALUE_TABLE[data.charAt(9)]); accelDeg = getDeg(VALUE_TABLE[data.charAt(10)]);
    frame = VALUE_TABLE[data.charAt(11)];
    len = VALUE_TABLE[data.charAt(12)]; num = VALUE_TABLE[data.charAt(13)];
    way = VALUE_TABLE[data.charAt(14)]; wide = VALUE_TABLE[data.charAt(15)];
    startFrame = VALUE_TABLE[data.charAt(16)] * 20;
    stepFrame = VALUE_TABLE[data.charAt(17)];
    endFrame = VALUE_TABLE[data.charAt(18)] * 20;
  }
}
class ShotManager extends DataManager{
  private static const PI:Number = Math.PI;
  private static const SHOT_FUNCS:Vector.<Function> = Vector.<Function>([shotWay, shotLineWay, shotCurve, shotLineToArrow, shotCircle, shotRandom]);
  
  public function ShotManager(data:String) {
    super(data, ShotData);
  }
  public function shot(token:IToken, shotData:ShotData):void {
    SHOT_FUNCS[shotData.func](token, shotData);
  }
  private static function shotBullet(id:int, x:int, y:int, direction:int, speed:int, motionID:int, shotID:int):Token {
    return Main.root.addBullet(id, x, y, direction, speed, motionID, shotID);
  }
  private static function getShotDirection(data:ShotData, bx:int, by:int, direction:int):int {
    return data.aim ? data.direction + getAimDirection(bx, by) + direction : data.direction + direction;
  }
  private static function getDirectionX(direction:int):Number {
    return int(Math.sin((direction / 180) * PI) * 100) / 100;
  }
  private static function getDirectionY(direction:int):Number {
    return -int(Math.sin((direction / 180) * PI + PI / 2) * 100) / 100;
  }
  public static function getAimDirection(x:int, y:int):int {
    var player:Player = Main.root.player;
    return Math.floor(((Math.atan2(player.y - y, player.x - x) * 180 / PI) + 90)) % 360;
  }
  private static function shotWay(token:IToken, data:ShotData, x:int = 0, y:int = 0, direction:int = 0, speed:int = 0):void {
    if (data.accelDeg != 0) {
      var maxFrame:int = data.frame == 0 ? Token.DEG_CIRCLE / data.accelDeg : data.frame;
      direction += token.frame % maxFrame * data.accelDeg;
    }
    var bx:int = token.x + data.x; var by:int = token.y + data.y;
    var dir:int = getShotDirection(data, bx, by, direction);
    if (data.way % 2 == 1)
      shotBullet(data.id, bx, by, dir, data.speed + speed, data.motion, data.shot);
    var n:int = data.way / 2;
    for (var i:int = 1; i <= n; i++) {
      shotBullet(data.id, bx, by, dir + data.deg * i, data.speed + speed, data.motion, data.shot);
      shotBullet(data.id, bx, by, dir - data.deg * i, data.speed + speed, data.motion, data.shot);
    }
  }
  private static function shotLineWay(token:IToken, data:ShotData, x:int = 0, y:int = 0, direction:int = 0, speed:int = 0):void {
    var bx:int = token.x + data.x; var by:int = token.y + data.y;
    var dir:int = getShotDirection(data, bx, by, direction);
    
    if (data.way % 2 == 1) { shotLineBullet(bx, by, dir, data, x, y, direction, speed) }
    var n:int = data.way >> 1;
    for (var i:int = 1; i <= n; i++) {
      shotLineBullet(bx, by, dir + data.deg * i, data, x, y, direction, speed);
      shotLineBullet(bx, by, dir - data.deg * i, data, x, y, direction, speed);
    }
    
    function shotLineBullet(bx:int, by:int, dir:int, data:ShotData, x:int = 0, y:int = 0, direction:int = 0, speed:int = 0):void
    {
      var vx:int = getDirectionX(dir) * data.wide;
      var vy:int = getDirectionY(dir) * data.wide;
      for (var i:int = 0; i < data.num; i++ ){
        var bullet:Token = shotBullet(data.id, bx + vx * i, by + vy * i, dir, data.speed + speed - i, data.motion, data.shot);
        bullet.accelSpeed = data.len * i;
      }
    }
  }
  private static function shotCurve(token:IToken, data:ShotData, x:int = 0, y:int = 0, direction:int = 0, speed:int = 0):void {
    var bx:int = token.x + data.x; var by:int = token.y + data.y;
    var dir:int = getShotDirection(data, bx, by, direction);
    
    var n:int = data.num / 2;
    for (var i:int = 0; i < data.num; i++) {
      var bullet:Token = shotBullet(data.id, bx, by, dir + ((i + 1) % data.num - n) * data.wide, data.speed + speed - Math.abs(((i + 1) % data.num - n) * 2), data.motion, data.shot);
      bullet.accelSpeed = ((i + 1) % data.num - n) * data.len;
   }
  }
  private static function shotLineToArrow(token:IToken, data:ShotData, x:int = 0, y:int = 0, direction:int = 0, speed:int = 0):void {
    var bx:int = token.x + data.x; var by:int = token.y + data.y;
    var dir:int = getShotDirection(data, bx, by, direction);
    var dir2:int = dir + 90;
    var vx:int = (getDirectionX(dir2) * data.wide) << Token.FIELD_SCALE;
    var vy:int = (getDirectionY(dir2) * data.wide) << Token.FIELD_SCALE;
    
    if (data.num % 2 == 1)
      shotBullet(data.id, bx, by, dir, data.speed + speed, data.motion, data.shot);
    var n:int = data.num / 2;
    var bullet:Token;
    for (var i:int = 1; i <= n; i++) {
      bullet = shotBullet(data.id, bx+vx*i, by+vy*i, dir, data.speed + speed, data.motion, data.shot)
      bullet.accelSpeed += i * 2;
      bullet = shotBullet(data.id, bx-vx*i, by-vy*i, dir, data.speed + speed, data.motion, data.shot)
      bullet.accelSpeed += i * 2;
    }
  }
  private static function shotCircle(token:IToken, data:ShotData, x:int = 0, y:int = 0, direction:int = 0, speed:int = 0):void {
    var bx:int = token.x + data.x; var by:int = token.y + data.y;
    var dir:int = getShotDirection(data, bx, by, direction);
    var degUnit:int = 360 / data.num;
    
    for (var i:int = 0; i < data.num; i++ ) {
      var vx:int = (getDirectionX(degUnit*i) * data.wide * 2) << Token.FIELD_SCALE;
      var vy:int = (getDirectionY(degUnit*i) * data.wide * 2) << Token.FIELD_SCALE;
      var bullet:Token =  shotBullet(data.id , bx + vy, by + vx, dir, data.speed + speed, data.motion, data.shot);
    }
  }
  private static function shotRandom(token:IToken, data:ShotData, x:int = 0, y:int = 0, direction:int = 0, speed:int = 0):void {
    var bx:int = token.x + data.x; var by:int = token.y + data.y;
    var dir:int = getShotDirection(data, bx, by, direction);
    var deg2:int = data.deg >> 1;
    
    for (var i:int = 0; i < data.num; i++ ) {
      var bullet:Token = shotBullet(data.id, bx, by, dir + Main.root.random() % data.deg - deg2, data.speed + speed + Main.root.random() % data.wide, data.motion, data.shot);
      if(data.len != 0)
          bullet.accelSpeed = Main.root.random() % data.len;
    }
  }
}
class TokenContainer
{
  private var _tokenClass:Class;
  private var _layer:BitmapData;
  private var _tokens:Vector.<IToken> = new Vector.<IToken>();
  public function get tokens():Vector.<IToken> { return _tokens; }
  private var _oldTokens:Vector.<Token> = new Vector.<Token>();
  
  public function TokenContainer(tokenClass:Class, layer:BitmapData) {
    _tokenClass = tokenClass; _layer = layer;
  }
  public function addToken(id:int=0, x:int=0, y:int=0, direction:int=0, speed:int=0, motionID:int=0, shotID:int=0):Token {
    var token:Token = _oldTokens.length > 0 ? _oldTokens.pop() : new _tokenClass();
    token.initialize(id, x, y, direction, speed, motionID, shotID);
    _tokens.push(token);
    return token;
  }
  public function update():void {
    var n:int = _tokens.length;
    for (var i:int = 0; i < n; i++) {
      var token:Token = tokens[i] as Token;
      token.update();
      if (token.isVanished) {
        tokens.splice(i, 1); n--; i--;
        _oldTokens.push(token);
      }
      else{
        token.draw(_layer);
      }
    }
  }
  public function clear():void {
    _tokens = new Vector.<IToken>();
  }
}
class LinearCongruentialGenerator {
  private static const A:Number = 1664525;
  private static const C:Number = 1013904223;
  private static const M:Number = 0x100000000;
  private var x:Number;
  
  public function LinearCongruentialGenerator(seed:Number = 1.0):void {
    initialize(seed);
  }
  public function initialize(seed:Number = 1.0):void {
    x = seed;
  }
  public function random():uint {
    x = (x * A + C) % M;
    return x;
  }
}