forked from: forked from: ActionScript で弾幕ゲーをこしらえてみた

by uwi forked from forked from: ActionScript で弾幕ゲーをこしらえてみた (diff: 304)
Arrow Key で移動
Shift で低速移動
今のところ避けることしかできません
♥0 | Line 734 | Modified 2009-09-24 20:59:52 | MIT License
play

ActionScript3 source code

/**
 * Copyright uwi ( http://wonderfl.net/user/uwi )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/33Rb
 */

// forked from 5ivestar's forked from: ActionScript で弾幕ゲーをこしらえてみた
// forked from 5ivestar's ActionScript で弾幕ゲーをこしらえてみた
// Arrow Key で移動
// Shift で低速移動
// 今のところ避けることしかできません

package {
	import flash.display.Loader;
	import flash.display.Sprite;
	import flash.display.BitmapData;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.events.KeyboardEvent;
	import flash.geom.Rectangle;
	import flash.geom.Point;
	import flash.net.URLRequest;
	import flash.text.TextField;
	import flash.text.TextFormat;
	import flash.system.LoaderContext;
	import flash.system.Security;
	import net.hires.debug.Stats;

	[SWF(width="465", height="465", backgroundColor="#262626", frameRate="60")] 

	public class Sakege extends Sprite {

		private var eField:BulletField;
		private var pField:BulletField;
		private var player:Player;
		private var enemy:Enemy;
		private var status:TextField;
		private var t:uint;
		private var hit:uint;
		private var loader:Loader;
		
		private var _aa : AvoidanceAlgorithm;

		public function Sakege() {
			stage.align = "TL";
			stage.scaleMode = "noScale";

			Security.loadPolicyFile("http://5ivestar.org/misc/crossdomain.xml");
			loader = new Loader();
			loader.contentLoaderInfo.addEventListener("complete", loadingComplete);
			loader.load(new URLRequest("http://5ivestar.org/misc/bullets.png"), new LoaderContext(true));

			pField = new BulletField(380, 445);
			pField.x = 10, pField.y = 10;
			addChild(pField);
			eField = new BulletField(380, 445);
			eField.x = 10, eField.y = 10;
			addChild(eField);

			var frame:Sprite = new Sprite();
			frame.graphics.beginFill(0x000000);
			frame.graphics.drawRect(0, 0, 465, 465);
			frame.graphics.drawRect(10, 10, 380, 445);
			addChild(frame);

			var stats:Stats = new Stats();
			stats.x = 395;
			stats.y = 10;
			frame.addChild(stats);
			status = new TextField();
			status.defaultTextFormat = new TextFormat("_sans", 9);
			status.autoSize = "left";
			status.textColor = 0xffffff;
			status.x = 395;
			status.y = 110;
			frame.addChild(status);
			
			_aa = new AvoidanceAlgorithm(eField);
		}

		public function loadingComplete(e:Event):void {
			var source:BitmapData = new BitmapData(55, 28, true, 0x00000000);
			source.draw(loader);

			var bmp1:BitmapData = new BitmapData(12, 12, true, 0x00000000);
			bmp1.copyPixels(source, new Rectangle(0, 0, 12, 12), new Point());
			Player.loadImage(bmp1);
			var bmp2:BitmapData = new BitmapData(15, 15, true, 0x00000000);
			bmp2.copyPixels(source, new Rectangle(12, 0, 15, 15), new Point());
			GrainBullet.loadImage(bmp2);
			var bmp3:BitmapData = new BitmapData(28, 28, true, 0x00000000);
			bmp3.copyPixels(source, new Rectangle(27, 0, 28, 28), new Point());
			BigBall.loadImage(bmp3);

			var s:Sprite = new Sprite();
			s.graphics.beginFill(0x000000, 0.5);
			s.graphics.drawRect(10, 10, 380, 445);
			addChild(s);
			var tf:TextField = new TextField();
			tf.defaultTextFormat = new TextFormat("_sans", 18);
			tf.autoSize = "left";
			tf.textColor = 0xffffff;
			tf.text = "click start";
			tf.x = 200 - tf.width / 2;
			tf.y = 230 - tf.height / 2;
			s.addChild(tf);
			s.addEventListener(MouseEvent.MOUSE_UP, function(e:MouseEvent):void {
				removeChild(s);
				stage.focus = stage;
				start();
			});
		}

		public function start():void {
			t = 0;
			hit = 0;
			eField.clear();
			pField.clear();
			player = new Player(185, 390);
			pField.addBullet(player);
			stage.addEventListener(KeyboardEvent.KEY_DOWN, player.keyDown);
			stage.addEventListener(KeyboardEvent.KEY_UP, player.keyUp);
			enemy = new Enemy(185, 150);
			eField.addBullet(enemy);
			Bullet.player = player;
			Bullet.enemy  = enemy;
			Bullet.field  = eField;
			Player.field  = pField;

			addEventListener(Event.ENTER_FRAME, update);
		}

		private var _dicision : int = 0;

		public function update(e:Event):void {
			t++;
			if(t % 6 == 0){
				_dicision = _aa.calcAvoidanceAtRoot(player.x, player.y, 1);
				dispatchEvent(new KeyboardEvent(_dicision <= 4 ? KeyboardEvent.KEY_DOWN : KeyboardEvent.KEY_UP, true, false, 0, 16));
				dispatchEvent(new KeyboardEvent(KeyboardEvent.KEY_UP, true, false, 0, 39));
				dispatchEvent(new KeyboardEvent(KeyboardEvent.KEY_UP, true, false, 0, 37));
				dispatchEvent(new KeyboardEvent(KeyboardEvent.KEY_UP, true, false, 0, 40));
				dispatchEvent(new KeyboardEvent(KeyboardEvent.KEY_UP, true, false, 0, 38));
				if(_dicision == 1 || _dicision == 5){
					dispatchEvent(new KeyboardEvent(KeyboardEvent.KEY_DOWN, true, false, 0, 39));
				}
				if(_dicision == 3 || _dicision == 7){
					dispatchEvent(new KeyboardEvent(KeyboardEvent.KEY_DOWN, true, false, 0, 37));
				}
				if(_dicision == 2 || _dicision == 6){
					dispatchEvent(new KeyboardEvent(KeyboardEvent.KEY_DOWN, true, false, 0, 40));
				}
				if(_dicision == 4 || _dicision == 8){
					dispatchEvent(new KeyboardEvent(KeyboardEvent.KEY_DOWN, true, false, 0, 38));
				}
/*
				dispatchEvent(new KeyboardEvent(KeyboardEvent.KEY_DOWN, true, false, 0, 16));
				dispatchEvent(new KeyboardEvent(KeyboardEvent.KEY_UP, true, false, 0, 39));
				dispatchEvent(new KeyboardEvent(KeyboardEvent.KEY_UP, true, false, 0, 37));
				dispatchEvent(new KeyboardEvent(KeyboardEvent.KEY_UP, true, false, 0, 40));
				dispatchEvent(new KeyboardEvent(KeyboardEvent.KEY_UP, true, false, 0, 38));
				if(_dicision == 1 || _dicision == 5 || _dicision == 8){
					dispatchEvent(new KeyboardEvent(KeyboardEvent.KEY_DOWN, true, false, 0, 39));
				}
				if(_dicision == 3 || _dicision == 6 || _dicision == 7){
					dispatchEvent(new KeyboardEvent(KeyboardEvent.KEY_DOWN, true, false, 0, 37));
				}
				if(_dicision == 2 || _dicision == 5 || _dicision == 6){
					dispatchEvent(new KeyboardEvent(KeyboardEvent.KEY_DOWN, true, false, 0, 40));
				}
				if(_dicision == 4 || _dicision == 7 || _dicision == 8){
					dispatchEvent(new KeyboardEvent(KeyboardEvent.KEY_DOWN, true, false, 0, 38));
				}
*/
			}
			pField.update();
			var h:uint = eField.update();
			if (h) player.hit();
			//if (player.dead) finish();
			hit += h;

			var s:String = ["BULLETS: " + eField.bullets.length.toString(),
			                "HIT: " + hit.toString(),
			                "LIFE: ∞"].join("\n");
			//for (var i:int = 0; i < player.life; i++) {
			//	s += "★";
			//}
			status.text = s;
		}

		public function finish():void {
			removeEventListener(Event.ENTER_FRAME, update);
			stage.removeEventListener(KeyboardEvent.KEY_DOWN, player.keyDown);
			stage.removeEventListener(KeyboardEvent.KEY_UP, player.keyUp);
			var s:Sprite = new Sprite();
			s.graphics.beginFill(0x000000, 0.5);
			s.graphics.drawRect(10, 10, 380, 445);
			addChild(s);
			var tf:TextField = new TextField();
			tf.defaultTextFormat = new TextFormat("_sans", 18);
			tf.autoSize = "left";
			tf.textColor = 0xffffff;
			tf.text = "retry";
			tf.x = 200 - tf.width / 2;
			tf.y = 230 - tf.height / 2;
			s.addChild(tf);
			s.addEventListener(MouseEvent.MOUSE_UP, function(e:MouseEvent):void {
				removeChild(s);
				stage.focus = stage;
				start()
			});
		}

	}
}

