In case Flash no longer exists; a copy of this site is included in the Flashpoint archive's "ultimate" collection.

Dead Code Preservation :: Archived AS3 works from wonderfl.net

TKTank

このコードを新着タンク一覧http://flash-games.wonderfl.net/tank/list/new
に表示させるにはinfinite-tank-entry
というタグをつけてください
/**
 * Copyright tkinjo ( http://wonderfl.net/user/tkinjo )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/mxWv
 */

// forked from 9re's Wonderfl Tank Game Tank Sample 1
// このコードを新着タンク一覧http://flash-games.wonderfl.net/tank/list/new
// に表示させるにはinfinite-tank-entry
// というタグをつけてください
package 
{
    import flash.display.*;
    import flash.geom.*;
    import flash.events.*;
    import flash.filters.*;
    import flash.text.TextField;
    import flash.text.TextFieldAutoSize;
    
    import net.wonderfl.game.infinity_tank.development.*;
    import net.wonderfl.game.infinity_tank.core.*;
    import net.wonderfl.math.*;
    
    [SWF(backgroundColor="#000000")]
    
    /**
     * @author 9re
     */
    public class Tank extends TankBase
    {
        /** ==================================================
         *  constants
         *  ==================================================
         */
        
        /** --------------------------------------------------
         * action constant
         */
        private const INTERCEPT_ITERATION:Number = 10;
        private const INTERCEPT_ACCURACY:Number = 1;
        
        
        
        /** --------------------------------------------------
         * draw & common constant
         */
        private const DEBUG:Boolean = false;
        private const INTERCEPT:Boolean = true;
        
        private const BULLET_RENDERER_URL:String = "http://swf.wonderfl.net/swf/usercode/2/2a/2a1c/2a1c4faef5a34a6cb98cd005db878f576fe8d2e4.swf";//"http://swf.wonderfl.net/swf/usercode/0/04/046c/046cee45b4334c4f2dac8dfa7ec9ea2b2b2eb27d.swf";
        
        private const STAGE_WIDTH :Number = 600;
        private const STAGE_HEIGHT:Number = 550;
        private const TANK_WIDTH  :Number = 50;
        private const TANK_HEIGHT :Number = 30;
        private const TANK_RADIUS :Number = 25;
        private const GUN_WIDTH   :Number = 24;
        private const GUN_HEIGHT  :Number = 10;
        
        private const AFTERIMAGE_COLOR_TRANSFORM:ColorTransform = new ColorTransform(1, 1, 1, 1 - 1 / 4);
        private const TANK_BODY_GLOW_FILTER:GlowFilter = new GlowFilter( 0xffffff, 1, 8, 8, 0.2 );
        
        
        
        
        
        /** ==================================================
         *  variables
         *  ==================================================
         */
        
        /** --------------------------------------------------
         * action variables
         */
        private var moveForward:Boolean = true;
        private var intercept:Intercept;
        private var linearIntercept:Intercept;
        private var circularIntercept:Intercept;
        
        
        
        
        /** --------------------------------------------------
         * draw & common variables
         */
        public function get stageWidth():Number { return STAGE_WIDTH; };
        public function get stageHeight():Number { return STAGE_HEIGHT };
        public function get tankRadius():Number { return TANK_RADIUS };
        private var tankWidth:Number;
        private var tankHeight:Number;
        
        private var tankBody:Sprite;
        private var gun:Sprite;
        
        private var previousStageBitmapData:BitmapData;
        private var drawTankMatrix:Matrix = new Matrix();
        
        
        
        
        
        
        /** ==================================================
         *  methods
         *  ==================================================
         */
        
        /**
         * 
         */
        public function Tank() 
        {
            Wonderfl.disable_capture();
            //Wonderfl.capture_delay(1);
            
            _bulletRenderer = BULLET_RENDERER_URL;
        }
        
        /**
         * 
         */
        //override protected function init():void {
        protected function init():void {
            
            //super._init();
            __init();
            
            
            
            // draw
            tankWidth   = TANK_WIDTH;
            tankHeight  = TANK_HEIGHT;
            
            createTankBody();
            
            if ( DEBUG )
                createDebugObject();
            
            drawTankMatrix = new Matrix();
            
            
            
            // action
            linearIntercept = new LinearIntercept( tkBattleScene, INTERCEPT_ITERATION, INTERCEPT_ACCURACY );
            circularIntercept = new CircularIntercept( tkBattleScene, INTERCEPT_ITERATION, INTERCEPT_ACCURACY );
        }
        
        /* 速度の測定に使用
        private var maxLinearVelocity:Number = 0;
        private var maxAngularVelocity:Number = 0;
        //*/
        /**
         * 常に攻撃
         * 
         * @return
         */
        public function attackAction():int {
            
            var action:int = Command.DO_NOTHING;
            
            
            
            if ( INTERCEPT ) {
                
                // intercept
                if( Math.abs( enemyTank.angularVelocity ) <= 1 / 180 * Math.PI )
                    intercept = linearIntercept;
                
                else
                    intercept = circularIntercept;
                
                intercept.calculate();
            }
            
            
            
            
            
            var turnGunRight:Number;
            
            if( INTERCEPT )
                turnGunRight = intercept.turnGunRight;
                
            else
                turnGunRight = Angle.normalizeRadianM180toP180( myTank.enemyTankBearingFromGunHeading );
            
            
            
            
            
            
            // set fire
            if ( INTERCEPT )
                /*
                if ( 
                        !tkBattleScene.bulletSpeed || 
                        ( 
                            Math.abs(turnGunRight) <= intercept.threshold && 
                            !intercept.targetBumpAgainstAWall 
                        )
                    )//*/
                    action += Command.FIRE;
                
            else
                action += Command.FIRE;
            //trace( Math.abs(turnGunRight) * 180 / Math.PI , intercept.threshold * 180 / Math.PI );
            
            
            
            
            
            
            
            
            // set turn gun
            if( turnGunRight > 0 )
                action += Command.GUN_TURN_RIGHT;
            
            else 
                action += Command.GUN_TURN_LEFT;
            
            return action;
        }
        
        
        
        /**
         * 敵に対して ENEMY_BEARING_FROM_FLANKING 度に構える
         * 
         * @return
         */
        public function avoidAction():int {
            
            var action:int = Command.DO_NOTHING;
            
            action = antiGravityMoveAction();
            
            return action;
        }
        
        
        
        private function antiGravityMoveAction():int {
            
            // enemyTank
            var myTankPosition:WVector2D = myTank.position;
            var enemyTankPosition:WVector2D = enemyTank.position;
            
            var enemyTankGravity:GravityPoint = new GravityPoint( enemyTankPosition.x, enemyTankPosition.y, -1000 );
            var enemyTankForce:WVector2D = enemyTankGravity.force( myTankPosition );
            
            var forceX:Number = enemyTankForce.x;
            var forceY:Number = enemyTankForce.y;
            
            
            
            // wall
            var topWallGravity:GravityPoint    = new GravityPoint( myTankPosition.x, 0,                -1000 );
            var rightWallGravity:GravityPoint  = new GravityPoint( stageWidth,       myTankPosition.y, -1000 );
            var bottomWallGravity:GravityPoint = new GravityPoint( myTankPosition.x, stageHeight,      -1000 );
            var leftWallGravity:GravityPoint   = new GravityPoint( 0,                myTankPosition.y, -1000 );
            
            var topWallForce:WVector2D   = topWallGravity.force(    myTankPosition );
            var rightWallForce:WVector2D  = rightWallGravity.force(  myTankPosition );
            var bottomWallForce:WVector2D = bottomWallGravity.force( myTankPosition );
            var leftWallForce:WVector2D   = leftWallGravity.force(   myTankPosition );
            
            forceY += topWallForce.y;
            forceX += rightWallForce.x;
            forceY += bottomWallForce.y;
            forceX += leftWallForce.x;
            
            
            
            // bullet
            var enemyTankBullets:Vector.<BoundBox> = enemyTank.bullets;
            
            for( var i:int = 0; i < enemyTankBullets.length; i++ ) {
                
                var enemyTankBulletGravity:GravityPoint = new GravityPoint( enemyTankBullets[i].position.x, enemyTankBullets[i].position.y, -500 );
                var enemyTankBulletForce:WVector2D = enemyTankBulletGravity.force( myTankPosition );
                
                forceX += enemyTankBulletForce.x;
                forceY += enemyTankBulletForce.y;
            }
            
            return goTo( new WVector2D( myTankPosition.x - forceX, myTankPosition.y - forceY ) );
        }
        
        
        
        private function goTo( destination:WVector2D ):int {
            
            var action:int = Command.DO_NOTHING;
            
            
            
            var myTankHeading:Number = myTank.heading;
            var myTankPosition:WVector2D = myTank.position;

            var destinationFromMyTank:WVector2D = destination.copy();
            destinationFromMyTank.subtract(myTankPosition);
            
            var destinationBearingFromMyTank:Number = Math.atan2(destinationFromMyTank.y, destinationFromMyTank.x);
            destinationBearingFromMyTank = Angle.normalizeRadian0to360(destinationBearingFromMyTank);
            
            var destinationBearingFromMyTankHeading:Number = destinationBearingFromMyTank - myTankHeading;
            destinationBearingFromMyTankHeading = Angle.normalizeRadian0to360( destinationBearingFromMyTankHeading );
            
            var turnRight:Number = Angle.normalizeRadianM180toP180( destinationBearingFromMyTankHeading );
            
            
            
            if( Math.abs( turnRight ) < Angle.RADIAN_90_DEGREE ) {
                
                action += Command.TANK_MOVE_FORWARD;
                
            } else {
                
                action += Command.TANK_MOVE_BACKWARD;
                turnRight = Angle.normalizeRadianM180toP180( destinationBearingFromMyTankHeading + Angle.RADIAN_180_DEGREE );
            }
            
            if( turnRight > Angle.RADIAN_0_DEGREE )
                action += Command.TANK_TURN_RIGHT;
            
            else 
                action += Command.TANK_TURN_LEFT;
            
            
            
            return action;
        }
        
        
        
        /**
         * 敵の銃弾に当たれば方向転換
         */
        override public function hit():void {
            
            moveForward = !moveForward;
        }
        
        
        
        
        
        /** ==================================================
         *  draw
         *  ==================================================
         */
        
        
        
        /**
         * 
         * @param    tankBodyBitmapData
         */
        //override public function drawStageBitmapData(stageBitmapData:BitmapData):void
        public function drawStageBitmapData(stageBitmapData:BitmapData):BitmapData
        {
            if( !DEBUG )
                gun.rotation = _scene.myGunAngle * 180 / Math.PI;
            else
                gun.rotation = myTank.gunBearingFromHeading * 180 / Math.PI;
            
            // ----------
            
            if ( previousStageBitmapData == null )
                previousStageBitmapData = new BitmapData( stageBitmapData.width, stageBitmapData.height, true, 0 );
            
            previousStageBitmapData.colorTransform(previousStageBitmapData.rect, AFTERIMAGE_COLOR_TRANSFORM );
            
            // -----
            
            drawTankMatrix.identity();
            
            if( !DEBUG ) {
                drawTankMatrix.rotate( _scene.myTankAngle );
                drawTankMatrix.translate( _scene.myTankPosition.x, _scene.myTankPosition.y );
            
            } else {
                drawTankMatrix.rotate( myTank.heading );
                drawTankMatrix.translate( myTank.position.x, myTank.position.y );
            }
            
            previousStageBitmapData.draw(tankBody, drawTankMatrix, null, null, null, true);
            
            // -----
            
            stageBitmapData.colorTransform( stageBitmapData.rect, CLEAR_COLOR_TRANSFORM );
            stageBitmapData.draw(previousStageBitmapData);
            
            // ----------
            
            if ( DEBUG ) {
                
                var debugBitmapData:BitmapData = new BitmapData( stageBitmapData.width, stageBitmapData.height, true, 0 );
                debugBitmapData = drawDebugBitmapData( debugBitmapData );
                
                stageBitmapData.draw(debugBitmapData);
            }
            
            // -----
            
            return stageBitmapData;
        }
        
        
        
        
        /**
         * 
         */
        private function createTankBody():void {
            
            // draw tankBody
            tankBody = new Sprite();
            
            // body
            var gradientMatrix:Matrix = new Matrix();
            gradientMatrix.createGradientBox( TANK_WIDTH + 10, TANK_HEIGHT + 10, 0, -5 - ( TANK_WIDTH / 2 ), -5 - ( TANK_HEIGHT / 2 ) );
            tankBody.graphics.lineStyle( 1, 0xffffff, 0.5, true );
            tankBody.graphics.beginGradientFill( 
                    GradientType.RADIAL, 
                    new Array( 0x666666, 0x333333, 0x000000 ), 
                    new Array( 1, 1, 1 ), 
                    new Array( 0, 160, 255 ), 
                    gradientMatrix
                );
            tankBody.graphics.drawRoundRect( -( TANK_WIDTH / 2 ), -( TANK_HEIGHT / 2 ), TANK_WIDTH, TANK_HEIGHT, 10, 10 );
            
            // direction mark
            tankBody.graphics.beginFill( 0xffffff );
            tankBody.graphics.moveTo(  3,  0 );
            tankBody.graphics.lineTo( -3,  3 );
            tankBody.graphics.lineTo( -3, -3 );
            
            tankBody.graphics.endFill();
            
            tankBody.filters = new Array( TANK_BODY_GLOW_FILTER );
            
            
            
            // gun
            gun = new Sprite();
            gun.graphics.beginFill( 0xffffff );
            gun.graphics.drawEllipse( 9, -1, 2, 2 );
            gun.graphics.endFill();
            
            tankBody.addChild( gun );
        }
        
        
        
        
        
        /** --------------------------------------------------
         *  debug
         */
        private const DEBUG_POINT_RADIUS:Number = 30;
        private const DEBUG_AFTERIMAGE_COLOR_TRANSFORM:ColorTransform = new ColorTransform(1, 1, 1, 1 - 1 / 16);
        
        private var debugPointSprite:Sprite;
        private var debugTextField:TextField;
        private var debugMatrix:Matrix;
        private var previousDebugBitmapData:BitmapData;
        
        
        private function createDebugObject():void {
            
            debugPointSprite = new Sprite();
            debugPointSprite.graphics.lineStyle( 1, 0x00ff00, 0.2 );
            debugPointSprite.graphics.drawCircle( 0, 0, DEBUG_POINT_RADIUS );
            debugPointSprite.graphics.moveTo( 0, -DEBUG_POINT_RADIUS );
            debugPointSprite.graphics.lineTo( 0,  DEBUG_POINT_RADIUS );
            debugPointSprite.graphics.moveTo( -DEBUG_POINT_RADIUS, 0 );
            debugPointSprite.graphics.lineTo( DEBUG_POINT_RADIUS, 0 );
            
            debugTextField = new TextField();
            debugTextField.textColor = 0xffffff;
            debugTextField.autoSize = TextFieldAutoSize.LEFT;
            
            debugMatrix = new Matrix();
        }
        
        private function drawDebugBitmapData( debugBitmapData:BitmapData ):BitmapData {
            
            if ( intercept == null )
                return debugBitmapData;
            
            if ( previousDebugBitmapData == null )
                previousDebugBitmapData = new BitmapData( debugBitmapData.width, debugBitmapData.height, true, 0 );
            
            previousDebugBitmapData.colorTransform(previousDebugBitmapData.rect, DEBUG_AFTERIMAGE_COLOR_TRANSFORM );
            
            // -----
            /*
            // myTank.position
            debugMatrix.identity();
            debugMatrix.translate( myTank.position.x, myTank.position.y );
            previousDebugBitmapData.draw( debugPointSprite, debugMatrix );
            //*//*
            // enemyTank.position
            debugMatrix.identity();
            debugMatrix.translate( enemyTank.position.x, enemyTank.position.y );
            previousDebugBitmapData.draw( debugPointSprite, debugMatrix );
            //*/
            // intercept.impactPoint
            debugMatrix.identity();
            debugMatrix.translate( intercept.impactPoint.x, intercept.impactPoint.y );
            previousDebugBitmapData.draw( debugPointSprite, debugMatrix );
            
            // -----
            
            debugBitmapData.draw( previousDebugBitmapData );
            
            // -----
            
            debugTextField.text = "";/*
            debugTextField.appendText("myTank.position.x = " + myTank.position.x + "\n" );
            debugTextField.appendText("myTank.position.y = " + myTank.position.y + "\n" );
            debugTextField.appendText("myTank.positionFromPreviousPosition.x = " + myTank.positionFromPreviousPosition.x + "\n" );
            debugTextField.appendText("myTank.positionFromPreviousPosition.y = " + myTank.positionFromPreviousPosition.y + "\n" );
            debugTextField.appendText("myTank.velocity.x = " + myTank.velocity.x + "\n" );
            debugTextField.appendText("myTank.velocity.y = " + myTank.velocity.y + "\n" );
            debugTextField.appendText("myTank.velocity.length = " + myTank.velocity.length + "\n" );
            debugTextField.appendText("myTank.angularVelocity = " + myTank.angularVelocity * 180 / Math.PI + "\n" );
            
            debugTextField.appendText("enemyTank.position.x = " + enemyTank.position.x + "\n" );
            debugTextField.appendText("enemyTank.position.y = " + enemyTank.position.y + "\n" );
            debugTextField.appendText("enemyTank.positionFromPreviousPosition.x = " + enemyTank.positionFromPreviousPosition.x + "\n" );
            debugTextField.appendText("enemyTank.positionFromPreviousPosition.y = " + enemyTank.positionFromPreviousPosition.y + "\n" );
            debugTextField.appendText("enemyTank.velocity.x = " + enemyTank.velocity.x + "\n" );
            debugTextField.appendText("enemyTank.velocity.y = " + enemyTank.velocity.y + "\n" );
            debugTextField.appendText("enemyTank.velocity.length = " + enemyTank.velocity.length + "\n" );
            debugTextField.appendText("enemyTank.angularVelocity = " + enemyTank.angularVelocity * 180 / Math.PI + "\n" );
            
            debugTextField.appendText("tkBattleScene.bulletSpeed = " + tkBattleScene.bulletSpeed + "\n" );
            
            debugTextField.appendText("intercept = " + intercept + "\n" );//*/
            debugTextField.appendText("intercept.impactTime = " + intercept.impactTime + "\n" );
            debugTextField.appendText("intercept.impactPoint.x = " + intercept.impactPoint.x + "\n" );
            debugTextField.appendText("intercept.impactPoint.y = " + intercept.impactPoint.y + "\n" );
            
            // -----
            
            debugBitmapData.draw( debugTextField );
            
            // -----
            
            
            return debugBitmapData;
        }
        
        
        
        
        
        
        /** ==================================================
         *  TKTankBase
         *  ==================================================
         */
        
        /** --------------------------------------------------
         *  draw constant
         */
        protected const CLEAR_COLOR_TRANSFORM:ColorTransform = new ColorTransform( 1, 1, 1, 0 );
        
        
        
        /** --------------------------------------------------
         *  action variable
         */
        protected var tkBattleScene:TKBattleScene;
        
        protected var myTank:MyTank;
        protected var enemyTank:EnemyTank;
        
        private var battleMode:Boolean;
        
        /*
        public function get stageWidth():Number {
            return 0;
        }
        public function get stageHeight():Number {
            return 0;
        }
        protected function init():void {
            
            super._init();
            
            tkBattleScene = new TKBattleScene( _scene, this, stageWidth, stageHeight );
        }
        //*/
        
        
        
        /**
         * 
         */
        protected function __init():void {
            
            tkBattleScene = new TKBattleScene( _scene, this, stageWidth, stageHeight, tankRadius );
            
            myTank = tkBattleScene.myTank;
            enemyTank = tkBattleScene.enemyTank;
        }
        
        
        
        /**
         * 
         * @return
         */
        //override final public function action():int {
        override public function action():int {
            
            //if( !_world.battleMode ) 
            if ( !battleMode )
                battleMode = true;
            
            var action:int = Command.DO_NOTHING;
            
            if( _scene.enemyTankPosition == null )
                return action;
            
            if( tkBattleScene == null )
                _init();
            
            tkBattleScene.run();
            
            // -----
            
            action += attackAction();
            
            action += avoidAction();
            
            return action;
        }
        
        
        /*
        public function fireAction():int {
            
            var action:int = Command.DO_NOTHING;
            
            return action;
        }
        
        
        
        public function turnTankAction():int {
            
            var action:int = Command.DO_NOTHING;
            
            return action;
        }

        
        
        public function moveTankAction():int {
            
            var action:int = Command.DO_NOTHING;
            
            return action;
        }
        
        
        
        public function turnGunAction():int {
            
            var action:int = Command.DO_NOTHING;
            
            return action;
        }
        //*/
        
        
        
        
        /** --------------------------------------------------
         *  draw
         *  --------------------------------------------------
         */
        
        /**
         * 
         * @param    bitmapData
         */
        //override final public function draw(bitmapData:BitmapData):void
        override public function draw(bitmapData:BitmapData):void
        {
            if( tkBattleScene == null )
                init();
            
            //if( !_world.battleMode ) 
            if( !battleMode ) 
                tkBattleScene.run();
            
            // -----
            
            var stageBitmapData:BitmapData = new BitmapData( bitmapData.width, bitmapData.height, true, 0 );
            stageBitmapData.draw( bitmapData );
            
            stageBitmapData = drawStageBitmapData( stageBitmapData );
            
            bitmapData.colorTransform( bitmapData.rect, CLEAR_COLOR_TRANSFORM );
            bitmapData.draw( stageBitmapData );
        }
        /*
        public function drawStageBitmapData(stageBitmapData:BitmapData):BitmapData {
            return null;
        }
        //*/
    }
}





