Left 2 Die (Multiplayer] forked from: colin challenge for professionals

by swingpants forked from colin challenge for professionals (diff: 1312)
Create a Flash app using Union Platform,
* ...
* @author Swingpants
*
* Left 2 Die - "Zombie Mayhem"
* An exercise in cooperative play
* 
* Built for the Moock Checkmate Challenge - ran out of time - misjudged when deadline was
* 
* This is a single player version
* 
* Aim of game take your team to the safe house (top left corner)
* Got to stay together to survive
*
* To Do:
*
* Register death
* Register Safe House arrival inc stats
* Display health - in speech bubble - using modulos / every 2nd health lost
* Display score in speech bubble every 5pts
* Multiplayer layer - will us messages with encoded string for each user.
*     packet format: [from ID]_[TurnNum]_[M/S]_[Direction]
*    Needd to listen for players dropping out - then convert to bot
* Audio
* Special Infected (Smoker & Hunter)
* Use spawning zones rather than initiating all zombies at beginning
* Get designer to do GFX. The current one's are placeholders. Took 1 train commute to do.
*  
* Turn system: Players send turn m
♥2 | Line 1134 | Modified 2009-08-30 10:01:33 | MIT License
play

ActionScript3 source code

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

// forked from checkmate's colin challenge for professionals
/**
 * Create a Flash app using Union Platform,
	 * ...
	 * @author Swingpants
         *
         * Left 2 Die - "Zombie Mayhem"
         * An exercise in cooperative play
         * 
         * Built for the Moock Checkmate Challenge - ran out of time - misjudged when deadline was
         * 
         * This is a single player version
         * 
         * Aim of game take your team to the safe house (top left corner)
         * Got to stay together to survive
         *
         * To Do:
         *
         * Register death
         * Register Safe House arrival inc stats
         * Display health - in speech bubble - using modulos / every 2nd health lost
         * Display score in speech bubble every 5pts
         * Multiplayer layer - will us messages with encoded string for each user.
         *     packet format: [from ID]_[TurnNum]_[M/S]_[Direction]
         *    Needd to listen for players dropping out - then convert to bot
         * Audio
         * Special Infected (Smoker & Hunter)
         * Use spawning zones rather than initiating all zombies at beginning
         * Get designer to do GFX. The current one's are placeholders. Took 1 train commute to do.
		 *  
		 * Turn system: Players send turn message. Collated by room 'leader' posted to room attribute. Collected by players, displayed then next turn starts.
 */

