package {
import flash.display.*;
import flash.events.*;
import flash.text.*;
import flash.utils.getTimer;
import flash.utils.Timer;
import flash.geom.Point;

public class SpaceRocks extends MovieClip {
static const shipRotationSpeed:Number = .1;
static const rockSpeedStart:Number = .03;
static const rockSpeedIncrease:Number = .02;
static const missileSpeed:Number = .2;
static const thrustPower:Number = .15;
static const shipRadius:Number = 20;
static const startingShips:uint = 3;

// game objects
private var ship:Ship;
private var rocks:Array;
private var missiles:Array;

// animation timer
private var lastTime:uint;

// arrow keys
private var rightArrow:Boolean = false;
private var leftArrow:Boolean = false;
private var upArrow:Boolean = false;

// ship velocity
private var shipMoveX:Number;
private var shipMoveY:Number;

// timers
private var delayTimer:Timer;
private var shieldTimer:Timer;

// game mode
private var gameMode:String;
private var shieldOn:Boolean;

// ships and shields
private var shipsLeft:uint;
private var shieldsLeft:uint;
private var shipIcons:Array;
private var shieldIcons:Array;
private var scoreDisplay:TextField;

// score and level
private var gameScore:Number;
private var gameLevel:uint;

// sprites
private var gameObjects:Sprite;
private var scoreObjects:Sprite;


// start the game
public function startSpaceRocks() {
// set up sprites
gameObjects = new Sprite();
addChild(gameObjects);
scoreObjects = new Sprite();
addChild(scoreObjects);

// reset score objects
gameLevel = 1;
shipsLeft = startingShips;
gameScore = 0;
createShipIcons();
createScoreDisplay();

// set up listeners
addEventListener(Event.ENTER_FRAME,moveGameObjects);
stage.addEventListener(KeyboardEvent.KEY_DOWN,keyDownFunction);
stage.addEventListener(KeyboardEvent.KEY_UP,keyUpFunction);

// start
gameMode = "delay";
shieldOn = false;
missiles = new Array();
nextRockWave(null);
newShip(null);
}


// SCORE OBJECTS

// draw number of ships left
public function createShipIcons() {
shipIcons = new Array();
for(var i:uint=0;i<shipsLeft;i++) {
var newShip:ShipIcon = new ShipIcon();
newShip.x = 20+i*15;
newShip.y = 375;
scoreObjects.addChild(newShip);
shipIcons.push(newShip);
}
}

// draw number of shields left
public function createShieldIcons() {
shieldIcons = new Array();
for(var i:uint=0;i<shieldsLeft;i++) {
var newShield:ShieldIcon = new ShieldIcon();
newShield.x = 530-i*15;
newShield.y = 375;
scoreObjects.addChild(newShield);
shieldIcons.push(newShield);
}
}

// put the numerical score at the upper right
public function createScoreDisplay() {
scoreDisplay = new TextField();
scoreDisplay.x = 500;
scoreDisplay.y = 10;
scoreDisplay.width = 40;
scoreDisplay.selectable = false;
var scoreDisplayFormat = new TextFormat();
scoreDisplayFormat.color = 0xFFFFFF;
scoreDisplayFormat.font = "Arial";
scoreDisplayFormat.align = "right";
scoreDisplay.defaultTextFormat = scoreDisplayFormat;
scoreObjects.addChild(scoreDisplay);
updateScore();
}

// new score to show
public function updateScore() {
scoreDisplay.text = String(gameScore);
}

// remove a ship icon
public function removeShipIcon() {
scoreObjects.removeChild(shipIcons.pop());
}

// remove a shield icon
public function removeShieldIcon() {
scoreObjects.removeChild(shieldIcons.pop());
}

// remove the rest of the ship icons
public function removeAllShipIcons() {
while (shipIcons.length > 0) {
removeShipIcon();
}
}

// remove the rest of the shield icons
public function removeAllShieldIcons() {
while (shieldIcons.length > 0) {
removeShieldIcon();
}
}


// SHIP CREATION AND MOVEMENT

// create a new ship
public function newShip(event:TimerEvent) {
// if ship exists, remove it
if (ship != null) {
gameObjects.removeChild(ship);
ship = null;
}

// no more ships
if (shipsLeft < 1) {
endGame();
return;
}

// create, position, and add new ship
ship = new Ship();
ship.gotoAndStop(1);
ship.x = 275;
ship.y = 200;
ship.rotation = -90;
ship.shield.visible = false;
gameObjects.addChild(ship);

// set up ship properties
shipMoveX = 0.0;
shipMoveY = 0.0;
gameMode = "play";

// set up shields
shieldsLeft = 3;
createShieldIcons();

// all lives but the first start with a free shield
if (shipsLeft != startingShips) {
startShield(true);
}
}

// register key presses
public function keyDownFunction(event:KeyboardEvent) {
if (event.keyCode == 37) {
leftArrow = true;
} else if (event.keyCode == 39) {
rightArrow = true;
} else if (event.keyCode == 38) {
upArrow = true;
// show thruster
if (gameMode == "play") ship.gotoAndStop(2);
} else if (event.keyCode == 32) { // space
newMissile();
} else if (event.keyCode == 90) { // z
startShield(false);
}
}

// register key ups
public function keyUpFunction(event:KeyboardEvent) {
if (event.keyCode == 37) {
leftArrow = false;
} else if (event.keyCode == 39) {
rightArrow = false;
} else if (event.keyCode == 38) {
upArrow = false;
// remove thruster
if (gameMode == "play") ship.gotoAndStop(1);
}
}

// animate ship
public function moveShip(timeDiff:uint) {

// rotate and thrust
if (leftArrow) {
ship.rotation -= shipRotationSpeed*timeDiff;
} else if (rightArrow) {
ship.rotation += shipRotationSpeed*timeDiff;
} else if (upArrow) {
shipMoveX += Math.cos(Math.PI*ship.rotation/180)*thrustPower;
shipMoveY += Math.sin(Math.PI*ship.rotation/180)*thrustPower;
}

// move
ship.x += shipMoveX;
ship.y += shipMoveY;

// wrap around screen
if ((shipMoveX > 0) && (ship.x > 570)) {
ship.x -= 590;
}
if ((shipMoveX < 0) && (ship.x < -20)) {
ship.x += 590;
}
if ((shipMoveY > 0) && (ship.y > 420)) {
ship.y -= 440;
}
if ((shipMoveY < 0) && (ship.y < -20)) {
ship.y += 440;
}
}

// remove ship
public function shipHit() {
gameMode = "delay";
ship.gotoAndPlay("explode");
removeAllShieldIcons();
delayTimer = new Timer(2000,1);
delayTimer.addEventListener(TimerEvent.TIMER_COMPLETE,newShip);
delayTimer.start();
removeShipIcon();
shipsLeft--;
}

// turn on shield for 3 seconds
public function startShield(freeShield:Boolean) {
if (shieldsLeft < 1) return; // no shields left
if (shieldOn) return; // shield already on

// turn on shield and set timer to turn off
ship.shield.visible = true;
shieldTimer = new Timer(3000,1);
shieldTimer.addEventListener(TimerEvent.TIMER_COMPLETE,endShield);
shieldTimer.start();

// update shields remaining
if (!freeShield) {
removeShieldIcon();
shieldsLeft--;
}
shieldOn = true;
}

// turn off shield
public function endShield(event:TimerEvent) {
ship.shield.visible = false;
shieldOn = false;
}

// ROCKS

// create a single rock of a specific size
public function newRock(x,y:int, rockType:String) {

// create appropriate new class
var newRock:MovieClip;
var rockRadius:Number;
if (rockType == "Big") {
newRock = new Rock_Big();
rockRadius = 35;
} else if (rockType == "Medium") {
newRock = new Rock_Medium();
rockRadius = 20;
} else if (rockType == "Small") {
newRock = new Rock_Small();
rockRadius = 10;
}

// Here is an alternate way to do the above, without a case statement
// Need to import flash.utils.getDefinitionByName to use
/*
var rockClass:Object = getDefinitionByName("Rock_"+rockType);
var newRock:MovieClip = new rockClass();
*/

// choose a random look
newRock.gotoAndStop(Math.ceil(Math.random()*3));

// set start position
newRock.x = x;
newRock.y = y;

// set random movement and rotation
var dx:Number = Math.random()*2.0-1.0;
var dy:Number = Math.random()*2.0-1.0;
var dr:Number = Math.random();

// add to stage and to rocks list
gameObjects.addChild(newRock);
rocks.push({rock:newRock, dx:dx, dy:dy, dr:dr, rockType:rockType, rockRadius: rockRadius});
}

// create four rocks
public function nextRockWave(event:TimerEvent) {
rocks = new Array();
newRock(100,100,"Big");
newRock(100,300,"Big");
newRock(450,100,"Big");
newRock(450,300,"Big");
gameMode = "play";
}

// animate all rocks
public function moveRocks(timeDiff:uint) {
for(var i:int=rocks.length-1;i>=0;i--) {

// move the rocks
var rockSpeed:Number = rockSpeedStart + rockSpeedIncrease*gameLevel;
rocks[i].rock.x += rocks[i].dx*timeDiff*rockSpeed;
rocks[i].rock.y += rocks[i].dy*timeDiff*rockSpeed;

// rotate rocks
rocks[i].rock.rotation += rocks[i].dr*timeDiff*rockSpeed;

// wrap rocks
if ((rocks[i].dx > 0) && (rocks[i].rock.x > 570)) {
rocks[i].rock.x -= 590;
}
if ((rocks[i].dx < 0) && (rocks[i].rock.x < -20)) {
rocks[i].rock.x += 590;
}
if ((rocks[i].dy > 0) && (rocks[i].rock.y > 420)) {
rocks[i].rock.y -= 440;
}
if ((rocks[i].dy < 0) && (rocks[i].rock.y < -20)) {
rocks[i].rock.y += 440;
}
}
}

public function rockHit(rockNum:uint) {
// create two smaller rocks
if (rocks[rockNum].rockType == "Big") {
newRock(rocks[rockNum].rock.x,rocks[rockNum].rock.y,"Medium");
newRock(rocks[rockNum].rock.x,rocks[rockNum].rock.y,"Medium");
} else if (rocks[rockNum].rockType == "Medium") {
newRock(rocks[rockNum].rock.x,rocks[rockNum].rock.y,"Small");
newRock(rocks[rockNum].rock.x,rocks[rockNum].rock.y,"Small");
}
// remove original rock
gameObjects.removeChild(rocks[rockNum].rock);
rocks.splice(rockNum,1);
}


// MISSILES

// create a new Missile
public function newMissile() {
// create
var newMissile:Missile = new Missile();

// set direction
newMissile.dx = Math.cos(Math.PI*ship.rotation/180);
newMissile.dy = Math.sin(Math.PI*ship.rotation/180);

// placement
newMissile.x = ship.x + newMissile.dx*shipRadius;
newMissile.y = ship.y + newMissile.dy*shipRadius;

// add to stage and array
gameObjects.addChild(newMissile);
missiles.push(newMissile);
}

// animate missiles
public function moveMissiles(timeDiff:uint) {
for(var i:int=missiles.length-1;i>=0;i--) {
// move
missiles[i].x += missiles[i].dx*missileSpeed*timeDiff;
missiles[i].y += missiles[i].dy*missileSpeed*timeDiff;
// moved off screen
if ((missiles[i].x < 0) || (missiles[i].x > 550) || (missiles[i].y < 0) || (missiles[i].y > 400)) {
gameObjects.removeChild(missiles[i]);
delete missiles[i];
missiles.splice(i,1);
}
}
}

// remove a missile
public function missileHit(missileNum:uint) {
gameObjects.removeChild(missiles[missileNum]);
missiles.splice(missileNum,1);
}

// GAME INTERACTION AND CONTROL

public function moveGameObjects(event:Event) {
// get timer difference and animate
var timePassed:uint = getTimer() - lastTime;
lastTime += timePassed;
moveRocks(timePassed);
if (gameMode != "delay") {
moveShip(timePassed);
}
moveMissiles(timePassed);
checkCollisions();
}

// look for missiles colliding with rocks
public function checkCollisions() {
// loop through rocks
rockloop: for(var j:int=rocks.length-1;j>=0;j--) {
// loop through missiles
missileloop: for(var i:int=missiles.length-1;i>=0;i--) {
// collision detection
if (Point.distance(new Point(rocks[j].rock.x,rocks[j].rock.y),
new Point(missiles[i].x,missiles[i].y))
< rocks[j].rockRadius) {

// remove rock and missile
rockHit(j);
missileHit(i);

// add score
gameScore += 10;
updateScore();

// break out of this loop and continue next one
continue rockloop;
}
}

// check for rock hitting ship
if (gameMode == "play") {
if (shieldOn == false) { // only if shield is off
if (Point.distance(new Point(rocks[j].rock.x,rocks[j].rock.y),
new Point(ship.x,ship.y))
< rocks[j].rockRadius+shipRadius) {

// remove ship and rock
shipHit();
rockHit(j);
}
}
}
}

// all out of rocks, change game mode and trigger more
if ((rocks.length == 0) && (gameMode == "play")) {
gameMode = "betweenlevels";
gameLevel++; // advance a level
delayTimer = new Timer(2000,1);
delayTimer.addEventListener(TimerEvent.TIMER_COMPLETE,nextRockWave);
delayTimer.start();
}
}

public function endGame() {
// remove all objects and listeners
removeChild(gameObjects);
removeChild(scoreObjects);
gameObjects = null;
scoreObjects = null;
removeEventListener(Event.ENTER_FRAME,moveGameObjects);
stage.removeEventListener(KeyboardEvent.KEY_DOWN,keyDownFunction);
stage.removeEventListener(KeyboardEvent.KEY_UP,keyUpFunction);

gotoAndStop("gameover");
}

}
}