/** ==================================================
 *  library
 *  ==================================================
 */
import net.wonderfl.game.infinity_tank.development.*;
import net.wonderfl.math.*;
import flash.display.*;
import flash.geom.*;

/**
 * helper class
 */
class TKBattleScene {
    
    /* ==================================================
     * field
     * ==================================================
     */
    private var battleScene:BattleScene;
    private var tank:TankBase;
    
    
    
    
    
    /* ==================================================
     * property
     * ==================================================
     */
    
    
    
    /* --------------------------------------------------
     * -
     */
    private var _bulletSpeed:Number;
    public function get bulletSpeed():Number {
        return _bulletSpeed;
    }
    
    private var _robotRadius:Number;
    public function get robotRadius():Number {
        return _robotRadius;
    }
    
    
    
    /* --------------------------------------------------
     * stage
     */
    private var _stageWidth:Number;
    public function get stageWidth():Number {
        return _stageWidth;
    }
    
    private var _stageHeight:Number;
    public function get stageHeight():Number {
        return _stageHeight;
    }

    private var _stageCenter:WVector2D;
    public function get stageCenter():WVector2D {
        return _stageCenter;
    }

    private var _stageDiagonal:Number;
    public function get stageDiagonal():Number {
        return _stageDiagonal;
    }
    
    
    
