forked from: flash on 2014-5-16

by raa
♥0 | Line 1995 | Modified 2016-01-10 08:04:43 | MIT License | (replaced)
play

ActionScript3 source code

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

// forked from kakeanco's flash on 2014-5-16
/* ------------------------------------------------------

 * Ver.0.7214545

 * ------------------------------------------------------

 * [HotKey]

 * 1-7   : select a tower to build

 * 0,ESC : cancel a selection

 * U     : upgrade the selected tower

 * S     : sell the selected tower

 * M     : show menu window

 * N     : send the next wave

 * ------------------------------------------------------

 * [inspired by]

 * Desktop Tower Defense

 * http://www.handdrawngames.com/DesktopTD/game.asp

 * ------------------------------------------------------

 * [適当な取説]

 * ・ルールとか遊び方とかよくワカランという方は以下を参照。

 * Tower Defense Wiki - TD系とは

 * http://www32.atwiki.jp/tower_d/pages/32.html

 * 

 * [タワーの特徴]

 * Arrow   : 弱いが安価。しかしレベルを上げると…

 * Gatling : 攻撃速度が速く、安定した強さを誇る。

 * Bomb    : 範囲攻撃できる。空中に攻撃できない。

 * Missile : 強力な対空兵器。地上に攻撃できない。

 * Frost   : 威力は無いが、敵を一定時間スローにする。

 * Vortex  : 地上のみで範囲も狭いが、高威力全体攻撃。

 * Laser   : 高価だが、弾が貫通する!

 * 

 * [敵の特徴]

 * Normal  : 数だけの雑魚。

 * Immune  : スロー効果を受けない。

 * Fast    : 移動が速いぞ。

 * Flying  : 一直線に向かってくる、要注意!

 * ------------------------------------------------------

 * [更新]

 * ・敵を倒した時、皆さんの大好きなお金がピョッと飛び出るエフェクトを追加 (Ver.0.7214545)

 * ・Wave数を50に、バランスを調整、弾の画像追加 (Ver.0.721454)

 * ・範囲攻撃が範囲内の敵全てに当たっていなかったバグを修正 (Ver.0.72145)

 * ・簡単な操作説明を追加 (Ver.0.7214)

 * ・とりあえずランキング設置 (Ver.0.721)

 * ・最低限遊べるバランスに修正 (Ver.0.72)

 * ・公開 (Ver.0.7)

 */



/* ---------------------------------------------------------------------------------------------------------

 * Main

 * ---------------------------------------------------------------------------------------------------------

 */

package {

    import com.bit101.components.PushButton;

    import flash.display.Sprite;

    import flash.events.Event;

    import flash.events.MouseEvent;

    import flash.ui.Keyboard;

    import flash.utils.Dictionary;

    

    public class Main extends Sprite {

        // 各状態を表す定数

        public static const STATE_READY:int = 0;

        public static const STATE_PLAYING:int = 1;

        public static const STATE_MENU:int = 2;

        public static const STATE_GAMEOVER:int = 3;

        

        private var _states:Vector.<IState>;    // 状態のコレクション

        private var _currentState:IState;        // 現在の状態の参照

        private var _previousState:IState;        // 一つ前の状態の参照

        

        private var _world:World;                // フィールド・タワー・敵等を表示する画面

        private var _frontend:Frontend;            // ステータス・メニュー等を表示するウインドウ

        private var _waveManager:WaveManager;    // Waveを管理・表示するウインドウ

        private var _nextWaveButtonHandler:Function;

        

        public function get world():World { return _world; }

        public function get waveManager():WaveManager { return _waveManager; }

        

        public function Main() {

            graphics.beginFill(0x000000);

            graphics.drawRect(0, 0, 465, 465);

            graphics.endFill();

            

            // 外部アセットを読み込んだ後に初期化を開始する

            var imagePaths:Dictionary = ImageFactory.getExternalImagePaths();

            var imageHolder:Dictionary = ImageFactory.load();

            var preloader:Preloader = new Preloader(this, imagePaths, imageHolder);

            preloader.addEventListener(Event.COMPLETE, initialize);

        }

        

        // 全体の初期化

        private function initialize(e:Event):void {

            e.target.removeEventListener(Event.COMPLETE, initialize);

            HotKey.setStage(this.stage);

            EnemyType.initialize();

            

            // 各状態の初期化

            _states = new Vector.<IState>();

            _states[Main.STATE_READY] = new ReadyState(this);

            _states[Main.STATE_PLAYING] = new PlayingState(this);

            _states[Main.STATE_MENU] = new MenuState(this);

            _states[Main.STATE_GAMEOVER] = new GameoverState(this);

            

            // 画面の初期化

            addChild(_world = new World( -9, 12));

            addChild(_frontend = new Frontend(0, 0));

            _frontend.addChild(_waveManager = new WaveManager(0, 415, _world));

            

            // 各ボタンの初期化と、対応するホットキーを設定する

            var window:Sprite = _frontend.addChild(ImageFactory.createWindow(365, 415, 100, 50)) as Sprite;

            var menuButton:PushButton = new PushButton(window, 5, 4, "Menu (M)", onClickMenuButton);

            var nextWaveButton:PushButton = new PushButton(window, 5, 26, "Next Wave (N)", onClickNextWaveButton);

            menuButton.width = nextWaveButton.width = 90;

            

            HotKey.bind(Keyboard.M, onClickMenuButton);

            HotKey.bind(Keyboard.N, onClickNextWaveButton);

            

            // 初期状態の設定

            _currentState = _states[Main.STATE_GAMEOVER];

            _currentState.enter();

            

            addEventListener(Event.ENTER_FRAME, mainLoop);

        }

        private function onClickMenuButton(e:MouseEvent = null):void { changeState(Main.STATE_MENU); }

        private function onClickNextWaveButton(e:MouseEvent = null):void { _nextWaveButtonHandler(); }

        private function mainLoop(e:Event):void { _currentState.update(); }

        

        // NextWaveボタンを押した時の処理を設定する

        public function setNextWaveButtonHandler(handler:Function = null):void {

            _nextWaveButtonHandler = (handler != null) ? handler : doNothing;

        }

        private function doNothing():void { }

        

        // ゲームの状態を変更する

        public function changeState(stateType:int):void {

            _previousState = _currentState;

            

            _currentState.exit();

            _currentState = _states[stateType];

            _currentState.enter();

        }

        

        // ゲームの状態を一つ前の状態に戻す

        public function revertToPreviousState():void {

            _currentState.exit();

            _currentState = _previousState;

            _currentState.enter();

        }

    }

}

/* ----------------------------------------------------------------------------------------------------------------------

 * IState

 * -----------------------------------------------------------------------------------------------------------------------

 */

//package {

    //public 

    interface IState {

        function enter():void;

        function update():void;

        function exit():void;

    }

//}

/* ----------------------------------------------------------------------------------------------------------------------

 * GameoverState

 * -----------------------------------------------------------------------------------------------------------------------

 */

//package {

    import com.bit101.components.PushButton;

    import flash.display.Sprite;

    import flash.events.MouseEvent;

    import net.wonderfl.score.basic.BasicScoreForm;

    import net.wonderfl.score.basic.BasicScoreRecordViewer;

    

    //public 

    class GameoverState implements IState {

        private var _main:Main;

        private var _gameoverScreen:Sprite;

        private var _instructionWindow:Sprite;

        private var _isBeginning:Boolean;

        

        private var _scoreForm:BasicScoreForm;

        private var _ranking:BasicScoreRecordViewer;

        

        public function GameoverState(main:Main) {

            _main = main;

            _gameoverScreen = createGameoverScreen();

            _instructionWindow = createInstructionWindow();

            _isBeginning = true;

        }

        

        private function createGameoverScreen():Sprite {

            var screen:Sprite = new Sprite();

            screen.graphics.beginFill(0x000000, 0.5);

            screen.graphics.drawRect(0, 0, 465, 465);

            screen.graphics.endFill();

            new PushButton(screen, 182, 182, "Start Game", onClickStartButton);

            return screen;

        }

        private function onClickStartButton(e:MouseEvent):void { _main.changeState(Main.STATE_READY); }

        

        private function createInstructionWindow():Sprite {

            var container:Sprite = new Sprite();

            

            var window1:Sprite = container.addChild(ImageFactory.createWindow(180, 60, 190, 70)) as Sprite;

            window1.addChild(ImageFactory.createInstructionText(5, 7, 190, 24, "Select a tower to build\nby clicking right button.", 12));

            window1.addChild(ImageFactory.createInstructionText(5, 39, 190, 24, "設置したいタワーを、\n右のボタンから選択できます。", 12));

            

            var window2:Sprite = container.addChild(ImageFactory.createWindow(30, 220, 190, 70)) as Sprite;

            window2.addChild(ImageFactory.createInstructionText(5, 7, 190, 24, "Click on the map\nto build the selected tower.", 12));

            window2.addChild(ImageFactory.createInstructionText(5, 39, 190, 24, "マップをクリックすると、\n選択したタワーを設置できます。", 12));

            

            var window3:Sprite = container.addChild(ImageFactory.createWindow(240, 270, 190, 70)) as Sprite;

            window3.addChild(ImageFactory.createInstructionText(5, 7, 190, 24, "Click on a tower on the map\nto upgrade or sell it.", 12));

            window3.addChild(ImageFactory.createInstructionText(5, 39, 190, 24, "マップ上のタワーをクリックすると、\nレベルアップや売却を行えます。", 12));

            

            var window4:Sprite = container.addChild(ImageFactory.createWindow(170, 380, 190, 70)) as Sprite;

            window4.addChild(ImageFactory.createInstructionText(5, 7, 190, 24, "Press \"Next Wave\" button\nto send the next wave.", 12));

            window4.addChild(ImageFactory.createInstructionText(5, 39, 190, 24, "「Next Wave」ボタンを押すと、\nゲームを進めることができます。", 12));

            

            return container;

        }

        

        public function enter():void {

            _main.addChild(_gameoverScreen);

            

            if (_isBeginning) {

                _main.addChild(_instructionWindow);

            }else {

                if (_scoreForm != null) { _gameoverScreen.removeChild(_scoreForm); }

                _scoreForm = new BasicScoreForm(_gameoverScreen, 92, 10, GameData.instance.score, "Score", showRanking);

                showRanking(false);

            }

        }

        private function showRanking(b:Boolean):void {

            if (_ranking != null) { _gameoverScreen.removeChild(_ranking); }

            _ranking = new BasicScoreRecordViewer(_gameoverScreen, 122, 215, "Ranking", 99, true, null);

        }

        

        public function update():void { }

        

        public function exit():void {

            if (_isBeginning) {

                _main.removeChild(_instructionWindow);

                _instructionWindow = null;

            }

            _isBeginning = false;

            _main.removeChild(_gameoverScreen);

            

            _main.world.clear();

            _main.waveManager.initialize();

            GameData.instance.initialize();

        }

    }

//}

/* ----------------------------------------------------------------------------------------------------------------------

 * ReadyState

 * -----------------------------------------------------------------------------------------------------------------------

 */

//package {

    //public 

    class ReadyState implements IState {

        private var _main:Main;

        

        public function ReadyState(main:Main) {

            _main = main;

        }

        

        public function enter():void {

            _main.setNextWaveButtonHandler(startPlaying);

            HotKey.enable();

        }

        private function startPlaying():void { _main.changeState(Main.STATE_PLAYING); }

        

        public function update():void {

            _main.world.update();

        }

        

        public function exit():void {

            HotKey.disable();

            _main.setNextWaveButtonHandler(_main.waveManager.sendNextWave);

        }

    }

//}

/* ----------------------------------------------------------------------------------------------------------------------

 * PlayingState

 * -----------------------------------------------------------------------------------------------------------------------

 */

//package {

    //public 

    class PlayingState implements IState {

        private var _main:Main;

        

        public function PlayingState(main:Main) {

            _main = main;

        }

        

        public function enter():void {

            GameData.instance.sellingRatio = 0.8;

            HotKey.enable();

        }

        

        public function update():void {

            _main.world.update();

            _main.waveManager.update();

            

            // ライフが0になるか、全ての敵を倒したら、ゲームオーバー

            if ((GameData.instance.lives <= 0) || (_main.world.numEnemies() == 0 && _main.waveManager.finished())) {

                _main.changeState(Main.STATE_GAMEOVER);

            }

        }

        

        public function exit():void {

            HotKey.disable();

            GameData.instance.sellingRatio = 1;

        }

    }

//}

/* ----------------------------------------------------------------------------------------------------------------------

 * MenuState

 * -----------------------------------------------------------------------------------------------------------------------

 */

//package {

    import com.bit101.components.PushButton;

    import flash.display.Sprite;

    import flash.events.MouseEvent;

    

    //public 

    class MenuState implements IState {

        private var _main:Main;

        private var _menuScreen:Sprite;

        

        public function MenuState(main:Main) {

            _main = main;

            _menuScreen = createMenuScreen();

        }

        

        private function createMenuScreen():Sprite {

            var screen:Sprite = new Sprite();

            screen.graphics.beginFill(0x000000, 0.5);

            screen.graphics.drawRect(0, 0, 465, 465);

            screen.graphics.endFill();

            

            var window:Sprite = screen.addChild(ImageFactory.createWindow(122, 172, 120, 95)) as Sprite;

            window.addChild(ImageFactory.createWindow(0, 0, 120, 25));

            window.addChild(ImageFactory.createSimpleText(0, 0, 120, 25, "Menu", 12, 0xffffff));

            new PushButton(window, 10, 35, "Resume", onClickResumeButton);

            new PushButton(window, 10, 65, "Give up", onClickGiveUpButton);

            return screen;

        }

        private function onClickResumeButton(e:MouseEvent):void { _main.revertToPreviousState(); }

        private function onClickGiveUpButton(e:MouseEvent):void { _main.changeState(Main.STATE_GAMEOVER); }

        

        public function enter():void { _main.addChild(_menuScreen); }

        

        public function update():void { }

        

        public function exit():void { _main.removeChild(_menuScreen); }

    }

//}

/* ----------------------------------------------------------------------------------------------------------------------

 * World

 * -----------------------------------------------------------------------------------------------------------------------

 */

