package { import flash.display.MovieClip; import flash.display.Sprite; import flash.display.Shape; import flash.events.Event; import flash.geom.Point; /** * Tower Defense Game v 0.1.2 * Source: http://www.flashgametuts.com/tutorials/as3/how-to-create-a-tower-defense-game-in-as3-part-3/ * @author */ public class Game extends MovieClip { // Game State Constants public static const STATE_INIT:int = 10; public static const STATE_TITLESCREEN:int = 15; public static const STATE_NEWLEVEL:int = 20 public static const STATE_PLAY:int = 25; public static const STATE_SHOWSCORE:int = 30; public static const STATE_GAMEOVER:int = 35; public static const HITPOINTS_TOTAL:int = 1; public static const NUM_ENEMIES:int = 10; public static const ENEMY_SPEED:int = 55; public static const ENEMY_SPAWN_RATE:int = 24; // Game Map Constants public static const MAP_COLS:int = 22; public static const MAP_ROWS:int = 12; public var gameState:int = 0; // Global State variable public function Game():void { gameState = STATE_INIT; // Sets global game state to initialize the game addEventListener(Event.ENTER_FRAME, gameLoop); // Starts up game loop to be called every ENTER_FRAME addEventListener(CustomEventTurret.ADD_TURRET, addTurretHandler); // Listens for the Block calling the Add Turret event } /********************************** * LEVEL DEFINING CODE */ // Array to hold the first map level and road. Empty Space = 0, Straight Road = 1, Waypoints start from 2 through the end // This is a bit clunky but a step in the right direction - the goal was to change to a waypoint system rather (vector) rather than a plot graph private var lvlArray:Array = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,6,1,1,7,0,0,10,1,1,11,0,0,14,1,1,15,0,0, 0,0,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0, 0,0,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0, 2,3,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,16,1,17, 0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,0,0,0, 0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,0,0,0, 0,4,1,1,5,0,0,8,1,1,9,0,0,12,1,1,13,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, ]; private var lvlMinWaypoint:int = 2; private var lvlMaxWaypoint:int = 17; // Necessary to pre-index our travelPath array // ******************************** // Difficulty Settings private var enemySpeed:int = 0; // Holds the default level speed (should probably move into a level settings object private var enemySpawnRate:int = 0; // Holds default speed which enemies spawn, deterimes wait time between enemies // Current Level Settings private var currentLevel:int = 0; // Current level that the game is presently playing. private var enemyFrameCounter:int = 0; // Counter to check spawn and wave rates private var blockModel:Array = []; // Model array to hold all tiles private var enemiesModel:Array = []; // Model array to hold all visible enemies private var travelPath:Array = []; // Array of Point obects which represent the path the enemies will take private var numEnemies:int; // Max number of enemies in the level private var incomingCount:int; // Test if we have reached the max number of enemies private var score:int = 0; // User's kill score private var tempEnemy:Enemy; // We evaluate from our enemies array frequently so having a global enemy prevents having to allocate the variable private var tempTurret:Turret; // We evaluate from our turrets array (global turret) private var tempBullet:Bullet; private var turretsModel:Array = []; // Holds any turrets that the player has created private var bulletsModel:Array = []; public function startGame():void{ // We'll run this function every the game begins. currentLevel = 0; gameState = STATE_TITLESCREEN; // Game has been initialized, show the title screen on next game loop pass } public function showTitleScreen():void { // TODO: Show Title Screen and make it clickable to start the game gameState = STATE_NEWLEVEL; // User has clicked off the title screen, start the new level on the next game loop pass } public function startLevel():void{ // We'll run this function every a level begins. blockModel = []; // Initialize or empty the arrays for the new level enemiesModel = []; travelPath = new Array(lvlMaxWaypoint - lvlMinWaypoint + 1); // Create a new travelPath array with empty indeces equal to the total number of waypoints enemyFrameCounter = 0; // Set all level variables to 0s numEnemies = 0; incomingCount = 0; score = 0; makeRoad(); // Create the level map getLevelSettings(); // Initialize the global settings for the current level gameState = STATE_PLAY; // Game is ready for the main game loop } public function getLevelSettings():void { numEnemies = NUM_ENEMIES; enemySpeed = ENEMY_SPEED; enemySpawnRate = ENEMY_SPAWN_RATE; } public function makeRoad():void { var col:int = 0; var row:int = 0; // Locational variable for rows var block:Block; var x:int = 0; var y:int = 0; var p:Point; // Point objects hold x and y coordinates and have functions to test the distance between two points // This temporary point object will be added to as waypoints travelPath array for (var i:int = 0; i < lvlArray.length; i++) { // Loop through the Level Array and create the Blocks x = (i - row * 22) * 25; y = row * 25; if (lvlArray[i] >= lvlMinWaypoint) // If it is a waypoint block add it to travelPath { p = new Point(x, y); // Instantiate a Point object travelPath[lvlArray[i] - lvlMinWaypoint] = p; // Add the Point to the travelPath array at the index location specified by the lvlArray map // (subtract lvlMinWaypoint since arrays start at 0 not 2 in this case) } block = new Block(lvlArray[i],x,y); // Instatiate a new block addChild(block); // Adds the Block (which is an empty MovieClip) to the stage block.render(); // Calls the block's render function which draws the rectangle blockModel.push(block); // Store our new block in the model array col++; // Each time we add a block is a new column if (col == MAP_COLS) // Increment the row each time col reaches the end { row++; col = 0; } } } public function addTurretHandler(event:CustomEventTurret):void { makeTurret(event.block.x, event.block.y); event.block.turretAdded(); } // Creates a new Turret object at x and y and inserts it into the turretsModel array public function makeTurret(xValue:int,yValue:int):void { var turret:Turret = new Turret(); // Temporary variable to create the new Turret turret.x = xValue + 12.5; // Set the coordinates to the middle of the object hence the + 12.5 turret.y = yValue + 12.5; turretsModel.push(turret); // Add it to our model addChild(turret); // Add it to the stage } // Game loop code from Week 3 lab - stores current game mode and is called every ENTER_FRAME public function gameLoop(e:Event):void { switch(gameState) { // gameState is a global variable which holds the current game mode case STATE_INIT : initGame(); // Start a new game break; case STATE_TITLESCREEN: showTitleScreen(); // Doesn't currently do anything except advance the state break; case STATE_NEWLEVEL: startLevel(); // Sets up current level variables break; case STATE_PLAY: playGame(); // Game is currently playing break; case STATE_SHOWSCORE: break; case STATE_GAMEOVER: gameOver(); // Run the game over code break; } } public function initGame():void { startGame(); } public function playGame():void // Main game loop { checkEnemies(); // Checks if we need to add any new enemies update(); // Calls all visible objects that need to update cleanUpEnemies() // Check collisions or if any enemies made it to the end render(); // Calls all visible objects that need to render } public function update():void { for each (tempEnemy in enemiesModel) { tempEnemy.update(); } for each(tempTurret in turretsModel) { //makes sure turrets don't shoot out of range tempTurret.target = null; for each (tempEnemy in enemiesModel) { var distance:Number = (Math.sqrt(Math.pow(tempEnemy.y - tempTurret.y, 2) + Math.pow(tempEnemy.x - tempTurret.x, 2))); //let's define a variable which will be how far the nearest enemy is if(distance < tempTurret.range) { if (tempTurret.fire(tempEnemy)) { tempBullet = new Bullet(tempTurret.x, tempTurret.y, tempEnemy); addChild(tempBullet); bulletsModel.push(tempBullet); trace("fired"); break; } } } tempTurret.update(); } for each(var tempBullet in bulletsModel) { var i:int = 0; i < bulletsModel.length; i++ if (tempBullet.hitTarget == true) { tempEnemy.adjustHealth(tempBullet.damage); removeChild(bulletsModel[i]); //Remove it from the stage bulletsModel.splice(i, 1); } } } public function render():void { for each (tempEnemy in enemiesModel) { tempEnemy.render(); } for each(tempBullet in bulletsModel) { tempBullet.update(); tempBullet.render(); } } public function checkEnemies():void { enemyFrameCounter++; // Increment the counter so we know how many game loop frames have passed for spawn rate and wave delay if ((enemyFrameCounter >= enemySpawnRate) && (incomingCount < numEnemies)) { // If we've waited long enough and we still have more enemies to spawn tempEnemy = new Enemy(enemySpeed, Enemy.HIT_POINTS_TOTAL, travelPath); // Create an enemy addChild(tempEnemy); // Add it to the stage enemiesModel.push(tempEnemy); // Add it to the Model array incomingCount++; // Note that we've added a new enemy for this wave enemyFrameCounter = 0; // Reset the frame timer } } public function cleanUpEnemies():void { for (var i:String in enemiesModel) // A For...in Loop - Loop through the enemies array. For...in loops return the index of the item { if (enemiesModel[i].finished) // If the enemy got to the end { removeChild(enemiesModel[i]); // Remove it from the stage enemiesModel.splice(i, 1); // Remove it from the array tempEnemy == null; if (tempEnemy == null) { for (var j:int = 0; j < enemiesModel.length; j++) { tempEnemy = enemiesModel[i];// clean up and find another target } } } dispose(); } } public function dispose():void { } public function gameOver():void { //gameState = STATE_INIT; } } }