    /* --------------------------------------------------
     * tank
     */
    private var _myTank:MyTank;
    public function get myTank():MyTank {
        return _myTank;
    }
    private var _enemyTank:EnemyTank;
    public function get enemyTank():EnemyTank {
        return _enemyTank;
    }
    
    
    
    /* --------------------------------------------------
     * enemyTank property
     */
    public function get enemyBulletCount():int {
        return battleScene.enemyBulletCount;
    }
    
    private var _enemyBullets:Vector.<BoundBox>;
    public function get enemyBullets():Vector.<BoundBox> {
        return _enemyBullets;
    }
    
    public function get enemyTankHeading():Number {
        
        var enemyTankAngle:Number;
        try {
            enemyTankAngle = angleRegularization( battleScene.enemyTankAngle );
        } catch( e:Error ) {
            //trace( "error : get enemyTankPosition" );
        } finally {
            return enemyTankAngle;
        }
    }

    public function get enemyTankAngularVelocity():Number {
        
        var enemyTankAngularVelocity:Number;
        try {
            enemyTankAngularVelocity = battleScene.enemyTankAngularVelocity;
        } catch( e:Error ) {
            //trace( "error : get enemyTankPosition" );
        } finally {
            return enemyTankAngularVelocity;
        }
    }

    public function get enemyTankVelocity():WVector2D {
        
        var enemyTankVelocity:WVector2D = new WVector2D( 0, 0 );
        try {
            enemyTankVelocity = battleScene.enemyTankLinearVelocity;
        } catch( e:Error ) {
            //trace( "error : get enemyTankPosition" );
        } finally {
            return enemyTankVelocity;
        }
    }