import flash.display.Bitmap;
import flash.display.Sprite;

// 弾のコンテナ

class BulletField extends Bitmap {

	public var fieldWidth:Number;
	public var fieldHeight:Number;
	public var bullets:Vector.<Bullet>;
	private var rect:Rectangle;

	public function BulletField(w:Number, h:Number) {
		fieldWidth = w;
		fieldHeight = h;
		bullets = new Vector.<Bullet>();
		var bmp:BitmapData = new BitmapData(w, h, true, 0x00000000);
		rect = bmp.rect;
		super(bmp);
	}

	public function addBullet(bullet:Bullet):void {
		//bullet.field = this;
		bullets.push(bullet);
	}

	public function update():uint {
		bitmapData.lock();
		bitmapData.fillRect(rect, 0x00000000);
		var hit:uint = 0;
		var n:uint = bullets.length;
		while (n--) {
			var bullet:Bullet = bullets[n];
			bullet.update();
			bullet.draw();
			if (bullet.hitTest()) hit++;
			if (bullet.dead) {
				bullets.splice(n, 1);
			}
		}
		bitmapData.unlock()
		return hit;
	}

	public function clear():void {
		bullets.splice(0, bullets.length);
	}
}

import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Shape;
import flash.events.Event;
import flash.events.KeyboardEvent;
import flash.geom.ColorTransform;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.geom.Rectangle;