package {
  import flash.display.Sprite;
  import flash.events.KeyboardEvent;
  import flash.ui.Keyboard;
  import net.user1.reactor.*;
  import net.user1.logger.Logger;
  
  import flash.text.TextField
  import flash.events.Event
  import flash.events.MouseEvent
  
  import flash.display.Loader
  import flash.net.URLRequest
  
  [SWF(width = 480, height = 480, backgroundColor = 0x333333)] 
  
  // The main application class
  public class Left2Die extends Sprite 
  {
    // Union objects
    protected var reactor:Reactor;
    protected var _room:Room;
    protected var _game:Room;
    protected var _room_manager:RoomManager;
    // Visual objects
    protected var ramenBowl:Sprite;
    private var _count:int=0
    private var _status:String
    
    private var _tf:TextField=new TextField()
    private var _p_tf:TextField = new TextField()
	private var temp_tf:TextField=new TextField()
    
    private var _all_rooms:Array
    private var _hold:int
    
    private var _room_num:int
    private var _go_button:Button
    
    private var _l2d:Main
	private var _player_id:int = 0
	private var _player_num:int=0
	private var _player_IDs:Array = []
	private var _player_nums:Object = { }//Quick converter from id to num
	
	private var _isLeader:Boolean = false
	private var _moves:Array = []
	
	private var _turn_count:int = 0
	private var _num_of_players:int = 0
	private var _rx_count:int = 0
	private var _latest_turns:Array=[]
    
    // Constructor
    public function Left2Die () 
    { 
      // Create the visuals
      //buildUI();
      
      _l2d=new Main(this.stage)
      
      var game_title:TextField=new TextField()
      var title_container:Sprite=new Sprite()
      addChild(title_container)
      title_container.addChild(game_title)
      title_container.x=100; title_container.y=100;
      game_title.text="LEFT 2 DIE"
      title_container.width=400; title_container.height=400;
      game_title.textColor=0xFF0000
      
      _p_tf.text="PLAYERS:."
      _p_tf.textColor=0x00AA00
      _p_tf.x=194; _p_tf.y=170;
      addChild(_p_tf)
      
      var instructions:TextField=new TextField()
      addChild(instructions)
      instructions.width=300
      instructions.x=90; instructions.y=220
      instructions.text="\n You will need to work as a team to reach the safehouse \n Turns are fast. Move or shoot. Make your mind up quick. \n If you try to go solo the zombie horde WILL get you"
      instructions.textColor=0xBBBBBB
      
      // Make the UConnection object
      reactor = new Reactor();
      // Run readyListener() when the connection is ready
      reactor.addEventListener(ReactorEvent.READY, readyListener);
      // Connect to Union
      reactor.connect("tryunion.com", 9100);
      reactor.getLog().setLevel(Logger.DEBUG);
   
    }
    
    // Method invoked when the connection is ready
    protected function readyListener (e:ReactorEvent):void 
    {
      // Create the application room
      // use a unique identifier for your app
      _room_manager=reactor.getRoomManager()
      
      setUpGameRoom()
    }
    
   
    private function setUpGameRoom():void
    {
         _tf.appendText("\nSETTUPGAMEROOM")
        var settings:RoomSettings = new RoomSettings();
        settings.dieOnEmpty = true;
        settings.maxClients = 4
        _game = reactor.getRoomManager().createRoom("wonderfl.zombieGameRoom"+_room_num,settings);
        _game.addEventListener(RoomEvent.JOIN, joinGameRoomHandler);
        _game.join()
         _p_tf.text="PLAYERS:.."
    }


    private function joinGameRoomHandler(event:RoomEvent):void
    {
        _game.removeEventListener(RoomEvent.JOIN,joinGameRoomHandler)
        _game.addEventListener(RoomEvent.SYNCHRONIZE,roomSynchronisedHandler)
        _tf.appendText("\n__"+event.getStatus())
        if (event.getStatus() == Status.SUCCESS) 
            {
                 _p_tf.text="PLAYERS:..."
                _tf.appendText("\nJOINEDROOM!!")
                _tf.appendText("\nplayer"+_player_id+"\n game:"+_game.getRoomID())
            } 
          else 
            {
                _room_num++
                setUpGameRoom()
            }
    }
    
    private function roomSynchronisedHandler(event:RoomEvent):void
    {
        _tf.appendText("\nROOM SYNCHRONISED!!")
        _p_tf.text="PLAYERS:...."
        if(_game.getAttribute("started"))
            {
                _game.leave()
                _room_num++
                setUpGameRoom()
            }
            else
            {
                 _game.addMessageListener("INITIATOR", initiatorListener);
                 _game.sendMessage("INITIATOR", true, null,"initiating");
            }

    }
    
    private function initiatorListener(from:IClient,msg:String):void
    {
        if(from.isSelf()) 
            {
                 if(!_player_id) _player_id = int(from.getClientID())
                _tf.appendText("\nME_:"+msg+_player_id+"\n__"+_game.getNumClients()+"\nP:"+_player_id+"\nclients:"+_game.getNumClients())
                
            }
          else 
            {
                _tf.appendText(msg+_player_id+"\n__"+_game.getNumClients()+"\nP:"+_player_id)
            }
        _game.removeMessageListener("INITIATOR", initiatorListener);
        _game.addEventListener(	RoomEvent.CLIENT_COUNT, countChangedHandler)
	_num_of_players=_game.getNumClients()
        _p_tf.text = "PLAYERS: " + _num_of_players + "  " + _player_id+ "__"+_game.getRoomID()
        setUpGoButton()
        
    }
    
    private function countChangedHandler(event:RoomEvent):void
    {
        _num_of_players=_game.getNumClients()
        _p_tf.text = "PLAYERS: " + _num_of_players + "  " + _player_id
        for(var i:int=0;i<_game.getClientIDs().length;i++)
            {
                 _p_tf.appendText(", \n"+_game.getClientIDs()[i])
             }
    }
    
    private function setUpGoButton():void
    {
        _go_button=new Button("go","rectangle",10)
        _go_button.x=200; _go_button.y=200
        _go_button.addEventListener(MouseEvent.CLICK, goClickHandler)
        addChild(_go_button)
        
         _game.addMessageListener("GO_BUTTON_PRESSED", goButtonPressedListener);
    }
    private function goClickHandler(event:MouseEvent):void
    {
        _game.sendMessage("GO_BUTTON_PRESSED",true, null, "GO")
        //Start game
        _go_button.removeEventListener(MouseEvent.CLICK, goClickHandler)
        removeChild(_go_button)
        
    }
    
    private function goButtonPressedListener(from:IClient,msg:String):void
    {
        if(msg=="GO")
            {
                _game.removeMessageListener("GO_BUTTON_PRESSED", goButtonPressedListener);
                _game.removeEventListener(RoomEvent.CLIENT_COUNT, countChangedHandler)
				_player_IDs = _game.getClientIDs().concat()
				var len:int = _player_IDs.length
				for (var i:int = 0; i < len; i++)
					{
						_player_nums["ID"+_player_IDs[i]]=i
						if (_player_IDs[i] == _player_id) 
							{
								if(i==0)_isLeader=true
								_player_num = i
								_game.setAttribute("started","yes")
							}
					}
				addEventListener(RoomEvent.UPDATE_CLIENT_ATTRIBUTE, turnPostedToServerHandler) //Listen for next turn
                startGame()
            }
    }
    private function startGame():void
    {
        _game.addMessageListener("ZOMBIE_TURN", zombieTurnListener);
        _game.addEventListener(	RoomEvent.CLIENT_COUNT, countChangedInGameHandler)
		_l2d.addEventListener("PLAYER_TURN",txPlayerTurnHandler)
         addChild(_l2d)
        _l2d.startGame(_player_num)
		_turn_count=0
        
       // _game.sendMessage("ZOMBIE_TURN",true, null, "Yo from "+_player_id)
    }
	
	private function txPlayerTurnHandler(event:Event):void
	{
		temp_tf.text = "txPlayerTurnHandler"
		addChild(temp_tf)
		_rx_count = 0
		_moves=[]
		_latest_turns = _l2d.txPlayerTurn()
		
		var len:int = _latest_turns.length
		for (var i:int = 0; i < len; i++)
			{
				temp_tf.appendText("\n :"+i+": sel="+_latest_turns[i].sel+"  dir="+_latest_turns[i].dir)
			}
		var obj:Object={sel:"MOVE",dir:-1}
		if (_latest_turns[_player_num]) obj = _latest_turns[_player_num]
		if(!obj.sel)obj.sel="MOVE"
		var dir:int = 9
		if(obj.dir) dir=obj.dir
		if (dir == -1) dir = 9
		temp_tf.appendText("\nSending:"+_player_num+"_"+(obj.sel=="MOVE"?"M_":"S_")+dir)
		_game.sendMessage("ZOMBIE_TURN",true, null, _player_num+"_"+(obj.sel=="MOVE"?"M_":"S_")+dir)
	}
    
	//Collect all of the turns   
    private function zombieTurnListener(from:IClient,msg:String):void
    {
        //_tf.appendText(msg+"\nMe="+_player_id+"\nGame="+_game.getRoomID()+"\nclients:"+_game.getNumClients())
		if (_isLeader)
			{
				_moves[int(msg.substr(0, 1))] = msg
				temp_tf.appendText("\nmsg="+msg+" _rx_count="+_rx_count+" \n _num_of_players="+_num_of_players+"\n _player_id="+_player_id)
				_rx_count++
				if (_rx_count == _num_of_players)
					{//All recieved so post to room
						var packet:String=""
						for (var i:int = 0; i < 4; i++)
							{
								if (i > 0) packet += "_"
								if (_moves[i])
									{//Move exists
										packet+=_moves[i]
									}
									else
									{
										var sel:String=_latest_turns[i].sel == "MOVE"?"M_":"S_" 
										packet += i+"_"+sel
										if (_latest_turns[i].dir == -1) packet += "9"
										else packet += String(_latest_turns[i].dir)
									}
								temp_tf.appendText("..."+packet)
							}
						temp_tf.appendText("\npacket="+packet)
						_game.setAttribute("turn", packet)
						_turn_count++
					}
			}
			else temp_tf.appendText("\nNOT LEADER")
			
    }
	
//Turn posted	//  Format 27_0_M_2_1_S_
	private function turnPostedToServerHandler(event:RoomEvent):void
    {
		temp_tf.appendText("turnPostedToServerHandler")
        var turn_str:String = _game.getAttribute("turn")
		var turn_num:int = int(str.substr(0, str.indexOf("_")))
		turn_str=turn_str.substr(str.indexOf("_")+1,99)

		if (turn_str)
			{
				var str:String
				var selection:String
				var dir_to_use:int
				for (var i:int = 0; i < 4; i++)
					{
						str = turn_str.substr(i * 6, 5)
						selection = str.substr(2, 1)
						if (selection == "M") selection = "MOVE"
						else
						if (selection == "S") selection = "SHOOT"
						dir_to_use = int(str.substr(4, 1))
						if(dir_to_use==9)dir_to_use=-1
						_l2d.turnReceived(turn_num,i, selection, dir_to_use)
					}
				_l2d.renderTurn()
			}
    }
//Somebody has dropped out of the game
    private function countChangedInGameHandler(event:RoomEvent):void
    {
        var new_player_list:Array = _game.getClientIDs()
		new_player_list.sort()
		if(new_player_list[0]==_player_id)_isLeader=true

		for (var i :int = 0; i < 4; i++)
			{//Loop through to register whether player is still active or not
				//USE FILTER TO FIND IF LISTED PLAYER IS IN NEW LIST IF NOT THE MAKE BOT
				//if(_player_nums["ID"+new_player_list[i]]) isActive(_player_nums["ID"+new_player_list[i]])
			}
		_num_of_players=_game.getNumClients()
        
    }
    // Creates the user interface
    protected function buildUI ():void 
    {
     
      _tf.text="HELLO WORLD"
      _tf.width=250
      _tf.height=300
      
      addChild(_tf)
    }
    
  }
}