    public function get enemyTankPosition():WVector2D {
        
        var enemyTankPosition:WVector2D = new WVector2D( 0, 0 );
        try {
            enemyTankPosition = battleScene.enemyTankPosition;
        } catch( e:Error ) {
            //trace( "error : get enemyTankPosition" );
        } finally {
            return enemyTankPosition;
        }
    }
    
    
    
    /* --------------------------------------------------
     * myTank property
     */
    public function get myBulletCount():int {
        return battleScene.myBulletCount;
    }
    
    private var _myBullets:Vector.<BoundBox>;
    public function get myBullets():Vector.<BoundBox> {
        return _myBullets;
    }
    
    public function get myGunBearingFromMyTankHeading():Number {
        return angleRegularization( battleScene.myGunAngle );
    }

    public function get myGunAngularVelocity():Number {
        return battleScene.myGunAngularVelocity;
    }

    public function get myTankHeading() :Number{
        return angleRegularization( battleScene.myTankAngle );
    }

    public function get myTankAngularVelocity():Number {
        return battleScene.myTankAngularVelocity;
    }

    public function get myTankVelocity():WVector2D   {
        var myTankVelocity:WVector2D = new WVector2D( 0, 0 );
        try {
            myTankVelocity = battleScene.myTankLinearVelocity;
        } catch( e:Error ) {
            //trace( "error : get enemyTankPosition" );
        } finally {
            return myTankVelocity;
        }
    }

    public function get myTankPosition():WVector2D  {
        var myTankPosition:WVector2D = new WVector2D( 0, 0 );
        try {
            myTankPosition = battleScene.myTankPosition;
        } catch( e:Error ) {
            //trace( "error : get enemyTankPosition" );
        } finally {
            return myTankPosition;
        }
    }
    
    
    
    

    /* ==================================================
     * method
     * ==================================================
     */
    
    
    
    public function TKBattleScene( battleScene:BattleScene, tank:TankBase, stageWidth:Number, stageHeight:Number, robotRadius:Number ):void {
        
        this.battleScene = battleScene;
        this.tank = tank;
        
        _stageWidth = stageWidth;
        _stageHeight = stageHeight;
        _stageCenter = new WVector2D( stageWidth / 2, stageHeight / 2 );
        _stageDiagonal = Math.sqrt( stageWidth * stageWidth + stageHeight * stageHeight );
        
        _robotRadius = robotRadius;
        
        
        
        _myTank = new MyTank( this );
        _enemyTank = new EnemyTank( this );
    }
    
    
    
    
    private var previousBulletPosition:WVector2D;
    