// BulletField 上に表示されるオブジェクトの基底クラス
// 動きを変更するには       update
// 当たり判定を変更するには hitTest
// をそれぞれオーバーライドする

class Bullet {

	public static var player:Player;
	public static var enemy:Enemy;
	public        var x:Number = 0;
	public        var y:Number = 0;
	public        var direction:Number = 0;
	public static var field:BulletField;
	public        var dead:Boolean = false;

	public var vx : Number = 0.0;
	public var vy : Number = 0.0;
	public var r2 : Number = 0.0;

	public function Bullet(x0:Number, y0:Number, d:Number) {
		x = x0, y = y0;
		direction = d;
	}

	public function update():void {}

	public function hitTest():Boolean {
		return false;
	}

	public function draw():void {}

	public function nWay(bulletClass:Class, n:uint, direction:Number, range:Number, f:BulletField = null):void {
		var bullets:Vector.<Bullet> = (f || field).bullets;
		var length:uint = bullets.length;
		if (n == 1) {
			bullets[length] = new bulletClass(x, y, direction);
		} else {
			for (var i:int = 0; i < n; i++) {
				bullets[length + i] = new bulletClass(x, y, direction + range * (i/(n-1)-0.5));
			}
		}
	}

	public function allDirections(bulletClass:Class, n:uint, direction:Number, f:BulletField = null):void {
		var bullets:Vector.<Bullet> = (f || field).bullets;
		var length:uint = bullets.length;
		for (var i:int = 0; i < n; i++) {
			bullets[length + i] = new bulletClass(x, y, direction + Math.PI * 2 * i/n);
		}
	}

}