/***********************************************************************************************************************************************************
 * GAME
 ***********************************************************************************************************************************************************/


	import flash.display.Sprite;
	import flash.events.Event;
	import flash.display.Loader
	import flash.net.URLRequest
	import flash.display.Bitmap
    import flash.system.LoaderContext;
    import flash.display.Stage

	class Main extends Sprite 
	{
		private var _container:Sprite = new Sprite()
		private var _message_layer:Sprite=new Sprite()
		private var _controller:Controller
		private var _turn_timer:TurnTimer = new TurnTimer()
		private var _move_panel:MovePanel = new MovePanel()
		private var _loader:Loader 
		private const IMAGE_URLS:Array = ["http://www.swingpantsflash.com/images/terrain.jpg",
							"http://www.swingpantsflash.com/images/player1.png",
							"http://www.swingpantsflash.com/images/player2.png",
							"http://www.swingpantsflash.com/images/player3.png",
							"http://www.swingpantsflash.com/images/player4.png",
							"http://www.swingpantsflash.com/images/zombie.png"
										]
		private var _url_index:int = 0
		private var _terrain:Sprite = new Sprite()
		private var _canvas:Sprite = new Sprite()
		private var _p1_bmp:Bitmap 
		private var _p2_bmp:Bitmap
		private var _p3_bmp:Bitmap 
		private var _p4_bmp:Bitmap 
		private var _zombie_bmp:Bitmap 
        private var _stage:Stage
		
		public function Main(s:Stage):void 
		{
			_stage=s
			init()
		}
		
		private function init(e:Event = null):void 
		{
			removeEventListener(Event.ADDED_TO_STAGE, init);
			loadNextImage()
		}
		
		private function loadNextImage():void
		{
			var context:LoaderContext = new LoaderContext();
			context.checkPolicyFile=true;
			_loader= new Loader();
			var req:URLRequest = new URLRequest(IMAGE_URLS[_url_index]);
			_loader.contentLoaderInfo.addEventListener( Event.COMPLETE, onLoadHandler );
			_loader.load(req,context);
		}
		
		private function onLoadHandler(event:Event):void 
		{
			//switch throwing error "Expecting CaseLabel"!!?
			if(_url_index==0)_terrain.addChild(_loader);
			if(_url_index==1)_p1_bmp = Bitmap(_loader.content);
			if(_url_index==2)_p2_bmp = Bitmap(_loader.content);
			if(_url_index==3)_p3_bmp = Bitmap(_loader.content);
			if(_url_index==4)_p4_bmp = Bitmap(_loader.content);
			if(_url_index==5)_zombie_bmp = Bitmap(_loader.content);
			
			_url_index++
			if (_url_index < 6)
				{
					loadNextImage()
				}
				else
				{
					this.graphics.beginFill(0x00FF00,0.6)
					this.graphics.drawCircle(120, 120, 100)
					this.graphics.endFill()
					continueInit()
				}
         }
		
		private function continueInit():void
		{
			var gb:GameBoard = new GameBoard(_terrain)
			addChild(gb)
			
			//Set up the controller
			_controller = new Controller(_stage,gb,_message_layer,_move_panel,_turn_timer)
			//Add players (max 4)
			_controller.addPlayer(GameBoard.ROWS-4,GameBoard.COLS-1,_p1_bmp)
			_controller.addPlayer(GameBoard.ROWS-3,GameBoard.COLS-2,_p2_bmp)
			_controller.addPlayer(GameBoard.ROWS-2,GameBoard.COLS-3,_p3_bmp)
			_controller.addPlayer(GameBoard.ROWS-1,GameBoard.COLS-4,_p4_bmp)
			//Need to calc zombie patterns
			addZombieCluster(55, 55)
			addZombieCluster(54, 59)
			var spread:Number=4
			for (var i:int = 0; i < GameBoard.COLS; i += Math.round(spread))
				{
					for (var j:int = 0; j < GameBoard.ROWS; j += Math.round(spread))
						{
							if((i<GameBoard.COLS-10 || j<GameBoard.ROWS-10) && (i>10 || j>10))	addZombieCluster(i, j)
						}
					spread*=1.08
				}
			
			_turn_timer.x = 200
			_turn_timer.y = 300
		
			gb.addChild(_message_layer)
			
			_move_panel.x = GameBoard.SCREENW*0.5;_move_panel.y=GameBoard.SCREENH*0.5
		}

        public function startGame(player_num:int):void
			{
				_controller.setPlayerNum(player_num)
				_controller.update()
				addChild(_turn_timer)
				addChild(_move_panel)
				_controller.startNextTurn()
				_controller.addEventListener("PLAYER_TURN", dispatchTurnHandler)
			}
		
		private function addZombieCluster(posx:int, posy:int):void
		{
			var cluster:Array=[[1,0,0,1],[0,1,1,0],[0,1,1,0],[1,0,0,1]]
			for (var i:int = 0; i < 4; i++)
				{
					for (var j:int = 0; j < 4; j++)
						{
							if (cluster[i][j] == 1 && posx+j<GameBoard.COLS && posy+i<GameBoard.ROWS)
								{
									_controller.addZombie(posx+j,posy+i,duplicateImage(_zombie_bmp))
								}
						}
				}
		}
		
		private function duplicateImage(original:Bitmap):Bitmap 
		{
            var image:Bitmap = new Bitmap(original.bitmapData.clone());
            //image.x = size * numChildren;
            //addChild(image);
            return image;
        }
		
		public function turnReceived(turn_num:int,player:int, selection:String, dir_to_use:int):void
		{
			_controller.turnReceived(turn_num,player, selection, dir_to_use)
		}
		public function renderTurn():void { _controller.renderTurn() }
		
		public function dispatchTurnHandler(event:Event):void
		{
			dispatchEvent(new Event("PLAYER_TURN"))
		}
		public function txPlayerTurn():Array
		{
			return _controller.PlayerTurns
		}
	}