    public function run():void {
        
        var i:int;
        
        // enemyBullets
        _enemyBullets = new Vector.<BoundBox>( enemyBulletCount );
        var enemyBullet:BoundBox = battleScene.enemyBulletList;
        for( i = 0; i < enemyBulletCount; i++ ) {
            
            _enemyBullets[i] = enemyBullet;
            
            if( enemyBullet.next != null )
                enemyBullet = enemyBullet.next;
        }
        
        // myBullets
        _myBullets = new Vector.<BoundBox>( myBulletCount );
        var myBullet:BoundBox = battleScene.myBulletList;
        for( i = 0; i < myBulletCount; i++ ) {

            _myBullets[i] = myBullet;
            
            if( myBullet.next != null )
                myBullet = myBullet.next;
        }
        
        
        
        // tank update
        enemyTank.update();    //enemyTank の update が先 
        myTank.update();    //myTank は enemyTank の情報も内部で処理する
        
        // 後で修正する
        // bulletSpeed
        if ( !_bulletSpeed && battleScene.myBulletList ) {
            
            //_bulletSpeed = battleScene.myBulletList.linearVelocity.length;
            
            if ( previousBulletPosition == null ) {
                
                previousBulletPosition = battleScene.myBulletList.position;
                return;
            }
            
            var bulletVelocity:WVector2D = battleScene.myBulletList.position;
            bulletVelocity.subtract(previousBulletPosition);
            
            _bulletSpeed = bulletVelocity.length;
            
            //trace( _bulletSpeed, battleScene.myBulletList.linearVelocity.length );
        }
    }
    
    
    
    public function nearWall( tankAndWallDistance:Number, position:WVector2D ):Boolean {
        
        if( 
                position != null && 
                (
                    position.x <= tankAndWallDistance || stageWidth - tankAndWallDistance <= position.x || 
                    position.y <= tankAndWallDistance || stageHeight - tankAndWallDistance <= position.y
                )
            ) {

            return true;
        }
        
        return false;
    }
    
    
    
    private function angleRegularization( value:Number ):Number {
        value = value % ( 2 * Math.PI );
        if( value < 0 )
            value = ( 2 * Math.PI ) + value;
        return value;
    }
}

class Angle {
    
    public static const RADIAN_0_DEGREE:Number   = 0;
    public static const RADIAN_90_DEGREE:Number  = Math.PI / 2;
    public static const RADIAN_180_DEGREE:Number = Math.PI;
    public static const RADIAN_270_DEGREE:Number = Math.PI / 2 * 3;
    public static const RADIAN_360_DEGREE:Number = 2 * Math.PI;
    
    public static function normalizeRadian0to360( value:Number ):Number {
        
        value %= Angle.RADIAN_360_DEGREE;

        if( value < RADIAN_0_DEGREE )
            value += Angle.RADIAN_360_DEGREE;
        
        return value;
    }
    
    public static function normalizeRadianM180toP180( value:Number ):Number {
        
        value = normalizeRadian0to360( value );
        
        if( value > RADIAN_180_DEGREE )
            value -= Angle.RADIAN_360_DEGREE;
        
        return value;
    }
}







class Intercept {
    
    /* ==================================================
     * field
     * ==================================================
     */
    protected var tkBattleScene:TKBattleScene;
    protected var iteration:Number;
    protected var accuracy:Number;
    
    protected var target:ModelBase;
    protected var myTank:MyTank;
    
    
    
    
    
    /* ==================================================
     * property
     * ==================================================
     */
    private var _turnGunRight:Number;
    public function get turnGunRight():Number {
        
        return _turnGunRight;
    }
    
    private var _impactTime:Number;
    public function get impactTime():Number {
        
        return _impactTime;
    }
    
    private var _impactPoint:WVector2D;
    public function get impactPoint():WVector2D {
        
        return _impactPoint;
    }
    
    private var _threshold:Number;
    public function get threshold():Number {
        
        return _threshold;
    }
    
    private var _targetBumpAgainstAWall:Boolean;
    public function get targetBumpAgainstAWall():Boolean {
        
        return _targetBumpAgainstAWall;
    }
    
    
    
    
    
    /* ==================================================
     * method
     * ==================================================
     */
    public function Intercept( tkBattleScene:TKBattleScene, iteration:Number, accuracy:Number ) {
        
        this.tkBattleScene = tkBattleScene;
        this.iteration     = iteration;
        this.accuracy      = accuracy;
        
        this.myTank = tkBattleScene.myTank;
        this.target = tkBattleScene.enemyTank;
    }
    
    public function calculate():void {
        
        // impactTime
        _impactTime = getImpactTime();
        
        
        
        // impactPoint
        _impactPoint = getTargetEstimatedPosition(impactTime);
        
        
        
        // targetPositionFromMyTank
        var myTankPosition:WVector2D = myTank.position;
        
        var targetPositionFromMyTank:WVector2D = impactPoint.copy();
        targetPositionFromMyTank.subtract(myTankPosition);
        
        
        
        // goalBulletHeading
        var goalBulletHeading:Number = Math.atan2(targetPositionFromMyTank.y,targetPositionFromMyTank.x);
        goalBulletHeading = Angle.normalizeRadian0to360( goalBulletHeading );
        
        
        
        // turnBulletHeading
        var myGunHeading:Number = myTank.gunHeading;
        
        _turnGunRight = goalBulletHeading - myGunHeading;
        _turnGunRight = Angle.normalizeRadianM180toP180( _turnGunRight );
        
        
        
        // threshold
        _threshold = Math.atan( target.radius / targetPositionFromMyTank.length );
        
        
        
        // targetBumpAgainstAWall
        if (
                impactPoint.x < 0 || tkBattleScene.stageWidth < impactPoint.x || 
                impactPoint.y < 0 || tkBattleScene.stageHeight < impactPoint.y
            )
            _targetBumpAgainstAWall = true;
        
        else 
            _targetBumpAgainstAWall = false;
    }
    
    
    