// 自機

class Player extends Bullet {

	public static var images:Vector.<BitmapData>;
	public static var field:BulletField;
	private       var image:BitmapData;
	public        var life:int = 4;
	private       var t:uint;
	private       var damaged:uint;
	private       var slow:Boolean = false;
	private       var left:Boolean = false;
	private       var up:Boolean = false;
	private       var right:Boolean = false;
	private       var down:Boolean = false;
	private       var alpha:Number = 1;
	private       var p:Point;
	private       var rect:Rectangle;

	public static function loadImage(bmp:BitmapData):void {
		images = new Vector.<BitmapData>(3, true);
		images[0] = bmp;
		var bmp1:BitmapData = bmp.clone();
		bmp1.colorTransform(bmp.rect, new ColorTransform(1.0, 1.0, 1.0, 0.8));
		images[1] = bmp1;
		var bmp2:BitmapData = bmp.clone();
		bmp2.colorTransform(bmp.rect, new ColorTransform(1.0, 1.0, 1.0, 0.2));
		images[2] = bmp2;
	}

	public function Player(x0:Number = 0, y0:Number = 0) {
		super(x0, y0, 0);
		image = images[0];
		rect = image.rect;
		p = new Point();
	}

	override public function update():void {
		t++;
		var vx:Number = 0;
		var vy:Number = 0;
		if (left && !right) vx = -1;
		if (!left && right) vx = 1;
		if (up && !down) vy = -1;
		if (!up && down) vy = 1;
		if (slow) {
			x += vx;
			y += vy;
		} else {
			x += vx * 3;
			y += vy * 3;
		}
		x = Math.max(0, x);
		x = Math.min(field.fieldWidth, x);
		y = Math.max(0, y);
		y = Math.min(field.fieldHeight, y);

		if (damaged) {
			var n:int = t - damaged;
			if (n % 8 < 4) {
				image = images[2];
			} else {
				image = images[1];
			}
			if (n > 75) {
				image = images[0];
				damaged = 0;
			}
		}

	}

	override public function draw():void {
		p.x = x - rect.width / 2;
		p.y = y - rect.height / 2;
		field.bitmapData.copyPixels(image, rect, p, null, null, true);
	}

	public function hit():void {
		if (!damaged) {
			damaged = t;
			life--;
			if (!life) {
				//dead = true;
			}
		}
	}

	public function keyDown(e:KeyboardEvent):void {
		switch (e.keyCode) {
			case 16: slow  = true; break;
			case 37: left  = true; break;
			case 38: up    = true; break;
			case 39: right = true; break;
			case 40: down  = true; break;
		}
	}

	public function keyUp(e:KeyboardEvent):void {
		switch (e.keyCode) {
			case 16: slow  = false; break;
			case 37: left  = false; break;
			case 38: up    = false; break;
			case 39: right = false; break;
			case 40: down  = false; break;
		}
	}

}

// 敵機

class Enemy extends	Bullet {

	private var t:uint;
	private var n:uint;

	public function Enemy(x0:Number = 0, y0:Number = 0) {
		super(x0, y0, 0);
	}