import flash.display.Bitmap;
import flash.display.Sprite;
import flash.events.EventDispatcher
import flash.events.Event;
import flash.events.MouseEvent;
import flash.text.TextField;
import flash.geom.Point
import caurina.transitions.Tweener
import flash.display.Stage
import flash.display.Bitmap

/***********************************************************************************************************************************************************
 * Controller - control of all views
 ***********************************************************************************************************************************************************/
class Controller extends EventDispatcher
	{
		private const COLOURS:Array = [0xFF0000, 0xFF9900, 0x0000FF, 0xFFFF00]
		private const ZOMBIE_COLOUR:uint = 0x006600
		private const ACTIVE_RANGE:int = 10
		private const LONG_RANGE:int = 72
		private const ADJACENT:int=11
		private var _players:Array = []
		private var _distances:Array=[]
		private var _turn:Array=[{},{},{},{}]
		private var _zombies:Array = []
		private var _gameboard:GameBoard
		private var _message_layer:Sprite
		private var _move_panel:MovePanel
		private var _turn_timer:TurnTimer
		private var _stage:Stage
		private var _target_posn:Point=new Point()
		private var _bullets:Array = []
		
		private var _test_canvas:Sprite = new Sprite()
		private var _player_num:int=0
		
	public function Controller(stage:Stage,gameboard:GameBoard,message_layer:Sprite,move_panel:MovePanel,turn_timer:TurnTimer):void
		{
			_gameboard = gameboard
			_message_layer = message_layer
			_move_panel=move_panel
			_turn_timer = turn_timer
			_turn_timer.addEventListener(Event.COMPLETE, turnOverHandler)
			_stage = stage
			
			for (var i:int = 0; i < 4;i++){_distances[i]=[]}
		}
		
		public function setPlayerNum(val:int):void { _player_num = val}
		
		//Add a player to the game board
		public function addPlayer(posx:int, posy:int, bmp:Bitmap):void
		{
			var p:Actor = new Actor(_message_layer, COLOURS[_players.length])
			bmp.x = 20; 
			bmp.y = -20
			bmp.width = 40; bmp.height = 40
			bmp.rotation=90
			p.addChild(bmp)
			p.health=20
			p.position=new Point(posx,posy)
			p.x = GameBoard.GRID_SIZE * posx+GameBoard.GRID_SIZE*0.5//_players.length
			p.y = GameBoard.GRID_SIZE * posy+GameBoard.GRID_SIZE*0.5//_players.length
			
			p.rotation=GameMaths.angleToPoint(p.position,new Point())
			
			var b:Sprite = new Sprite()
			b.graphics.beginFill(0xAAAAAA, 0.6)
			b.graphics.drawCircle(0, 0, 2)
			b.graphics.endFill()
			b.alpha=0
			p.bullet=b
			_gameboard.addChild(b)
			_gameboard.addChild(p)
			p.name="player"+_players.length
			p.addMessage(p.name)
			_players.push(p)
		}
		
		//Add a zombie to the game board
		public function addZombie(posx:int,posy:int,bmp:Bitmap):void
		{
			var zombie:Actor = new Actor(_message_layer, ZOMBIE_COLOUR)
			bmp.x = 20; 
			bmp.y = -20
			bmp.width = 40; bmp.height = 40
			bmp.rotation = 90
			trace("bmp="+bmp)
			zombie.addChild(bmp)
			zombie.position=new Point(posx,posy)
			zombie.x = GameBoard.GRID_SIZE * posx+GameBoard.GRID_SIZE*0.5
			zombie.y = GameBoard.GRID_SIZE * posy + GameBoard.GRID_SIZE * 0.5
			zombie.name="z_"+_zombies.length
			_gameboard.addChild(zombie)
			_zombies.push(zombie)
		}
		
		private function turnOverHandler(event:Event):void
		{trace("turnOverHandler")
			_move_panel.outOfTime()
			_turn[_player_num] = _move_panel.option_selection
			calcPlayerDistances()
			calcBotMoves()
			dispatchTurn()
		}
		private function calcPlayerDistances():void
		{
			var i:int; 
			var j:int
			var active_players:int=0
			for (i = 0; i < 3; i++)
				{
					for (j = i+1; j < 4; j++)
						{
							_distances[i][j] = GameMaths.distanceFromToSquared(_players[i].position, _players[j].position)
							_distances[j][i]=_distances[i][j]
						}
				}
			var tgtx:int
			var tgty:int
			for (i = 0; i < 4; i++)
				{
					if (_players[i].active)
						{
							active_players++
							var nearest:int = 9999
							var furthest:int= -1
							for (j = 0; j < 4; j++)
								{
									if (i != j)
										{
											if (_distances[i][j] < nearest) { _players[i].nearest = j; nearest = _distances[i][j] }
											if (_distances[i][j] > furthest) { _players[i].furthest = j; furthest = _distances[i][j] }
										}
								}
							tgtx += _players[i].position.x//Calc centre point for group - to be used by long range zombies to aim at.
							tgty += _players[i].position.y
						}
				}
			if(active_players>0)_target_posn=new Point(Math.round(tgtx/active_players),Math.round(tgty/active_players))
		}
		private function calcZombieMoves():void
		{
			var len:int = _zombies.length
			var posn:Point
			var nearest_inf:Object
			var aim_for :Point
			var ang:Number
			var adj:Boolean
			var diffx:int
			var diffy:int
			var turn:Object
			
			for (var i:int = 0; i < len; i++)
				{
				if (_zombies[i].active)
					{	
					posn = _zombies[i].position
					adj=false
					if (posn.x > _target_posn.x - ACTIVE_RANGE && posn.x < _target_posn.x + ACTIVE_RANGE
						&& posn.y > _target_posn.y - ACTIVE_RANGE && posn.y < _target_posn.y + ACTIVE_RANGE)
						{//ACTIVE
							nearest_inf = findNearestPlayer(posn)
							
							diffx = Math.abs(posn.x - _players[nearest_inf.nearest].position.x)
							diffy = Math.abs(posn.y - _players[nearest_inf.nearest].position.y)
							//trace("ZOMBIE "+i+" DISTANCE " + nearest_inf.dist+"  diffs:"+diffx+","+diffy)
							if(diffx<=1 && diffy<=1)
								{
									trace("ZOMBIE "+i+" ADJACENT SO FIGHT nearest:"+nearest_inf.nearest)
									aim_for = _players[nearest_inf.nearest].position
									_players[nearest_inf.nearest].health--//=i
									_players[nearest_inf.nearest].attackedBy=_zombies[i]
									_players[nearest_inf.nearest].doBloodSplatter(3, 0.2)
									adj=true
								}
								else
							if (nearest_inf > LONG_RANGE)
								{//LONG RANGE
									aim_for=_target_posn
								}
								else
								{//SHORT RANGE
									aim_for=_players[nearest_inf.nearest].position
								}

							ang = Math.round(GameMaths.angleToPoint(_zombies[i].position, aim_for ) / 45) +3//Div by 45 to make out of 8
							ang = ang<0?ang+8:ang>8?ang-8:ang
							turn =  { sel:adj?"FIGHT":"MOVE", dir:ang } 
							if (spaceEmptyOfCharacters(_zombies[i], turn)){_zombies[i].doTurn(turn)}
							else
								{
									turn.dir = turn.dir-1>=0?turn.dir-1:turn.dir+7
									if (spaceEmptyOfCharacters(_zombies[i], turn)) { _zombies[i].doTurn(turn) }
										else
											{
												turn.dir = turn.dir + 2 < 8?turn.dir + 2:turn.dir - 6
												if (spaceEmptyOfCharacters(_zombies[i], turn)) { _zombies[i].doTurn(turn) }
											}
								}
							
						}
					_gameboard.positionGrid[posn.x ][posn.y ] = _zombies[i]
					}
				}
		}
		private function findNearestPlayer(posn:Point):Object
		{
			var dist:int
			var ndist:int=9999
			var nearest:int
			for (var i:int = 0; i < 4; i++)
				{
					if (_players[i].active)
						{
							dist = GameMaths.distanceFromToSquared(posn, _players[i].position)
							if (dist < ndist)
								{
									nearest = i
									ndist=dist
								}
						}
				}
			return { nearest:nearest, dist:dist }
		}
		private function calcBotMoves():void
		{
			//Randomish for now
			//Aim for top left. Avoid stepping onto other players.
			var dir_to_use:int = 0
			var selection:String="MOVE"
			var calcx:int
			var calcy:int
			var hit_obj:Object
			for (var i:int = 1; i < 4; i++)
				{
					dir_to_use = 0
					selection="MOVE"
					calcx = Math.abs(_players[i].position.x - _target_posn.x)
					calcy = Math.abs(_players[i].position.y - _target_posn.y)
					if (_players[i].attackedBy)
						{//Being attacked so shoot
							selection = "SHOOT"
							dir_to_use = dirToUse(_players[i].position, _players[i].attackedBy.position)
							_players[i].attackedBy=null
						}
						else
					if (calcx>2 || calcy>2)
						{//Too far from group
							dir_to_use = dirToUse(_players[i].position, _target_posn )
						}
						else
						{//Check if zombie in range for shot
							for (var k:int = 0; k < 8; k++)
								{
									hit_obj=shotWillHit(_players[i], { sel:"SHOOT", dir:k})
									if (hit_obj.hit && hit_obj.target.name.substr(0,1)=="z")
										{
											selection = "SHOOT"
											dir_to_use = k
											break;
										}
										else if (hit_obj.hit) { break;}
								}
						}
					_turn[i] = { sel:selection, dir:dir_to_use }
					if (selection == "MOVE")
						{
							if (!spaceEmptyOfCharacters(_players[i], _turn[i]))
								{
									_turn[i].dir = _turn[i].dir - 1 >= 0?_turn[i].dir - 1:_turn[i].dir + 7
									if (!spaceEmptyOfCharacters(_players[i], _turn[i]))
										{
											_turn[i].dir = _turn[i].dir + 2 < 8?_turn[i].dir + 2:_turn[i].dir - 6
										}
								}
						}
				}

		}
		private function dirToUse(player_posn:Point, target:Point):int
		{
			var dir_to_use:int = 0
			dir_to_use = Math.round(GameMaths.angleToPoint(player_posn, target) / 45) +3//Div by 45 to make out of 8
			dir_to_use = dir_to_use<0?dir_to_use+8:dir_to_use>8?dir_to_use-8:dir_to_use
			return dir_to_use
		}
		private function spaceEmptyOfCharacters(player:Actor, turn:Object):Boolean
		{
			if (turn.sel != "MOVE")
				{
					return true
				}
			else if(turn.dir>-1)
				{
					var posx:int = player.position.x + MovePanel.BUTTON_POSITIONS[turn.dir].x
					var posy:int = player.position.y + MovePanel.BUTTON_POSITIONS[turn.dir].y
					if (posx >= GameBoard.ROWS || posy >= GameBoard.COLS || posx < 0 || posy < 0)
						{
							return false
						}
					else
					if (_gameboard.positionGrid[posx][posy] && _gameboard.positionGrid[posx][posy].name!=player.name) 
						{
							if (posx == _gameboard.positionGrid[posx][posy].position.x && posy== _gameboard.positionGrid[posx][posy].position.y){return false}
								else {return true}
						}
						else return true
				}
			else return false
		}
		private function shotWillHit(player:Actor,turn:Object):Object
		{
			var obj:Object = { }
			var posx:int
			var posy:int
			var dir:Object = MovePanel.BUTTON_POSITIONS[turn.dir]
			for (var i:int = 0; i < 6; i++)
				{
					posx = player.position.x + dir.x * (i + 1)
					posy = player.position.y + dir.y * (i + 1)
					//trace("bullet path i="+i+"  pos="+posx+","+posy)
					if (posx >= 0 && posy >= 0 && posx < GameBoard.COLS && posy < GameBoard.ROWS)
						{
							if (_gameboard.positionGrid[posx][posy])
								{
									if (_gameboard.positionGrid[posx][posy].active)
										{
											obj.hit=true
											obj.target = _gameboard.positionGrid[posx][posy]
											obj.position = obj.target.position
											obj.dist = i
											break;
										}
								}
						}
				}
				if (!obj.hit)
					{
						obj.hit = false
						obj.position = { x:player.position.x + dir.x * 6, y:player.position.y + dir.y * 6 }
						obj.dist = 6
					}
			return obj
		}
		
		public function doTurns():void
		{
			var i:int
			for (i = 0; i < 4; i++)
				{
				if (_players[i].active)
					{
					if (_turn[i].sel == "MOVE")
						{
							_gameboard.positionGrid[_players[i].position.x][_players[i].position.y]=null//Remove player from board
							if (spaceEmptyOfCharacters(_players[i], _turn[i]))	_players[i].doTurn(_turn[i])//Do turn
							_gameboard.positionGrid[_players[i].position.x][_players[i].position.y] = _players[i]//Put player back on board
						}
						else
					if (_turn[i].sel == "SHOOT" && _turn[i].dir>-1)
						{
							var shot:Object = shotWillHit(_players[i], _turn[i])
							if (shot.hit)
								{
									trace("-------------BANG!-------------" + shot.position)
									shot.target.health--
									shot.target.doBloodSplatter(_turn[i].dir, shot.dist * 0.2)
									if (!shot.target.active)	
										{
											_gameboard.positionGrid[shot.position.x][shot.position.y]=null
											Tweener.addTween(shot.target,{alpha:0,delay:shot.dist * 0.2,time:1.5})
										}
								}
								else
								{
									trace("-------------BANG! MISSED-------------"+shot.position)
								}
							_players[i].bullet.alpha = 0.6
							_players[i].bullet.x = _players[i].x
							_players[i].bullet.y = _players[i].y
							Tweener.addTween(_players[i].bullet, { x:(shot.position.x+0.5) * GameBoard.GRID_SIZE, y:(shot.position.y+0.5) * GameBoard.GRID_SIZE, time:shot.dist * 0.2, transition:"Linear" } )
							Tweener.addTween(_players[i].bullet, { alpha:0, delay:shot.dist * 0.2, time:0.1 } )
							_players[i].doTurn(_turn[i])
						}
					}
				}
			_gameboard.clearPositionGrid()
			for (i = 0; i < 4; i++) { if (_players[i].active)_gameboard.positionGrid[_players[i].position.x][_players[i].position.y] = _players[i]  }
			
			calcZombieMoves()
			
			//PLAYER HEALTH CHECK
			for (i = 0; i < 4; i++) 
				{ 
					if (!_players[i].active && _players[i].parent)
						{
							_players[i].parent.removeChild(_players[i])
							_gameboard.positionGrid[_players[i].position.x][_players[i].position.y]=null
						}
				}
			
			if (_turn_timer.parent)_turn_timer.parent.removeChild(_turn_timer)
			Tweener.addTween(_stage,{time:3,onComplete:update})
			Tweener.addTween(_stage,{time:5,onComplete:startNextTurn})
		}
		public function startNextTurn():void
		{
			_turn_timer.startTimer()
			_move_panel.startPanel()
			_stage.addChild(_turn_timer)
		}
		public function update():void
		{
			_gameboard.centrePlayerPiece(_players[_player_num])
		}
		
		public function turnReceived(turn_num:int,player:int, selection:String, dir_to_use:int):void
		{
			//Need to do turn num check
			_turn[player] = { sel:selection, dir:dir_to_use }
		}
		public function renderTurn():void 
		{
			doTurns()
		}
		
		private function dispatchTurn():void
		{
			dispatchEvent(new Event("PLAYER_TURN"))
		}
		public function get PlayerTurns():Array
		{
			return _turn
		}
	}