    private function getImpactTime():Number {
        
        var estimatedMaxImpactTime:Number = estimatedMaxImpactTime();
        
        var time:Vector.<Number> = Vector.<Number>([ 0, estimatedMaxImpactTime ]);
        var distance:Vector.<Number> = Vector.<Number>([ estimatedDistanceFromBulletToTarget( time[0] ), estimatedDistanceFromBulletToTarget( time[1] ) ]);
        
        var iterationI:int = ( estimatedMaxImpactTime >= 5 ) ? estimatedMaxImpactTime : 5;
        
        var timeI:Vector.<Number> = new Vector.<Number>(iterationI);
        var distanceI:Vector.<Number> = new Vector.<Number>(iterationI);
        
        timeI[0]                  = time[0];
        timeI[iterationI - 1]     = time[1];
        distanceI[0]              = distance[0];
        distanceI[iterationI - 1] = distance[1];
        
        var j:int;
        
        
        
        // timeI and distanceI
        for ( j = 1; j < iterationI - 1; j++ ) {
            
            timeI[j] = time[0] + ( ( ( time[1] - time[0] ) / iterationI ) * j );
            distanceI[j] = estimatedDistanceFromBulletToTarget( timeI[j] );
        }
        
        
        
        // minDistanceIIndex
        var minDistanceIIndex:int = 0;
        for ( j = 1; j < iterationI; j++ ) {
            
            if( distanceI[minDistanceIIndex] > distanceI[j] )
                minDistanceIIndex = j;
        }
        
        if ( estimatedDistanceFromBulletToTarget(timeI[minDistanceIIndex]) < myTank.radius )
            return timeI[minDistanceIIndex];
        /*trace(estimatedDistanceFromBulletToTarget(timeI[minDistanceIIndex]));
        if ( estimatedDistanceFromBulletToTarget(timeI[minDistanceIIndex]) < myTank.radius )
            return timeI[minDistanceIIndex];
        //*/
        // impactTime
        var impactTime:Number;
        
        if( minDistanceIIndex == 0 )
            impactTime = getImpactTimeSecant( timeI[minDistanceIIndex], timeI[minDistanceIIndex + 1] );
            
        else if( minDistanceIIndex == iterationI - 1 )
            impactTime = getImpactTimeSecant( timeI[minDistanceIIndex - 1], timeI[minDistanceIIndex] );
            
        else {
            
            if( distanceI[minDistanceIIndex - 1] - distanceI[minDistanceIIndex] < distanceI[minDistanceIIndex + 1] - distanceI[minDistanceIIndex] )
                impactTime = getImpactTimeSecant( timeI[minDistanceIIndex - 1], timeI[minDistanceIIndex] );
            
            else
                impactTime = getImpactTimeSecant( timeI[minDistanceIIndex], timeI[minDistanceIIndex + 1] );
        }
        /*
        if( impactTime != 0 )
            return impactTime;
        //*///*
        if( estimatedDistanceFromBulletToTarget( impactTime ) < accuracy )
            return impactTime;
        //*/
        
        
        
        // minDistanceIIndex2
        var minDistanceIIndex2:int = ( minDistanceIIndex > 1 ) ? 0 : minDistanceIIndex + 2;
        
        for ( j = 0; j < iterationI; j++ ) {
            
            if( minDistanceIIndex - 1 <= j && j <= minDistanceIIndex + 1 )
                continue;
            
            if( distanceI[minDistanceIIndex2] > distanceI[j] )
                minDistanceIIndex2 = j;
        }
        
        
        
        // impactTime2
        var impactTime2:Number;
        
        if( minDistanceIIndex2 == 0 )
            impactTime2 = getImpactTimeSecant( timeI[minDistanceIIndex2], timeI[minDistanceIIndex2 + 1] );
            
        else if( minDistanceIIndex2 == iterationI - 1 )
            impactTime2 = getImpactTimeSecant( timeI[minDistanceIIndex2 - 1], timeI[minDistanceIIndex2] );
            
        else {
            
            if( distanceI[minDistanceIIndex2 - 1] - distanceI[minDistanceIIndex2] < distanceI[minDistanceIIndex2 + 1] - distanceI[minDistanceIIndex2] )
                impactTime2 = getImpactTimeSecant( timeI[minDistanceIIndex2 - 1], timeI[minDistanceIIndex2] );
            
            else
                impactTime2 = getImpactTimeSecant( timeI[minDistanceIIndex2], timeI[minDistanceIIndex2 + 1] );
        }
        /*
        if( impactTime2 != 0 )
            return impactTime2;
        //*///*
        if( estimatedDistanceFromBulletToTarget( impactTime2 ) < accuracy )
            return impactTime2;
        //*/
        
        if ( estimatedDistanceFromBulletToTarget(timeI[minDistanceIIndex]) < myTank.radius )
            return timeI[minDistanceIIndex];
        
        //trace(0, impactTime, estimatedDistanceFromBulletToTarget( impactTime ));
        //trace(1, impactTime2, estimatedDistanceFromBulletToTarget( impactTime2 ));
        
        return 0;
    }
    
    
    
    /**
     * 割線法で近似解を求める。
     * 二分法とはさみうち法は、解く方程式の性質により区間[a,b]内に解を保持することができないので使用不可。
     * ニュートン法は、解く方程式を微分した式の解を求める関数を用意するのが面倒。
     * 
     * @return
     */
    private function getImpactTimeSecant( time0:Number, time1:Number ):Number {
        
        var distance0:Number = estimatedDistanceFromBulletToTarget( time0 );
        
        for( var i:Number = 0; i < iteration; i++ ) {
            
            var distance1:Number = estimatedDistanceFromBulletToTarget( time1 );
            
            var time2:Number = time1 - distance1 * ( time1 - time0 ) / ( distance1 - distance0 );
            
            time0 = time1;
            distance0 = distance1;
            
            time1 = time2;
            
            if( distance0 < accuracy )
                return time0;
        }
        
        //return 0;
        return time0;
    }
    
    
    
    private function estimatedMaxImpactTime():Number {
        
        // targetPositionFromMyTank
        var targetPositionFromMyTank:WVector2D = target.position;
        targetPositionFromMyTank.subtract( myTank.position );
        
        // targetVelocity
        var targetVelocity:WVector2D = target.velocity;
        
        var targetSpeed:Number = targetVelocity.length;
        var bulletSpeed:Number = tkBattleScene.bulletSpeed;
        
        var time:Number = 0;
        
        if( bulletSpeed > targetSpeed ) {
        
            // T = distanceFromTargetPositionToMyTank / bulletSpeed
            time = targetPositionFromMyTank.length / bulletSpeed;
            
            // T * enemyTankSpeed + Tx * enemyTankSpeed = Tx * bulletSpeed
            time += Math.abs( ( time * targetSpeed ) / ( bulletSpeed - targetSpeed ) );
        }
        
        // 改良の余地あり(弾が壁に当たるまでの時間にするなど)
        if ( bulletSpeed <= targetSpeed || time > tkBattleScene.stageDiagonal / bulletSpeed )
            time = tkBattleScene.stageDiagonal / bulletSpeed;
        
        return time;
    }
    
    private function estimatedDistanceFromBulletToTarget(time:Number):Number {
        
        var bulletSpeed:Number = tkBattleScene.bulletSpeed;
        var myTankPosition:WVector2D = myTank.position;
        
        var estimatedDistanceFromBulletToTarget:WVector2D = getTargetEstimatedPosition(time);
        estimatedDistanceFromBulletToTarget.subtract( myTankPosition );

        return Math.abs( ( estimatedDistanceFromBulletToTarget.length - myTank.radius ) - bulletSpeed * time );
    }
    
    
    
    
    /* --------------------------------------------------
     * abstract
     */
    protected function getTargetEstimatedPosition(time:Number):WVector2D {
        
        return new WVector2D( 0, 0 );
    }
}





class LinearIntercept extends Intercept {
    
    public function LinearIntercept( tkBattleScene:TKBattleScene, iteration:Number, accuracy:Number ) {
        
        super( tkBattleScene, iteration, accuracy );
    }
    
    override protected function getTargetEstimatedPosition(time:Number):WVector2D {
        
        var targetPosition:WVector2D = target.position;
        var targetVelocity:WVector2D = target.velocity;
        
        var x:Number = targetPosition.x + targetVelocity.x * time;
        var y:Number = targetPosition.y + targetVelocity.y * time;
        
        return new WVector2D( x, y );
    }
}
class CircularIntercept extends Intercept {
    
    public function CircularIntercept( tkBattleScene:TKBattleScene, iteration:Number, accuracy:Number ) {
        
        super( tkBattleScene, iteration, accuracy );
    }
    