//package {

    import flash.display.Bitmap;

    import flash.display.Sprite;

    import flash.events.MouseEvent;

    

    //public 

    class World extends Sprite {

        private var _nodes:Vector.<Vector.<Node>>;

        private var _starts:Vector.<Start>;

        private var _goals:Vector.<Goal>;

        

        private var _towers:Vector.<Tower>;

        private var _bullets:Vector.<Bullet>;

        private var _groundEnemies:Vector.<Enemy>;

        private var _flyingEnemies:Vector.<Enemy>;

        

        private var _towerLayer:Sprite;

        private var _enemyLayer:Sprite;

        private var _bulletLayer:Sprite;

        private var _effectLayer:Sprite;

        private var _cursor:Cursor;

        

        public function getNode(col:int, row:int):Node { return _nodes[row][col]; }

        public function getStartingPos(startIndex:int):TileBasedPoint {

            return _starts[startIndex].getRandomSpawningPosition();

        }

        public function numEnemies():int { return _groundEnemies.length + _flyingEnemies.length; }

        

        public function World(posx:int, posy:int) {

            x = posx;

            y = posy;

            

            initializeNodes();

            initializeStarts();

            initializeGoals();

            

            _towers = new Vector.<Tower>();

            _bullets = new Vector.<Bullet>();

            _groundEnemies = new Vector.<Enemy>();

            _flyingEnemies = new Vector.<Enemy>();

            

            addChild(new Bitmap(ImageFactory.getImage("World")));

            addChild(_towerLayer = new Sprite());

            addChild(_enemyLayer = new Sprite());

            addChild(_bulletLayer = new Sprite());

            addChild(_effectLayer = new Sprite());

            addChild(_cursor = new Cursor(this));

            _enemyLayer.mouseChildren = _bulletLayer.mouseChildren = false;

            _enemyLayer.mouseEnabled = _bulletLayer.mouseEnabled = false;

            

            addEventListener(MouseEvent.CLICK, addTower);

        }

        

        private function initializeNodes():void {

            _nodes = new Vector.<Vector.<Node>>();

            for (var row:int = 0; row < Const.NODE_ROWS; row++) {

                _nodes[row] = new Vector.<Node>();

                for (var col:int = 0; col < Const.NODE_COLS; col++) {

                    _nodes[row][col] = new Node(col, row, Config.NODE_TYPE[row][col]);

                }

            }

        }

        

        private function initializeStarts():void {

            _starts = Config.getStarts();

        }

        

        private function initializeGoals():void {

            _goals = Config.getGoals();

            for (var i:int = 0; i < _goals.length; i++) {

                var goal:Goal = _goals[i];

                goal.setNode(_nodes[goal.tileY][goal.tileX]);

                goal.search(this);

            }

        }

        

        public function update():void {

            var i:int;

            

            for (i = 0; i < _towers.length; i++) {

                _towers[i].update();

            }

            

            for (i = 0; i < _groundEnemies.length; i++) {

                var groundEnemy:Enemy = _groundEnemies[i];

                _nodes[groundEnemy.tileY][groundEnemy.tileX].numEnemy--;

                _groundEnemies[i].update(this);

                _nodes[groundEnemy.tileY][groundEnemy.tileX].numEnemy++;

            }

            

            for (i = 0; i < _flyingEnemies.length; i++) {

                _flyingEnemies[i].update(this);

            }

            

            for (i = 0; i < _bullets.length; i++) {

                _bullets[i].update();

            }

            

            _cursor.update(mouseX - Const.CURSOR_OFFSET, mouseY - Const.CURSOR_OFFSET);

        }

        

        // すべてのタワー・弾・敵を消去する

        public function clear():void {

            var i:int;

            for (i = _towers.length - 1; i >= 0; i--) { removeTower(_towers[i], true); }

            for (i = 0; i < _goals.length; i++) { _goals[i].search(this); }

            for (i = _groundEnemies.length - 1; i >= 0; i--) { removeEnemy(_groundEnemies[i]); }

            for (i = _flyingEnemies.length - 1; i >= 0; i--) { removeEnemy(_flyingEnemies[i]); }

            for (i = _bullets.length - 1; i >= 0; i--) { removeBullet(_bullets[i]); }

        }

        

        // タワーを(設置できるなら)設置する

        private function addTower(e:MouseEvent):void {

            var position:TileBasedPoint = TileBasedPoint.createFromWorldPos(mouseX - Const.CURSOR_OFFSET, mouseY - Const.CURSOR_OFFSET);

            position.setTilePos(position.tileX, position.tileY);

            

            if (canBuildTower(position.tileX, position.tileY)) {

                var i:int, j:int;

                var tower:Tower = new Tower(position.x, position.y, GameData.instance.selectedTower.type, this);

                

                // タワーが設置される場所を通行不能にする

                for (var row:int = 0; row < 2; row++) {

                    for (var col:int = 0; col < 2; col++) {

                        var node:Node = _nodes[position.tileY + row][position.tileX + col];

                        node.passable = node.buildable = false;

                    }

                }

                

                // 全ての経路の最探索

                for (i = 0; i < _goals.length; i++) {

                    _goals[i].search(this);

                }

                

                // 全てのスタート、全ての敵(地上)、のゴールを更新

                // ブロッキングが発生していたら中断

                var nearestGoal:Goal;

                var blocking:Boolean = false;

                for (i = 0; !blocking && (i < _starts.length); i++) {

                    var start:Start = _starts[i];

                    if (start.forFlying) { continue; }

                    nearestGoal = getNearestGoalFrom(start.posX, start.posY, false);

                    if (!nearestGoal.hasPath(start.tileX,start.tileY)) { blocking = true; }

                }

                for (i = 0; !blocking && (i < _groundEnemies.length); i++) {

                    var enemy:Enemy = _groundEnemies[i];

                    nearestGoal = getNearestGoalFrom(enemy.posX, enemy.posY, false);

                    enemy.setGoal(nearestGoal);

                    if (!enemy.hasPathToGoal()) { blocking = true; }

                }

                

                // ブロッキングが発生していたら、

                // 全ての変更を元に戻してタワー設置失敗で終了

                // そうでなければ、実際にタワーを設置する

                if (blocking) {

                    // 通行不能にした場所を元に戻す

                    for (row = 0; row < 2; row++) {

                        for (col = 0; col < 2; col++) {

                            node = _nodes[position.tileY + row][position.tileX + col];

                            node.passable = node.buildable = true;

                        }

                    }

                    // 全ての経路を元に戻す

                    for (i = 0; i < _goals.length; i++) {

                        _goals[i].revertToPrevious();

                    }

                    // 全ての敵(地上)のゴールを更新

                    for (i = 0; i < _groundEnemies.length; i++) {

                        _groundEnemies[i].setGoal(getNearestGoalFrom(_groundEnemies[i].posX, _groundEnemies[i].posY, false));

                    }

                }else {

                    _towers.push(tower);

                    _towerLayer.addChild(tower);

                    

                    GameData.instance.gold -= tower.type.getStatus(tower.level).cost;

                }

            }

        }

        

        public function canBuildTower(tilex:int, tiley:int):Boolean {

            var selectedTower:Tower = GameData.instance.selectedTower;

            // タワー選択ボタンを選択中か、十分な所持金があるか調べる

            if (selectedTower == null || selectedTower.active || (GameData.instance.gold < selectedTower.type.getStatus(selectedTower.level).cost)) { return false; }

            

            // 引数で指定した場所にタワーが建てられるか調べる

            for (var row:int = 0; row < 2; row++) {

                for (var col:int = 0; col < 2; col++) {

                    var node:Node = _nodes[tiley + row][tilex + col];

                    if (!node.buildable || (node.numEnemy > 0)) { return false; }

                }

            }

            

            return true;

        }

        

        // タワーのターゲットを探してそれを返す(いなければnullを返す)

        public function findTarget(posx:Number, posy:Number, range:int, ground:Boolean, air:Boolean):Enemy {

            var i:int, enemy:Enemy, diffX:Number, diffY:Number;

            var rangeSq:int = range * range;

            

            // 地上に攻撃できるなら、地上の敵のターゲットを探す

            if (ground) {

                for (i = 0; i < _groundEnemies.length; i++) {

                    enemy = _groundEnemies[i];

                    diffX = posx - enemy.posX;

                    diffY = posy - enemy.posY;

                    if (diffX * diffX + diffY * diffY < rangeSq) {

                        return enemy;

                    }

                }

            }

            

            // 空中に攻撃できるなら、飛んでいる敵のターゲットを探す

            if (air) {

                for (i = 0; i < _flyingEnemies.length; i++) {

                    enemy = _flyingEnemies[i];

                    diffX = posx - enemy.posX;

                    diffY = posy - enemy.posY;

                    if (diffX * diffX + diffY * diffY < rangeSq) {

                        return enemy;

                    }

                }

            }

            

            // ターゲットが見つからなければnull

            return null;

        }

        

        // 引数で指定した位置から最も近いゴールを返す

        private function getNearestGoalFrom(posx:Number, posy:Number, flying:Boolean):Goal {

            var nearest:Goal = _goals[0];

            for (var i:int = 1; i < _goals.length; i++) {

                if (_goals[i].getCost(posx, posy, flying) < nearest.getCost(posx, posy, flying)) {

                    nearest = _goals[i];

                }

            }

            return nearest;

        }

        

        // タワーを消去する

        public function removeTower(tower:Tower, clear:Boolean = false):void {

            var position:TileBasedPoint = TileBasedPoint.createFromWorldPos(tower.x, tower.y);

            for (var row:int = 0; row < 2; row++) {

                for (var col:int = 0; col < 2; col++) {

                    var node:Node = _nodes[position.tileY + row][position.tileX + col];

                    node.passable = node.buildable = true;

                }

            }

            

            // ブロッキングを考慮せずに

            // 全ての経路の最探索と、全ての敵(地上)のゴールを更新

            // clear()時は最探索する必要

            if (!clear) {

                var i:int;

                for (i = 0; i < _goals.length; i++) {

                    _goals[i].search(this);

                }

                for (i = 0; i < _groundEnemies.length; i++) {

                    _groundEnemies[i].setGoal(getNearestGoalFrom(_groundEnemies[i].posX, _groundEnemies[i].posY, false));

                }

            }

            

            _towerLayer.removeChild(tower);

            _towers.splice(_towers.indexOf(tower), 1);

        }

        

        // 弾を出現させる

        public function addBullet(bullet:Bullet):void {

            _bullets.push(bullet);

            _bulletLayer.addChild(bullet);

        }

        

        // 弾を消去する

        public function removeBullet(bullet:Bullet):void {

            _bulletLayer.removeChild(bullet);

            _bullets.splice(_bullets.indexOf(bullet), 1);

        }

        

        // 敵を出現させる

        public function addEnemy(enemy:Enemy):void {

            enemy.setGoal(getNearestGoalFrom(enemy.posX, enemy.posY, enemy.type.flying));

            

            if (enemy.type.flying) {

                _flyingEnemies.push(enemy);

            }else {

                _groundEnemies.push(enemy);

                _nodes[enemy.tileY][enemy.tileX].numEnemy++;

            }

            _enemyLayer.addChild(enemy);

        }

        

        // 敵を消去する

        public function removeEnemy(enemy:Enemy):void {

            _enemyLayer.removeChild(enemy);

            if (enemy.type.flying) {

                _flyingEnemies.splice(_flyingEnemies.indexOf(enemy), 1);

            }else {

                _nodes[enemy.tileY][enemy.tileX].numEnemy--;

                _groundEnemies.splice(_groundEnemies.indexOf(enemy), 1);

            }

        }

        

        // エフェクトを発生させる

        public function addEffect(effect:Sprite):void {

            _effectLayer.addChild(effect);

        }

        

        public function damageSurroundingEnemies(bullet:Bullet):void {

            var i:int, enemy:Enemy;

            if (bullet.ground) {

                for (i = _groundEnemies.length - 1; i >= 0; i--) {

                    enemy = _groundEnemies[i];

                    if (bullet.isHitTarget(enemy, bullet.splashRadius)) { bullet.damageTarget(enemy); }

                }

            }

            if (bullet.air) {

                for (i = _flyingEnemies.length - 1; i >= 0; i--) {

                    enemy = _flyingEnemies[i];

                    if (bullet.isHitTarget(enemy, bullet.splashRadius)) { bullet.damageTarget(enemy); }

                }

            }

        }

    }

//}

/* ----------------------------------------------------------------------------------------------------------------------

 * Node

 * -----------------------------------------------------------------------------------------------------------------------

 */

//package {

    //public 

    class Node {

        private var _center:TileBasedPoint;    // 中心の座標

        private var _passable:Boolean;        // 敵が通れるかどうか

        private var _buildable:Boolean;        // タワーが建てられるかどうか

        private var _numEnemy:int;            // このノード上にいる敵の数

        

        public function get tileX():int { return _center.tileX; }

        public function get tileY():int { return _center.tileY; }

        public function get centerX():Number { return _center.x; }

        public function get centerY():Number { return _center.y; }

        public function get passable():Boolean { return _passable; }

        public function get buildable():Boolean { return _buildable; }

        public function get numEnemy():int { return _numEnemy; }

        

        public function set passable(value:Boolean):void { _passable = value; }

        public function set buildable(value:Boolean):void { _buildable = value; }

        public function set numEnemy(value:int):void { _numEnemy = value; }

        

        public function Node(tilex:int, tiley:int, type:int) {

            _center = TileBasedPoint.createFromTilePos(tilex, tiley);

            _center.x += Const.NODE_SIZE / 2;

            _center.y += Const.NODE_SIZE / 2;

            

            switch(type) {

                case 0: { _passable = true; _buildable = true; break; }

                case 1: { _passable = false; _buildable = false; break; }

                case 2: { _passable = true; _buildable = false; break; }

            }

            _numEnemy = 0;

        }

    }

//}

/* ----------------------------------------------------------------------------------------------------------------------

 * Start

 * -----------------------------------------------------------------------------------------------------------------------

 */

//package {

    //public 

    class Start {

        private var _position:TileBasedPoint;

        private var _width:int;

        private var _forFlying:Boolean;

        

        public function get posX():Number { return _position.x; }

        public function get posY():Number { return _position.y; }

        public function get tileX():int { return _position.tileX; }

        public function get tileY():int { return _position.tileY; }

        public function get forFlying():Boolean { return _forFlying; }

        

        public function Start(tilex:int, tiley:int, width:int, forFlying:Boolean) {

            _position = TileBasedPoint.createFromTilePos(tilex, tiley);

            _width = width * Const.NODE_SIZE;

            _forFlying = forFlying;

        }

        

        // ランダムな出現位置を取得する

        public function getRandomSpawningPosition():TileBasedPoint {

            var posx:int = int(_position.x + ((2 * _width * Math.random()) - _width));

            var posy:int = int(_position.y + Const.NODE_SIZE);

            return TileBasedPoint.createFromWorldPos(posx, posy);

        }

    }

//}

/* ----------------------------------------------------------------------------------------------------------------------

 * Goal

 * -----------------------------------------------------------------------------------------------------------------------

 */