/***********************************************************************************************************************************************************
 * Actor - Class to contain actor view
 ***********************************************************************************************************************************************************/
class Actor extends Sprite
	{
		private var _holder:Sprite = new Sprite()
		private var _bubble:Sprite = new Sprite()
		private var _bubble_bg:Sprite = new Sprite()
		private var _bullet:Sprite
		private var _tf:TextField = new TextField()
		private var _position:Point = new Point()
		private var _nearest:int
		private var _furthest:int
		private var _close_fighting:int = -1
		private var _health:int = 1
		private var _active:Boolean = true
		private var _blood_splatter:Array = []
		private var _attacked_by:Actor
		
		public function Actor(message_layer:Sprite, colour:uint=0xFFFF00):void
		{
			var gsize:int=GameBoard.GRID_SIZE
			message_layer.addChild(_bubble)
			//Set up textfield
			_tf.textColor=0x000000
			_tf.selectable=false
			_tf.x = gsize+3
			_tf.y = -gsize * 0.2
			
			_bubble.addChild(_bubble_bg)//Add background container for bubble
		}
		//Add a message to speech bubble
		public function addMessage(msg:String):void
		{
			var gsize:int = GameBoard.GRID_SIZE
			_bubble.x = this.x-gsize*0.5
			_bubble.y = this.y-gsize*0.5
			trace("___"+_bubble.x+","+_bubble.y)
			
			_bubble.addChild(_tf)
			_tf.text = msg//Add message
			Tweener.removeTweens(_bubble) //1st remove current tweens on this bubble
			Tweener.addTween(_bubble, { alpha:1, time:0.5 } ) //Tween on
			Tweener.addTween(_bubble, { alpha:0, delay:3, time:0.5, onComplete:removeTextFromBubble } )//Tween off after delay
			//Draw bubble to correct size
			_bubble_bg.graphics.clear()
			_bubble_bg.graphics.lineStyle(2, 0xFFFFFF)
			_bubble_bg.graphics.beginFill(0xDDDDDD,0.7)
			_bubble_bg.graphics.drawRoundRectComplex(gsize,-gsize*0.2,_tf.textWidth+10,gsize*0.5,10,10,1,10)
		}
		//Needed to get rid of text - can't be bothered dealing with embedding text, etc at this stage.
		private function removeTextFromBubble():void { _bubble.removeChild(_tf); }
		
		public function get active():Boolean { return _active }
		
		public function set health(val:int):void { _health = val;_active=_health>0?true:false }
		public function get health():int { return _health }
		
		public function set bullet(b:Sprite):void { _bullet = b }
		public function get bullet():Sprite { return _bullet }
		
		public function set position(pt:Point):void { _position = pt }
		public function get position():Point { return _position }
		
		public function set furthest(val:int):void { _furthest = val}
		public function get furthest():int { return _furthest }
		
		public function set nearest(val:int):void { _nearest = val}
		public function get nearest():int { return _nearest }
		
		public function set fighting(val:int):void { _close_fighting = val}
		public function get fighting():int { return _close_fighting }
		
		public function set attackedBy(attacker:Actor):void { _attacked_by = attacker}
		public function get attackedBy():Actor { return _attacked_by }
		
		public function doTurn(turnObj:Object):void
		{
			//trace("[Actor] - doTurn turnObj.sel="+turnObj.sel+"  dir="+turnObj.dir)
			if (turnObj.sel == "MOVE" && turnObj.dir >= 0) { moveActor(turnObj.dir) }
				else if (turnObj.sel == "FIGHT") fightActor()
				else if(turnObj.sel=="SHOOT") rotateActor(MovePanel.BUTTON_POSITIONS[turnObj.dir].rot-90)
		}
		public function rotateActor(rotate:Number):void
		{
			Tweener.addTween(this,{rotation:rotate, time:0.3})
		}
		private function fightActor():void
		{
			trace("FIGHT"+this.rotation)
			var orig_rot:Number=this.rotation
			Tweener.addTween(this, { rotation:orig_rot - 60, time:0.2 } )
			Tweener.addTween(this, { rotation:orig_rot, delay:0.2,time:2, transition:"easeOutElastic"})
		}
		public function moveActor(dir_index:int):void
		{
			if (_close_fighting == -1)
				{
					var move_obj:Object = MovePanel.BUTTON_POSITIONS[dir_index]
					rotateActor(move_obj.rot - 90)
					Tweener.addTween(this, { x:this.x + move_obj.x * GameBoard.GRID_SIZE, y:this.y + move_obj.y * GameBoard.GRID_SIZE, delay:1, time:3 } )
					_position.x += move_obj.x
					_position.y += move_obj.y
				}
		}
		public function doBloodSplatter(hit_from:int, delay:Number=0):void
		{
			var splatter_dir:int = hit_from - 4 < 0?hit_from + 4:hit_from - 4//Opposite direction for splatter
			var dir_obj:Object=MovePanel.BUTTON_POSITIONS[splatter_dir]
			if (!_blood_splatter[splatter_dir]) _blood_splatter[splatter_dir] = new Sprite()
			_blood_splatter[splatter_dir].alpha=0
			addChild(_blood_splatter[splatter_dir])
			bloodSplatter(_blood_splatter[splatter_dir], 20)
			Tweener.addTween(_blood_splatter[splatter_dir],{alpha:1,delay:delay})
			Tweener.addTween(_blood_splatter[splatter_dir],{alpha:0,delay:delay,time:1.5,transition:"Linear"})
		}
		private function bloodSplatter(container:Sprite,radius:Number):void
		{
			var diameter:Number=radius*2
			container.graphics.clear()
			container.graphics.beginFill(0xFF0000,0.8)
			container.graphics.drawRoundRectComplex(-radius, -radius, diameter, diameter, radius-diameter*Math.random(), radius*2-diameter*2*Math.random(), radius-diameter*Math.random(), radius*2-diameter*Math.random())
			container.graphics.endFill()
			container.rotation = Math.random() * 360
			container.width = radius + radius * Math.random()
			container.height = radius+radius*Math.random()
		}
	}
