Source

LudumDare23 / src / PlayState.as

package
{
	import avmplus.getQualifiedClassName;
	
	import org.flixel.*;
	
	public class PlayState extends FlxState
	{
		public static const MAX_INVADERSHIP_COUNT:int = 10;
		
		private var _entities:Vector.<Entity> = new Vector.<Entity>();
		private var _particles:Vector.<ParticleFX> = new Vector.<ParticleFX>();
		
		private var _guiLayer:FlxGroup = new FlxGroup();
		private var _gameLayer:FlxGroup = new FlxGroup();
		
		private var _sky:FlxSprite;
		private var _vignette:FlxSprite;
		private var _healthBar:FlxSprite;
		private var _chargedTowersText:FlxText;
		
		public static const GROUP_BEACONS:uint = 0;
		public static const GROUP_ENEMIES:uint = 1;
		public static const GROUP_PLAYER:uint = 2;
		public static const GROUP_BULLETS:uint = 3;
		private var _groups:Vector.<FlxGroup> = Vector.<FlxGroup>([new FlxGroup, new FlxGroup, new FlxGroup, new FlxGroup]);
		
		private var _invaderShipCount:int = 0;
		private var _invaderShipSpawnerTimer:Number = 0;
		private var _playerDied:Boolean = false;
		private var _deadTimer:Number = 0;
		
		private function get world():World {
			return _entities[0] as World;
		}
		
		private function get player():Player {
			return _entities[1] as Player;
		}
		
		override public function create():void {
			FlxG.watch(_entities, "length", "entities count");
			
			createGUI();
			createSky();
			
			var world:World = new World();
			var player:Player = new Player(world);
			FlxG.camera.follow(player.view, FlxCamera.STYLE_LOCKON);
			// listen dead signal
			player.dead.add(playerDied);
			
			addEntity(world);
			addEntity(player);
			
			createClouds();
			
			for (var group:String in _groups)
				_gameLayer.add(_groups[group]);
			
			createBeacons();
			
			_gameLayer.add(world.foreground);
			
			add(_gameLayer);
			add(_guiLayer);
			
			// add vignette fx
			_vignette = new FlxSprite(0, 0, EmbeddedAssets.ImgVignette);
			add(_vignette);
			
			// play music
			FlxG.playMusic(EmbeddedAssets.SndMusic, 0.5);
		}
		
		private function createClouds():void
		{
			const NUM_CLOUDS:int = 30;
			for (var i:int = 0; i < NUM_CLOUDS; ++i) {
				var cloud:WorldAsset = new WorldAsset(world, EmbeddedAssets.ImgCloud);
				cloud.worldHeight = 80 + Math.random() * 30;
				cloud.worldAngle = Math.random() * Math.PI * 2;
				cloud.speed = Math.random() * 0.2 - 0.1;
				addEntity(cloud);
			}
		}
		
		private function createBeacons():void
		{
			const NUM_BEACONS:int = 3;
			var angle:Number = Math.PI / NUM_BEACONS;
			for (var i:int = 0; i < NUM_BEACONS; ++i) {
				var beacon:Beacon = new Beacon(world);
				beacon.worldAngle = angle;
				FlxG.log(beacon.worldAngle);
				angle += Math.PI * 2 / NUM_BEACONS;
				addEntity(beacon);
			}
		}
		
		private function createGUI():void
		{
			_healthBar = new FlxSprite();
			_healthBar.loadGraphic(EmbeddedAssets.ImgHealthBar, true, false, 60, 8);
			_healthBar.x = FlxG.width - _healthBar.width - 4;
			_healthBar.y = FlxG.height - _healthBar.height - 4;
			_guiLayer.add(_healthBar);
			_guiLayer.add(new FlxText(_healthBar.x - 40, _healthBar.y - 3, 40, "Health:"));
			_chargedTowersText = new FlxText(4, 4, 200, ""); 
			_guiLayer.add(_chargedTowersText);
			_guiLayer.setAll("scrollFactor", new FlxPoint(0, 0));
		}
		
		private function createSky():void
		{
			_sky = new FlxSprite();
			_sky.loadGraphic(EmbeddedAssets.ImgSky, true, false, 1, 1);
			_sky.scale = new FlxPoint(1000, 1000);
			add(_sky);
		}
		
		override public function update():void {
			spawnEnemies();
			
			_sky.x = player.view.x - _sky.width / 2;
			_sky.y = player.view.y - _sky.height / 2;
			_sky.frame = player.worldAngle * _sky.frames / (Math.PI * 2);
			// update vignette fx
			_vignette.x = player.view.x - _vignette.width / 2;
			_vignette.y = player.view.y - _vignette.width / 2;
			
			for each (var entity:Entity in _entities)
				entity.update();
			
			// "manual" collision
			for each (var enemy:FlxObject in _groups[GROUP_ENEMIES].members) {
				// check enemy with player
				if (overlap(enemy, player.view))
					collided(enemy, player.view);
				// check with bullets
				for each (var bullet:FlxObject in _groups[GROUP_BULLETS].members) {
					if (overlap(bullet, enemy))
						collided(bullet, enemy);
				}
			}
			// check beacons with player
			var win:Boolean = true;
			for each (var beacon:FlxObject in _groups[GROUP_BEACONS].members) {
				if (overlap(beacon, player.view))
					collided(beacon, player.view);
				// also check if charged
				if (!(entityWithView(beacon) as Beacon).isCharged) {
					win = false;
				}
			}
			
			updateGUI();
			
			super.update();
			
			if (_playerDied || win) {
				_deadTimer += FlxG.elapsed;
				if (_deadTimer > 1)
					FlxG.switchState(new PlayState());
			}
		}
		
		private function get chargedTowers():int {
			var count:int = 0;
			for each (var beacon:FlxObject in _groups[GROUP_BEACONS].members) {
				// also check if charged
				if ((entityWithView(beacon) as Beacon).isCharged) {
					count++;
				}
			}
			return count;
		}
		
		private function updateGUI():void
		{
			var f:Number = 1 - (player.view.health / Player.INITIAL_HEALTH);
			_healthBar.frame = f * _healthBar.frames;
			
			_chargedTowersText.text = "Charged towers: " + chargedTowers + "/" + _groups[GROUP_BEACONS].length;
		}
		
		private function playerDied():void
		{
			_playerDied = true;
			player.view.visible = false;
		}
		
		private function overlap(obj1:FlxObject, obj2:FlxObject):Boolean
		{
			var e1:Entity = entityWithView(obj1);
			var e2:Entity = entityWithView(obj2);
			if (e1 && e2) {
				return e1.overlap(e2);
			}
			return false;
		}
		
		private function collided(obj1:FlxObject, obj2:FlxObject):void
		{
			var e1:Entity = entityWithView(obj1);
			var e2:Entity = entityWithView(obj2);
			if (e1 && e2) {
				e1.collided(e2);
				e2.collided(e1);
			}
		}
		
		private function entityWithView(view:FlxObject):Entity {
			for each (var entity:Entity in _entities) {
				if (entity.view == view)
					return entity;
			}
			return null;
		}
		
		public function addEntity(entity:Entity):void {
			if (_entities.length > 50)
				FlxG.log("WARNING: too many entities: " + _entities.length);
			
			_entities.push(entity);
			if (entity.groupName < _groups.length)
				_groups[entity.groupName].add(entity.view);
			else
				_gameLayer.add(entity.view);
			
			if (entity is InvaderShip)
				_invaderShipCount++;
		}
		
		public function removeEntity(entity:Entity):void {
			var idx:int = _entities.indexOf(entity);
			if (idx >= 0) {
				if (entity.groupName < _groups.length) 
					_groups[entity.groupName].remove(entity.view);
				else
					_gameLayer.remove(entity.view);
				_entities.splice(idx, 1);
			}
			
			if (entity is InvaderShip)
				_invaderShipCount--;
		}
		
		private function updateParticleFX():void {
			for each (var pfx:ParticleFX in _particles.concat()) {
				if (!pfx.update()) {
					_particles.splice(_particles.indexOf(pfx), 1);
					_gameLayer.remove(pfx.emitter);
				}
			}
		}
		
		public function addParticleFX(pfx:ParticleFX):void {
			_gameLayer.add(pfx.emitter);
			pfx.emitter.start(pfx.explode, pfx.duration);
			_particles.push(pfx);
		}
		
		private function spawnEnemies():void
		{
			if (_invaderShipCount >= MAX_INVADERSHIP_COUNT) return;
			
			var ratio:Number = (_invaderShipCount + 1 / MAX_INVADERSHIP_COUNT);
			var time:Number = ratio * 1.5;
			
			_invaderShipSpawnerTimer += FlxG.elapsed;
			if (_invaderShipSpawnerTimer > time) {
				_invaderShipSpawnerTimer = 0;
				spawnInvaderShip();
			}
		}
		
		private function spawnInvaderShip():void {
			var invaderShip:InvaderShip = new InvaderShip(world, player);
			addEntity(invaderShip);
		}
	}
}