//package {

    //public 

    class Goal {

        private static const DX:Array = [0, -1, 1, 0, -1, 1, -1, 1];

        private static const DY:Array = [ -1, 0, 0, 1, -1, -1, 1, 1];

        private static const DCOST:Array = [1, 1, 1, 1, Math.SQRT2, Math.SQRT2, Math.SQRT2, Math.SQRT2];

        

        private var _center:TileBasedPoint;

        private var _node:Node;

        private var _openNodes:Vector.<Node>;            // 保留ノードリスト

        private var _nodeCost:Vector.<Vector.<Number>>;    // 各ノードの移動コスト

        private var _nodeNext:Vector.<Vector.<Node>>;    // 各ノードの次の経路となるノード

        

        private var _previousNodeCost:Vector.<Vector.<Number>>;    // 以前のnodeCost

        private var _previousNodeNext:Vector.<Vector.<Node>>;    // 以前のnodeNext

        

        public function get tileX():int { return _center.tileX; }

        public function get tileY():int { return _center.tileY; }

        

        public function Goal(tilex:int, tiley:int) {

            _center = TileBasedPoint.createFromTilePos(tilex, tiley);

            _center.setWorldPos(_center.x + Const.NODE_SIZE / 2, _center.y + Const.NODE_SIZE / 2);

            _openNodes = new Vector.<Node>();

            _nodeCost = null; _nodeNext = null;

        }

        public function setNode(node:Node):void { _node = node; }

        

        // 引数で指定した位置から、ゴールまでのコストを返す

        public function getCost(posx:Number, posy:Number, flying:Boolean):Number {

            if (flying) {

                var diffx:Number = _center.x - posx;

                var diffy:Number = _center.y - posy;

                return diffx * diffx + diffy * diffy;

            }

            

            var postion:TileBasedPoint = TileBasedPoint.createFromWorldPos(posx, posy);

            return _nodeCost[postion.tileY][postion.tileX];

        }

        

        // 引数で指定した位置から、ゴールへ向かう経路が存在しているかどうか

        public function hasPath(tilex:int, tiley:int):Boolean {

            return _nodeCost[tiley][tilex] != Number.MAX_VALUE;

        }

        

        // 引数で指定した位置から、ゴールへ向かう為の次のノードを返す

        public function getNext(posx:Number, posy:Number, flying:Boolean):Node {

            if (flying) {

                return _node;

            }

            

            var position:TileBasedPoint = TileBasedPoint.createFromWorldPos(posx, posy);

            return _nodeNext[position.tileY][position.tileX];

        }

        

        // Dijkstra法による経路探索

        public function search(world:World):void {

            setup(world);

            

            while (_openNodes.length > 0) {

                var subject:Node = _openNodes.pop() as Node;

                

                // 周囲8方向のノードを訪問する

                for (var i:int = 0; i < 8; i++) {

                    // 画面外の存在しないノードを指すなら次の周囲ノードへ進む

                    if (!isValid(subject.tileX + Goal.DX[i], subject.tileY + Goal.DY[i])) { continue; }

                    // 通れないノード、計算済み(確定)ノード、直進することができないノードなら次の周囲ノードへ進む

                    var test:Node = world.getNode(subject.tileX + Goal.DX[i], subject.tileY + Goal.DY[i]);

                    if (!test.passable || isCalculatedNode(test) || !canGoStraightTo(subject, test, world)) { continue; }

                    

                    // 移動コストを計算する

                    _nodeCost[test.tileY][test.tileX] = _nodeCost[subject.tileY][subject.tileX] + Goal.DCOST[i];

                    // 次の経路ノードをsubjectノードに設定する

                    _nodeNext[test.tileY][test.tileX] = subject;

                    // 保留ノードリストに追加する

                    insertToOpenNodes(test);

                }

            }

        }

        

        // 探索前の準備

        private function setup(world:World):void {

            _previousNodeCost = _nodeCost;

            _previousNodeNext = _nodeNext;

            

            _nodeCost = new Vector.<Vector.<Number>>();

            _nodeNext = new Vector.<Vector.<Node>>();

            for (var row:int = 0; row < Const.NODE_ROWS; row++) {

                _nodeCost[row] = new Vector.<Number>();

                _nodeNext[row] = new Vector.<Node>();

                for (var col:int = 0; col < Const.NODE_COLS; col++) {

                    _nodeCost[row][col] = Number.MAX_VALUE;

                    _nodeNext[row][col] = world.getNode(col, row);

                }

            }

            

            // Goalのノードを経路探索のスタートノードとする

            _nodeCost[_center.tileY][_center.tileX] = 0;

            _openNodes.push(_node);

        }

        

        // indexの値が有効な値かどうか

        private function isValid(col:int, row:int):Boolean {

            return (col >= 0) && (col < Const.NODE_COLS) && (row >= 0) && (row < Const.NODE_ROWS);

        }

        

        // 既にコストを計算済みのノードかどうか

        private function isCalculatedNode(node:Node):Boolean {

            return _nodeCost[node.tileY][node.tileX] != Number.MAX_VALUE;

        }

        

        // subjectノードからtestノードへ直進できるかどうか

        private function canGoStraightTo(subject:Node, test:Node, world:World):Boolean {

            return world.getNode(subject.tileX, test.tileY).passable && world.getNode(test.tileX, subject.tileY).passable;

        }

        

        // nodeを保留ノードリストの適切な場所に挿入する

        private function insertToOpenNodes(node:Node):void {

            var insertIndex:int;

            var nodeCost:Number = _nodeCost[node.tileY][node.tileX];

            

            for (insertIndex = 0; insertIndex < _openNodes.length; insertIndex++) {

                var openNode:Node = _openNodes[insertIndex];

                if (nodeCost > _nodeCost[openNode.tileY][openNode.tileX]) { break; }

            }

            _openNodes.splice(insertIndex, 0, node);

        }

        

        // 最探索更新前の経路情報に戻す

        public function revertToPrevious():void {

            _nodeCost = _previousNodeCost;

            _nodeNext = _previousNodeNext;

        }

    }

//}

/* ----------------------------------------------------------------------------------------------------------------------

 * Tower

 * -----------------------------------------------------------------------------------------------------------------------

 */

//package {

    import flash.display.Bitmap;

    import flash.display.Sprite;

    import flash.events.MouseEvent;

    import flash.text.TextField;

    

    //public 

    class Tower extends Sprite {

        private var _center:TileBasedPoint;

        private var _type:TowerType;        // 種類

        private var _level:int;                // 現在のレベル

        private var _world:World;

        private var _target:Enemy;            // 現在のターゲット

        private var _reloadCount:Number;    // 100を超えたら弾発射

        

        private var _body:Sprite;

        private var _levelText:TextField;

        

        public function get tileX():int { return _center.tileX; }

        public function get tileY():int { return _center.tileY; }

        public function get type():TowerType { return _type; }

        public function get level():int { return _level; }

        public function get active():Boolean { return _world != null; }

        

        public function Tower(posx:int, posy:int, type:TowerType, world:World = null) {

            x = posx;

            y = posy;

            

            _center = TileBasedPoint.createFromWorldPos(posx + Const.TOWER_SIZE / 2, posy + Const.TOWER_SIZE / 2);

            _type = type;

            _level = 1;

            _world = world;

            _target = null;

            _reloadCount = 0;

            

            draw();

            

            buttonMode = true;

            mouseChildren = false;

            addEventListener(MouseEvent.CLICK, isSelected, false, 0, true);

        }

        

        // タワーの画像を描画する

        private function draw():void {

            addChild(new Bitmap(ImageFactory.getImage("Base")));

            _body = addChild(new Sprite()) as Sprite;

            var bitmap:Bitmap = _body.addChild(new Bitmap(ImageFactory.getImage(_type.name), "auto", true)) as Bitmap;

            bitmap.x = bitmap.y = int( -Const.TOWER_SIZE / 2);

            _body.x = _body.y = int(Const.TOWER_SIZE / 2);

            

            if (active) {

                _body.rotation = 360 * Math.random() - 180;

                addChild(_levelText = ImageFactory.createBorderedText(Const.TOWER_SIZE - 10, Const.TOWER_SIZE - 10, 10, 10, _level.toString(), 10, 0xffffff, 0x000000));

            }

        }

        

        public function isSelected(e:MouseEvent = null):void {

            GameData.instance.selectedTower = this;

        }

        

        public function update():void {

            var range:int = _type.getStatus(_level).range;

            // ターゲット無しかターゲットがやられていたら、新しいターゲットを探す

            if (_target == null || _target.isDead()) {

                _target = _world.findTarget(_center.x, _center.y, range, _type.ground, _type.air);

            }

            

            // リロードカウントを進める

            if (_reloadCount < 100) { _reloadCount += _type.getStatus(_level).reloadSpeed; }

            

            if (_target != null) {

                var rangeSq:int = range * range;

                var diffX:Number = _target.posX - _center.x;

                var diffY:Number = _target.posY - _center.y;

                if (diffX * diffX + diffY * diffY < rangeSq) {

                    _body.rotation = _type.adjustBodyRotation(diffX, diffY, _body.rotation);

                    // リロードを終えていて、ターゲットがいるなら弾を発射する

                    if (_reloadCount >= 100 && _target != null) {

                        _reloadCount = 0;

                        _world.addBullet(type.createBullet(_center.x, _center.y, _level, _target, _world));

                    }

                }else {

                    // ターゲットが射程外になっていたらターゲットから外す

                    _target = null;

                }

            }

        }

        

        public function upgrade():void {

            // 既に最高レベルになっていたら終了

            if (!_type.hasStatus(_level + 1)) { return; }

            

            // アップグレードするだけの所持金があれば、アップグレード

            var upgradeCost:int = _type.getStatus(_level + 1).cost - _type.getStatus(_level).cost;

            if (upgradeCost <= GameData.instance.gold) {

                _level++;

                _levelText.text = _level.toString();

                GameData.instance.gold -= upgradeCost;

                GameData.instance.selectedTower = this;

            }

        }

        

        public function sell():void {

            _world.removeTower(this);

            GameData.instance.gold += _type.getStatus(_level).cost * GameData.instance.sellingRatio;

            GameData.instance.selectedTower = null;

        }

    }

//}

/* ----------------------------------------------------------------------------------------------------------------------

 * TowerType

 * -----------------------------------------------------------------------------------------------------------------------

 */

//package {

    import flash.display.BitmapData;

    

    //public 

    class TowerType {

        private var _name:String;                    // 種類名

        private var _ground:Boolean;                // 対地攻撃可能か

        private var _air:Boolean;                    // 対空攻撃可能か

        private var _slow:Boolean;                    // スロー効果有か

        private var _splash:Boolean;                // 範囲攻撃か

        private var _status:Vector.<TowerStatus>;    // (レベルごとの)各ステータス

        

        public function get name():String { return _name; }

        public function get ground():Boolean { return _ground; }

        public function get air():Boolean { return _air; }

        public function get slow():Boolean { return _slow; }

        public function get splash():Boolean { return _splash; }

        public function getStatus(level:int):TowerStatus { return _status[level - 1]; }

        

        // ※インスタンスは Config.getTowerType() から生成する

        public function TowerType(name:String, ground:Boolean, air:Boolean, slow:Boolean, splash:Boolean) {

            _name = name;

            _ground = ground;

            _air = air;

            _slow = slow;

            _splash = splash;

            _status = Config.getTowerStatus(_name);

        }

        

        // 指定したレベルのステータスがあるかどうか

        public function hasStatus(level:int):Boolean {

            return level <= _status.length;

        }

        

        // 種類に応じて、砲頭の角度を調整する

        public function adjustBodyRotation(diffx:Number, diffy:Number, currentRotation:Number):Number {

            switch(_name) {

                case "Vortex": { return currentRotation + 20; }

                default: { return Math.atan2(diffy, diffx) * 180 / Math.PI; }

            }

        }

        

        // 種類に応じた弾を生成する

        public function createBullet(posx:int, posy:int, level:int, target:Enemy, world:World):Bullet {

            var image:BitmapData = ImageFactory.getImage("Bullet_" + _name);

            var pos:TileBasedPoint = TileBasedPoint.createFromWorldPos(posx, posy);

            var status:TowerStatus = getStatus(level);

            

            switch(_name) {

                case "Arrow":

                { return new Bullet(pos, status.range, status.damage, 0, 6, _ground, _air, _slow, false, false, target, image, world); }

                case "Gatling":

                { return new Bullet(pos, status.range, status.damage, 0, 10, _ground, _air, _slow, false, false, target, image, world); }

                case "Bomb":

                { return new Bullet(pos, status.range, status.damage, 24, 3, _ground, _air, _slow, true, false, target, image, world); }

                case "Missile":

                { return new Bullet(pos, status.range, status.damage, 0, 8, _ground, _air, _slow, true, false, target, image, world); }

                case "Frost":

                { return new Bullet(pos, status.range, status.damage, 12, 6, _ground, _air, _slow, false, false, target, image, world); }

                case "Vortex":

                { return new Bullet(pos, 0, status.damage, status.range, 0, _ground, _air, _slow, false, false, target, image, world); }

                case "Laser":

                { return new Bullet(pos, 900, status.damage, (Const.ENEMY_SIZE + Const.BULLET_SIZE) / 2, Const.NODE_SIZE, _ground, _air, _slow, false, true, target, image, world); }

            }

            return null; // never called

        }

    }

//}

/* ----------------------------------------------------------------------------------------------------------------------

 * TowerStatus

 * -----------------------------------------------------------------------------------------------------------------------

 */

//package {

    //public 

    class TowerStatus {

        private var _level:int;            // レベル 

        private var _cost:int;            // コスト

        private var _damage:int;        // ダメージ

        private var _range:int;            // 射程

        private var _firerate:Number;    // 射撃間隔(発/秒)

        private var _reloadSpeed:Number;

        

        public function get level():int { return _level; }

        public function get cost():int { return _cost; }

        public function get damage():int { return _damage; }

        public function get range():int { return _range; }

        public function get firerate():Number { return _firerate; }

        public function get reloadSpeed():Number { return _reloadSpeed; }

        

        // ※インスタンスは Config.getTowerStatus() から生成する

        public function TowerStatus(level:int, cost:int, damage:int, range:int, firerate:Number) {

            _level = level;

            _cost = cost;

            _damage = damage;

            _range = range;

            _firerate = firerate;

            _reloadSpeed = _firerate * 100 / Const.FRAME_RATE;

        }

    }

//}

/* ----------------------------------------------------------------------------------------------------------------------

 * Bullet

 * -----------------------------------------------------------------------------------------------------------------------

 */