/***********************************************************************************************************************************************************
 * GameBoard - class to contain gameboard view
 ***********************************************************************************************************************************************************/
class GameBoard extends Sprite
	{
		private var _holder:Sprite = new Sprite()
		public static const GRID_SIZE:int = 40
		public static const COLS:int = 32
		public static const ROWS:int = 32
		
		public static const SCREENW:int = 480
		public static const SCREENH:int = 480
		
		private var _pos_grid:Array
		private var _blank_grid:Array
		
		public function GameBoard(terrain:Sprite):void
		{
			addChild(terrain)
			_holder.graphics.lineStyle(4, 0x000000)
			_holder.graphics.beginFill(0x666666,0.5)
			_holder.graphics.drawRect(0, 0, ROWS * GRID_SIZE, COLS * GRID_SIZE)
			_holder.graphics.endFill()
			for (var i:int = 0; i < ROWS; i++)
				{
					for (var j:int = 0; j < COLS; j++)
					{
						_holder.graphics.lineStyle(1, 0x333333)
						_holder.graphics.drawRect(i*GRID_SIZE,j*GRID_SIZE, GRID_SIZE,GRID_SIZE)
					}
				}
			addChild(_holder)
			initPositionGrid()
		}
		private function initPositionGrid():void
		{
			var r:Array = []
			for (var i:int = 0; i < COLS; i++)
				{
					r[i]=new Array(ROWS)
				}
			_blank_grid = r
			clearPositionGrid()
		}
		public function clearPositionGrid():void { _pos_grid = _blank_grid.concat() }
		public function get positionGrid():Array { return _pos_grid }

		//Move player piece on game board to centre of screen
		public function centrePlayerPiece(p:Actor):void
		{
			Tweener.addTween(this,{x:SCREENW * 0.5 - p.x+GRID_SIZE*0.5, y:SCREENH * 0.5 - p.y+GRID_SIZE*0.5,time:1})
		}
	}