    // 式が微妙な感じなので暇があれば修正する
    override protected function getTargetEstimatedPosition(time:Number):WVector2D {
        
        var pointA:Point = new Point( target.getPosition(0).x, target.getPosition(0).y );
        var pointB:Point = new Point( target.getPosition(1).x, target.getPosition(1).y );
        var pointC:Point = new Point( target.getPosition(2).x, target.getPosition(2).y );
        
        var centerPoint:Point = getCenterPoint( pointA, pointB, pointC );
        var radius:Number = getRadius( centerPoint, pointA );
        
        var pointAFromPointB:Point = pointA.subtract(pointB);
        
        var angularVelocity:Number = Math.asin( pointAFromPointB.length / ( 2 * radius ) ) * 2 ;
        if( target.angularVelocity < 0 ) {
            angularVelocity *= -1;
            radius *= -1;
        }
        //double angularVelocity = target.velocity().length() / radius;
        
        var targetBearingFromCircleCenter:Number = Angle.normalizeRadian0to360( Math.atan2( pointAFromPointB.y, pointAFromPointB.x ) + Angle.RADIAN_270_DEGREE );
        var nextTargetHeading:Number = ( targetBearingFromCircleCenter + angularVelocity * time ) % Angle.RADIAN_360_DEGREE;
        
        var x:Number = pointA.x + radius * ( Math.cos(nextTargetHeading) - Math.cos(targetBearingFromCircleCenter) );
        var y:Number = pointA.y + radius * ( Math.sin(nextTargetHeading) - Math.sin(targetBearingFromCircleCenter) );
        
        return new WVector2D( x, y );
    }
}


class ModelBase {

    /* ==================================================
     * field
     * ==================================================
     */
    protected var tkBattleScene:TKBattleScene;
    
    
    
    
    
    /* ==================================================
     * property
     * ==================================================
     */
    
    /* --------------------------------------------------
     * 
     */
    protected var _radius:Number;
    public function get radius():Number {
        return _radius;
    }
    
    /* --------------------------------------------------
     * basic
     */
    protected var _positions:Vector.<WVector2D> = new Vector.<WVector2D>(10);
    public function getPosition( index:int ):WVector2D {
        return _positions[index].copy();
    }
    
    protected var _position:WVector2D = new WVector2D( 0, 0 );
    public function get position():WVector2D {
        return _position.copy();
    }
    
    protected var _previousHeading:Number;
    public function get previousHeading():Number {
        return _previousHeading;
    }
    
    protected var _heading:Number;
    public function get heading():Number {
        return _heading;
    }
    
    protected var _velocity:WVector2D = new WVector2D( 0, 0 );
    public function get velocity():WVector2D {
        return _positionFromPreviousPosition.copy();//_velocity.copy();
    }
    
    protected var _angularVelocity:Number;
    public function get angularVelocity():Number {
        return _heading - _previousHeading;//_angularVelocity;
    }
    
    
    
    /* --------------------------------------------------
     * 
     */
    protected var _positionFromPreviousPosition:WVector2D = new WVector2D( 0, 0 );
    public function get positionFromPreviousPosition():WVector2D {
        return _positionFromPreviousPosition.copy();
    }
    
    protected var _movedDirection:Number;
    public function get movedDirection():Number {
        return _movedDirection;
    }
    
    
    
    
    
    /* ==================================================
     * method
     * ==================================================
     */
    public function ModelBase( tkBattleScene:TKBattleScene ):void {
        
        this.tkBattleScene = tkBattleScene;
        
        for( var i:uint = 0; i < _positions.length; i++ )
            _positions[i] = new WVector2D( 0, 0 );
    }
    
    public function update():void {
        
        // positions
        for( var i:uint = 0; i < _positions.length - 1; i++ )
            _positions[_positions.length - ( 1 + i ) ] = _positions[_positions.length - ( 2 + i )];
        _positions[0] = position;
        
        
        
        // positionFromPreviousPosition
        _positionFromPreviousPosition = position;
        _positionFromPreviousPosition.subtract( getPosition(1) );
        
        
        
        // movedDirection
        _movedDirection = Math.atan2( velocity.y, velocity.x );
        _movedDirection = Angle.normalizeRadian0to360( _movedDirection );
        /*
        _movedDirection = Math.atan2( movedDistance.y, movedDistance.x );
        _movedDirection = Angle.normalizeRadian0to360( _movedDirection );
        //*/
    }
}


class TankModel extends ModelBase {
    
    /* ==================================================
     * property
     * ==================================================
     */
    
    /* --------------------------------------------------
     * sub class calculate
     */
    protected var _bullets:Vector.<BoundBox>;
    public function get bullets():Vector.<BoundBox> {
        return _bullets;
    }
    
    protected var _gunBearingFromHeading:Number;
    public function get gunBearingFromHeading():Number {
        return _gunBearingFromHeading;
    }
    
    
    
    /* --------------------------------------------------
     * this class calculate
     */
    protected var _gunHeading:Number;
    public function get gunHeading():Number {
        return _gunHeading;
    }
    
    protected var _bearingFromStageCenter:Number;
    public function get bearingFromStageCenter():Number {
        return _bearingFromStageCenter;
    }

    protected var _movedDirectionFromHeading:Number;
    public function get movedDirectionFromHeading():Number {
        return _movedDirectionFromHeading;
    }
    
    protected var _velocityFromHeading:Number;
    public function get velocityFromHeading():Number {
        return _velocityFromHeading;
    }
    
    
    
    
    
    /* ==================================================
     * method
     * ==================================================
     */
    public function TankModel( tkBattleScene:TKBattleScene ):void {
        
        super( tkBattleScene );
        _radius = tkBattleScene.robotRadius;
    }

    public override function update():void {
        
        super.update();
        
        
        
        // gunHeading
        _gunHeading = heading + gunBearingFromHeading;
        _gunHeading = Angle.normalizeRadian0to360( _gunHeading );
        
        
        
        // bearingFromStageCenter
        var stageCenter:WVector2D = tkBattleScene.stageCenter;
        
        var positionFromStageCenter:WVector2D = position;
        positionFromStageCenter.subtract(stageCenter);
        
        _bearingFromStageCenter = Math.atan2( positionFromStageCenter.y, positionFromStageCenter.x );
        _bearingFromStageCenter = Angle.normalizeRadian0to360( _bearingFromStageCenter );
        
        
        
        // movedDirectionFromHeading
        _movedDirectionFromHeading = movedDirection - heading;
        _movedDirectionFromHeading = Angle.normalizeRadian0to360( _movedDirectionFromHeading );
        
        
        
        // velocityFromHeading
        _velocityFromHeading = ( movedDirectionFromHeading + Angle.RADIAN_90_DEGREE ) % Angle.RADIAN_360_DEGREE;
        
        if( 0 <= _velocityFromHeading && _velocityFromHeading < Math.PI )
            _velocityFromHeading = velocity.length;
        
        else 
            _velocityFromHeading = -velocity.length;
        
    }
}

class EnemyTank extends TankModel {

    /* ==================================================
     * method
     * ==================================================
     */
    public function EnemyTank( tkBattleScene:TKBattleScene ):void {
        
        super( tkBattleScene );
    }