//package {

    import flash.display.Bitmap;

    import flash.display.BitmapData;

    import flash.display.Sprite;

    import flash.geom.Point;

    

    //public 

    class Bullet extends Sprite {

        private var _life:int;            // 寿命(最大射程)

        private var _power:int;            // 威力

        private var _splashRadius:int;    // 範囲攻撃の半径(範囲攻撃で無いなら0)

        private var _speed:int;            // 速さ

        private var _ground:Boolean;    // 地上の敵に当たるか

        private var _air:Boolean;        // 空中の敵にあたるか

        private var _slow:Boolean;        // スロー効果

        private var _homing:Boolean;    // 追尾能力

        private var _pierce:Boolean;    // 貫通力

        private var _target:Enemy;        // ターゲット

        private var _world:World;

        

        private var _position:TileBasedPoint;    // 位置

        private var _velocity:Point;            // 速度

        

        public function get splashRadius():int { return _splashRadius; }

        public function get ground():Boolean { return _ground; }

        public function get air():Boolean { return _air; }

        

        public function Bullet(

            pos:TileBasedPoint, 

            life:int, power:int, radius:int, speed:int, 

            ground:Boolean, air:Boolean, slow:Boolean, 

            homing:Boolean, pierce:Boolean, 

            target:Enemy, image:BitmapData, world:World)

        {

            _life = life;

            _power = power;

            _splashRadius = radius;

            _speed = speed;

            _ground = ground;

            _air = air;

            _slow = slow;

            _homing = homing;

            _pierce = pierce;

            _target = target;

            _world = world;

            

            _position = pos;

            x = int(_position.x);

            y = int(_position.y);

            _velocity = new Point();

            changeVelocity();

            draw(image);

        }

        

        private function draw(image:BitmapData):void {

            var bitmap:Bitmap = new Bitmap(image);

            bitmap.x = bitmap.y = -int(Const.BULLET_SIZE / 2);

            addChild(bitmap);

        }

        

        // 速度を変更し、向きを変える

        private function changeVelocity():void {

            var diffX:Number = _target.posX - _position.x;

            var diffY:Number = _target.posY - _position.y;

            var radian:Number = Math.atan2(diffY, diffX);

            _velocity.x = _speed * Math.cos(radian);

            _velocity.y = _speed * Math.sin(radian);

            

            var degree:Number = radian * 180 / Math.PI;

            rotation = degree;

        }

        

        public function update():void {

            // 移動する

            _life -= _speed;

            x = int(_position.x += _velocity.x);

            y = int(_position.y += _velocity.y);

            if (_homing) { changeVelocity(); }

            

            // 画面外に出たら、消滅する

            if (_position.x < 0 || _position.x > 465 || _position.y < 0 || _position.y > 465) {

                _world.removeBullet(this);

                return;

            }

            

            // 射程外に出たら、(範囲攻撃の弾なら周辺の敵にダメージを与え)消滅する

            if (_life <= 0) {

                if (_splashRadius > 0) { _world.damageSurroundingEnemies(this); }

                _world.removeBullet(this);

                return;

            }

            

            // 貫通力ありなら、(ターゲットを問わず)周辺の敵にダメージを与える

            // ターゲットが死んでいたら、直線軌道に変更

            // ターゲットに当たっていたら、ダメージを与え消滅する

            if (_pierce) {

                _world.damageSurroundingEnemies(this);

            }else if (_target.isDead()) {

                _homing = false;

            }else if (isHitTarget(_target, (Const.BULLET_SIZE + Const.ENEMY_SIZE) / 2)) {

                if (_splashRadius > 0) { _world.damageSurroundingEnemies(this); }

                else { damageTarget(_target); }

                _world.removeBullet(this);

                return;

            }

        }

        

        public function isHitTarget(target:Enemy, radius:Number):Boolean {

            var diffX:Number = target.posX - _position.x;

            var diffY:Number = target.posY - _position.y;

            return diffX * diffX + diffY * diffY < radius * radius;

        }

        

        public function damageTarget(target:Enemy):void {

            target.damage(_power, _slow);

        }

    }

//}

/* ----------------------------------------------------------------------------------------------------------------------

 * Enemy

 * -----------------------------------------------------------------------------------------------------------------------

 */

//package {

    import flash.display.Bitmap;

    import flash.display.Sprite;

    import flash.filters.GlowFilter;

    import flash.geom.Point;

    

    //public 

    class Enemy extends Sprite {

        private var _world:World;

        private var _center:TileBasedPoint;    // 中心の座標

        private var _velocity:Point;        // 速度

        private var _type:EnemyType;        // 種族

        private var _HP:int;                // 現在のHP

        private var _maxHP:int;                // 最大HP

        private var _point:int;                // 倒した時のポイント

        private var _money:int;                // 倒した時のお金

        private var _slowingCount:int;

        

        private var _image:Sprite;

        private var _HPbar:Sprite;

        

        private var _goal:Goal;

        private var _nextNode:Node;

        

        public function get posX():Number { return _center.x; }

        public function get posY():Number { return _center.y; }

        public function get tileX():int { return _center.tileX; }

        public function get tileY():int { return _center.tileY; }

        public function get type():EnemyType { return _type; }

        public function isDead():Boolean { return _HP <= 0; }

        public function setGoal(value:Goal):void {

            _goal = value;

            _nextNode = _goal.getNext(_center.x, _center.y, _type.flying);

        }

        

        public function Enemy(center:TileBasedPoint, type:EnemyType, HP:int, point:int, money:int, world:World) {

            _world = world;

            _center = center;

            x = int(_center.x);

            y = int(_center.y);

            _velocity = new Point();

            _type = type;

            _HP = _maxHP = HP;

            _point = point;

            _money = money;

            _slowingCount = 0;

            

            draw();

        }

        

        private function draw():void {

            _image = addChild(new Sprite()) as Sprite;

            var bitmap:Bitmap = _image.addChild(new Bitmap(ImageFactory.getImage(_type.name), "auto", true)) as Bitmap;

            bitmap.x = bitmap.y = int( -Const.ENEMY_SIZE / 2);

            

            var HPbarBackground:Sprite = addChild(new Sprite()) as Sprite;

            HPbarBackground.x = -Const.ENEMY_SIZE / 2;

            HPbarBackground.y = -(Const.ENEMY_SIZE / 2 + 2);

            HPbarBackground.graphics.beginFill(0xff0000);

            HPbarBackground.graphics.drawRect(0, 0, Const.ENEMY_SIZE, 1);

            HPbarBackground.graphics.endFill();

            

            _HPbar = HPbarBackground.addChild(new Sprite()) as Sprite;

            _HPbar.graphics.beginFill(0x00ff00);

            _HPbar.graphics.drawRect(0, 0, Const.ENEMY_SIZE, 1);

            _HPbar.graphics.endFill();

        }

        

        public function update(world:World):void {

            // スロー状態を更新

            var slowed:Boolean = (_slowingCount > 0);

            if (slowed) { _slowingCount--; }

            else { _image.filters = CONDITION_GOOD; }

            

            // 移動する

            x = int(_center.x += (slowed ? _velocity.x * 0.7 : _velocity.x));

            y = int(_center.y += (slowed ? _velocity.y * 0.7 : _velocity.y));

            

            // ゴールに到着していたら、ライフを減らして消滅

            if (_center.tileX == _goal.tileX && _center.tileY == _goal.tileY) {

                GameData.instance.lives--;

                _HP = 0;

                _world.removeEnemy(this);

                return;

            }

            

            // 次に進むべきノードに到着していたら、その次に進むべきノードに更新

            if (_center.tileX == _nextNode.tileX && _center.tileY == _nextNode.tileY) {

                _nextNode = _goal.getNext(_center.x, _center.y, _type.flying);

            }

            

            // 速度と向きの更新

            var radian:Number = Math.atan2(_nextNode.centerY - _center.y, _nextNode.centerX - _center.x);

            _velocity.x = (_velocity.x + _type.speed * Math.cos(radian)) * 0.5;

            _velocity.y = (_velocity.y + _type.speed * Math.sin(radian)) * 0.5;

            var degree:Number = radian * 180 / Math.PI;

            _image.rotation = degree;

        }

        

        private static const CONDITION_GOOD:Array = [];

        private static const CONDITION_SLOWED:Array = [new GlowFilter(0x0088ff, 1, 4, 4)];

        

        // ダメージを受ける

        public function damage(amount:int, slow:Boolean):void {

            _HP -= amount;

            if (_HP <= 0) {

                GameData.instance.score += _point;

                GameData.instance.gold += _money;

                _world.removeEnemy(this);

                _world.addEffect(new Coin(_center.x, _center.y));

                return;

            }

            _HPbar.scaleX = _HP / _maxHP;

            

            

            // 攻撃にスロー効果が付いていて、それに免疫が無かったら、スロー状態になる

            if (slow && !_type.immunity) { 

                _slowingCount = Const.SLOWING_DURATION;

                _image.filters = CONDITION_SLOWED;

            }

        }

        

        public function hasPathToGoal():Boolean {

            return _goal.hasPath(_center.tileX, _center.tileY);

        }

    }

//}

/* ----------------------------------------------------------------------------------------------------------------------

 * EnemyType

 * -----------------------------------------------------------------------------------------------------------------------

 */

//package {

    import flash.utils.Dictionary;

    

    //public 

    class EnemyType {

        private static var _types:Dictionary;

        public static function initialize():void { _types = Config.getEnemyType(); }

        public static function getType(typeName:String):EnemyType { return _types[typeName]; }

        

        private var _name:String;        // 種族名称

        private var _speed:Number;        // 移動の速さ

        private var _flying:Boolean;    // 飛んでいるかどうか

        private var _immunity:Boolean;    // 免疫(状態異常スローに対する)があるかどうか

        

        public function get name():String { return _name; }

        public function get speed():Number { return _speed; }

        public function get flying():Boolean { return _flying; }

        public function get immunity():Boolean { return _immunity; }

        

        // ※インスタンスは Config.getEnemyType() から生成する

        public function EnemyType(name:String, speed:Number, flying:Boolean, immunity:Boolean) {

            _name = name;

            _speed = speed;

            _flying = flying;

            _immunity = immunity;

        }

    }

//}

/* ----------------------------------------------------------------------------------------------------------------------

 * Coin

 * -----------------------------------------------------------------------------------------------------------------------

 */

//package {

    import flash.display.Bitmap;

    import flash.display.Sprite;

    import org.libspark.betweenas3.BetweenAS3;

    import org.libspark.betweenas3.easing.Back;

    import org.libspark.betweenas3.easing.Expo;

    

    //public 

    class Coin extends Sprite {

        public function Coin(posx:int, posy:int) {

            x = posx;

            y = posy;

            

            draw();

            

            BetweenAS3.serial (

                BetweenAS3.tween(this, { $y: -16 }, null, 0.4, Back.easeOut),

                BetweenAS3.tween(this, { alpha: 0 }, null, 0.4, Expo.easeIn),

                BetweenAS3.removeFromParent(this)

            ).play();

        }

        

        private function draw():void {

            var bitmap:Bitmap = new Bitmap(ImageFactory.getImage("Coin"));

            bitmap.smoothing = true;

            bitmap.x = bitmap.y = -4;

            addChild(bitmap);

        }

    }

//}

/* ----------------------------------------------------------------------------------------------------------------------

 * Cursor

 * -----------------------------------------------------------------------------------------------------------------------

 */

//package {

    import flash.display.Bitmap;

    import flash.display.Sprite;

    import flash.events.Event;

    

    //public 

    class Cursor extends Sprite {

        private var _world:World;

        private var _border:Sprite;        // 枠線

        private var _towerImage:Sprite;    // タワーの画像

        private var _towerBody:Bitmap;    // タワー本体の画像

        

        public function Cursor(world:World) {

            _world = world;

            _border = createBorder();

            _towerImage = new Sprite();

            _towerImage.addChild(new Bitmap(ImageFactory.getImage("Base")));

            _towerImage.addChild(_towerBody = new Bitmap());

            _towerImage.alpha = 0.5;

            

            mouseChildren = mouseEnabled = false;

            

            GameData.instance.addEventListener(Const.EVENT_CHANGE_SELECTEDTOWER, onUpdateSelectedTower);

        }

        

        private function createBorder():Sprite {

            var sprite:Sprite = new Sprite();

            sprite.graphics.lineStyle(2, 0xffff00);

            sprite.graphics.drawRect(0, 0, Const.TOWER_SIZE, Const.TOWER_SIZE);

            return sprite;

        }

        

        public function update(posx:int, posy:int):void {

            var selectedTower:Tower = GameData.instance.selectedTower;

            var position:TileBasedPoint;

            

            if (selectedTower != null && selectedTower.active) {

                position = TileBasedPoint.createFromWorldPos(selectedTower.x, selectedTower.y);

            }else {

                position = TileBasedPoint.createFromWorldPos(posx, posy);

                position.setTilePos(position.tileX, position.tileY);

            }

            

            x = position.x;

            y = position.y;

            

            updateRangeCircle(position.tileX, position.tileY);

        }

        

        private static const POSSIBLE:uint = 0xffffff;

        private static const IMPOSSIBLE:uint = 0xff0000;

        

        private function updateRangeCircle(tilex:int, tiley:int):void {

            graphics.clear();

            

            var selectedTower:Tower = GameData.instance.selectedTower;

            if (selectedTower == null) { return; }

            

            var color:uint = (selectedTower.active || _world.canBuildTower(tilex, tiley)) ? POSSIBLE : IMPOSSIBLE;

            graphics.lineStyle(0, color, 0.6);

            graphics.beginFill(color, 0.2);

            graphics.drawCircle(int(Const.TOWER_SIZE / 2), int(Const.TOWER_SIZE / 2), selectedTower.type.getStatus(selectedTower.level).range);

            graphics.endFill();

        }

        

        private function onUpdateSelectedTower(e:Event):void {

            var selectedTower:Tower = GameData.instance.selectedTower;

            if (contains(_border)) { removeChild(_border); }

            if (contains(_towerImage)) { removeChild(_towerImage); }

            

            if (selectedTower == null) { return; }

            

            if (selectedTower.active) {

                addChild(_border);

            }else {

                _towerBody.bitmapData = ImageFactory.getImage(selectedTower.type.name);

                _towerBody.smoothing = true;

                addChild(_towerImage);

            }

            

        }

    }

//}

/* ----------------------------------------------------------------------------------------------------------------------

 * Frontend

 * -----------------------------------------------------------------------------------------------------------------------

 */