/***********************************************************************************************************************************************************
 * TurnTimer - class to run & display timer
 ***********************************************************************************************************************************************************/
	class TurnTimer extends Sprite
	{
		private var _tf:TextField = new TextField()
		private var _colour_block:Sprite=new Sprite()
		public function TurnTimer():void
		{
			addChild(_colour_block)
			_colour_block.graphics.beginFill(0xFF0000)
			_colour_block.graphics.drawRect(2, 1, 100, 6)
			addChild(_colour_block)
			
			_tf.x = 5
			_tf.y = 8
			_tf.textColor=0xEEEEEE
			addChild(_tf)
			addMessage("ENTER TURN")
			
			var brder:Sprite = new Sprite()
			addChild(brder)
			brder.graphics.lineStyle(2, 0x003300)
			brder.graphics.drawRoundRect(0, 0, 102, 8, 5, 5)
			
			//startTimer()
		}
		public function addMessage(msg:String):void
		{
			_tf.text=msg
		}
		public function startTimer():void
		{
			Tweener.removeTweens(_colour_block)
			Tweener.addTween(_colour_block, { width:1, time:5, transition:"Linear", onComplete:timesUp } )
		}
		private function timesUp():void
		{
			_colour_block.width = 100
			dispatchEvent(new Event(Event.COMPLETE))
			//startTimer()
		}
	}