	override public function update():void {
		t++;
		var d:Number = Math.atan2(y - player.y, player.x - x);
		if (t % 200 > 26 && t % 200 < 74) allDirections(GrainBullet1, 15, Math.tan(Math.PI*t*0.02) * 0.03 + int(t*0.01)/3);
		if (t % 200 > 26 && t % 200 < 74) allDirections(GrainBullet1, 15, -Math.tan(Math.PI*t*0.02) * 0.03 + int(t*0.01)/3);
		if (t % 200 > 75 && t % 200 < 180 && !(t % 5)) allDirections(GrainBullet2, 8, Math.PI * t / 200);
		if (t % 200 > 75 && t % 200 < 180 && !(t % 5)) allDirections(GrainBullet2, 8, -Math.PI * t / 200);
		if (t % 200 == 10) nWay(BigBall3, 6, d, Math.PI / 2);
		if (t % 200 == 25) nWay(BigBall3, 4, d, Math.PI / 2);
	}
}

// Bullet に弾のイメージを管理する機能を付与したクラス
// AS3 では静的プロパティは継承されないため
// イメージ毎にこうしたクラスを作る必要がある

// 粒弾

class GrainBullet extends Bullet {

	private static var images:Vector.<BitmapData>;
	private static var p:Point;
	private static var rect:Rectangle;
	private static var centerX:Number;
	private static var centerY:Number;
	public         var image:BitmapData;

	public static function loadImage(source:BitmapData):void {
		images = new Vector.<BitmapData>(360, true);
		var s:Number = source.width;
		for (var i:int; i < 360; i++) {
			var bmp:BitmapData = new BitmapData(s, s, true, 0x00000000);
			var d:Number = i / 180 * Math.PI;
			var m:Matrix = new Matrix();
			m.translate(-s/2, -s/2);
			m.rotate(-d);
			m.translate(s/2, s/2);
			bmp.draw(source, m, null, null, null, true);
			images[i] = bmp;
		}
		p = new Point();
		rect = images[0].rect;
		centerX = rect.width / 2;
		centerY = rect.height / 2;
	}

	public function GrainBullet(x0:Number, y0:Number, d:Number) {
		super(x0, y0, d);
		r2 = 9;
		updateImage();
	}

	override public function draw():void {
		p.x = x - centerX;
		p.y = y - centerY;
		field.bitmapData.copyPixels(image, rect, p, null, null, true);
	}

	override public function hitTest():Boolean {
		var dx:Number, dy:Number;
		return (dx = player.x - x) * dx + (dy = player.y - y) * dy < 9;
	}

	public function updateImage():void {
		var i:int = direction / Math.PI * 180;
		image = images[((i %= 360) < 0) ? i + 360 : i];
	}

}

// 大玉

class BigBall extends Bullet {

	private static var image:BitmapData;
	private static var p:Point;
	private static var rect:Rectangle;
	private static var centerX:Number;
	private static var centerY:Number;

	public static function loadImage(bmp:BitmapData):void {
		image = bmp;
		p = new Point()
		rect = image.rect;
		centerX = rect.width / 2;
		centerY = rect.height / 2;
	}

	public function BigBall(x0:Number, y0:Number, d:Number) {
		super(x0, y0, d);
		r2 = 100;
	}

	override public function draw():void {
		p.x = x - centerX;
		p.y = y - centerY;
		field.bitmapData.copyPixels(image, rect, p, null, null, true);
	}

	override public function hitTest():Boolean {
		var dx:Number, dy:Number;
		return (dx = player.x - x) * dx + (dy = player.y - y) * dy < 100;
	}

}

// 等速直進運動する弾

class GrainBullet1 extends GrainBullet {

	private       var t:uint = 0;

	public function GrainBullet1(x0:Number, y0:Number, d:Number) {
		super(x0, y0, d);
		vx =  Math.cos(direction) * 2.5;
		vy = -Math.sin(direction) * 2.5;
	}