//package {

    import flash.display.Bitmap;

    import flash.display.Sprite;

    import flash.events.Event;

    import flash.events.MouseEvent;

    import flash.filters.GlowFilter;

    import flash.geom.ColorTransform;

    import flash.text.TextField;

    import flash.ui.Keyboard;

    

    //public 

    class Frontend extends Sprite {

        private var _wave:TextField;

        private var _score:TextField;

        private var _lives:TextField;

        private var _gold:TextField;

        

        private var _buttons:Sprite;                    // ボタンをまとめる表示オブジェクト

        private var _towerStatus:TowerStatusWindow;        // タワーのステータスを表示するウインドウ

        

        public function Frontend(posx:int, posy:int) {

            x = posx;

            y = posy;

            

            addChild(createGameStatusWindow(0, 0));

            addChild(createTowerSelectionWindow(365, 0));

            

            GameData.instance.addEventListener(Const.EVENT_CHANGE_GAMESTATUS, onUpdateGameStatus);

            GameData.instance.addEventListener(Const.EVENT_CHANGE_SELECTEDTOWER, onUpdateSelectedTower);

        }

        

        // ゲームのステータスを表示するウインドウを作成する

        private function createGameStatusWindow(posx:int, posy:int):Sprite {

            var gameStatus:Sprite = ImageFactory.createWindow(posx, posy, 365, 25);

            gameStatus.addChild(ImageFactory.createBorderedText(14, 0, 40, 25, "Wave :", 12, 0xffffff, 0x808080));

            gameStatus.addChild(_wave = ImageFactory.createScoreText(54, 0, 25, 25, "0", 14, 0xffffff));

            gameStatus.addChild(ImageFactory.createBorderedText(93, 0, 40, 25, "Score :", 12, 0xffffff, 0x808080));

            gameStatus.addChild(_score = ImageFactory.createScoreText(133, 0, 50, 25, "0", 14, 0xffffff));

            gameStatus.addChild(ImageFactory.createBorderedText(197, 0, 40, 25, "Lives :", 12, 0xffffff, 0xf00000));

            gameStatus.addChild(_lives = ImageFactory.createScoreText(237, 0, 20, 25, "0", 14, 0xff0000));

            gameStatus.addChild(ImageFactory.createBorderedText(271, 0, 35, 25, "Gold :", 12, 0xffffff, 0xb0b000));

            gameStatus.addChild(_gold = ImageFactory.createScoreText(306, 0, 45, 25, "0", 14, 0xffff00));

            return gameStatus;

        }

        

        // タワー選択ボタンを配置するウインドウと、タワーのステータスを表示するウインドウを作成する

        private function createTowerSelectionWindow(posx:int, posy:int):Sprite {

            var towerSelection:Sprite = ImageFactory.createWindow(posx, posy, 100, 415);

            towerSelection.addChild(_buttons = new Sprite());

            

            // タワー選択ボタンを7つ作成して配置する

            var towertypes:Vector.<TowerType> = Config.getTowerType();

            for (var i:int = 0; i < 7; i++) {

                var tower:Tower = _buttons.addChild(new Tower(((i < 4) ? 15 : 53), 7 + 38 * (i % 4), towertypes[i])) as Tower;

                tower.addChild(ImageFactory.createSimpleText(((i < 4) ? -12 : 32), 0, 12, 32, String(i + 1), 10, 0xffffff));

                HotKey.bind(Keyboard.NUMBER_1 + i, tower.isSelected);

                HotKey.bind(Keyboard.NUMPAD_1 + i, tower.isSelected);

            }

            // 選択キャンセルボタンを作成して配置する

            var cancelButton:Sprite = towerSelection.addChild(createCancelButton(53, 7 + 38 * 3)) as Sprite;

            cancelButton.addChild(ImageFactory.createSimpleText(32, 0, 12, 32, "0", 10, 0xffffff));

            HotKey.bind(Keyboard.NUMBER_0, isSelectedCancelButton);

            HotKey.bind(Keyboard.NUMPAD_0, isSelectedCancelButton);

            HotKey.bind(Keyboard.ESCAPE, isSelectedCancelButton);

            

            towerSelection.addChild(_towerStatus = new TowerStatusWindow(5, 160));

            _towerStatus.visible = false;

            return towerSelection;

        }

        

        // 選択キャンセルボタンを作成する

        private function createCancelButton(posx:int, posy:int):Sprite {

            var cancel:Sprite = new Sprite();

            cancel.x = posx;

            cancel.y = posy;

            cancel.addChild(new Bitmap(ImageFactory.getImage("Base")));

            cancel.addChild(new Bitmap(ImageFactory.getImage("Cancel")));

            cancel.buttonMode = true;

            cancel.addEventListener(MouseEvent.CLICK, isSelectedCancelButton);

            return cancel;

        }

        private function isSelectedCancelButton(e:MouseEvent = null):void { GameData.instance.selectedTower = null; }

        

        private static const LIGHTEN:ColorTransform = new ColorTransform();

        private static const DARKEN:ColorTransform = new ColorTransform(0.5, 0.5, 0.5);

        

        public function onUpdateGameStatus(e:Event):void {

            var data:GameData = GameData.instance;            

            _wave.text = data.wave.toString();

            _score.text = data.score.toString();

            _lives.text = data.lives.toString();

            _gold.text = data.gold.toString();

            

            // タワーが購入できるかどうかで、タワー選択ボタンに明暗をつける

            for (var i:int = 0; i < _buttons.numChildren; i++) {

                var button:Tower = _buttons.getChildAt(i) as Tower;

                button.transform.colorTransform = ((button.type.getStatus(button.level).cost <= data.gold) ? LIGHTEN: DARKEN);

            }

            

            // アップグレードボタンの有効/無効を反映させる

            if (_towerStatus.visible) { _towerStatus.updateUpgradeButton(); }

        }

        

        private static const NON_GLOW:Array = [];

        private static const GLOW:Array = [new GlowFilter(0xffff00, 1, 8, 8)];

        

        public function onUpdateSelectedTower(e:Event):void {

            var selectedTower:Tower = GameData.instance.selectedTower;

            

            // タワー選択ボタンのハイライトを解除する

            for (var i:int = 0; i < _buttons.numChildren; i++) {

                _buttons.getChildAt(i).filters = NON_GLOW;

            }

            

            if (selectedTower != null) {

                _buttons.removeEventListener(MouseEvent.MOUSE_OVER, onMouseOverButton);

                _buttons.removeEventListener(MouseEvent.MOUSE_OUT, onMouseOutButton);

                

                _towerStatus.update(selectedTower);

                _towerStatus.visible = true;

                // 選択されたボタンにハイライトをつける

                if (!selectedTower.active) { selectedTower.filters = GLOW; }

            }else {

                _buttons.addEventListener(MouseEvent.MOUSE_OVER, onMouseOverButton);

                _buttons.addEventListener(MouseEvent.MOUSE_OUT, onMouseOutButton);

                _towerStatus.visible = false;

            }

        }

        

        // タワー非選択時、選択ボタンにマウスオーバーでステータスを表示するようにする

        private function onMouseOverButton(e:MouseEvent):void {

            _towerStatus.update(Tower(e.target));

            _towerStatus.visible = true;

        }

        private function onMouseOutButton(e:MouseEvent):void {

            _towerStatus.visible = false;

        }

    }

//}

/* ----------------------------------------------------------------------------------------------------------------------

 * TowerStatusWindow

 * -----------------------------------------------------------------------------------------------------------------------

 */

//package {

    import com.bit101.components.PushButton;

    import flash.display.Sprite;

    import flash.events.MouseEvent;

    import flash.text.TextField;

    import flash.ui.Keyboard;

    

    //public 

    class TowerStatusWindow extends Sprite {

        private var _towerName:TextField;

        private var _attlibuteGround:TextField;

        private var _attlibuteAir:TextField;

        private var _attlibuteSlow:TextField;

        private var _attlibuteSplash:TextField;

        private var _level:TextField;

        private var _cost:TextField;

        private var _damage:TextField;

        private var _range:TextField;

        private var _firerate:TextField;

        

        private var _upgradeButton:PushButton;

        private var _sellButton:PushButton;

        

        private static const LIGHTEN:uint = 0xffffff;

        private static const DARKEN:uint = 0x606060;

        

        public function TowerStatusWindow(posx:int, posy:int):void {

            x = posx;

            y = posy;

            

            addChild(ImageFactory.createWindow(0, 0, 90, 250));

            addChild(ImageFactory.createWindow(0, 0, 90, 20));

            addChild(_towerName = ImageFactory.createSimpleText(0, 0, 90, 20, "Tower", 12, 0xffffff));

            addChild(_attlibuteGround = ImageFactory.createSimpleText(5, 25, 40, 10, "Ground", 10, TowerStatusWindow.LIGHTEN));

            addChild(_attlibuteAir = ImageFactory.createSimpleText(5, 35, 40, 10, "Air", 10, TowerStatusWindow.LIGHTEN));

            addChild(_attlibuteSlow = ImageFactory.createSimpleText(45, 25, 40, 10, "Slow", 10, TowerStatusWindow.DARKEN));

            addChild(_attlibuteSplash = ImageFactory.createSimpleText(45, 35, 40, 10, "Splash", 10, TowerStatusWindow.DARKEN));

            addChild(ImageFactory.createBorderedText(0, 50, 90, 10, "Level", 10, 0xffffff, 0x00b000));

            addChild(_level = ImageFactory.createStatusText(0, 62, 90, 15, "E", 14, 0x00ff00));

            addChild(ImageFactory.createBorderedText(0, 80, 90, 10, "Cost", 10, 0xffffff, 0xb0b000));

            addChild(_cost = ImageFactory.createStatusText(0, 92, 90, 15, "0", 14, 0xffff00));

            addChild(ImageFactory.createBorderedText(0, 110, 90, 10, "Damage", 10, 0xffffff, 0xf00000));

            addChild(_damage = ImageFactory.createStatusText(0, 122, 90, 15, "0", 14, 0xff0000));

            addChild(ImageFactory.createBorderedText(0, 140, 90, 10, "Range", 10, 0xffffff, 0x00b0b0));

            addChild(_range = ImageFactory.createStatusText(0, 152, 90, 15, "0", 14, 0x00ffff));

            addChild(ImageFactory.createBorderedText(0, 170, 90, 10, "FireRate", 10, 0xffffff, 0xb000b0));

            addChild(_firerate = ImageFactory.createStatusText(0, 182, 90, 15, "0", 14, 0xff00ff));

            

            _upgradeButton = new PushButton(this, 5, 204, "Upgrade (U)", upgradeSelectedTower);

            _sellButton = new PushButton(this, 5, 225, "Sell (S)", sellSelectedTower);

            _upgradeButton.width = _sellButton.width = 80;

            

            // 各ボタンに対応するホットキーを設定する

            HotKey.bind(Keyboard.U, upgradeSelectedTower);

            HotKey.bind(Keyboard.S, sellSelectedTower);

        }

        

        private function upgradeSelectedTower(e:MouseEvent = null):void {

            var selectedTower:Tower = GameData.instance.selectedTower;

            if (selectedTower != null && selectedTower.active) {

                selectedTower.upgrade();

            }

        }

        

        private function sellSelectedTower(e:MouseEvent = null):void {

            var selectedTower:Tower = GameData.instance.selectedTower;

            if (selectedTower != null && selectedTower.active) {

                selectedTower.sell();

            }

        }

        

        // 引数で与えたタワーのステータス表示に更新する

        public function update(tower:Tower):void {

            var type:TowerType = tower.type;

            _towerName.text = type.name + " Tower";

            _attlibuteGround.textColor = (type.ground ? TowerStatusWindow.LIGHTEN : TowerStatusWindow.DARKEN);

            _attlibuteAir.textColor = (type.air ? TowerStatusWindow.LIGHTEN : TowerStatusWindow.DARKEN);

            _attlibuteSlow.textColor = (type.slow ? TowerStatusWindow.LIGHTEN : TowerStatusWindow.DARKEN);

            _attlibuteSplash.textColor = (type.splash ? TowerStatusWindow.LIGHTEN : TowerStatusWindow.DARKEN);

            

            var status:TowerStatus = type.getStatus(tower.level);

            _level.text = tower.level.toString();

            _cost.text = status.cost.toString();

            _damage.text = status.damage.toString();

            _range.text = status.range.toString();

            _firerate.text = status.firerate.toFixed(1);

            

            // タワーが選択ボタンのものならボタンを隠して、処理を終える

            if (!tower.active) {

                _upgradeButton.visible = _sellButton.visible = false;

                return;

            }

            

            // アップグレード可能か、アップグレード時の上昇値はいくつかを表示に反映させる

            _upgradeButton.visible = _sellButton.visible = true;

            updateUpgradeButton();

            if (type.hasStatus(tower.level + 1)) {

                var nextStatus:TowerStatus = type.getStatus(tower.level + 1);

                _level.appendText(" → " + nextStatus.level);

                _cost.appendText(" + " + (nextStatus.cost - status.cost));

                if (nextStatus.damage - status.damage > 0) { _damage.appendText(" + " + (nextStatus.damage - status.damage)); }

                if (nextStatus.range - status.range > 0) { _range.appendText(" + " + (nextStatus.range - status.range)); }

                if (nextStatus.firerate - status.firerate >= 0.09) { _firerate.appendText(" + " + (nextStatus.firerate - status.firerate).toFixed(1)); }

                else if (nextStatus.firerate - status.firerate < 0) { _firerate.appendText(" - " + Math.abs(nextStatus.firerate - status.firerate).toFixed(1)); }

            }

        }

        

        public function updateUpgradeButton():void {

            var selectedTower:Tower = GameData.instance.selectedTower;

            if (selectedTower == null || !selectedTower.active || !selectedTower.type.hasStatus(selectedTower.level + 1)) {

                _upgradeButton.enabled = false;

                return;

            }

            

            var upgradeCost:int = selectedTower.type.getStatus(selectedTower.level + 1).cost - selectedTower.type.getStatus(selectedTower.level).cost;

            _upgradeButton.enabled = (GameData.instance.gold >= upgradeCost) ? true : false;

        }

    }

//}

/* ----------------------------------------------------------------------------------------------------------------------

 * WaveManager

 * -----------------------------------------------------------------------------------------------------------------------

 */

//package {

    import flash.display.Bitmap;

    import flash.display.BitmapData;

    import flash.display.Sprite;

    import flash.geom.Rectangle;

    

    //public 

    class WaveManager extends Sprite {

        private var _currentWave:int;

        private var _waves:Vector.<Wave>;

        private var _onScreen:BitmapData;

        private var _offScreen:Sprite;

        private var _offScreenPosX:int;

        private var _nextWaveCount:int;

        

        public function finished():Boolean { return _currentWave == _waves.length; }

        

        public function WaveManager(posx:int, posy:int, world:World) {

            x = posx;

            y = posy;

            

            _waves = Config.getWaves();

            _offScreen = new Sprite();

            for (var i:int = 0; i < _waves.length; i++) {

                var wave:Wave = _waves[i];

                wave.x = i * Const.WAVE_WIDTH;

                wave.setWorld(world);

                _offScreen.addChild(wave);

            }

            createWaveManagementWindow();

            initialize();

        }

        

        public function initialize():void {

            _currentWave = 0;

            _offScreenPosX = 301;

            _offScreen.x = _offScreenPosX / 10;

            updateOnScreen();

            _nextWaveCount = 1;

            

            for (var i:int = 0; i < _waves.length; i++) {

                _waves[i].initialize();

            }

        }

        

        private function updateOnScreen():void {

            _onScreen.fillRect(ONSCREEN_RECT, 0x000000);

            _onScreen.draw(_offScreen, _offScreen.transform.matrix);

        }

        

        private function createWaveManagementWindow():void {

            var window:Sprite = addChild(ImageFactory.createWindow(-10, 0, 475, 50)) as Sprite;

            _onScreen = new BitmapData(365, 30, false, 0x000000);

            var bitmap:Bitmap = window.addChild(new Bitmap(_onScreen)) as Bitmap;

            bitmap.x = bitmap.y = 10;

            

            var line:Sprite = window.addChild(new Sprite()) as Sprite;

            line.graphics.lineStyle(1, 0xffff00);

            line.graphics.moveTo(40, 2);

            line.graphics.lineTo(40, 48);

        }

        

        private static const ONSCREEN_RECT:Rectangle = new Rectangle(0, 0, 365, 30);

        

        public function update():void {

            _offScreenPosX--;

            if (_offScreenPosX % 10 == 0) { _offScreen.x = _offScreenPosX / 10; }

            updateOnScreen();

            

            _nextWaveCount--;

            if (_nextWaveCount == 0) {

                sendNextWave();

            }

            

            for (var i:int = 0; i < _waves.length; i++) {

                _waves[i].update();

            }

        }

        

        public function sendNextWave():void {

            // 全てのWaveが送出されていたら何もせずに終了する

            if (_currentWave == _waves.length) { return; }

            

            _currentWave++;

            GameData.instance.wave = _currentWave;

            _waves[_currentWave - 1].send();

            

            // 先送りしていたら、その分をスコアに加算する

            GameData.instance.score += int(_nextWaveCount / 10);

            _offScreenPosX -= _nextWaveCount;

            _offScreen.x = _offScreenPosX / 10;

            updateOnScreen();

            _nextWaveCount = Const.WAVE_INTERVAL;

        }

    }