/***********************************************************************************************************************************************************
 * MovePanel - panel containing movement buttons
 ***********************************************************************************************************************************************************/
	class MovePanel extends Sprite
	{
		private var _button_array:Array = []
		public static const BUTTON_POSITIONS:Array = [ { x:-1, y:-1, rot: -45 }, { x:0, y:-1, rot:0 }, { x:1, y:-1, rot:45 }, { x:1, y:0, rot:90 }, { x:1, y:1, rot:135 }, { x:0, y:1, rot:180 }, { x:-1, y:1, rot: -135 }, { x:-1, y:0, rot: -90 } ]
		private var _directions:Sprite = new Sprite()
		private var _buttons:Sprite = new Sprite()
		private var _selection:Object={}
		public function MovePanel():void
		{
			var unit:Number = GameBoard.GRID_SIZE * 0.5
			
			var b1:Button = new Button("MOVE", "rectangle")
			b1.x=-5
			_buttons.addChild(b1)
			var b2:Button = new Button("SHOOT", "rectangle")
			b2.x=-5
			b2.y = 25
			_buttons.addChild(b2)
			b1.addEventListener(MouseEvent.CLICK, buttonClickedHandler)
			b2.addEventListener(MouseEvent.CLICK,buttonClickedHandler)
			addChild(_buttons)
			for (var i:int = 0; i < 8; i++)
				{
					var b:Button = new Button("b" + i, "triangle",8)
					b.setPosition(BUTTON_POSITIONS[i].x * unit, BUTTON_POSITIONS[i].y * unit, BUTTON_POSITIONS[i].rot)
					b.extra=i
					_button_array.push(b)
					_directions.addChild(b)
					b.addEventListener(MouseEvent.CLICK, directionClickedHandler)
				}
			_directions.x = _directions.y=unit
		}
		public function outOfTime():void
		{
			if (_buttons.parent) removeChild(_buttons)
			if(_directions.parent) removeChild(_directions)
		}
		public function startPanel():void
		{
			_selection = {dir:-1 }
			addChild(_buttons)
		}
		private function buttonClickedHandler(event:MouseEvent):void
		{
			_selection.sel = event.target.name
			trace(event.target.name)
			removeChild(_buttons)
			addChild(_directions)
		}
		private function directionClickedHandler(event:MouseEvent):void
		{
			trace("directionClickedHandler dir="+event.target.extra)
			_selection.dir = event.target.extra
			removeChild(_directions)
		}
		public function timeStarted():void { _selection = { }}
		public function get option_selection():Object{return _selection}
	}
/***********************************************************************************************************************************************************
 * Button - generic button class - triangle or rectangle
 ***********************************************************************************************************************************************************/	
	class Button extends Sprite
	{
		private var _alpha:Number = 0.5
		private var _extra:Object={}
		public function Button(id:String, type:String, size:int=6,bg_colour:uint=0x0000FF,br_colour:uint=0x000099):void
		{
			this.name=id
			this.graphics.beginFill(bg_colour, 1)
			this.graphics.lineStyle(2,br_colour)
			switch(type)
				{
					case "triangle":
						this.graphics.moveTo(0, -size)
						this.graphics.lineTo(size, size)
						this.graphics.lineTo( -size, size)
						this.graphics.lineTo(0, -size)
						
						break;
					case "rectangle":
						this.graphics.drawRoundRect(0, 0, 50, 20, 5, 5)
						var tf:TextField = new TextField()
						tf.x = 2; tf.y = 2;
						tf.textColor=0xFFFFFF
						tf.text = id
						tf.selectable = false
						tf.mouseEnabled=false
						addChild(tf)
						_alpha=0.8
						break;
				}
			this.graphics.endFill()
			this.addEventListener(MouseEvent.MOUSE_OVER, mouseOverHandler)
			this.addEventListener(MouseEvent.MOUSE_OUT, mouseOutHandler)
			//this.addEventListener(MouseEvent.CLICK, mouseClickHandler)
			this.useHandCursor = true
			this.buttonMode = true
			this.alpha=_alpha
		}
		private function mouseOverHandler(event:MouseEvent):void { this.alpha = 1 }
		private function mouseOutHandler(event:MouseEvent):void { this.alpha = _alpha }
		private function mouseClickHandler(event:MouseEvent):void { dispatchEvent(event) }
		
		public function setPosition(posx:Number, posy:Number, rot:Number=9999):void	
		{
			this.x = posx
			this.y = posy
			if(rot!=9999)this.rotation=rot
		}
		public function set extra(obj:Object):void { _extra = obj }
		public function get extra():Object { return _extra }
	}


	class GameMaths extends EventDispatcher
	{
		public function GameMaths():void {}
		
		public static function angleToPoint(from:Point, to:Point):Number
		{
			var dx:Number = to.x - from.x
			var dy:Number = to.y - from.y
			return Math.atan2(dy,dx) *57.29577951308232   //  180/Math.PI=57.29577951308232
		}
		public static function distanceFromToSquared(from:Point, to:Point):Number
		{
			var dx:Number = from.x - to.x
			var dy:Number = from.y - to.y
			return dx*dx+dy*dy
		}

	}