	override public function update():void {
		if (t < 25) {
			vx *= 0.98;
			vy *= 0.98;
			t++;
		}
		x += vx;
		y += vy;

		if (x < -10 || x > field.fieldWidth + 10 ||
			y < -10 || y > field.fieldHeight + 10) {
			dead = true;
		}
	}

}

class GrainBullet2 extends GrainBullet {

	private var t:uint = 0;

	public function GrainBullet2(x0:Number, y0:Number, d:Number) {
		super(x0, y0, d);
		vx =  Math.cos(direction) * 2.5;
		vy = -Math.sin(direction) * 2.5;
	}

	override public function update():void {
		if (t < 25) {
			vx *= 0.97;
			vy *= 0.97;
			t++;
		}
		x += vx;
		y += vy;

		if (x < -10 || x > field.fieldWidth + 10 ||
			y < -10 || y > field.fieldHeight + 10) {
			dead = true;
		}
	}

}

class BigBall1 extends BigBall {

	public function BigBall1(x0:Number, y0:Number, d:Number, v:Number = 1) {
		super(x0, y0, d);
		vx =  Math.cos(direction) * v;
		vy = -Math.sin(direction) * v;
	}

	override public function update():void {
		x += vx;
		y += vy;

		if (x < -10 || x > field.fieldWidth + 10 ||
			y < -10 || y > field.fieldHeight + 10) {
			dead = true;
		}
	}

}

// 等加速度運動する弾

class BigBall2 extends BigBall {

	private var t:uint = 0;

	public function BigBall2(x0:Number, y0:Number, d:Number, v:Number = 2) {
		super(x0, y0, d);
		vx =  Math.cos(direction) * v;
		vy = -Math.sin(direction) * v;
	}

	override public function update():void {
		t++;
		x += vx * (1 + t * 0.012);
		y += vy * (1 + t * 0.012);

		if (x < -10 || x > field.fieldWidth + 10 ||
			y < -10 || y > field.fieldHeight + 10) {
			dead = true;
		}
	}

}

// はじけた後自機狙い

class BigBall3 extends BigBall {

	private var t:uint = 0;

	public function BigBall3(x0:Number, y0:Number, d:Number) {
		super(x0, y0, d);
		vx =  Math.cos(direction) * 2.5;
		vy = -Math.sin(direction) * 2.5;
	}

	override public function update():void {
		t++;
		if (t < 50) {
			vx *= 0.98;
			vy *= 0.98;
			x += vx;
			y += vy;
		} else if (t == 50) {
			direction = Math.atan2(y - player.y, player.x - x) + (Math.random() - 0.5) * 0.5;
			vx =  Math.cos(direction) * 2;
			vy = -Math.sin(direction) * 2;
		} else {
			x += vx * (1 + (t-50) * 0.012);
			y += vy * (1 + (t-50) * 0.012);
			if (x < -10 || x > field.fieldWidth + 10 ||
			    y < -10 || y > field.fieldHeight + 10) {
				dead = true;
			}
		}
	}

}

class AvoidanceAlgorithm
{
	private var _bf : BulletField;

	public function AvoidanceAlgorithm(bf : BulletField)
	{
            _bf = bf;
	}

/*
    // 低速移動オンリー
    public static const PTN_MOVE : Array = [
        [0, 0], 
        [1, 0],
        [0, 1],
        [-1, 0],
        [0, -1],
        [1, 1],
        [-1, 1],
        [-1, -1],
        [1, -1]
        ];
        */
    // 低速移動オンリー
    public static const PTN_MOVE : Array = [
        [0, 0], 
        [1, 0],
        [0, 1],
        [-1, 0],
        [0, -1],
        [3, 0],
        [0, 3],
        [-3, 0],
        [0, -3],
        ];
    private const W : int = 380;
    private const H : int = 245;
    // 決定間隔
    private const INTERVAL : int = 6;
        