//}

/* ----------------------------------------------------------------------------------------------------------------------

 * Wave

 * -----------------------------------------------------------------------------------------------------------------------

 */

//package {

    import flash.display.Sprite;

    import flash.utils.Dictionary;

    

    //public 

    class Wave extends Sprite {

        private var _world:World;

        private var _number:int;            // Wave数

        

        private var _enemyType:EnemyType;    // 敵のタイプ

        private var _enemyHP:int;            // 敵のHP

        private var _enemyPoint:int;        // 敵を倒した時のポイント

        private var _enemyMoney:int;        // 敵を倒した時の入手金

        private var _enemyNum:Vector.<int>;    // 出現する敵の数

        

        private var _isActivated:Boolean;        // 送出中かどうか

        private var _allEnemiesSent:Boolean;     // 全ての敵が送出されたかどうか

        private var _spawnInterval:int;            // 敵の出現間隔

        private var _spawnCount:int;            // 出現カウント

        private var _waitingNum:Vector.<int>;    // 待機中の敵の数

        

        public function setWorld(value:World):void { _world = value; }

        

        public function Wave(number:int, type:EnemyType, HP:int, point:int, money:int, numSpawning:Vector.<int>) {

            _number = number

            _enemyType = type;

            _enemyHP = HP;

            _enemyPoint = point;

            _enemyMoney = money;

            _enemyNum = numSpawning;

            

            initialize();

            var max:int = 0;

            for (var i:int = 0; i < _enemyNum.length; i++) {

                if (_enemyNum[i] > max) { max = _enemyNum[i]; }

            }

            _spawnInterval = int((Const.WAVE_INTERVAL / 4) / max);

            _spawnCount = 0;

            _waitingNum = new Vector.<int>(_enemyNum.length);

            

            draw();

        }

        

        public function initialize():void {

            _isActivated = false;

            _allEnemiesSent = false;

        }

        

        private function draw():void {

            var bodyColor:uint, borderColor:uint;

            switch(_enemyType.name) {

                case "Immune": { bodyColor = 0xcc00cc; borderColor = 0x660066; break; }

                case "Fast":   { bodyColor = 0x4444ff; borderColor = 0x222288; break; }

                case "Flying": { bodyColor = 0xffcc00; borderColor = 0x886600; break; }

                default:       { bodyColor = 0xff0000; borderColor = 0x880000; break; }

            }

            

            graphics.lineStyle(1, borderColor);

            graphics.beginFill(bodyColor);

            graphics.drawRect(0.5, 0, Const.WAVE_WIDTH - 1, 30);

            graphics.endFill();

            

            addChild(ImageFactory.createStatusText(0, 0, Const.WAVE_WIDTH, 30, _number.toString(), 30, borderColor));

            addChild(ImageFactory.createBorderedText(0, 0, Const.WAVE_WIDTH, 30, _enemyType.name, 12, 0xffffff, borderColor));

            cacheAsBitmap = true;

        }

        

        // 敵の送出を開始する

        public function send():void {

            _isActivated = true;

            _spawnCount = 0;

            for (var i:int = 0; i < _enemyNum.length; i++) {

                _waitingNum[i] = _enemyNum[i];

            }

        }

        

        public function update():void {

            if (!_isActivated || _allEnemiesSent) { return; }

            

            // カウントを進める

            _spawnCount--;

            if (_spawnCount > 0) { return; }

            _spawnCount = _spawnInterval;

            

            // カウントが0になったら、敵を出現させる

            _allEnemiesSent = true;

            for (var i:int = 0; i < _waitingNum.length; i++) {

                if (_waitingNum[i] > 0) {

                    _waitingNum[i]--;

                    _allEnemiesSent = false;

                    

                    var enemy:Enemy = new Enemy(_world.getStartingPos(i), _enemyType, _enemyHP, _enemyPoint, _enemyMoney, _world);

                    _world.addEnemy(enemy);

                }

            }

        }

    }

//}

/* ----------------------------------------------------------------------------------------------------------------------

 * GameData

 * -----------------------------------------------------------------------------------------------------------------------

 */

//package {

    import flash.events.Event;

    import flash.events.EventDispatcher;

    

    //public 

    class GameData extends EventDispatcher {

        private var _wave:int;

        private var _score:int;

        private var _lives:int;

        private var _gold:int;

        private var _selectedTower:Tower;

        private var _sellingRatio:Number;

        

        public function get wave():int { return _wave; }

        public function get score():int { return _score; }

        public function get lives():int { return _lives; }

        public function get gold():int { return _gold; }

        public function get selectedTower():Tower { return _selectedTower; }

        public function get sellingRatio():Number { return _sellingRatio; }

        

        public function set wave(value:int):void { _wave = value; dispatchEvent(new Event(Const.EVENT_CHANGE_GAMESTATUS)); }

        public function set score(value:int):void { _score = value; dispatchEvent(new Event(Const.EVENT_CHANGE_GAMESTATUS)); }

        public function set lives(value:int):void { _lives = Math.max(0, value); dispatchEvent(new Event(Const.EVENT_CHANGE_GAMESTATUS)); }

        public function set gold(value:int):void { _gold = value; dispatchEvent(new Event(Const.EVENT_CHANGE_GAMESTATUS)); }

        public function set selectedTower(value:Tower):void { _selectedTower = value; dispatchEvent(new Event(Const.EVENT_CHANGE_SELECTEDTOWER)); }

        public function set sellingRatio(value:Number):void { _sellingRatio = value; }

        

        private static var _instance:GameData = null;

        public static function get instance():GameData {

            if (GameData._instance == null) { _instance = new GameData(new SingletonEnforcer()); }

            return _instance;

        }

        public function GameData(enforcer:SingletonEnforcer) { initialize(); }

        

        public function initialize():void {

            _wave = 0;

            _score = 0;

            _lives = Config.INITIAL_LIVES;

            _gold = Config.INITIAL_GOLD;

            _selectedTower = null;

            _sellingRatio = 1;

            

            dispatchEvent(new Event(Const.EVENT_CHANGE_GAMESTATUS));

            dispatchEvent(new Event(Const.EVENT_CHANGE_SELECTEDTOWER));

        }

    }

//}

internal class SingletonEnforcer { }

/* ----------------------------------------------------------------------------------------------------------------------

 * Const

 * -----------------------------------------------------------------------------------------------------------------------

 */

//package {

    //public 

    class Const {

        public static const FRAME_RATE:int = 30;    // フレームレート

        public static const NODE_SIZE:int = 16;        // ノードの大きさ

        public static const NODE_COLS:int = 24;        // ノードの列数

        public static const NODE_ROWS:int = 26;        // ノードの行数

        public static const TOWER_SIZE:int = 32;    // タワーの大きさ

        public static const BULLET_SIZE:int = 8;    // 弾の大きさ

        public static const ENEMY_SIZE:int = 16;    // 敵の大きさ

        

        public static const SLOWING_DURATION:int = 5 * Const.FRAME_RATE;

        public static const WAVE_INTERVAL:int = 20 * Const.FRAME_RATE;

        public static const WAVE_WIDTH:int = int(WAVE_INTERVAL / 10);

        public static const CURSOR_OFFSET:int = Const.NODE_SIZE / 2;

        

        public static const EVENT_CHANGE_GAMESTATUS:String = "change_gamestatus";

        public static const EVENT_CHANGE_SELECTEDTOWER:String = "change_selectedtower";

        

        //availableFonts["Aqua","Azuki","Cinecaption","Mona","Sazanami","YSHandy","VLGothic","IPAGP","IPAM","UmeUgo","UmePms","Bebas"]

        public static const EMBED_FONT:String = "IPAGP";

        public static const USE_FONTLOADER:Boolean = true;

    }

//}

/* ----------------------------------------------------------------------------------------------------------------------

 * HotKey

 * -----------------------------------------------------------------------------------------------------------------------

 */

//package {

    import flash.display.Stage;

    import flash.events.KeyboardEvent;

    

    //public 

    class HotKey {

        private static var _keymap:Vector.<Function> = new Vector.<Function>(256, true);

        private static var _stage:Stage;

        

        public static function setStage(value:Stage):void { HotKey._stage = value; }

        public static function enable():void { HotKey._stage.addEventListener(KeyboardEvent.KEY_DOWN, HotKey.onKeyDown); _stage.focus = null; }

        public static function disable():void { HotKey._stage.removeEventListener(KeyboardEvent.KEY_DOWN, HotKey.onKeyDown); }

        

        // 押したキーに処理が割り当てられていたら、それを実行する

        private static function onKeyDown(e:KeyboardEvent):void {

            var command:Function = HotKey._keymap[e.keyCode];

            if (command != null) { command(); }    // command.execute

            _stage.focus = null;

        }

        

        // keyCodeに対応するキーに、commandで指定した処理を割り当てる

        public static function bind(keyCode:uint, command:Function):void {

            HotKey._keymap[keyCode] = command;

        }

    }

//}

/* ----------------------------------------------------------------------------------------------------------------------

 * Preloader

 * -----------------------------------------------------------------------------------------------------------------------

 */

//package {

    import com.bit101.components.ProgressBar;

    import flash.display.DisplayObjectContainer;

    import flash.events.Event;

    import flash.events.EventDispatcher;

    import flash.utils.Dictionary;

    import net.wonderfl.utils.FontLoader;

    

    //public 

    class Preloader extends EventDispatcher {

        private var _assetsNum:int;                // 読み込むアセットの数

        private var _loadedNum:int;                // 読み込み完了したアセットの数

        private var _progressBar:ProgressBar;    // プログレスバー

        

        private var _imageLoaders:Dictionary;    // 画像のローダを保持する連想配列

        private var refImageHolder:Dictionary;    // 画像を保持する連想配列の参照

        

        public function Preloader(parentOfProgressBar:DisplayObjectContainer, imagePaths:Dictionary, imageHolder:Dictionary) {

            _assetsNum = _loadedNum = 0;

            _progressBar = new ProgressBar(parentOfProgressBar, 182, 227);

            

            startLoadingImages(imagePaths, imageHolder);

            if (Const.USE_FONTLOADER) { startLoadingFont(); }

            

            _progressBar.maximum = _assetsNum;

        }

        

        // 外部画像の読み込みを開始する

        private function startLoadingImages(imagePaths:Dictionary, imageHolder:Dictionary):void {

            _imageLoaders = new Dictionary();

            refImageHolder = imageHolder;

            

            for (var imageName:String in imagePaths) {

                _assetsNum++;

                var imageLoader:ExternalImageLoader = new ExternalImageLoader();

                _imageLoaders[imageName] = imageLoader;

                imageLoader.addEventListener(Event.COMPLETE, assetLoaded);

                imageLoader.load(imagePaths[imageName]);

            }

        }

        

        // フォントの読み込みを開始する

        private function startLoadingFont():void {

            _assetsNum++;

            

            var fontLoader:FontLoader = new FontLoader();

            fontLoader.addEventListener(Event.COMPLETE, assetLoaded);

            fontLoader.load(Const.EMBED_FONT);

        }

        

        // 一つのアセットの読み込みが完了した際に呼ばれるメソッド

        private function assetLoaded(e:Event):void {

            e.target.removeEventListener(Event.COMPLETE, assetLoaded);

            

            _loadedNum++;

            _progressBar.value = _loadedNum;

            checkLoadComplete();

        }

        

        // 読み込んだアセット数を調べ、全て読み込んでいたら完了イベントを通知する

        private function checkLoadComplete():void {

            if (_loadedNum == _assetsNum) {

                for (var imageName:String in _imageLoaders) {

                    refImageHolder[imageName] = _imageLoaders[imageName].content;

                }

                

                _progressBar.parent.removeChild(_progressBar);

                dispatchEvent(new Event(Event.COMPLETE));

            }

        }

    }

//}

/* ----------------------------------------------------------------------------------------------------------------------

 * ExternalImageLoader

 * -----------------------------------------------------------------------------------------------------------------------

 */

//package {

    import flash.display.BitmapData;

    import flash.display.Loader;

    import flash.events.Event;

    import flash.events.EventDispatcher;

    import flash.net.URLRequest;

    import flash.system.LoaderContext;

    

    //public 

    class ExternalImageLoader extends EventDispatcher {

        private var _content:BitmapData;

        private var _tempA:Loader;

        private var _tempB:Loader;

        

        public function get content():BitmapData { return _content; }

        

        public function ExternalImageLoader() {

            _content = null; _tempA = new Loader(); _tempB = new Loader();

        }

        

        public function load(url:String):void {

            _tempA.contentLoaderInfo.addEventListener(Event.INIT, tempALoaded);

            _tempA.load(new URLRequest(url), new LoaderContext(true));

        }

        

        private function tempALoaded(e:Event):void {

            _tempA.contentLoaderInfo.removeEventListener(Event.INIT, tempALoaded);

            _content = new BitmapData(int(_tempA.width), int(_tempA.height), true, 0x00ffffff);

            _tempB.contentLoaderInfo.addEventListener(Event.INIT, tempBLoaded);

            _tempB.loadBytes(_tempA.contentLoaderInfo.bytes);

        }

        

        private function tempBLoaded(e:Event):void {

            _tempB.contentLoaderInfo.removeEventListener(Event.INIT, tempBLoaded);

            _content.draw(_tempB); _tempA.unload(); _tempB.unload();

            dispatchEvent(new Event(Event.COMPLETE));

        }

    }

//}

/* ----------------------------------------------------------------------------------------------------------------------

 * TileBasedPoint

 * -----------------------------------------------------------------------------------------------------------------------

 */