    public override function update():void {
        
        _bullets  = tkBattleScene.enemyBullets;
        _previousHeading  = heading;
        _position = tkBattleScene.enemyTankPosition;
        _heading  = tkBattleScene.enemyTankHeading;
        _velocity = tkBattleScene.enemyTankVelocity;
        _angularVelocity = tkBattleScene.enemyTankAngularVelocity;
        
        super.update();
    }
}


class MyTank extends TankModel {


    /* ==================================================
     * field
     * ==================================================
     */
    protected var enemyTank:EnemyTank;
    
    
    
    
    
    /* ==================================================
     * property
     * ==================================================
     */
    
    /* --------------------------------------------------
     * enemyTank
     */
    protected var _enemyTankPositionFromPosition:WVector2D = new WVector2D( 0, 0 );
    public function get enemyTankPositionFromPosition():WVector2D {
        return _enemyTankPositionFromPosition.copy();
    }
    
    protected var _enemyTankBearingFromPosition:Number;
    public function get enemyTankBearingFromPosition():Number {
        return _enemyTankBearingFromPosition;
    }
    
    protected var _enemyTankBearingFromHeading:Number;
    public function get enemyTankBearingFromHeading():Number {
        return _enemyTankBearingFromHeading;
    }
    
    protected var _enemyTankBearingFromGunHeading:Number;
    public function get enemyTankBearingFromGunHeading():Number {
        return _enemyTankBearingFromGunHeading;
    }
    
    
    
    

    /* ==================================================
     * method
     * ==================================================
     */
    public function MyTank( tkBattleScene:TKBattleScene ):void {
        
        super( tkBattleScene );
    }

    override public function update():void {

        if( enemyTank == null )
            enemyTank = tkBattleScene.enemyTank;
        
        _previousHeading  = heading;
        _bullets  = tkBattleScene.myBullets;
        _position = tkBattleScene.myTankPosition;
        _heading  = tkBattleScene.myTankHeading;
        _velocity = tkBattleScene.myTankVelocity;
        _angularVelocity = tkBattleScene.myTankAngularVelocity;
        _gunBearingFromHeading  = tkBattleScene.myGunBearingFromMyTankHeading;
        
        super.update();
        
        
        
        // positionFromEnemyTankPosition 
        _enemyTankPositionFromPosition = enemyTank.position;
        _enemyTankPositionFromPosition.subtract( position );
        
        
        
        // enemyTankBearing
        _enemyTankBearingFromPosition = Math.atan2( enemyTankPositionFromPosition.y, enemyTankPositionFromPosition.x );
        
        if( _enemyTankBearingFromPosition < 0 )
            _enemyTankBearingFromPosition = ( _enemyTankBearingFromPosition + Angle.RADIAN_360_DEGREE ) % Angle.RADIAN_360_DEGREE;
        
        
        
        // enemyTankBearingFromGunHeading
        _enemyTankBearingFromGunHeading = enemyTankBearingFromPosition - gunHeading;
        _enemyTankBearingFromGunHeading = Angle.normalizeRadian0to360( _enemyTankBearingFromGunHeading );
        
        
        
        // enemyTankBearingFromHeading
        _enemyTankBearingFromHeading = enemyTankBearingFromPosition - heading;
        _enemyTankBearingFromHeading = Angle.normalizeRadian0to360( _enemyTankBearingFromHeading );
    }
}

class GravityPoint extends WVector2D {
    
    private var _power:Number;
    public function get power():Number {
        return _power;
    }
    
    public function force( point:WVector2D ):WVector2D {
        
        var positionFromPoint:WVector2D = copy();
        positionFromPoint.subtract( point );
        
        var forceLength:Number = power / Math.pow( positionFromPoint.length, 2 );
        
        
        var angle:Number = Angle.normalizeRadianM180toP180(Math.PI/2 - Math.atan2(point.y - y, point.x - x)); 
        
        var forceX:Number = Math.sin(angle) * forceLength;
        var forceY:Number = Math.cos(angle) * forceLength;
        
        return new WVector2D( forceX, forceY );
    }
    
    public function GravityPoint( x:Number, y:Number, power:Number):void {
        
        super(x,y);
        
        _power = power;
    }
}







function getRadius( centerPoint:Point, pointA:Point ):Number {
    
    var pointAFromCenterPoint:Point = pointA.subtract(centerPoint);
    
    var radius:Number = pointAFromCenterPoint.length;
    
    return radius;
}

function getCenterPoint( pointA:Point, pointB:Point, pointC:Point ):Point {
    
    var lineSegmentABPerpendicularBisectorEquationAB:Point;
    var lineSegmentBCPerpendicularBisectorEquationAB:Point;
    
    var lineSegmentABPerpendicularBisectorSlope:Number = -1 / slope( pointA, pointB );
    var lineSegmentBCPerpendicularBisectorSlope:Number = -1 / slope( pointB, pointC );
    
    var lineABMiddlePoint:Point = middlePoint( pointA, pointB );
    var lineBCMiddlePoint:Point = middlePoint( pointB, pointC );
    
    var lineSegmentABPerpendicularBisectorB:Number = linearEquationsSolutionB( lineABMiddlePoint, lineSegmentABPerpendicularBisectorSlope );
    var lineSegmentBCPerpendicularBisectorB:Number = linearEquationsSolutionB( lineBCMiddlePoint, lineSegmentBCPerpendicularBisectorSlope );
    
    lineSegmentABPerpendicularBisectorEquationAB = new Point(lineSegmentABPerpendicularBisectorSlope, lineSegmentABPerpendicularBisectorB);
    lineSegmentBCPerpendicularBisectorEquationAB = new Point(lineSegmentBCPerpendicularBisectorSlope, lineSegmentBCPerpendicularBisectorB);
    
    var centerPoint:Point = simultaneousEquationsSolutionXY( lineSegmentABPerpendicularBisectorEquationAB, lineSegmentBCPerpendicularBisectorEquationAB );
    
    return centerPoint;
}

function slope(point1:Point, point2:Point):Number {
    
    var x1:Number = point1.x;
    var y1:Number = point1.y;
    var x2:Number = point2.x;
    var y2:Number = point2.y;
    
    var a:Number = ( y2 - y1 ) / ( x2 - x1 );
    
    return a;
}

function middlePoint(point1:Point, point2:Point):Point {

    var x1:Number = point1.x;
    var y1:Number = point1.y;
    var x2:Number = point2.x;
    var y2:Number = point2.y;
    
    var x:Number = ( x1 + x2 ) / 2;
    var y:Number = ( y1 + y2 ) / 2;
    
    return new Point(x, y);
}

function linearEquationsSolutionB(point:Point, a:Number):Number {
    
    var x:Number = point.x;
    var y:Number = point.y;
    
    var b:Number = y - a * x;
    
    return b;
}

function simultaneousEquationsSolutionXY(point1:Point, point2:Point):Point {

    var a1:Number = point1.x;
    var b1:Number = point1.y;
    var a2:Number = point2.x;
    var b2:Number = point2.y;
    
    var x:Number = ( b1 - b2 ) / ( a2 - a1 );
    var y:Number = a1 * x + b1;
    
    return new Point(x,y);
}