    // 回避アルゴリズム
    // @param depth 読むパスの深さ
    public function calcAvoidanceAtRoot(x : Number, y : Number, depth : int) : int
    {
        var i : int;
        var t : Number;
        var mincs : Array = new Array(PTN_MOVE.length);
        // 壁への衝突
        for(i = 0;i < PTN_MOVE.length;i++){
            var minc : Number = Number.MAX_VALUE;
            if(PTN_MOVE[i][0] > 0){
                t = (W - x) / PTN_MOVE[i][0];
                if(t < minc)minc = t;
            }else if(PTN_MOVE[i][0] < 0){
                t = (x) / -PTN_MOVE[i][0];
                if(t < minc)minc = t;
            }
            // あんまり上に行かないように入れ知恵
            if(PTN_MOVE[i][1] > 0){
                t = (H - (y - 100)) / PTN_MOVE[i][1];
                if(t < minc)minc = t;
            }else if(PTN_MOVE[i][1] < 0){
                t = (y - 100) / -PTN_MOVE[i][1];
                if(t < minc)minc = t;
            }
            mincs[i] = minc;
        }
        
        // 弾への衝突
        for each(var b : Bullet in _bf.bullets){
            var rxx : Number = b.x - x;
            var rxy : Number = b.y - y;
            for(i = 0;i < PTN_MOVE.length;i++){
            	var rvx : Number = b.vx - PTN_MOVE[i][0];
            	var rvy : Number = b.vy - PTN_MOVE[i][1];
                t = mint(rxx, rxy, rvx, rvy);
                if(t > 0){
                    var d2 : Number = mind2(rxx, rxy, rvx, rvy, t);
                    if(d2 < b.r2){
                        t -= Math.sqrt((b.r2 - d2) / (rvx * rvx + rvy * rvy));
                        if(t < mincs[i])mincs[i] = t;
                    }
                }
            }
        }
        
        var mc : Number;
        var maxminc : Number = 0.0;
            var maxmini : int = Math.random() * PTN_MOVE.length; // 詰みの防止
        for(i = 0;i < PTN_MOVE.length;i++){
            var maxt : int = int(mincs[i] / INTERVAL) * INTERVAL;
            if(maxt <= INTERVAL)continue;
            if(depth == 0){
                mc = mincs[i] + calcAvoidanceAtLeaf(
                    x + PTN_MOVE[i][0] * maxt,
                    y + PTN_MOVE[i][1] * maxt,
                    maxt
                    );
            }else{
                mc = mincs[i] + calcAvoidanceAtNode(
                    x + PTN_MOVE[i][0] * maxt,
                    y + PTN_MOVE[i][1] * maxt,
                    maxt,
                    depth - 1
                    );
            }
            if(mc > maxminc){
                maxminc = mc;
                maxmini = i;
            }
        }
        return maxmini;
    }
    