//package {

    //public 

    class TileBasedPoint {

        private static const TILE_SIZE:int = Const.NODE_SIZE;

        private static const TILE_COLS:int = Const.NODE_COLS;

        private static const TILE_ROWS:int = Const.NODE_ROWS;

        

        // ワールド座標

        private var _worldX:Number;

        private var _worldY:Number;

        // タイル座標

        private var _tileX:int;

        private var _tileY:int;

        

        private var _isInvalidWorldPos:Boolean;    // ワールド座標値を更新する必要があるかどうか

        private var _isInvalidTilePos:Boolean;    // タイル座標値を更新する必要があるかどうか

        

        // Creation Method でインスタンスを生成する

        public static function createFromWorldPos(x:Number, y:Number):TileBasedPoint { return new TileBasedPoint(true, x, y, 0, 0); }

        public static function createFromTilePos(x:int, y:int):TileBasedPoint { return new TileBasedPoint(false, 0, 0, x, y); }

        public function TileBasedPoint(isWorldPos:Boolean, worldx:Number, worldy:Number, tilex:int, tiley:int) {

            _worldX = worldx;

            _worldY = worldy;

            _tileX = tilex;

            _tileY = tiley;

            _isInvalidWorldPos = !isWorldPos;

            _isInvalidTilePos = isWorldPos;

        }

        

        private function updateWorldPos():void {

            _worldX = Math.max(0, Math.min(_tileX * TILE_SIZE, (TILE_COLS - 1) * TILE_SIZE));

            _worldY = Math.max(0, Math.min(_tileY * TILE_SIZE, (TILE_ROWS - 1) * TILE_SIZE));

        }

        

        private function updateTilePos():void {

            _tileX = Math.max(0, Math.min(int(_worldX / TILE_SIZE), TILE_COLS - 1));

            _tileY = Math.max(0, Math.min(int(_worldY / TILE_SIZE), TILE_ROWS - 1));

        }

        

        // ワールド座標を有効にする

        private function validateWorldPos():void {

            if (_isInvalidWorldPos) {

                updateWorldPos();

                _isInvalidWorldPos = false;

            }

        }

        

        // タイル座標を有効にする

        private function validateTilePos():void {

            if (_isInvalidTilePos) {

                updateTilePos();

                _isInvalidTilePos = false;

            }

        }

        

        public function get x():Number { validateWorldPos(); return _worldX; }

        public function get y():Number { validateWorldPos(); return _worldY; }

        public function get tileX():int { validateTilePos(); return _tileX; }

        public function get tileY():int { validateTilePos(); return _tileY; }

        

        public function set x(value:Number):void { validateWorldPos(); _worldX = value; _isInvalidTilePos = true; }

        public function set y(value:Number):void { validateWorldPos(); _worldY = value; _isInvalidTilePos = true; }

        public function setWorldPos(x:Number, y:Number):void { validateWorldPos(); _worldX = x; _worldY = y; _isInvalidTilePos = true; }

        public function set tileX(value:int):void { validateTilePos(); _tileX = value; _isInvalidWorldPos = true; }

        public function set tileY(value:int):void { validateTilePos(); _tileX = value; _isInvalidWorldPos = true; }

        public function setTilePos(x:int, y:int):void { validateTilePos(); _tileX = x; _tileY = y; _isInvalidWorldPos = true; }

    }

//}

/* ----------------------------------------------------------------------------------------------------------------------

 * ImageFactory

 * -----------------------------------------------------------------------------------------------------------------------

 */

//package {

    import flash.display.BitmapData;

    import flash.display.GradientType;

    import flash.display.Sprite;

    import flash.filters.BevelFilter;

    import flash.filters.GlowFilter;

    import flash.geom.Matrix;

    import flash.text.AntiAliasType;

    import flash.text.TextField;

    import flash.text.TextFieldAutoSize;

    import flash.text.TextFormat;

    import flash.utils.Dictionary;

    

    //public 

    class ImageFactory {

        private static var _images:Dictionary;    // 画像を保持する連想配列

        

        public static function getImage(imageName:String):BitmapData {

            return ImageFactory._images[imageName];

        }

        

        // 外部画像のパスをまとめた配列を取得する

        public static function getExternalImagePaths():Dictionary {

            var imagePaths:Dictionary = new Dictionary();

            

            imagePaths["World"] = "http://assets.wonderfl.net/images/related_images/5/5f/5ff7/5ff7c989b7a1068c50a7008713931f651804468c";

            imagePaths["Arrow"] = "http://assets.wonderfl.net/images/related_images/9/90/90ce/90ce1e3f8938a517e1c47d21113e102e5b6a57b0";

            imagePaths["Gatling"] = "http://assets.wonderfl.net/images/related_images/7/70/701a/701ad9fbaa38a010a872676925fac4af33693305";

            imagePaths["Bomb"] = "http://assets.wonderfl.net/images/related_images/a/ab/ab27/ab270254fb43e2f47b8c1229c6e2e951b92de429";

            imagePaths["Missile"] = "http://assets.wonderfl.net/images/related_images/4/43/43f8/43f8fa0a40ee6080d29f0ef32e2c6b7600ce7c7f";

            imagePaths["Frost"] = "http://assets.wonderfl.net/images/related_images/8/86/8639/8639eba0d7f33aa2d1dcc1eb78165ffbb8872f29";

            imagePaths["Vortex"] = "http://assets.wonderfl.net/images/related_images/0/01/016d/016d884b33e407fbcd6cb9df98a117b4cf7550fb";

            imagePaths["Laser"] = "http://assets.wonderfl.net/images/related_images/f/f5/f508/f5082ab1d35ab1b1ee2e74669fe9aaaf923ae9c9";

            

            return imagePaths;

        }

        

        // (内部で作成できる)画像の読み込みを行う

        public static function load():Dictionary {

            ImageFactory._images = new Dictionary();

            

            ImageFactory._images["Base"] = ImageFactory.createTowerBase();

            ImageFactory._images["Cancel"] = ImageFactory.createCancelMark();

            ImageFactory._images["Normal"] = ImageFactory.createNormalEnemy(0xff0000, 0x880000);

            ImageFactory._images["Immune"] = ImageFactory.createNormalEnemy(0xcc00cc, 0x660066);

            ImageFactory._images["Fast"] = ImageFactory.createNormalEnemy(0x4444ff, 0x222288);

            ImageFactory._images["Flying"] = ImageFactory.createFlyingEnemy(0xffcc00, 0x886600);

            

            ImageFactory._images["Bullet_Arrow"] = ImageFactory.createArrowBullet();

            ImageFactory._images["Bullet_Gatling"] = ImageFactory.createNormalBullet(2);

            ImageFactory._images["Bullet_Bomb"] = ImageFactory.createNormalBullet(3);

            ImageFactory._images["Bullet_Missile"] = ImageFactory.createMissileBullet();

            ImageFactory._images["Bullet_Frost"] = ImageFactory.createNormalBullet(2);

            ImageFactory._images["Bullet_Vortex"] = ImageFactory.createNormalBullet(1);

            ImageFactory._images["Bullet_Laser"] = ImageFactory.createArrowBullet();

            

            ImageFactory._images["Coin"] = ImageFactory.createCoin();

            

            return ImageFactory._images;

        }

        

        // タワーの土台の画像を作成する

        private static function createTowerBase():BitmapData {            

            var bitmapData:BitmapData = new BitmapData(32, 32);

            var sprite:Sprite = new Sprite();

            sprite.graphics.beginFill(0xc0c0c0);

            sprite.graphics.drawRect(0, 0, 32, 32);

            sprite.graphics.endFill();

            sprite.filters = [new BevelFilter(1, 45, 0xffffff, 1, 0x606060, 1, 8, 8, 255)];

            bitmapData.draw(sprite);

            return bitmapData;

        }

        

        // キャンセルマークの画像を作成する

        private static function createCancelMark():BitmapData {

            var bitmapData:BitmapData = new BitmapData(32, 32, true, 0x00ffffff);

            var sprite:Sprite = new Sprite();

            sprite.graphics.lineStyle(4, 0xff0000);

            sprite.graphics.drawCircle(16, 16, 12);

            sprite.graphics.moveTo(16 + (12 * Math.cos(9 * Math.PI / 8)), 16 + (12 * Math.sin(9 * Math.PI / 8)));

            sprite.graphics.lineTo(16 + (12 * Math.cos(Math.PI / 8)), 16 + (12 * Math.sin(Math.PI / 8)));

            bitmapData.draw(sprite);

            return bitmapData;

        }

        

        // Normal タイプの敵の画像を作成する

        private static function createNormalEnemy(bodyColor:uint, borderColor:uint):BitmapData {

            var bitmapData:BitmapData = new BitmapData(16, 16, true, 0x00ffffff);

            var sprite:Sprite = new Sprite();

            sprite.graphics.beginFill(bodyColor);

            sprite.graphics.drawRect(4, 2, 4, 12);

            sprite.graphics.drawRect(8, 6, 4, 4);

            sprite.graphics.endFill();

            sprite.filters = [new GlowFilter(borderColor, 1, 2, 2, 255)];

            bitmapData.draw(sprite);

            return bitmapData;

        }

        

        // Flying タイプの敵の画像を作成する

        private static function createFlyingEnemy(bodyColor:uint, borderColor:uint):BitmapData {

            var bitmapData:BitmapData = new BitmapData(16, 16, true, 0x00ffffff);

            var sprite:Sprite = new Sprite();

            sprite.graphics.lineStyle(0, borderColor, 1, true);

            sprite.graphics.beginFill(bodyColor);

            sprite.graphics.moveTo(2, 4);

            sprite.graphics.lineTo(14, 8);

            sprite.graphics.lineTo(2, 12);

            sprite.graphics.lineTo(2, 4);

            bitmapData.draw(sprite);

            return bitmapData;

        }

        

        private static function createArrowBullet():BitmapData {

            var bitmapData:BitmapData = new BitmapData(8, 8, true, 0x00ffffff);

            var sprite:Sprite = new Sprite();

            sprite.graphics.lineStyle(2);

            sprite.graphics.moveTo(1, 4);

            sprite.graphics.lineTo(7, 4);

            bitmapData.draw(sprite);

            return bitmapData;

        }

        

        // 普通の弾の画像を作成する

        private static function createNormalBullet(radius:int):BitmapData {

            var bitmapData:BitmapData = new BitmapData(8, 8, true, 0x00ffffff);

            var sprite:Sprite = new Sprite();

            sprite.graphics.beginFill(0x000000);

            sprite.graphics.drawCircle(4, 4, radius);

            bitmapData.draw(sprite);

            return bitmapData;

        }

        

        private static function createMissileBullet():BitmapData {

            var bitmapData:BitmapData = new BitmapData(8, 8, true, 0x00ffffff);

            var sprite:Sprite = new Sprite();

            sprite.graphics.beginFill(0x000000);

            sprite.graphics.drawCircle(3, 2, 2);

            sprite.graphics.drawCircle(3, 6, 2);

            sprite.graphics.drawCircle(5, 4, 2);

            bitmapData.draw(sprite);

            return bitmapData;

        }

        

        // エフェクトのコインの画像を作成する

        private static function createCoin():BitmapData {

            var bitmapData:BitmapData = new BitmapData(8, 8, true, 0x00ffffff);

            var sprite:Sprite = new Sprite();

            sprite.graphics.beginFill(0xcccc00);

            sprite.graphics.drawEllipse(2, 1, 4, 6);

            sprite.graphics.endFill();

            sprite.filters = [new BevelFilter(1, 45, 0xffffff, 1, 0x000000, 1, 0, 0)];

            bitmapData.draw(sprite);

            return bitmapData;

        }

        

        // 普通のテキストを作成する

        public static function createSimpleText(posx:int, posy:int, width:int, height:int, text:String, fontSize:int, textColor:uint):TextField {

            return ImageFactory.createText(posx, posy, width, height, text, fontSize, textColor, TextFieldAutoSize.CENTER);

        }

        

        // 縁取り付きのテキストを作成する

        public static function createBorderedText(posx:int, posy:int, width:int, height:int, text:String, fontSize:int, textColor:uint, borderColor:uint):TextField {

            return ImageFactory.createText(posx, posy, width, height, text, fontSize, textColor, TextFieldAutoSize.CENTER, false, true, borderColor);

        }

        

        // スコア表示用のテキストを作成する

        public static function createScoreText(posx:int, posy:int, width:int, height:int, text:String, fontSize:int, textColor:uint):TextField {

            return ImageFactory.createText(posx, posy, width, height, text, fontSize, textColor, TextFieldAutoSize.RIGHT, true);

        }

        

        // タワーのステータス表示用テキストを作成する

        public static function createStatusText(posx:int, posy:int, width:int, height:int, text:String, fontSize:int, textColor:uint):TextField {

            return ImageFactory.createText(posx, posy, width, height, text, fontSize, textColor, TextFieldAutoSize.CENTER, true);

        }

        

        private static function createText(posx:int, posy:int, width:int, height:int, text:String, fontSize:int, textColor:uint, autoSize:String, bold:Boolean = false, border:Boolean = false, borderColor:uint = 0x000000, embed:Boolean = false):TextField {

            var textField:TextField = new TextField();

            if (embed) {

                textField.embedFonts = true;

                textField.antiAliasType = AntiAliasType.ADVANCED;

            }

            var font:String = embed ? Const.EMBED_FONT : "Arial";

            textField.defaultTextFormat = new TextFormat(font, fontSize, null, bold);

            textField.text = text;

            textField.x = posx;

            textField.y = posy + Math.ceil((height - (textField.textHeight + 4)) / 2);

            textField.width = Math.max(width, textField.textWidth + 4);

            textField.height = textField.textHeight + 4;

            textField.textColor = textColor;

            if (border) { textField.filters = [new GlowFilter(borderColor, 1, 4, 4)]; }

            textField.autoSize = autoSize;

            textField.mouseEnabled = textField.selectable = false;

            return textField;

        }

        

        // 説明用のテキストを作成する

        public static function createInstructionText(posx:int, posy:int, width:int, height:int, text:String, fontSize:int):TextField {

            return ImageFactory.createText(posx, posy, width, height, text, fontSize, 0xffffff, TextFieldAutoSize.LEFT, false, false, 0x000000, Const.USE_FONTLOADER);

        }

        

        // ウインドウを作成する

        public static function createWindow(posx:int, posy:int, width:int, height:int):Sprite {

            var sprite:Sprite = new Sprite();

            sprite.x = posx;

            sprite.y = posy;

            sprite.mouseEnabled = false;

            var matrix:Matrix = new Matrix();

            matrix.createGradientBox(width, height, Math.PI / 2);

            sprite.graphics.lineStyle(2);

            sprite.graphics.lineGradientStyle(GradientType.LINEAR, [0xf0f0f0, 0x808080], [1, 1], [0, 255], matrix);

            sprite.graphics.beginGradientFill(GradientType.LINEAR, [0x303090, 0x000030], [1, 1], [0, 255], matrix);

            sprite.graphics.drawRoundRect(1, 1, width - 2, height - 2, 10);

            sprite.graphics.endFill();

            return sprite;

        }

    }

//}

/* ----------------------------------------------------------------------------------------------------------------------

 * Config

 * -----------------------------------------------------------------------------------------------------------------------

 */

