PageRenderTime 140ms CodeModel.GetById 25ms app.highlight 106ms RepoModel.GetById 0ms app.codeStats 1ms

/org/flixel/FlxObject.as

https://github.com/taitehughes/flixel
ActionScript | 1173 lines | 682 code | 84 blank | 407 comment | 187 complexity | 1aca37ce89b9e7a5b655b3c1ece7db3d MD5 | raw file
   1package org.flixel
   2{
   3	import flash.display.Graphics;
   4	import flash.display.Sprite;
   5	import flash.geom.Point;
   6	
   7	import org.flixel.FlxBasic;
   8	
   9	/**
  10	 * This is the base class for most of the display objects (<code>FlxSprite</code>, <code>FlxText</code>, etc).
  11	 * It includes some basic attributes about game objects, including retro-style flickering,
  12	 * basic state information, sizes, scrolling, and basic physics and motion.
  13	 * 
  14	 * @author	Adam Atomic
  15	 */
  16	public class FlxObject extends FlxBasic
  17	{
  18		/**
  19		 * Generic value for "left" Used by <code>facing</code>, <code>allowCollisions</code>, and <code>touching</code>.
  20		 */
  21		static public const LEFT:uint	= 0x0001;
  22		/**
  23		 * Generic value for "right" Used by <code>facing</code>, <code>allowCollisions</code>, and <code>touching</code>.
  24		 */
  25		static public const RIGHT:uint	= 0x0010;
  26		/**
  27		 * Generic value for "up" Used by <code>facing</code>, <code>allowCollisions</code>, and <code>touching</code>.
  28		 */
  29		static public const UP:uint		= 0x0100;
  30		/**
  31		 * Generic value for "down" Used by <code>facing</code>, <code>allowCollisions</code>, and <code>touching</code>.
  32		 */
  33		static public const DOWN:uint	= 0x1000;
  34		
  35		/**
  36		 * Special-case constant meaning no collisions, used mainly by <code>allowCollisions</code> and <code>touching</code>.
  37		 */
  38		static public const NONE:uint	= 0;
  39		/**
  40		 * Special-case constant meaning up, used mainly by <code>allowCollisions</code> and <code>touching</code>.
  41		 */
  42		static public const CEILING:uint= UP;
  43		/**
  44		 * Special-case constant meaning down, used mainly by <code>allowCollisions</code> and <code>touching</code>.
  45		 */
  46		static public const FLOOR:uint	= DOWN;
  47		/**
  48		 * Special-case constant meaning only the left and right sides, used mainly by <code>allowCollisions</code> and <code>touching</code>.
  49		 */
  50		static public const WALL:uint	= LEFT | RIGHT;
  51		/**
  52		 * Special-case constant meaning any direction, used mainly by <code>allowCollisions</code> and <code>touching</code>.
  53		 */
  54		static public const ANY:uint	= LEFT | RIGHT | UP | DOWN;
  55		
  56		/**
  57		 * Handy constant used during collision resolution (see <code>separateX()</code> and <code>separateY()</code>).
  58		 */
  59		static public const OVERLAP_BIAS:Number = 4;
  60		
  61		/**
  62		 * Path behavior controls: move from the start of the path to the end then stop.
  63		 */
  64		static public const PATH_FORWARD:uint			= 0x000000;
  65		/**
  66		 * Path behavior controls: move from the end of the path to the start then stop.
  67		 */
  68		static public const PATH_BACKWARD:uint			= 0x000001;
  69		/**
  70		 * Path behavior controls: move from the start of the path to the end then directly back to the start, and start over.
  71		 */
  72		static public const PATH_LOOP_FORWARD:uint		= 0x000010;
  73		/**
  74		 * Path behavior controls: move from the end of the path to the start then directly back to the end, and start over.
  75		 */
  76		static public const PATH_LOOP_BACKWARD:uint		= 0x000100;
  77		/**
  78		 * Path behavior controls: move from the start of the path to the end then turn around and go back to the start, over and over.
  79		 */
  80		static public const PATH_YOYO:uint				= 0x001000;
  81		/**
  82		 * Path behavior controls: ignores any vertical component to the path data, only follows side to side.
  83		 */
  84		static public const PATH_HORIZONTAL_ONLY:uint	= 0x010000;
  85		/**
  86		 * Path behavior controls: ignores any horizontal component to the path data, only follows up and down.
  87		 */
  88		static public const PATH_VERTICAL_ONLY:uint		= 0x100000;
  89		
  90		/**
  91		 * X position of the upper left corner of this object in world space.
  92		 */
  93		public var x:Number;
  94		/**
  95		 * Y position of the upper left corner of this object in world space.
  96		 */
  97		public var y:Number;
  98		/**
  99		 * The width of this object.
 100		 */
 101		public var width:Number;
 102		/**
 103		 * The height of this object.
 104		 */
 105		public var height:Number;
 106
 107		/**
 108		 * Whether an object will move/alter position after a collision.
 109		 */
 110		public var immovable:Boolean;
 111		
 112		/**
 113		 * The basic speed of this object.
 114		 */
 115		public var velocity:FlxPoint;
 116		/**
 117		 * The virtual mass of the object. Default value is 1.
 118		 * Currently only used with <code>elasticity</code> during collision resolution.
 119		 * Change at your own risk; effects seem crazy unpredictable so far!
 120		 */
 121		public var mass:Number;
 122		/**
 123		 * The bounciness of this object.  Only affects collisions.  Default value is 0, or "not bouncy at all."
 124		 */
 125		public var elasticity:Number;
 126		/**
 127		 * How fast the speed of this object is changing.
 128		 * Useful for smooth movement and gravity.
 129		 */
 130		public var acceleration:FlxPoint;
 131		/**
 132		 * This isn't drag exactly, more like deceleration that is only applied
 133		 * when acceleration is not affecting the sprite.
 134		 */
 135		public var drag:FlxPoint;
 136		/**
 137		 * If you are using <code>acceleration</code>, you can use <code>maxVelocity</code> with it
 138		 * to cap the speed automatically (very useful!).
 139		 */
 140		public var maxVelocity:FlxPoint;
 141		/**
 142		 * Set the angle of a sprite to rotate it.
 143		 * WARNING: rotating sprites decreases rendering
 144		 * performance for this sprite by a factor of 10x!
 145		 */
 146		public var angle:Number;
 147		/**
 148		 * This is how fast you want this sprite to spin.
 149		 */
 150		public var angularVelocity:Number;
 151		/**
 152		 * How fast the spin speed should change.
 153		 */
 154		public var angularAcceleration:Number;
 155		/**
 156		 * Like <code>drag</code> but for spinning.
 157		 */
 158		public var angularDrag:Number;
 159		/**
 160		 * Use in conjunction with <code>angularAcceleration</code> for fluid spin speed control.
 161		 */
 162		public var maxAngular:Number;
 163		/**
 164		 * Should always represent (0,0) - useful for different things, for avoiding unnecessary <code>new</code> calls.
 165		 */
 166		static protected const _pZero:FlxPoint = new FlxPoint();
 167		
 168		/**
 169		 * A point that can store numbers from 0 to 1 (for X and Y independently)
 170		 * that governs how much this object is affected by the camera subsystem.
 171		 * 0 means it never moves, like a HUD element or far background graphic.
 172		 * 1 means it scrolls along a the same speed as the foreground layer.
 173		 * scrollFactor is initialized as (1,1) by default.
 174		 */
 175		public var scrollFactor:FlxPoint;
 176		/**
 177		 * Internal helper used for retro-style flickering.
 178		 */
 179		protected var _flicker:Boolean;
 180		/**
 181		 * Internal helper used for retro-style flickering.
 182		 */
 183		protected var _flickerTimer:Number;
 184		/**
 185		 * Handy for storing health percentage or armor points or whatever.
 186		 */
 187		public var health:Number;
 188		/**
 189		 * This is just a pre-allocated x-y point container to be used however you like
 190		 */
 191		protected var _point:FlxPoint;
 192		/**
 193		 * This is just a pre-allocated rectangle container to be used however you like
 194		 */
 195		protected var _rect:FlxRect;
 196		/**
 197		 * Set this to false if you want to skip the automatic motion/movement stuff (see <code>updateMotion()</code>).
 198		 * FlxObject and FlxSprite default to true.
 199		 * FlxText, FlxTileblock, FlxTilemap and FlxSound default to false.
 200		 */
 201		public var moves:Boolean;
 202		/**
 203		 * Bit field of flags (use with UP, DOWN, LEFT, RIGHT, etc) indicating surface contacts.
 204		 * Use bitwise operators to check the values stored here, or use touching(), justStartedTouching(), etc.
 205		 * You can even use them broadly as boolean values if you're feeling saucy!
 206		 */
 207		public var touching:uint;
 208		/**
 209		 * Bit field of flags (use with UP, DOWN, LEFT, RIGHT, etc) indicating surface contacts from the previous game loop step.
 210		 * Use bitwise operators to check the values stored here, or use touching(), justStartedTouching(), etc.
 211		 * You can even use them broadly as boolean values if you're feeling saucy!
 212		 */
 213		public var wasTouching:uint;
 214		/**
 215		 * Bit field of flags (use with UP, DOWN, LEFT, RIGHT, etc) indicating collision directions.
 216		 * Use bitwise operators to check the values stored here.
 217		 * Useful for things like one-way platforms (e.g. allowCollisions = UP;)
 218		 * The accessor "solid" just flips this variable between NONE and ANY.
 219		 */
 220		public var allowCollisions:uint;
 221		
 222		/**
 223		 * Important variable for collision processing.
 224		 * By default this value is set automatically during <code>preUpdate()</code>.
 225		 */
 226		public var last:FlxPoint;
 227		
 228		/**
 229		 * A reference to a path object.  Null by default, assigned by <code>followPath()</code>.
 230		 */
 231		public var path:FlxPath;
 232		/**
 233		 * The speed at which the object is moving on the path.
 234		 * When an object completes a non-looping path circuit,
 235		 * the pathSpeed will be zeroed out, but the <code>path</code> reference
 236		 * will NOT be nulled out.  So <code>pathSpeed</code> is a good way
 237		 * to check if this object is currently following a path or not.
 238		 */
 239		public var pathSpeed:Number;
 240		/**
 241		 * The angle in degrees between this object and the next node, where 0 is directly upward, and 90 is to the right.
 242		 */
 243		public var pathAngle:Number;
 244		/**
 245		 * Internal helper, tracks which node of the path this object is moving toward.
 246		 */
 247		protected var _pathNodeIndex:int;
 248		/**
 249		 * Internal tracker for path behavior flags (like looping, horizontal only, etc).
 250		 */
 251		protected var _pathMode:uint;
 252		/**
 253		 * Internal helper for node navigation, specifically yo-yo and backwards movement.
 254		 */
 255		protected var _pathInc:int;
 256		/**
 257		 * Internal flag for whether hte object's angle should be adjusted to the path angle during path follow behavior.
 258		 */
 259		protected var _pathRotate:Boolean;
 260		
 261		/**
 262		 * Instantiates a <code>FlxObject</code>.
 263		 * 
 264		 * @param	X		The X-coordinate of the point in space.
 265		 * @param	Y		The Y-coordinate of the point in space.
 266		 * @param	Width	Desired width of the rectangle.
 267		 * @param	Height	Desired height of the rectangle.
 268		 */
 269		public function FlxObject(X:Number=0,Y:Number=0,Width:Number=0,Height:Number=0)
 270		{
 271			x = X;
 272			y = Y;
 273			last = new FlxPoint(x,y);
 274			width = Width;
 275			height = Height;
 276			mass = 1.0;
 277			elasticity = 0.0;
 278
 279			immovable = false;
 280			moves = true;
 281			
 282			touching = NONE;
 283			wasTouching = NONE;
 284			allowCollisions = ANY;
 285			
 286			velocity = new FlxPoint();
 287			acceleration = new FlxPoint();
 288			drag = new FlxPoint();
 289			maxVelocity = new FlxPoint(10000,10000);
 290			
 291			angle = 0;
 292			angularVelocity = 0;
 293			angularAcceleration = 0;
 294			angularDrag = 0;
 295			maxAngular = 10000;
 296			
 297			scrollFactor = new FlxPoint(1.0,1.0);
 298			_flicker = false;
 299			_flickerTimer = 0;
 300			
 301			_point = new FlxPoint();
 302			_rect = new FlxRect();
 303			
 304			path = null;
 305			pathSpeed = 0;
 306			pathAngle = 0;
 307		}
 308		
 309		/**
 310		 * Override this function to null out variables or
 311		 * manually call destroy() on class members if necessary.
 312		 * Don't forget to call super.destroy()!
 313		 */
 314		override public function destroy():void
 315		{
 316			velocity = null;
 317			acceleration = null;
 318			drag = null;
 319			maxVelocity = null;
 320			scrollFactor = null;
 321			_point = null;
 322			_rect = null;
 323			last = null;
 324			cameras = null;
 325			if(path != null)
 326				path.destroy();
 327			path = null;
 328		}
 329		
 330		/**
 331		 * Pre-update is called right before <code>update()</code> on each object in the game loop.
 332		 * In <code>FlxObject</code> it controls the flicker timer,
 333		 * tracking the last coordinates for collision purposes,
 334		 * and checking if the object is moving along a path or not.
 335		 */
 336		override public function preUpdate():void
 337		{
 338			_ACTIVECOUNT++;
 339			
 340			if(_flickerTimer != 0)
 341			{
 342				if(_flickerTimer > 0)
 343				{
 344					_flickerTimer = _flickerTimer - FlxG.elapsed;
 345					if(_flickerTimer <= 0)
 346					{
 347						_flickerTimer = 0;
 348						_flicker = false;
 349					}
 350				}
 351			}
 352			
 353			last.x = x;
 354			last.y = y;
 355			
 356			if((path != null) && (pathSpeed != 0) && (path.nodes[_pathNodeIndex] != null))
 357				updatePathMotion();
 358		}
 359		
 360		/**
 361		 * Post-update is called right after <code>update()</code> on each object in the game loop.
 362		 * In <code>FlxObject</code> this function handles integrating the objects motion
 363		 * based on the velocity and acceleration settings, and tracking/clearing the <code>touching</code> flags.
 364		 */
 365		override public function postUpdate():void
 366		{
 367			if(moves)
 368				updateMotion();
 369			
 370			wasTouching = touching;
 371			touching = NONE;
 372		}
 373		
 374		/**
 375		 * Internal function for updating the position and speed of this object.
 376		 * Useful for cases when you need to update this but are buried down in too many supers.
 377		 * Does a slightly fancier-than-normal integration to help with higher fidelity framerate-independenct motion.
 378		 */
 379		protected function updateMotion():void
 380		{
 381			var delta:Number;
 382			var velocityDelta:Number;
 383
 384			velocityDelta = (FlxU.computeVelocity(angularVelocity,angularAcceleration,angularDrag,maxAngular) - angularVelocity)/2;
 385			angularVelocity += velocityDelta; 
 386			angle += angularVelocity*FlxG.elapsed;
 387			angularVelocity += velocityDelta;
 388			
 389			velocityDelta = (FlxU.computeVelocity(velocity.x,acceleration.x,drag.x,maxVelocity.x) - velocity.x)/2;
 390			velocity.x += velocityDelta;
 391			delta = velocity.x*FlxG.elapsed;
 392			velocity.x += velocityDelta;
 393			x += delta;
 394			
 395			velocityDelta = (FlxU.computeVelocity(velocity.y,acceleration.y,drag.y,maxVelocity.y) - velocity.y)/2;
 396			velocity.y += velocityDelta;
 397			delta = velocity.y*FlxG.elapsed;
 398			velocity.y += velocityDelta;
 399			y += delta;
 400		}
 401		
 402		/**
 403		 * Rarely called, and in this case just increments the visible objects count and calls <code>drawDebug()</code> if necessary.
 404		 */
 405		override public function draw():void
 406		{
 407			if(cameras == null)
 408				cameras = FlxG.cameras;
 409			var camera:FlxCamera;
 410			var i:uint = 0;
 411			var l:uint = cameras.length;
 412			while(i < l)
 413			{
 414				camera = cameras[i++];
 415				if(!onScreen(camera))
 416					continue;
 417				_VISIBLECOUNT++;
 418				if(FlxG.visualDebug && !ignoreDrawDebug)
 419					drawDebug(camera);
 420			}
 421		}
 422		
 423		/**
 424		 * Override this function to draw custom "debug mode" graphics to the
 425		 * specified camera while the debugger's visual mode is toggled on.
 426		 * 
 427		 * @param	Camera	Which camera to draw the debug visuals to.
 428		 */
 429		override public function drawDebug(Camera:FlxCamera=null):void
 430		{
 431			if(Camera == null)
 432				Camera = FlxG.camera;
 433
 434			//get bounding box coordinates
 435			var boundingBoxX:Number = x - int(Camera.scroll.x*scrollFactor.x); //copied from getScreenXY()
 436			var boundingBoxY:Number = y - int(Camera.scroll.y*scrollFactor.y);
 437			boundingBoxX = int(boundingBoxX + ((boundingBoxX > 0)?0.0000001:-0.0000001));
 438			boundingBoxY = int(boundingBoxY + ((boundingBoxY > 0)?0.0000001:-0.0000001));
 439			var boundingBoxWidth:int = (width != int(width))?width:width-1;
 440			var boundingBoxHeight:int = (height != int(height))?height:height-1;
 441
 442			//fill static graphics object with square shape
 443			var gfx:Graphics = FlxG.flashGfx;
 444			gfx.clear();
 445			gfx.moveTo(boundingBoxX,boundingBoxY);
 446			var boundingBoxColor:uint;
 447			if(allowCollisions)
 448			{
 449				if(allowCollisions != ANY)
 450					boundingBoxColor = FlxG.PINK;
 451				if(immovable)
 452					boundingBoxColor = FlxG.GREEN;
 453				else
 454					boundingBoxColor = FlxG.RED;
 455			}
 456			else
 457				boundingBoxColor = FlxG.BLUE;
 458			gfx.lineStyle(1,boundingBoxColor,0.5);
 459			gfx.lineTo(boundingBoxX+boundingBoxWidth,boundingBoxY);
 460			gfx.lineTo(boundingBoxX+boundingBoxWidth,boundingBoxY+boundingBoxHeight);
 461			gfx.lineTo(boundingBoxX,boundingBoxY+boundingBoxHeight);
 462			gfx.lineTo(boundingBoxX,boundingBoxY);
 463			
 464			//draw graphics shape to camera buffer
 465			Camera.buffer.draw(FlxG.flashGfxSprite);
 466		}
 467		
 468		/**
 469		 * Call this function to give this object a path to follow.
 470		 * If the path does not have at least one node in it, this function
 471		 * will log a warning message and return.
 472		 * 
 473		 * @param	Path		The <code>FlxPath</code> you want this object to follow.
 474		 * @param	Speed		How fast to travel along the path in pixels per second.
 475		 * @param	Mode		Optional, controls the behavior of the object following the path using the path behavior constants.  Can use multiple flags at once, for example PATH_YOYO|PATH_HORIZONTAL_ONLY will make an object move back and forth along the X axis of the path only.
 476		 * @param	AutoRotate	Automatically point the object toward the next node.  Assumes the graphic is pointing upward.  Default behavior is false, or no automatic rotation.
 477		 */
 478		public function followPath(Path:FlxPath,Speed:Number=100,Mode:uint=PATH_FORWARD,AutoRotate:Boolean=false):void
 479		{
 480			if(Path.nodes.length <= 0)
 481			{
 482				FlxG.log("WARNING: Paths need at least one node in them to be followed.");
 483				return;
 484			}
 485			
 486			path = Path;
 487			pathSpeed = FlxU.abs(Speed);
 488			_pathMode = Mode;
 489			_pathRotate = AutoRotate;
 490			
 491			//get starting node
 492			if((_pathMode == PATH_BACKWARD) || (_pathMode == PATH_LOOP_BACKWARD))
 493			{
 494				_pathNodeIndex = path.nodes.length-1;
 495				_pathInc = -1;
 496			}
 497			else
 498			{
 499				_pathNodeIndex = 0;
 500				_pathInc = 1;
 501			}
 502		}
 503		
 504		/**
 505		 * Tells this object to stop following the path its on.
 506		 * 
 507		 * @param	DestroyPath		Tells this function whether to call destroy on the path object.  Default value is false.
 508		 */
 509		public function stopFollowingPath(DestroyPath:Boolean=false):void
 510		{
 511			pathSpeed = 0;
 512			if(DestroyPath && (path != null))
 513			{
 514				path.destroy();
 515				path = null;
 516			}
 517		}
 518		
 519		/**
 520		 * Internal function that decides what node in the path to aim for next based on the behavior flags.
 521		 * 
 522		 * @return	The node (a <code>FlxPoint</code> object) we are aiming for next.
 523		 */
 524		protected function advancePath(Snap:Boolean=true):FlxPoint
 525		{
 526			if(Snap)
 527			{
 528				var oldNode:FlxPoint = path.nodes[_pathNodeIndex];
 529				if(oldNode != null)
 530				{
 531					if((_pathMode & PATH_VERTICAL_ONLY) == 0)
 532						x = oldNode.x - width*0.5;
 533					if((_pathMode & PATH_HORIZONTAL_ONLY) == 0)
 534						y = oldNode.y - height*0.5;
 535				}
 536			}
 537			
 538			_pathNodeIndex += _pathInc;
 539			
 540			if((_pathMode & PATH_BACKWARD) > 0)
 541			{
 542				if(_pathNodeIndex < 0)
 543				{
 544					_pathNodeIndex = 0;
 545					pathSpeed = 0;
 546				}
 547			}
 548			else if((_pathMode & PATH_LOOP_FORWARD) > 0)
 549			{
 550				if(_pathNodeIndex >= path.nodes.length)
 551					_pathNodeIndex = 0;
 552			}
 553			else if((_pathMode & PATH_LOOP_BACKWARD) > 0)
 554			{
 555				if(_pathNodeIndex < 0)
 556				{
 557					_pathNodeIndex = path.nodes.length-1;
 558					if(_pathNodeIndex < 0)
 559						_pathNodeIndex = 0;
 560				}
 561			}
 562			else if((_pathMode & PATH_YOYO) > 0)
 563			{
 564				if(_pathInc > 0)
 565				{
 566					if(_pathNodeIndex >= path.nodes.length)
 567					{
 568						_pathNodeIndex = path.nodes.length-2;
 569						if(_pathNodeIndex < 0)
 570							_pathNodeIndex = 0;
 571						_pathInc = -_pathInc;
 572					}
 573				}
 574				else if(_pathNodeIndex < 0)
 575				{
 576					_pathNodeIndex = 1;
 577					if(_pathNodeIndex >= path.nodes.length)
 578						_pathNodeIndex = path.nodes.length-1;
 579					if(_pathNodeIndex < 0)
 580						_pathNodeIndex = 0;
 581					_pathInc = -_pathInc;
 582				}
 583			}
 584			else
 585			{
 586				if(_pathNodeIndex >= path.nodes.length)
 587				{
 588					_pathNodeIndex = path.nodes.length-1;
 589					pathSpeed = 0;
 590				}
 591			}
 592
 593			return path.nodes[_pathNodeIndex];
 594		}
 595		
 596		/**
 597		 * Internal function for moving the object along the path.
 598		 * Generally this function is called automatically by <code>preUpdate()</code>.
 599		 * The first half of the function decides if the object can advance to the next node in the path,
 600		 * while the second half handles actually picking a velocity toward the next node.
 601		 */
 602		protected function updatePathMotion():void
 603		{
 604			//first check if we need to be pointing at the next node yet
 605			_point.x = x + width*0.5;
 606			_point.y = y + height*0.5;
 607			var node:FlxPoint = path.nodes[_pathNodeIndex];
 608			var deltaX:Number = node.x - _point.x;
 609			var deltaY:Number = node.y - _point.y;
 610			
 611			var horizontalOnly:Boolean = (_pathMode & PATH_HORIZONTAL_ONLY) > 0;
 612			var verticalOnly:Boolean = (_pathMode & PATH_VERTICAL_ONLY) > 0;
 613			
 614			if(horizontalOnly)
 615			{
 616				if(((deltaX>0)?deltaX:-deltaX) < pathSpeed*FlxG.elapsed)
 617					node = advancePath();
 618			}
 619			else if(verticalOnly)
 620			{
 621				if(((deltaY>0)?deltaY:-deltaY) < pathSpeed*FlxG.elapsed)
 622					node = advancePath();
 623			}
 624			else
 625			{
 626				if(Math.sqrt(deltaX*deltaX + deltaY*deltaY) < pathSpeed*FlxG.elapsed)
 627					node = advancePath();
 628			}
 629			
 630			//then just move toward the current node at the requested speed
 631			if(pathSpeed != 0)
 632			{
 633				//set velocity based on path mode
 634				_point.x = x + width*0.5;
 635				_point.y = y + height*0.5;
 636				if(horizontalOnly || (_point.y == node.y))
 637				{
 638					velocity.x = (_point.x < node.x)?pathSpeed:-pathSpeed;
 639					if(velocity.x < 0)
 640						pathAngle = -90;
 641					else
 642						pathAngle = 90;
 643					if(!horizontalOnly)
 644						velocity.y = 0;
 645				}
 646				else if(verticalOnly || (_point.x == node.x))
 647				{
 648					velocity.y = (_point.y < node.y)?pathSpeed:-pathSpeed;
 649					if(velocity.y < 0)
 650						pathAngle = 0;
 651					else
 652						pathAngle = 180;
 653					if(!verticalOnly)
 654						velocity.x = 0;
 655				}
 656				else
 657				{
 658					pathAngle = FlxU.getAngle(_point,node);
 659					FlxU.rotatePoint(0,pathSpeed,0,0,pathAngle,velocity);
 660				}
 661				
 662				//then set object rotation if necessary
 663				if(_pathRotate)
 664				{
 665					angularVelocity = 0;
 666					angularAcceleration = 0;
 667					angle = pathAngle;
 668				}
 669			}			
 670		}
 671		
 672		/**
 673		 * Checks to see if some <code>FlxObject</code> overlaps this <code>FlxObject</code> or <code>FlxGroup</code>.
 674		 * If the group has a LOT of things in it, it might be faster to use <code>FlxG.overlaps()</code>.
 675		 * WARNING: Currently tilemaps do NOT support screen space overlap checks!
 676		 * 
 677		 * @param	ObjectOrGroup	The object or group being tested.
 678		 * @param	InScreenSpace	Whether to take scroll factors into account when checking for overlap.  Default is false, or "only compare in world space."
 679		 * @param	Camera			Specify which game camera you want.  If null getScreenXY() will just grab the first global camera.
 680		 * 
 681		 * @return	Whether or not the two objects overlap.
 682		 */
 683		public function overlaps(ObjectOrGroup:FlxBasic,InScreenSpace:Boolean=false,Camera:FlxCamera=null):Boolean
 684		{
 685			if(ObjectOrGroup is FlxGroup)
 686			{
 687				var results:Boolean = false;
 688				var i:uint = 0;
 689				var members:Array = (ObjectOrGroup as FlxGroup).members;
 690				while(i < length)
 691				{
 692					if(overlaps(members[i++],InScreenSpace,Camera))
 693						results = true;
 694				}
 695				return results;
 696			}
 697			
 698			if(ObjectOrGroup is FlxTilemap)
 699			{
 700				//Since tilemap's have to be the caller, not the target, to do proper tile-based collisions,
 701				// we redirect the call to the tilemap overlap here.
 702				return (ObjectOrGroup as FlxTilemap).overlaps(this,InScreenSpace,Camera);
 703			}
 704			
 705			var object:FlxObject = ObjectOrGroup as FlxObject;
 706			if(!InScreenSpace)
 707			{
 708				return	(object.x + object.width > x) && (object.x < x + width) &&
 709						(object.y + object.height > y) && (object.y < y + height);
 710			}
 711
 712			if(Camera == null)
 713				Camera = FlxG.camera;
 714			var objectScreenPos:FlxPoint = object.getScreenXY(null,Camera);
 715			getScreenXY(_point,Camera);
 716			return	(objectScreenPos.x + object.width > _point.x) && (objectScreenPos.x < _point.x + width) &&
 717					(objectScreenPos.y + object.height > _point.y) && (objectScreenPos.y < _point.y + height);
 718		}
 719		
 720		/**
 721		 * Checks to see if this <code>FlxObject</code> were located at the given position, would it overlap the <code>FlxObject</code> or <code>FlxGroup</code>?
 722		 * This is distinct from overlapsPoint(), which just checks that point, rather than taking the object's size into account.
 723		 * WARNING: Currently tilemaps do NOT support screen space overlap checks!
 724		 * 
 725		 * @param	X				The X position you want to check.  Pretends this object (the caller, not the parameter) is located here.
 726		 * @param	Y				The Y position you want to check.  Pretends this object (the caller, not the parameter) is located here.
 727		 * @param	ObjectOrGroup	The object or group being tested.
 728		 * @param	InScreenSpace	Whether to take scroll factors into account when checking for overlap.  Default is false, or "only compare in world space."
 729		 * @param	Camera			Specify which game camera you want.  If null getScreenXY() will just grab the first global camera.
 730		 * 
 731		 * @return	Whether or not the two objects overlap.
 732		 */
 733		public function overlapsAt(X:Number,Y:Number,ObjectOrGroup:FlxBasic,InScreenSpace:Boolean=false,Camera:FlxCamera=null):Boolean
 734		{
 735			if(ObjectOrGroup is FlxGroup)
 736			{
 737				var results:Boolean = false;
 738				var basic:FlxBasic;
 739				var i:uint = 0;
 740				var members:Array = (ObjectOrGroup as FlxGroup).members;
 741				while(i < length)
 742				{
 743					if(overlapsAt(X,Y,members[i++],InScreenSpace,Camera))
 744						results = true;
 745				}
 746				return results;
 747			}
 748			
 749			if(ObjectOrGroup is FlxTilemap)
 750			{
 751				//Since tilemap's have to be the caller, not the target, to do proper tile-based collisions,
 752				// we redirect the call to the tilemap overlap here.
 753				//However, since this is overlapsAt(), we also have to invent the appropriate position for the tilemap.
 754				//So we calculate the offset between the player and the requested position, and subtract that from the tilemap.
 755				var tilemap:FlxTilemap = ObjectOrGroup as FlxTilemap;
 756				return tilemap.overlapsAt(tilemap.x - (X - x),tilemap.y - (Y - y),this,InScreenSpace,Camera);
 757			}
 758			
 759			var object:FlxObject = ObjectOrGroup as FlxObject;
 760			if(!InScreenSpace)
 761			{
 762				return	(object.x + object.width > X) && (object.x < X + width) &&
 763						(object.y + object.height > Y) && (object.y < Y + height);
 764			}
 765			
 766			if(Camera == null)
 767				Camera = FlxG.camera;
 768			var objectScreenPos:FlxPoint = object.getScreenXY(null,Camera);
 769			_point.x = X - int(Camera.scroll.x*scrollFactor.x); //copied from getScreenXY()
 770			_point.y = Y - int(Camera.scroll.y*scrollFactor.y);
 771			_point.x += (_point.x > 0)?0.0000001:-0.0000001;
 772			_point.y += (_point.y > 0)?0.0000001:-0.0000001;
 773			return	(objectScreenPos.x + object.width > _point.x) && (objectScreenPos.x < _point.x + width) &&
 774				(objectScreenPos.y + object.height > _point.y) && (objectScreenPos.y < _point.y + height);
 775		}
 776		
 777		/**
 778		 * Checks to see if a point in 2D world space overlaps this <code>FlxObject</code> object.
 779		 * 
 780		 * @param	Point			The point in world space you want to check.
 781		 * @param	InScreenSpace	Whether to take scroll factors into account when checking for overlap.
 782		 * @param	Camera			Specify which game camera you want.  If null getScreenXY() will just grab the first global camera.
 783		 * 
 784		 * @return	Whether or not the point overlaps this object.
 785		 */
 786		public function overlapsPoint(Point:FlxPoint,InScreenSpace:Boolean=false,Camera:FlxCamera=null):Boolean
 787		{
 788			if(!InScreenSpace)
 789				return (Point.x > x) && (Point.x < x + width) && (Point.y > y) && (Point.y < y + height);
 790
 791			if(Camera == null)
 792				Camera = FlxG.camera;
 793			var X:Number = Point.x - Camera.scroll.x;
 794			var Y:Number = Point.y - Camera.scroll.y;
 795			getScreenXY(_point,Camera);
 796			return (X > _point.x) && (X < _point.x+width) && (Y > _point.y) && (Y < _point.y+height);
 797		}
 798		
 799		/**
 800		 * Check and see if this object is currently on screen.
 801		 * 
 802		 * @param	Camera		Specify which game camera you want.  If null getScreenXY() will just grab the first global camera.
 803		 * 
 804		 * @return	Whether the object is on screen or not.
 805		 */
 806		public function onScreen(Camera:FlxCamera=null):Boolean
 807		{
 808			if(Camera == null)
 809				Camera = FlxG.camera;
 810			getScreenXY(_point,Camera);
 811			return (_point.x + width > 0) && (_point.x < Camera.width) && (_point.y + height > 0) && (_point.y < Camera.height);
 812		}
 813		
 814		/**
 815		 * Call this function to figure out the on-screen position of the object.
 816		 * 
 817		 * @param	Camera		Specify which game camera you want.  If null getScreenXY() will just grab the first global camera.
 818		 * @param	Point		Takes a <code>FlxPoint</code> object and assigns the post-scrolled X and Y values of this object to it.
 819		 * 
 820		 * @return	The <code>Point</code> you passed in, or a new <code>Point</code> if you didn't pass one, containing the screen X and Y position of this object.
 821		 */
 822		public function getScreenXY(Point:FlxPoint=null,Camera:FlxCamera=null):FlxPoint
 823		{
 824			if(Point == null)
 825				Point = new FlxPoint();
 826			if(Camera == null)
 827				Camera = FlxG.camera;
 828			Point.x = x - int(Camera.scroll.x*scrollFactor.x);
 829			Point.y = y - int(Camera.scroll.y*scrollFactor.y);
 830			Point.x += (Point.x > 0)?0.0000001:-0.0000001;
 831			Point.y += (Point.y > 0)?0.0000001:-0.0000001;
 832			return Point;
 833		}
 834		
 835		/**
 836		 * Tells this object to flicker, retro-style.
 837		 * Pass a negative value to flicker forever.
 838		 * 
 839		 * @param	Duration	How many seconds to flicker for.
 840		 */
 841		public function flicker(Duration:Number=1):void
 842		{
 843			_flickerTimer = Duration;
 844			if(_flickerTimer == 0)
 845				_flicker = false;
 846		}
 847		
 848		/**
 849		 * Check to see if the object is still flickering.
 850		 * 
 851		 * @return	Whether the object is flickering or not.
 852		 */
 853		public function get flickering():Boolean
 854		{
 855			return _flickerTimer != 0;
 856		}
 857		
 858		/**
 859		 * Whether the object collides or not.  For more control over what directions
 860		 * the object will collide from, use collision constants (like LEFT, FLOOR, etc)
 861		 * to set the value of allowCollisions directly.
 862		 */
 863		public function get solid():Boolean
 864		{
 865			return (allowCollisions & ANY) > NONE;
 866		}
 867		
 868		/**
 869		 * @private
 870		 */
 871		public function set solid(Solid:Boolean):void
 872		{
 873			if(Solid)
 874				allowCollisions = ANY;
 875			else
 876				allowCollisions = NONE;
 877		}
 878		
 879		/**
 880		 * Retrieve the midpoint of this object in world coordinates.
 881		 * 
 882		 * @Point	Allows you to pass in an existing <code>FlxPoint</code> object if you're so inclined.  Otherwise a new one is created.
 883		 * 
 884		 * @return	A <code>FlxPoint</code> object containing the midpoint of this object in world coordinates.
 885		 */
 886		public function getMidpoint(Point:FlxPoint=null):FlxPoint
 887		{
 888			if(Point == null)
 889				Point = new FlxPoint();
 890			Point.x = x + width*0.5;
 891			Point.y = y + height*0.5;
 892			return Point;
 893		}
 894		
 895		/**
 896		 * Handy function for reviving game objects.
 897		 * Resets their existence flags and position.
 898		 * 
 899		 * @param	X	The new X position of this object.
 900		 * @param	Y	The new Y position of this object.
 901		 */
 902		public function reset(X:Number,Y:Number):void
 903		{
 904			revive();
 905			touching = NONE;
 906			wasTouching = NONE;
 907			x = X;
 908			y = Y;
 909			last.x = x;
 910			last.y = y;
 911			velocity.x = 0;
 912			velocity.y = 0;
 913		}
 914		
 915		/**
 916		 * Handy function for checking if this object is touching a particular surface.
 917		 * For slightly better performance you can just &amp; the value directly into <code>touching</code>.
 918		 * However, this method is good for readability and accessibility.
 919		 * 
 920		 * @param	Direction	Any of the collision flags (e.g. LEFT, FLOOR, etc).
 921		 * 
 922		 * @return	Whether the object is touching an object in (any of) the specified direction(s) this frame.
 923		 */
 924		public function isTouching(Direction:uint):Boolean
 925		{
 926			return (touching & Direction) > NONE;
 927		}
 928		
 929		/**
 930		 * Handy function for checking if this object is just landed on a particular surface.
 931		 * 
 932		 * @param	Direction	Any of the collision flags (e.g. LEFT, FLOOR, etc).
 933		 * 
 934		 * @return	Whether the object just landed on (any of) the specified surface(s) this frame.
 935		 */
 936		public function justTouched(Direction:uint):Boolean
 937		{
 938			return ((touching & Direction) > NONE) && ((wasTouching & Direction) <= NONE);
 939		}
 940		
 941		/**
 942		 * Reduces the "health" variable of this sprite by the amount specified in Damage.
 943		 * Calls kill() if health drops to or below zero.
 944		 * 
 945		 * @param	Damage		How much health to take away (use a negative number to give a health bonus).
 946		 */
 947		public function hurt(Damage:Number):void
 948		{
 949			health = health - Damage;
 950			if(health <= 0)
 951				kill();
 952		}
 953		
 954		/**
 955		 * The main collision resolution function in flixel.
 956		 * 
 957		 * @param	Object1 	Any <code>FlxObject</code>.
 958		 * @param	Object2		Any other <code>FlxObject</code>.
 959		 * 
 960		 * @return	Whether the objects in fact touched and were separated.
 961		 */
 962		static public function separate(Object1:FlxObject, Object2:FlxObject):Boolean
 963		{
 964			var separatedX:Boolean = separateX(Object1,Object2);
 965			var separatedY:Boolean = separateY(Object1,Object2);
 966			return separatedX || separatedY;
 967		}
 968		
 969		/**
 970		 * The X-axis component of the object separation process.
 971		 * 
 972		 * @param	Object1 	Any <code>FlxObject</code>.
 973		 * @param	Object2		Any other <code>FlxObject</code>.
 974		 * 
 975		 * @return	Whether the objects in fact touched and were separated along the X axis.
 976		 */
 977		static public function separateX(Object1:FlxObject, Object2:FlxObject):Boolean
 978		{
 979			//can't separate two immovable objects
 980			var obj1immovable:Boolean = Object1.immovable;
 981			var obj2immovable:Boolean = Object2.immovable;
 982			if(obj1immovable && obj2immovable)
 983				return false;
 984			
 985			//If one of the objects is a tilemap, just pass it off.
 986			if(Object1 is FlxTilemap)
 987				return (Object1 as FlxTilemap).overlapsWithCallback(Object2,separateX);
 988			if(Object2 is FlxTilemap)
 989				return (Object2 as FlxTilemap).overlapsWithCallback(Object1,separateX,true);
 990			
 991			//First, get the two object deltas
 992			var overlap:Number = 0;
 993			var obj1delta:Number = Object1.x - Object1.last.x;
 994			var obj2delta:Number = Object2.x - Object2.last.x;
 995			if(obj1delta != obj2delta)
 996			{
 997				//Check if the X hulls actually overlap
 998				var obj1deltaAbs:Number = (obj1delta > 0)?obj1delta:-obj1delta;
 999				var obj2deltaAbs:Number = (obj2delta > 0)?obj2delta:-obj2delta;
1000				var obj1rect:FlxRect = new FlxRect(Object1.x-((obj1delta > 0)?obj1delta:0),Object1.last.y,Object1.width+((obj1delta > 0)?obj1delta:-obj1delta),Object1.height);
1001				var obj2rect:FlxRect = new FlxRect(Object2.x-((obj2delta > 0)?obj2delta:0),Object2.last.y,Object2.width+((obj2delta > 0)?obj2delta:-obj2delta),Object2.height);
1002				if((obj1rect.x + obj1rect.width > obj2rect.x) && (obj1rect.x < obj2rect.x + obj2rect.width) && (obj1rect.y + obj1rect.height > obj2rect.y) && (obj1rect.y < obj2rect.y + obj2rect.height))
1003				{
1004					var maxOverlap:Number = obj1deltaAbs + obj2deltaAbs + OVERLAP_BIAS;
1005					
1006					//If they did overlap (and can), figure out by how much and flip the corresponding flags
1007					if(obj1delta > obj2delta)
1008					{
1009						overlap = Object1.x + Object1.width - Object2.x;
1010						if((overlap > maxOverlap) || !(Object1.allowCollisions & RIGHT) || !(Object2.allowCollisions & LEFT))
1011							overlap = 0;
1012						else
1013						{
1014							Object1.touching |= RIGHT;
1015							Object2.touching |= LEFT;
1016						}
1017					}
1018					else if(obj1delta < obj2delta)
1019					{
1020						overlap = Object1.x - Object2.width - Object2.x;
1021						if((-overlap > maxOverlap) || !(Object1.allowCollisions & LEFT) || !(Object2.allowCollisions & RIGHT))
1022							overlap = 0;
1023						else
1024						{
1025							Object1.touching |= LEFT;
1026							Object2.touching |= RIGHT;
1027						}
1028					}
1029				}
1030			}
1031			
1032			//Then adjust their positions and velocities accordingly (if there was any overlap)
1033			if(overlap != 0)
1034			{
1035				var obj1v:Number = Object1.velocity.x;
1036				var obj2v:Number = Object2.velocity.x;
1037				
1038				if(!obj1immovable && !obj2immovable)
1039				{
1040					overlap *= 0.5;
1041					Object1.x = Object1.x - overlap;
1042					Object2.x += overlap;
1043
1044					var obj1velocity:Number = Math.sqrt((obj2v * obj2v * Object2.mass)/Object1.mass) * ((obj2v > 0)?1:-1);
1045					var obj2velocity:Number = Math.sqrt((obj1v * obj1v * Object1.mass)/Object2.mass) * ((obj1v > 0)?1:-1);
1046					var average:Number = (obj1velocity + obj2velocity)*0.5;
1047					obj1velocity -= average;
1048					obj2velocity -= average;
1049					Object1.velocity.x = average + obj1velocity * Object1.elasticity;
1050					Object2.velocity.x = average + obj2velocity * Object2.elasticity;
1051				}
1052				else if(!obj1immovable)
1053				{
1054					Object1.x = Object1.x - overlap;
1055					Object1.velocity.x = obj2v - obj1v*Object1.elasticity;
1056				}
1057				else if(!obj2immovable)
1058				{
1059					Object2.x += overlap;
1060					Object2.velocity.x = obj1v - obj2v*Object2.elasticity;
1061				}
1062				return true;
1063			}
1064			else
1065				return false;
1066		}
1067		
1068		/**
1069		 * The Y-axis component of the object separation process.
1070		 * 
1071		 * @param	Object1 	Any <code>FlxObject</code>.
1072		 * @param	Object2		Any other <code>FlxObject</code>.
1073		 * 
1074		 * @return	Whether the objects in fact touched and were separated along the Y axis.
1075		 */
1076		static public function separateY(Object1:FlxObject, Object2:FlxObject):Boolean
1077		{
1078			//can't separate two immovable objects
1079			var obj1immovable:Boolean = Object1.immovable;
1080			var obj2immovable:Boolean = Object2.immovable;
1081			if(obj1immovable && obj2immovable)
1082				return false;
1083			
1084			//If one of the objects is a tilemap, just pass it off.
1085			if(Object1 is FlxTilemap)
1086				return (Object1 as FlxTilemap).overlapsWithCallback(Object2,separateY);
1087			if(Object2 is FlxTilemap)
1088				return (Object2 as FlxTilemap).overlapsWithCallback(Object1,separateY,true);
1089
1090			//First, get the two object deltas
1091			var overlap:Number = 0;
1092			var obj1delta:Number = Object1.y - Object1.last.y;
1093			var obj2delta:Number = Object2.y - Object2.last.y;
1094			if(obj1delta != obj2delta)
1095			{
1096				//Check if the Y hulls actually overlap
1097				var obj1deltaAbs:Number = (obj1delta > 0)?obj1delta:-obj1delta;
1098				var obj2deltaAbs:Number = (obj2delta > 0)?obj2delta:-obj2delta;
1099				var obj1rect:FlxRect = new FlxRect(Object1.x,Object1.y-((obj1delta > 0)?obj1delta:0),Object1.width,Object1.height+obj1deltaAbs);
1100				var obj2rect:FlxRect = new FlxRect(Object2.x,Object2.y-((obj2delta > 0)?obj2delta:0),Object2.width,Object2.height+obj2deltaAbs);
1101				if((obj1rect.x + obj1rect.width > obj2rect.x) && (obj1rect.x < obj2rect.x + obj2rect.width) && (obj1rect.y + obj1rect.height > obj2rect.y) && (obj1rect.y < obj2rect.y + obj2rect.height))
1102				{
1103					var maxOverlap:Number = obj1deltaAbs + obj2deltaAbs + OVERLAP_BIAS;
1104					
1105					//If they did overlap (and can), figure out by how much and flip the corresponding flags
1106					if(obj1delta > obj2delta)
1107					{
1108						overlap = Object1.y + Object1.height - Object2.y;
1109						if((overlap > maxOverlap) || !(Object1.allowCollisions & DOWN) || !(Object2.allowCollisions & UP))
1110							overlap = 0;
1111						else
1112						{
1113							Object1.touching |= DOWN;
1114							Object2.touching |= UP;
1115						}
1116					}
1117					else if(obj1delta < obj2delta)
1118					{
1119						overlap = Object1.y - Object2.height - Object2.y;
1120						if((-overlap > maxOverlap) || !(Object1.allowCollisions & UP) || !(Object2.allowCollisions & DOWN))
1121							overlap = 0;
1122						else
1123						{
1124							Object1.touching |= UP;
1125							Object2.touching |= DOWN;
1126						}
1127					}
1128				}
1129			}
1130			
1131			//Then adjust their positions and velocities accordingly (if there was any overlap)
1132			if(overlap != 0)
1133			{
1134				var obj1v:Number = Object1.velocity.y;
1135				var obj2v:Number = Object2.velocity.y;
1136				
1137				if(!obj1immovable && !obj2immovable)
1138				{
1139					overlap *= 0.5;
1140					Object1.y = Object1.y - overlap;
1141					Object2.y += overlap;
1142
1143					var obj1velocity:Number = Math.sqrt((obj2v * obj2v * Object2.mass)/Object1.mass) * ((obj2v > 0)?1:-1);
1144					var obj2velocity:Number = Math.sqrt((obj1v * obj1v * Object1.mass)/Object2.mass) * ((obj1v > 0)?1:-1);
1145					var average:Number = (obj1velocity + obj2velocity)*0.5;
1146					obj1velocity -= average;
1147					obj2velocity -= average;
1148					Object1.velocity.y = average + obj1velocity * Object1.elasticity;
1149					Object2.velocity.y = average + obj2velocity * Object2.elasticity;
1150				}
1151				else if(!obj1immovable)
1152				{
1153					Object1.y = Object1.y - overlap;
1154					Object1.velocity.y = obj2v - obj1v*Object1.elasticity;
1155					//This is special case code that handles cases like horizontal moving platforms you can ride
1156					if(Object2.active && Object2.moves && (obj1delta > obj2delta))
1157						Object1.x += Object2.x - Object2.last.x;
1158				}
1159				else if(!obj2immovable)
1160				{
1161					Object2.y += overlap;
1162					Object2.velocity.y = obj1v - obj2v*Object2.elasticity;
1163					//This is special case code that handles cases like horizontal moving platforms you can ride
1164					if(Object1.active && Object1.moves && (obj1delta < obj2delta))
1165						Object2.x += Object1.x - Object1.last.x;
1166				}
1167				return true;
1168			}
1169			else
1170				return false;
1171		}
1172	}
1173}