package org.flixel
{
import flash.display.Graphics;
import org.flixel.plugin.DebugPathDisplay;
/**
* This is a simple path data container. Basically a list of points that
* a FlxObject
can follow. Also has code for drawing debug visuals.
* FlxTilemap.findPath()
returns a path object, but you can
* also just make your own, using the add()
functions below
* or by creating your own array of points.
*
* @author Adam Atomic
*/
public class FlxPath
{
/**
* The list of FlxPoint
s that make up the path data.
*/
public var nodes:Array;
/**
* Specify a debug display color for the path. Default is white.
*/
public var debugColor:uint;
/**
* Specify a debug display scroll factor for the path. Default is (1,1).
* NOTE: does not affect world movement! Object scroll factors take care of that.
*/
public var debugScrollFactor:FlxPoint;
/**
* Setting this to true will prevent the object from appearing
* when the visual debug mode in the debugger overlay is toggled on.
*/
public var ignoreDrawDebug:Boolean;
/**
* Internal helper for keeping new variable instantiations under control.
*/
protected var _point:FlxPoint;
/**
* Instantiate a new path object.
*
* @param Nodes Optional, can specify all the points for the path up front if you want.
*/
public function FlxPath(Nodes:Array=null)
{
if(Nodes == null)
nodes = new Array();
else
nodes = Nodes;
_point = new FlxPoint();
debugScrollFactor = new FlxPoint(1.0,1.0);
debugColor = 0xffffff;
var debugPathDisplay:DebugPathDisplay = manager;
if(debugPathDisplay != null)
debugPathDisplay.add(this);
}
/**
* Clean up memory.
*/
public function destroy():void
{
var debugPathDisplay:DebugPathDisplay = manager;
if(debugPathDisplay != null)
debugPathDisplay.remove(this);
debugScrollFactor = null;
_point = null;
nodes = null;
}
/**
* Add a new node to the end of the path at the specified location.
*
* @param X X position of the new path point in world coordinates.
* @param Y Y position of the new path point in world coordinates.
*/
public function add(X:Number,Y:Number):void
{
nodes.push(new FlxPoint(X,Y));
}
/**
* Add a new node to the path at the specified location and index within the path.
*
* @param X X position of the new path point in world coordinates.
* @param Y Y position of the new path point in world coordinates.
* @param Index Where within the list of path nodes to insert this new point.
*/
public function addAt(X:Number, Y:Number, Index:uint):void
{
if(Index > nodes.length)
Index = nodes.length;
nodes.splice(Index,0,new FlxPoint(X,Y));
}
/**
* Sometimes its easier or faster to just pass a point object instead of separate X and Y coordinates.
* This also gives you the option of not creating a new node but actually adding that specific
* FlxPoint
object to the path. This allows you to do neat things, like dynamic paths.
*
* @param Node The point in world coordinates you want to add to the path.
* @param AsReference Whether to add the point as a reference, or to create a new point with the specified values.
*/
public function addPoint(Node:FlxPoint,AsReference:Boolean=false):void
{
if(AsReference)
nodes.push(Node);
else
nodes.push(new FlxPoint(Node.x,Node.y));
}
/**
* Sometimes its easier or faster to just pass a point object instead of separate X and Y coordinates.
* This also gives you the option of not creating a new node but actually adding that specific
* FlxPoint
object to the path. This allows you to do neat things, like dynamic paths.
*
* @param Node The point in world coordinates you want to add to the path.
* @param Index Where within the list of path nodes to insert this new point.
* @param AsReference Whether to add the point as a reference, or to create a new point with the specified values.
*/
public function addPointAt(Node:FlxPoint,Index:uint,AsReference:Boolean=false):void
{
if(Index > nodes.length)
Index = nodes.length;
if(AsReference)
nodes.splice(Index,0,Node);
else
nodes.splice(Index,0,new FlxPoint(Node.x,Node.y));
}
/**
* Remove a node from the path.
* NOTE: only works with points added by reference or with references from nodes
itself!
*
* @param Node The point object you want to remove from the path.
*
* @return The node that was excised. Returns null if the node was not found.
*/
public function remove(Node:FlxPoint):FlxPoint
{
var index:int = nodes.indexOf(Node);
if(index >= 0)
return nodes.splice(index,1);
else
return null;
}
/**
* Remove a node from the path using the specified position in the list of path nodes.
*
* @param Index Where within the list of path nodes you want to remove a node.
*
* @return The node that was excised. Returns null if there were no nodes in the path.
*/
public function removeAt(Index:uint):FlxPoint
{
if(nodes.length <= 0)
return null;
if(Index >= nodes.length)
Index = nodes.length-1;
return nodes.splice(Index,1);
}
/**
* Get the first node in the list.
*
* @return The first node in the path.
*/
public function head():FlxPoint
{
if(nodes.length > 0)
return nodes[0];
return null;
}
/**
* Get the last node in the list.
*
* @return The last node in the path.
*/
public function tail():FlxPoint
{
if(nodes.length > 0)
return nodes[nodes.length-1];
return null;
}
/**
* While this doesn't override FlxBasic.drawDebug()
, the behavior is very similar.
* Based on this path data, it draws a simple lines-and-boxes representation of the path
* if the visual debug mode was toggled in the debugger overlay. You can use debugColor
* and debugScrollFactor
to control the path's appearance.
*
* @param Camera The camera object the path will draw to.
*/
public function drawDebug(Camera:FlxCamera=null):void
{
if(nodes.length <= 0)
return;
if(Camera == null)
Camera = FlxG.camera;
//Set up our global flash graphics object to draw out the path
var gfx:Graphics = FlxG.flashGfx;
gfx.clear();
//Then fill up the object with node and path graphics
var node:FlxPoint;
var nextNode:FlxPoint;
var i:uint = 0;
var l:uint = nodes.length;
while(i < l)
{
//get a reference to the current node
node = nodes[i] as FlxPoint;
//find the screen position of the node on this camera
_point.x = node.x - int(Camera.scroll.x*debugScrollFactor.x); //copied from getScreenXY()
_point.y = node.y - int(Camera.scroll.y*debugScrollFactor.y);
_point.x = int(_point.x + ((_point.x > 0)?0.0000001:-0.0000001));
_point.y = int(_point.y + ((_point.y > 0)?0.0000001:-0.0000001));
//decide what color this node should be
var nodeSize:uint = 2;
if((i == 0) || (i == l-1))
nodeSize *= 2;
var nodeColor:uint = debugColor;
if(l > 1)
{
if(i == 0)
nodeColor = FlxG.GREEN;
else if(i == l-1)
nodeColor = FlxG.RED;
}
//draw a box for the node
gfx.beginFill(nodeColor,0.5);
gfx.lineStyle();
gfx.drawRect(_point.x-nodeSize*0.5,_point.y-nodeSize*0.5,nodeSize,nodeSize);
gfx.endFill();
//then find the next node in the path
var linealpha:Number = 0.3;
if(i < l-1)
nextNode = nodes[i+1];
else
{
nextNode = nodes[0];
linealpha = 0.15;
}
//then draw a line to the next node
gfx.moveTo(_point.x,_point.y);
gfx.lineStyle(1,debugColor,linealpha);
_point.x = nextNode.x - int(Camera.scroll.x*debugScrollFactor.x); //copied from getScreenXY()
_point.y = nextNode.y - int(Camera.scroll.y*debugScrollFactor.y);
_point.x = int(_point.x + ((_point.x > 0)?0.0000001:-0.0000001));
_point.y = int(_point.y + ((_point.y > 0)?0.0000001:-0.0000001));
gfx.lineTo(_point.x,_point.y);
i++;
}
//then stamp the path down onto the game buffer
Camera.buffer.draw(FlxG.flashGfxSprite);
}
static public function get manager():DebugPathDisplay
{
return FlxG.getPlugin(DebugPathDisplay) as DebugPathDisplay;
}
}
}