    private function calcAvoidanceAtNode(x : Number, y : Number, delay : int, depth : int) : Number
    {
        var i : int;
        var t : Number;
        var mincs : Array = new Array(PTN_MOVE.length);
        // 壁への衝突
        for(i = 0;i < PTN_MOVE.length;i++){
            var minc : Number = Number.MAX_VALUE;
            if(PTN_MOVE[i][0] > 0){
                t = (W - x) / PTN_MOVE[i][0];
                if(t < minc)minc = t;
            }else if(PTN_MOVE[i][0] < 0){
                t = (x) / -PTN_MOVE[i][0];
                if(t < minc)minc = t;
            }
            // あんまり上に行かないように入れ知恵
            if(PTN_MOVE[i][1] > 0){
                t = (H - (y - 100)) / PTN_MOVE[i][1];
                if(t < minc)minc = t;
            }else if(PTN_MOVE[i][1] < 0){
                t = (y - 100) / -PTN_MOVE[i][1];
                if(t < minc)minc = t;
            }
            mincs[i] = minc;
        }
        
        // 弾への衝突
        for each(var b : Bullet in _bf.bullets){
            var rxx : Number = b.x - x + b.vx * delay;
            var rxy : Number = b.y - y + b.vy * delay;
            for(i = 0;i < PTN_MOVE.length;i++){
            	var rvx : Number = b.vx - PTN_MOVE[i][0];
            	var rvy : Number = b.vy - PTN_MOVE[i][1];
                t = mint(rxx, rxy, rvx, rvy);
                if(t > 0){
                    var d2 : Number = mind2(rxx, rxy, rvx, rvy, t);
                    if(d2 < b.r2){
                        t -= Math.sqrt((b.r2 - d2) / (rvx * rvx + rvy * rvy));
                        if(t < mincs[i])mincs[i] = t;
                    }
                }
            }
        }
        
        var mc : Number;
        var maxminc : Number = 0.0;
        for(i = 0;i < PTN_MOVE.length;i++){
            var maxt : int = int(mincs[i] / INTERVAL) * INTERVAL;
            if(maxt <= INTERVAL)continue;
            if(depth == 0){
                mc = mincs[i] + calcAvoidanceAtLeaf(
                    x + PTN_MOVE[i][0] * maxt,
                    y + PTN_MOVE[i][1] * maxt,
                    delay + maxt
                    );
            }else{
                mc = mincs[i] + calcAvoidanceAtNode(
                    x + PTN_MOVE[i][0] * maxt,
                    y + PTN_MOVE[i][1] * maxt,
                    delay + maxt,
                    depth - 1
                    );
            }
            if(mc > maxminc)maxminc = mc;
        }
        return maxminc;
    }
    
    private function calcAvoidanceAtLeaf(x : Number, y : Number, delay : int) : Number
    {
        var i : int;
        var t : Number;
        var minc : Number = Number.MAX_VALUE;
        // 壁への衝突
        for(i = 0;i < PTN_MOVE.length;i++){
            if(PTN_MOVE[i][0] > 0){
                t = (W - x) / PTN_MOVE[i][0];
                if(t < minc)minc = t;
            }else if(PTN_MOVE[i][0] < 0){
                t = (x) / -PTN_MOVE[i][0];
                if(t < minc)minc = t;
            }
            // あんまり上に行かないように入れ知恵
            if(PTN_MOVE[i][1] > 0){
                t = (H - (y - 100)) / PTN_MOVE[i][1];
                if(t < minc)minc = t;
            }else if(PTN_MOVE[i][1] < 0){
                t = (y - 100) / -PTN_MOVE[i][1];
                if(t < minc)minc = t;
            }
        }
        // 弾への衝突
        for each(var b : Bullet in _bf.bullets){
            var rxx : Number = b.x - x + b.vx * delay;
            var rxy : Number = b.y - y + b.vy * delay;
            for(i = 0;i < PTN_MOVE.length;i++){
            	var rvx : Number = b.vx - PTN_MOVE[i][0];
            	var rvy : Number = b.vy - PTN_MOVE[i][1];
                t = mint(rxx, rxy, rvx, rvy);
                if(t > 0){
                    var d2 : Number = mind2(rxx, rxy, rvx, rvy, t);
                    if(d2 < b.r2){
                        t -= Math.sqrt((b.r2 - d2) / (rvx * rvx + rvy * rvy));
                        if(t < minc)minc = t;
                    }
                }
            }
        }
        return minc;
    }
    
    private function mint(x : Number, y : Number, vx : Number, vy : Number) : Number
    {
        if(vx * vx + vy * vy < 0.01){
            return 0.0;
        }
        return -(x * vx + y * vy) / (vx * vx + vy * vy);
    }
    
    private function mind2(x : Number, y : Number, vx : Number, vy : Number, t : Number) : Number
    {
        var xx : Number = x + vx * t;
        var yy : Number = y + vy * t;
        return xx * xx + yy * yy;
    }
}

Forked