Wonderfl Tower Defense
------------------------------------------------------
* 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 : 一直線に向かってくる、要注意!
* --------------
/**
* Copyright o8que ( http://wonderfl.net/user/o8que )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/nvmc
*/
/* ------------------------------------------------------
* 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;
}
}
//}