//package {

    import flash.utils.Dictionary;

    

    //public 

    class Config {

        public static const INITIAL_LIVES:int = 20;

        public static const INITIAL_GOLD:int = 100;

        

        public static const NODE_TYPE:Array = [

            [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],

            [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],

            [1,1,0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,1,1],

            [1,0,0,0,0,0,0,0,0,0,2,1,1,2,0,0,0,0,0,0,0,0,0,1],

            [1,0,0,0,0,0,0,0,0,0,2,2,2,2,0,0,0,0,0,0,0,0,0,1],

            [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],

            [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],

            [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],

            [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],

            [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],

            [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],

            [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],

            [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],

            [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],

            [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],

            [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],

            [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],

            [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],

            [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],

            [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],

            [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],

            [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],

            [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],

            [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],

            [1,1,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,1,1],

            [1,1,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,1,1]

        ];

        

        private static const XML_DATA:XML =

        <root>

            <starts>

                <start x="4"  y="25" width="2" flying="0" />

                <start x="20" y="25" width="2" flying="0" />

                <start x="12" y="25" width="2" flying="1" />

            </starts>

            <goals>

                <goal x="10" y="3" /><goal x="13" y="3" />

                <goal x="10" y="4" /><goal x="11" y="4" />

                <goal x="12" y="4" /><goal x="13" y="4" />

            </goals>

            <tower>

                <type name="Arrow" ground="1" air="1" slow="0" splash="0">

                    <status level="1" cost="8"    damage="10"   range="60"  firerate="1.5" />

                    <status level="2" cost="16"   damage="20"   range="60"  firerate="1.5" />

                    <status level="3" cost="32"   damage="40"   range="60"  firerate="1.5" />

                    <status level="4" cost="64"   damage="80"   range="60"  firerate="1.5" />

                    <status level="5" cost="192"  damage="320"  range="180" firerate="0.8" />

                </type>

                <type name="Gatling" ground="1" air="1" slow="0" splash="0">

                    <status level="1" cost="20"   damage="5"    range="55"  firerate="4.0" />

                    <status level="2" cost="35"   damage="9"    range="60"  firerate="4.3" />

                    <status level="3" cost="65"   damage="17"   range="65"  firerate="4.6" />

                    <status level="4" cost="110"  damage="28"   range="70"  firerate="5.0" />

                    <status level="5" cost="310"  damage="45"   range="80"  firerate="9.9" />

                </type>

                <type name="Bomb" ground="1" air="0" slow="0" splash="1">

                    <status level="1" cost="12"   damage="10"   range="80"  firerate="0.8" />

                    <status level="2" cost="24"   damage="30"   range="88"  firerate="0.8" />

                    <status level="3" cost="52"   damage="60"   range="96"  firerate="0.9" />

                    <status level="4" cost="100"  damage="120"  range="104" firerate="1.0" />

                    <status level="5" cost="240"  damage="300"  range="112" firerate="1.0" />

                </type>        

                <type name="Missile" ground="0" air="1" slow="0" splash="0">

                    <status level="1" cost="25"   damage="15"   range="48"  firerate="3.0" />

                    <status level="2" cost="40"   damage="40"   range="48"  firerate="3.0" />

                    <status level="3" cost="75"   damage="80"   range="64"  firerate="3.0" />

                    <status level="4" cost="130"  damage="150"  range="64"  firerate="3.0" />

                    <status level="5" cost="370"  damage="290"  range="80"  firerate="4.5" />

                </type>        

                <type name="Frost" ground="1" air="1" slow="1" splash="1">

                    <status level="1" cost="50"   damage="10"   range="32"  firerate="1.2" />

                    <status level="2" cost="70"   damage="15"   range="40"  firerate="1.2" />

                    <status level="3" cost="90"   damage="20"   range="48"  firerate="1.2" />

                    <status level="4" cost="120"  damage="25"   range="56"  firerate="1.2" />

                    <status level="5" cost="300"  damage="50"   range="64"  firerate="2.4" />

                </type>        

                <type name="Vortex" ground="1" air="0" slow="0" splash="1">

                    <status level="1" cost="70"   damage="80"   range="42"  firerate="0.6" />

                    <status level="2" cost="130"  damage="150"  range="42"  firerate="0.6" />

                    <status level="3" cost="250"  damage="300"  range="42"  firerate="0.6" />

                    <status level="4" cost="490"  damage="600"  range="42"  firerate="0.7" />

                    <status level="5" cost="990"  damage="990"  range="42"  firerate="1.0" />

                </type>        

                <type name="Laser" ground="1" air="1" slow="0" splash="1">

                    <status level="1" cost="150"  damage="135"  range="80"  firerate="1.1" />

                    <status level="2" cost="270"  damage="255"  range="80"  firerate="1.1" />

                    <status level="3" cost="510"  damage="510"  range="80"  firerate="1.3" />

                    <status level="4" cost="990"  damage="990"  range="80"  firerate="1.5" />

                    <status level="5" cost="2000" damage="2500" range="80"  firerate="1.5" />

                </type>        

            </tower>

            <enemy>

                <type name="Normal" speed="1.0" flying="0" immunity="0" />

                <type name="Immune" speed="0.9" flying="0" immunity="1" />

                <type name="Fast"   speed="1.4" flying="0" immunity="0" />

                <type name="Flying" speed="1.0" flying="1" immunity="0" />

            </enemy>

            <waves>

                <wave type="Normal" HP="20"   point="8"   money="1"  ><spawn num="10" /><spawn num="10" /><spawn num="0"  /></wave>

                <wave type="Normal" HP="25"   point="8"   money="1"  ><spawn num="10" /><spawn num="10" /><spawn num="0"  /></wave>

                <wave type="Normal" HP="30"   point="8"   money="1"  ><spawn num="10" /><spawn num="10" /><spawn num="0"  /></wave>

                <wave type="Immune" HP="40"   point="8"   money="1"  ><spawn num="10" /><spawn num="10" /><spawn num="0"  /></wave>

                <wave type="Fast"   HP="40"   point="8"   money="1"  ><spawn num="10" /><spawn num="10" /><spawn num="0"  /></wave>

                <wave type="Normal" HP="50"   point="8"   money="1"  ><spawn num="10" /><spawn num="10" /><spawn num="0"  /></wave>

                <wave type="Immune" HP="70"   point="8"   money="1"  ><spawn num="10" /><spawn num="10" /><spawn num="0"  /></wave>

                <wave type="Fast"   HP="80"   point="8"   money="1"  ><spawn num="10" /><spawn num="10" /><spawn num="0"  /></wave>

                <wave type="Flying" HP="70"   point="10"  money="2"  ><spawn num="0"  /><spawn num="0"  /><spawn num="15" /></wave>

                <wave type="Normal" HP="400"  point="50"  money="20" ><spawn num="2"  /><spawn num="2"  /><spawn num="0"  /></wave>

                

                <wave type="Normal" HP="100"  point="10"  money="2"  ><spawn num="10" /><spawn num="10" /><spawn num="0"  /></wave>

                <wave type="Normal" HP="110"  point="10"  money="2"  ><spawn num="10" /><spawn num="10" /><spawn num="0"  /></wave>

                <wave type="Immune" HP="150"  point="10"  money="2"  ><spawn num="10" /><spawn num="10" /><spawn num="0"  /></wave>

                <wave type="Fast"   HP="140"  point="10"  money="2"  ><spawn num="10" /><spawn num="10" /><spawn num="0"  /></wave>

                <wave type="Flying" HP="120"  point="12"  money="2"  ><spawn num="0"  /><spawn num="0"  /><spawn num="15" /></wave>

                <wave type="Normal" HP="150"  point="12"  money="2"  ><spawn num="10" /><spawn num="10" /><spawn num="0"  /></wave>

                <wave type="Normal" HP="170"  point="12"  money="2"  ><spawn num="10" /><spawn num="10" /><spawn num="0"  /></wave>

                <wave type="Immune" HP="1000" point="60"  money="25" ><spawn num="2"  /><spawn num="2"  /><spawn num="0"  /></wave>

                <wave type="Flying" HP="150"  point="14"  money="3"  ><spawn num="8"  /><spawn num="8"  /><spawn num="0"  /></wave>

                <wave type="Normal" HP="200"  point="12"  money="3"  ><spawn num="10" /><spawn num="10" /><spawn num="0"  /></wave>

                

                <wave type="Normal" HP="240"  point="12"  money="3"  ><spawn num="10" /><spawn num="10" /><spawn num="0"  /></wave>

                <wave type="Fast"   HP="260"  point="12"  money="3"  ><spawn num="10" /><spawn num="10" /><spawn num="0"  /></wave>

                <wave type="Fast"   HP="300"  point="12"  money="3"  ><spawn num="10" /><spawn num="10" /><spawn num="0"  /></wave>

                <wave type="Flying" HP="3200" point="300" money="120"><spawn num="0"  /><spawn num="0"  /><spawn num="1"  /></wave>

                <wave type="Normal" HP="350"  point="14"  money="4"  ><spawn num="10" /><spawn num="10" /><spawn num="0"  /></wave>

                <wave type="Immune" HP="400"  point="14"  money="4"  ><spawn num="10" /><spawn num="10" /><spawn num="0"  /></wave>

                <wave type="Normal" HP="450"  point="14"  money="4"  ><spawn num="10" /><spawn num="10" /><spawn num="0"  /></wave>

                <wave type="Normal" HP="500"  point="14"  money="4"  ><spawn num="10" /><spawn num="10" /><spawn num="0"  /></wave>

                <wave type="Immune" HP="560"  point="14"  money="4"  ><spawn num="10" /><spawn num="10" /><spawn num="0"  /></wave>

                <wave type="Immune" HP="610"  point="14"  money="4"  ><spawn num="10" /><spawn num="10" /><spawn num="0"  /></wave>

                

                <wave type="Flying" HP="480"  point="14"  money="4"  ><spawn num="0"  /><spawn num="0"  /><spawn num="20" /></wave>

                <wave type="Fast"   HP="4000" point="150" money="80" ><spawn num="1"  /><spawn num="1"  /><spawn num="0"  /></wave>

                <wave type="Normal" HP="650"  point="15"  money="5"  ><spawn num="10" /><spawn num="10" /><spawn num="0"  /></wave>

                <wave type="Normal" HP="750"  point="15"  money="5"  ><spawn num="10" /><spawn num="10" /><spawn num="0"  /></wave>

                <wave type="Immune" HP="900"  point="15"  money="5"  ><spawn num="10" /><spawn num="10" /><spawn num="0"  /></wave>

                <wave type="Flying" HP="800"  point="15"  money="5"  ><spawn num="6"  /><spawn num="6"  /><spawn num="6"  /></wave>

                <wave type="Normal" HP="1000" point="15"  money="5"  ><spawn num="10" /><spawn num="10" /><spawn num="0"  /></wave>

                <wave type="Immune" HP="1800" point="30"  money="10" ><spawn num="5"  /><spawn num="5"  /><spawn num="0"  /></wave>

                <wave type="Fast"   HP="1200" point="15"  money="5"  ><spawn num="10" /><spawn num="10" /><spawn num="0"  /></wave>

                <wave type="Normal" HP="1800" point="20"  money="6"  ><spawn num="20" /><spawn num="20" /><spawn num="0"  /></wave>

                

                <wave type="Fast"   HP="1500" point="15"  money="7"  ><spawn num="20" /><spawn num="0"  /><spawn num="0"  /></wave>

                <wave type="Fast"   HP="1500" point="15"  money="7"  ><spawn num="0"  /><spawn num="20" /><spawn num="0"  /></wave>

                <wave type="Normal" HP="2200" point="15"  money="7"  ><spawn num="10" /><spawn num="10" /><spawn num="0"  /></wave>

                <wave type="Immune" HP="2500" point="15"  money="7"  ><spawn num="10" /><spawn num="10" /><spawn num="0"  /></wave>

                <wave type="Flying" HP="1500" point="15"  money="7"  ><spawn num="0"  /><spawn num="0"  /><spawn num="20" /></wave>

                <wave type="Normal" HP="2800" point="15"  money="8"  ><spawn num="10" /><spawn num="10" /><spawn num="0"  /></wave>

                <wave type="Fast"   HP="3000" point="15"  money="8"  ><spawn num="10" /><spawn num="10" /><spawn num="0"  /></wave>

                <wave type="Immune" HP="18000"point="200" money="100"><spawn num="1"  /><spawn num="1"  /><spawn num="0"  /></wave>

                <wave type="Normal" HP="24000"point="200" money="100"><spawn num="1"  /><spawn num="1"  /><spawn num="0"  /></wave>

                <wave type="Flying" HP="10000"point="200" money="100"><spawn num="1"  /><spawn num="1"  /><spawn num="1"  /></wave>

            </waves>

        </root>;

        

        // スタート地点の配列を返す

        public static function getStarts():Vector.<Start> {

            var value:Vector.<Start> = new Vector.<Start>();

            for each(var s:XML in Config.XML_DATA.starts.*) {

                value.push(new Start(int(s.@x), int(s.@y), int(s.@width), Boolean(int(s.@flying))));

            }

            return value;

        }

        

        // ゴールの配列を返す

        public static function getGoals():Vector.<Goal> {

            var value:Vector.<Goal> = new Vector.<Goal>();

            for each(var g:XML in Config.XML_DATA.goals.*) {

                value.push(new Goal(int(g.@x), int(g.@y)));

            }

            return value;

        }

        

        // タワーの種類の配列を返す

        public static function getTowerType():Vector.<TowerType> {

            var value:Vector.<TowerType> = new Vector.<TowerType>();

            for each(var t:XML in Config.XML_DATA.tower.*) {

                value.push(new TowerType(t.@name, Boolean(int(t.@ground)), Boolean(int(t.@air)), Boolean(int(t.@slow)), Boolean(int(t.@splash))));

            }

            return value;

        }

        

        // 引数で指定した種類の、ステータスの配列を返す

        public static function getTowerStatus(towerTypeName:String):Vector.<TowerStatus> {

            var value:Vector.<TowerStatus> = new Vector.<TowerStatus>();

            for each(var s:XML in Config.XML_DATA.tower.*.(@name == towerTypeName).*) {

                value.push(new TowerStatus(int(s.@level), int(s.@cost), int(s.@damage), int(s.@range), Number(s.@firerate)));

            }

            //value.sortOn("level", Array.NUMERIC);

            return value;

        }

        

        // 敵の種類の連想配列を返す

        public static function getEnemyType():Dictionary {

            var value:Dictionary = new Dictionary();

            for each(var e:XML in Config.XML_DATA.enemy.*) {

                var typeName:String = e.@name;

                value[typeName] = new EnemyType(typeName, Number(e.@speed), Boolean(int(e.@flying)), Boolean(int(e.@immunity)));

            }

            return value;

        }

        

        // ウェーブの配列を返す

        public static function getWaves():Vector.<Wave> {

            var waveNumber:int = 1;

            var value:Vector.<Wave> = new Vector.<Wave>();

            for each(var w:XML in Config.XML_DATA.waves.*) {

                var spawn:Vector.<int> = new Vector.<int>();

                for each(var s:XML in w.*) { spawn.push(int(s.@num)); }

                value.push(new Wave(waveNumber, EnemyType.getType(String(w.@type)), int(w.@HP), int(w.@point), int(w.@money), spawn));

                waveNumber++;

            }

            return value;

        }

    }