Left 2 Die (Multiplayer] forked from: colin challenge for professionals
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
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
}
}
