diff --git a/src/net/flashpunk/Engine.as b/src/net/flashpunk/Engine.as deleted file mode 100644 index f243ec3..0000000 --- a/src/net/flashpunk/Engine.as +++ /dev/null @@ -1,291 +0,0 @@ -package net.flashpunk -{ - import flash.display.Bitmap; - import flash.display.BitmapData; - import flash.display.MovieClip; - import flash.display.StageAlign; - import flash.display.StageDisplayState; - import flash.display.StageQuality; - import flash.display.StageScaleMode; - import flash.events.Event; - import flash.events.TimerEvent; - import flash.geom.Rectangle; - import flash.utils.getTimer; - import flash.utils.Timer; - import net.flashpunk.utils.Draw; - import net.flashpunk.utils.Input; - - /** - * Main game Sprite class, added to the Flash Stage. Manages the game loop. - */ - public class Engine extends MovieClip - { - /** - * If the game should stop updating/rendering. - */ - public var paused:Boolean = false; - - /** - * Cap on the elapsed time (default at 30 FPS). Raise this to allow for lower framerates (eg. 1 / 10). - */ - public var maxElapsed:Number = 0.0333; - - /** - * The max amount of frames that can be skipped in fixed framerate mode. - */ - public var maxFrameSkip:uint = 5; - - /** - * The amount of milliseconds between ticks in fixed framerate mode. - */ - public var tickRate:uint = 4; - - /** - * Constructor. Defines startup information about your game. - * @param width The width of your game. - * @param height The height of your game. - * @param frameRate The game framerate, in frames per second. - * @param fixed If a fixed-framerate should be used. - */ - public function Engine(width:uint, height:uint, frameRate:Number = 60, fixed:Boolean = false) - { - // global game properties - FP.width = width; - FP.height = height; - FP.assignedFrameRate = frameRate; - FP.fixed = fixed; - - // global game objects - FP.engine = this; - FP.screen = new Screen; - FP.bounds = new Rectangle(0, 0, width, height); - FP._world = new World; - - // miscellanious startup stuff - if (FP.randomSeed == 0) FP.randomizeSeed(); - FP.entity = new Entity; - FP._time = getTimer(); - - // on-stage event listener - addEventListener(Event.ADDED_TO_STAGE, onStage); - } - - /** - * Override this, called after Engine has been added to the stage. - */ - public function init():void - { - - } - - /** - * Updates the game, updating the World and Entities. - */ - public function update():void - { - if (FP._world.active) - { - if (FP._world._tween) FP._world.updateTweens(); - FP._world.update(); - } - FP._world.updateLists(); - if (FP._goto) checkWorld(); - } - - /** - * Renders the game, rendering the World and Entities. - */ - public function render():void - { - // timing stuff - var t:Number = getTimer(); - if (!_frameLast) _frameLast = t; - - // render loop - FP.screen.swap(); - Draw.resetTarget(); - FP.screen.refresh(); - if (FP._world.visible) FP._world.render(); - FP.screen.redraw(); - - // more timing stuff - t = getTimer(); - _frameListSum += (_frameList[_frameList.length] = t - _frameLast); - if (_frameList.length > 10) _frameListSum -= _frameList.shift(); - FP.frameRate = 1000 / (_frameListSum / _frameList.length); - _frameLast = t; - } - - /** - * Sets the game's stage properties. Override this to set them differently. - */ - public function setStageProperties():void - { - stage.frameRate = FP.assignedFrameRate; - stage.align = StageAlign.TOP_LEFT; - stage.quality = StageQuality.HIGH; - stage.scaleMode = StageScaleMode.NO_SCALE; - stage.displayState = StageDisplayState.NORMAL; - } - - /** @private Event handler for stage entry. */ - private function onStage(e:Event = null):void - { - // remove event listener - removeEventListener(Event.ADDED_TO_STAGE, onStage); - - // set stage properties - FP.stage = stage; - setStageProperties(); - - // enable input - Input.enable(); - - // switch worlds - if (FP._goto) checkWorld(); - - // game start - init(); - - // start game loop - _rate = 1000 / FP.assignedFrameRate; - if (FP.fixed) - { - // fixed framerate - _skip = _rate * maxFrameSkip; - _last = _prev = getTimer(); - _timer = new Timer(tickRate); - _timer.addEventListener(TimerEvent.TIMER, onTimer); - _timer.start(); - } - else - { - // nonfixed framerate - _last = getTimer(); - addEventListener(Event.ENTER_FRAME, onEnterFrame); - } - } - - /** @private Framerate independent game loop. */ - private function onEnterFrame(e:Event):void - { - // update timer - _time = _gameTime = getTimer(); - FP._flashTime = _time - _flashTime; - _updateTime = _time; - FP.elapsed = (_time - _last) / 1000; - if (FP.elapsed > maxElapsed) FP.elapsed = maxElapsed; - FP.elapsed *= FP.rate; - _last = _time; - - // update console - if (FP._console) FP._console.update(); - - // update loop - if (!paused) update(); - - // update input - Input.update(); - - // update timer - _time = _renderTime = getTimer(); - FP._updateTime = _time - _updateTime; - - // render loop - if (!paused) render(); - - // update timer - _time = _flashTime = getTimer(); - FP._renderTime = _time - _renderTime; - FP._gameTime = _time - _gameTime; - } - - /** @private Fixed framerate game loop. */ - private function onTimer(e:TimerEvent):void - { - // update timer - _time = getTimer(); - _delta += (_time - _last); - _last = _time; - - // quit if a frame hasn't passed - if (_delta < _rate) return; - - // update timer - _gameTime = _time; - FP._flashTime = _time - _flashTime; - - // update console - if (FP._console) FP._console.update(); - - // update loop - if (_delta > _skip) _delta = _skip; - while (_delta > _rate) - { - // update timer - _updateTime = _time; - _delta -= _rate; - FP.elapsed = (_time - _prev) / 1000; - if (FP.elapsed > maxElapsed) FP.elapsed = maxElapsed; - FP.elapsed *= FP.rate; - _prev = _time; - - // update loop - if (!paused) update(); - - // update input - Input.update(); - - // update timer - _time = getTimer(); - FP._updateTime = _time - _updateTime; - } - - // update timer - _renderTime = _time; - - // render loop - if (!paused) render(); - - // update timer - _time = _flashTime = getTimer(); - FP._renderTime = _time - _renderTime; - FP._gameTime = _time - _gameTime; - } - - /** @private Switch Worlds if they've changed. */ - private function checkWorld():void - { - if (!FP._goto) return; - FP._world.end(); - FP._world.updateLists(); - if (FP._world && FP._world.autoClear && FP._world._tween) FP._world.clearTweens(); - FP._world = FP._goto; - FP._goto = null; - FP.camera = FP._world.camera; - FP._world.updateLists(); - FP._world.begin(); - FP._world.updateLists(); - } - - // Timing information. - /** @private */ private var _delta:Number = 0; - /** @private */ private var _time:Number; - /** @private */ private var _last:Number; - /** @private */ private var _timer:Timer; - /** @private */ private var _rate:Number; - /** @private */ private var _skip:Number; - /** @private */ private var _prev:Number; - - // Debug timing information. - /** @private */ private var _updateTime:uint; - /** @private */ private var _renderTime:uint; - /** @private */ private var _gameTime:uint; - /** @private */ private var _flashTime:uint; - - // FrameRate tracking. - /** @private */ private var _frameLast:uint = 0; - /** @private */ private var _frameListSum:uint = 0; - /** @private */ private var _frameList:Vector. = new Vector.; - } -} \ No newline at end of file diff --git a/src/net/flashpunk/Entity.as b/src/net/flashpunk/Entity.as deleted file mode 100644 index f6f0298..0000000 --- a/src/net/flashpunk/Entity.as +++ /dev/null @@ -1,764 +0,0 @@ -package net.flashpunk -{ - import flash.display.BitmapData; - import flash.geom.Point; - import flash.geom.Rectangle; - import flash.utils.getQualifiedClassName; - import flash.utils.getDefinitionByName; - import net.flashpunk.masks.*; - import net.flashpunk.graphics.*; - - /** - * Main game Entity class updated by World. - */ - public class Entity extends Tweener - { - /** - * If the Entity should render. - */ - public var visible:Boolean = true; - - /** - * If the Entity should respond to collision checks. - */ - public var collidable:Boolean = true; - - /** - * X position of the Entity in the World. - */ - public var x:Number = 0; - - /** - * Y position of the Entity in the World. - */ - public var y:Number = 0; - - /** - * Width of the Entity's hitbox. - */ - public var width:int; - - /** - * Height of the Entity's hitbox. - */ - public var height:int; - - /** - * X origin of the Entity's hitbox. - */ - public var originX:int; - - /** - * Y origin of the Entity's hitbox. - */ - public var originY:int; - - /** - * The BitmapData target to draw the Entity to. Leave as null to render to the current screen buffer (default). - */ - public var renderTarget:BitmapData; - - /** - * Constructor. Can be usd to place the Entity and assign a graphic and mask. - * @param x X position to place the Entity. - * @param y Y position to place the Entity. - * @param graphic Graphic to assign to the Entity. - * @param mask Mask to assign to the Entity. - */ - public function Entity(x:Number = 0, y:Number = 0, graphic:Graphic = null, mask:Mask = null) - { - this.x = x; - this.y = y; - if (graphic) this.graphic = graphic; - if (mask) this.mask = mask; - HITBOX.assignTo(this); - _class = Class(getDefinitionByName(getQualifiedClassName(this))); - } - - /** - * Override this, called when the Entity is added to a World. - */ - public function added():void - { - - } - - /** - * Override this, called when the Entity is removed from a World. - */ - public function removed():void - { - - } - - /** - * Updates the Entity. - */ - override public function update():void - { - - } - - /** - * Renders the Entity. If you override this for special behaviour, - * remember to call super.render() to render the Entity's graphic. - */ - public function render():void - { - if (_graphic && _graphic.visible) - { - if (_graphic.relative) - { - _point.x = x; - _point.y = y; - } - else _point.x = _point.y = 0; - _camera.x = FP.camera.x; - _camera.y = FP.camera.y; - _graphic.render(renderTarget ? renderTarget : FP.buffer, _point, _camera); - } - } - - /** - * Checks for a collision against an Entity type. - * @param type The Entity type to check for. - * @param x Virtual x position to place this Entity. - * @param y Virtual y position to place this Entity. - * @return The first Entity collided with, or null if none were collided. - */ - public function collide(type:String, x:Number, y:Number):Entity - { - if (!_world) return null; - - var e:Entity = _world._typeFirst[type]; - if (!collidable || !e) return null; - - _x = this.x; _y = this.y; - this.x = x; this.y = y; - - if (!_mask) - { - while (e) - { - if (x - originX + width > e.x - e.originX - && y - originY + height > e.y - e.originY - && x - originX < e.x - e.originX + e.width - && y - originY < e.y - e.originY + e.height - && e.collidable && e !== this) - { - if (!e._mask || e._mask.collide(HITBOX)) - { - this.x = _x; this.y = _y; - return e; - } - } - e = e._typeNext; - } - this.x = _x; this.y = _y; - return null; - } - - while (e) - { - if (x - originX + width > e.x - e.originX - && y - originY + height > e.y - e.originY - && x - originX < e.x - e.originX + e.width - && y - originY < e.y - e.originY + e.height - && e.collidable && e !== this) - { - if (_mask.collide(e._mask ? e._mask : e.HITBOX)) - { - this.x = _x; this.y = _y; - return e; - } - } - e = e._typeNext; - } - this.x = _x; this.y = _y; - return null; - } - - /** - * Checks for collision against multiple Entity types. - * @param types An Array or Vector of Entity types to check for. - * @param x Virtual x position to place this Entity. - * @param y Virtual y position to place this Entity. - * @return The first Entity collided with, or null if none were collided. - */ - public function collideTypes(types:Object, x:Number, y:Number):Entity - { - if (!_world) return null; - var e:Entity; - for each (var type:String in types) - { - if ((e = collide(type, x, y))) return e; - } - return null; - } - - /** - * Checks if this Entity collides with a specific Entity. - * @param e The Entity to collide against. - * @param x Virtual x position to place this Entity. - * @param y Virtual y position to place this Entity. - * @return The Entity if they overlap, or null if they don't. - */ - public function collideWith(e:Entity, x:Number, y:Number):Entity - { - _x = this.x; _y = this.y; - this.x = x; this.y = y; - - if (x - originX + width > e.x - e.originX - && y - originY + height > e.y - e.originY - && x - originX < e.x - e.originX + e.width - && y - originY < e.y - e.originY + e.height - && collidable && e.collidable) - { - if (!_mask) - { - if (!e._mask || e._mask.collide(HITBOX)) - { - this.x = _x; this.y = _y; - return e; - } - this.x = _x; this.y = _y; - return null; - } - if (_mask.collide(e._mask ? e._mask : e.HITBOX)) - { - this.x = _x; this.y = _y; - return e; - } - } - this.x = _x; this.y = _y; - return null; - } - - /** - * Checks if this Entity overlaps the specified rectangle. - * @param x Virtual x position to place this Entity. - * @param y Virtual y position to place this Entity. - * @param rX X position of the rectangle. - * @param rY Y position of the rectangle. - * @param rWidth Width of the rectangle. - * @param rHeight Height of the rectangle. - * @return If they overlap. - */ - public function collideRect(x:Number, y:Number, rX:Number, rY:Number, rWidth:Number, rHeight:Number):Boolean - { - if (x - originX + width >= rX && y - originY + height >= rY - && x - originX <= rX + rWidth && y - originY <= rY + rHeight) - { - if (!_mask) return true; - _x = this.x; _y = this.y; - this.x = x; this.y = y; - FP.entity.x = rX; - FP.entity.y = rY; - FP.entity.width = rWidth; - FP.entity.height = rHeight; - if (_mask.collide(FP.entity.HITBOX)) - { - this.x = _x; this.y = _y; - return true; - } - this.x = _x; this.y = _y; - return false; - } - return false; - } - - /** - * Checks if this Entity overlaps the specified position. - * @param x Virtual x position to place this Entity. - * @param y Virtual y position to place this Entity. - * @param pX X position. - * @param pY Y position. - * @return If the Entity intersects with the position. - */ - public function collidePoint(x:Number, y:Number, pX:Number, pY:Number):Boolean - { - if (pX >= x - originX && pY >= y - originY - && pX < x - originX + width && pY < y - originY + height) - { - if (!_mask) return true; - _x = this.x; _y = this.y; - this.x = x; this.y = y; - FP.entity.x = pX; - FP.entity.y = pY; - FP.entity.width = 1; - FP.entity.height = 1; - if (_mask.collide(FP.entity.HITBOX)) - { - this.x = _x; this.y = _y; - return true; - } - this.x = _x; this.y = _y; - return false; - } - return false; - } - - /** - * Populates an array with all collided Entities of a type. - * @param type The Entity type to check for. - * @param x Virtual x position to place this Entity. - * @param y Virtual y position to place this Entity. - * @param array The Array or Vector object to populate. - * @return The array, populated with all collided Entities. - */ - public function collideInto(type:String, x:Number, y:Number, array:Object):void - { - if (!_world) return; - - var e:Entity = _world._typeFirst[type]; - if (!collidable || !e) return; - - _x = this.x; _y = this.y; - this.x = x; this.y = y; - var n:uint = array.length; - - if (!_mask) - { - while (e) - { - if (x - originX + width > e.x - e.originX - && y - originY + height > e.y - e.originY - && x - originX < e.x - e.originX + e.width - && y - originY < e.y - e.originY + e.height - && e.collidable && e !== this) - { - if (!e._mask || e._mask.collide(HITBOX)) array[n ++] = e; - } - e = e._typeNext; - } - this.x = _x; this.y = _y; - return; - } - - while (e) - { - if (x - originX + width > e.x - e.originX - && y - originY + height > e.y - e.originY - && x - originX < e.x - e.originX + e.width - && y - originY < e.y - e.originY + e.height - && e.collidable && e !== this) - { - if (_mask.collide(e._mask ? e._mask : e.HITBOX)) array[n ++] = e; - } - e = e._typeNext; - } - this.x = _x; this.y = _y; - return; - } - - /** - * Populates an array with all collided Entities of multiple types. - * @param types An array of Entity types to check for. - * @param x Virtual x position to place this Entity. - * @param y Virtual y position to place this Entity. - * @param array The Array or Vector object to populate. - * @return The array, populated with all collided Entities. - */ - public function collideTypesInto(types:Object, x:Number, y:Number, array:Object):void - { - if (!_world) return; - for each (var type:String in types) collideInto(type, x, y, array); - } - - /** - * If the Entity collides with the camera rectangle. - */ - public function get onCamera():Boolean - { - return collideRect(x, y, FP.camera.x, FP.camera.y, FP.width, FP.height); - } - - /** - * The World object this Entity has been added to. - */ - public function get world():World - { - return _world; - } - - /** - * Half the Entity's width. - */ - public function get halfWidth():Number { return width / 2; } - - /** - * Half the Entity's height. - */ - public function get halfHeight():Number { return height / 2; } - - /** - * The center x position of the Entity's hitbox. - */ - public function get centerX():Number { return x - originX + width / 2; } - - /** - * The center y position of the Entity's hitbox. - */ - public function get centerY():Number { return y - originY + height / 2; } - - /** - * The leftmost position of the Entity's hitbox. - */ - public function get left():Number { return x - originX; } - - /** - * The rightmost position of the Entity's hitbox. - */ - public function get right():Number { return x - originX + width; } - - /** - * The topmost position of the Entity's hitbox. - */ - public function get top():Number { return y - originY; } - - /** - * The bottommost position of the Entity's hitbox. - */ - public function get bottom():Number { return y - originY + height; } - - /** - * The rendering layer of this Entity. Higher layers are rendered first. - */ - public function get layer():int { return _layer; } - public function set layer(value:int):void - { - if (_layer == value) return; - if (!_added) - { - _layer = value; - return; - } - _world.removeRender(this); - _layer = value; - _world.addRender(this); - } - - /** - * The collision type, used for collision checking. - */ - public function get type():String { return _type; } - public function set type(value:String):void - { - if (_type == value) return; - if (!_added) - { - _type = value; - return; - } - if (_type) _world.removeType(this); - _type = value; - if (value) _world.addType(this); - } - - /** - * An optional Mask component, used for specialized collision. If this is - * not assigned, collision checks will use the Entity's hitbox by default. - */ - public function get mask():Mask { return _mask; } - public function set mask(value:Mask):void - { - if (_mask == value) return; - if (_mask) _mask.assignTo(null); - _mask = value; - if (value) _mask.assignTo(this); - } - - /** - * Graphical component to render to the screen. - */ - public function get graphic():Graphic { return _graphic; } - public function set graphic(value:Graphic):void - { - if (_graphic == value) return; - _graphic = value; - if (value && value._assign != null) value._assign(); - } - - /** - * Adds the graphic to the Entity via a Graphiclist. - * @param g Graphic to add. - */ - public function addGraphic(g:Graphic):Graphic - { - if (graphic is Graphiclist) (graphic as Graphiclist).add(g); - else - { - var list:Graphiclist = new Graphiclist; - if (graphic) list.add(graphic); - graphic = list; - } - return g; - } - - /** - * Sets the Entity's hitbox properties. - * @param width Width of the hitbox. - * @param height Height of the hitbox. - * @param originX X origin of the hitbox. - * @param originY Y origin of the hitbox. - */ - public function setHitbox(width:int = 0, height:int = 0, originX:int = 0, originY:int = 0):void - { - this.width = width; - this.height = height; - this.originX = originX; - this.originY = originY; - } - - /** - * Sets the Entity's hitbox to match that of the provided object. - * @param o The object defining the hitbox (eg. an Image or Rectangle). - */ - public function setHitboxTo(o:Object):void - { - if (o is Image || o is Rectangle) setHitbox(o.width, o.height, -o.x, -o.y); - else - { - if (o.hasOwnProperty("width")) width = o.width; - if (o.hasOwnProperty("height")) height = o.height; - if (o.hasOwnProperty("originX") && !(o is Graphic)) originX = o.originX; - else if (o.hasOwnProperty("x")) originX = -o.x; - if (o.hasOwnProperty("originY") && !(o is Graphic)) originX = o.originY; - else if (o.hasOwnProperty("y")) originX = -o.y; - } - } - - /** - * Sets the origin of the Entity. - * @param x X origin. - * @param y Y origin. - */ - public function setOrigin(x:int = 0, y:int = 0):void - { - originX = x; - originY = y; - } - - /** - * Center's the Entity's origin (half width & height). - */ - public function centerOrigin():void - { - originX = width / 2; - originY = height / 2; - } - - /** - * Calculates the distance from another Entity. - * @param e The other Entity. - * @param useHitboxes If hitboxes should be used to determine the distance. If not, the Entities' x/y positions are used. - * @return The distance. - */ - public function distanceFrom(e:Entity, useHitboxes:Boolean = false):Number - { - if (!useHitboxes) return Math.sqrt((x - e.x) * (x - e.x) + (y - e.y) * (y - e.y)); - return FP.distanceRects(x - originX, y - originY, width, height, e.x - e.originX, e.y - e.originY, e.width, e.height); - } - - /** - * Calculates the distance from this Entity to the point. - * @param px X position. - * @param py Y position. - * @param useHitboxes If hitboxes should be used to determine the distance. If not, the Entities' x/y positions are used. - * @return The distance. - */ - public function distanceToPoint(px:Number, py:Number, useHitbox:Boolean = false):Number - { - if (!useHitbox) return Math.sqrt((x - px) * (x - px) + (y - py) * (y - py)); - return FP.distanceRectPoint(px, py, x - originX, y - originY, width, height); - } - - /** - * Calculates the distance from this Entity to the rectangle. - * @param rx X position of the rectangle. - * @param ry Y position of the rectangle. - * @param rwidth Width of the rectangle. - * @param rheight Height of the rectangle. - * @return The distance. - */ - public function distanceToRect(rx:Number, ry:Number, rwidth:Number, rheight:Number):Number - { - return FP.distanceRects(rx, ry, rwidth, rheight, x - originX, y - originY, width, height); - } - - /** - * Gets the class name as a string. - * @return A string representing the class name. - */ - public function toString():String - { - var s:String = String(_class); - return s.substring(7, s.length - 1); - } - - /** - * Moves the Entity by the amount, retaining integer values for its x and y. - * @param x Horizontal offset. - * @param y Vertical offset. - * @param solidType An optional collision type to stop flush against upon collision. - * @param sweep If sweeping should be used (prevents fast-moving objects from going through solidType). - */ - public function moveBy(x:Number, y:Number, solidType:String = null, sweep:Boolean = false):void - { - _moveX += x; - _moveY += y; - x = Math.round(_moveX); - y = Math.round(_moveY); - _moveX -= x; - _moveY -= y; - if (solidType) - { - var sign:int, e:Entity; - if (x != 0) - { - if (collidable && (sweep || collide(solidType, this.x + x, this.y))) - { - sign = x > 0 ? 1 : -1; - while (x != 0) - { - if ((e = collide(solidType, this.x + sign, this.y))) - { - moveCollideX(e); - break; - } - else - { - this.x += sign; - x -= sign; - } - } - } - else this.x += x; - } - if (y != 0) - { - if (collidable && (sweep || collide(solidType, this.x, this.y + y))) - { - sign = y > 0 ? 1 : -1; - while (y != 0) - { - if ((e = collide(solidType, this.x, this.y + sign))) - { - moveCollideY(e); - break; - } - else - { - this.y += sign; - y -= sign; - } - } - } - else this.y += y; - } - } - else - { - this.x += x; - this.y += y; - } - } - - /** - * Moves the Entity to the position, retaining integer values for its x and y. - * @param x X position. - * @param y Y position. - * @param solidType An optional collision type to stop flush against upon collision. - * @param sweep If sweeping should be used (prevents fast-moving objects from going through solidType). - */ - public function moveTo(x:Number, y:Number, solidType:String = null, sweep:Boolean = false):void - { - moveBy(x - this.x, y - this.y, solidType, sweep); - } - - /** - * Moves towards the target position, retaining integer values for its x and y. - * @param x X target. - * @param y Y target. - * @param amount Amount to move. - * @param solidType An optional collision type to stop flush against upon collision. - * @param sweep If sweeping should be used (prevents fast-moving objects from going through solidType). - */ - public function moveTowards(x:Number, y:Number, amount:Number, solidType:String = null, sweep:Boolean = false):void - { - _point.x = x - this.x; - _point.y = y - this.y; - _point.normalize(amount); - moveBy(_point.x, _point.y, solidType, sweep); - } - - /** - * When you collide with an Entity on the x-axis with moveTo() or moveBy(). - * @param e The Entity you collided with. - */ - public function moveCollideX(e:Entity):void - { - - } - - /** - * When you collide with an Entity on the y-axis with moveTo() or moveBy(). - * @param e The Entity you collided with. - */ - public function moveCollideY(e:Entity):void - { - - } - - /** - * Clamps the Entity's hitbox on the x-axis. - * @param left Left bounds. - * @param right Right bounds. - * @param padding Optional padding on the clamp. - */ - public function clampHorizontal(left:Number, right:Number, padding:Number = 0):void - { - if (x - originX < left + padding) x = left + originX + padding; - if (x - originX + width > right - padding) x = right - width + originX - padding; - } - - /** - * Clamps the Entity's hitbox on the y axis. - * @param top Min bounds. - * @param bottom Max bounds. - * @param padding Optional padding on the clamp. - */ - public function clampVertical(top:Number, bottom:Number, padding:Number = 0):void - { - if (y - originY < top + padding) y = top + originY + padding; - if (y - originY + height > bottom - padding) y = bottom - height + originY - padding; - } - - // Entity information. - /** @private */ internal var _class:Class; - /** @private */ internal var _world:World; - /** @private */ internal var _added:Boolean; - /** @private */ internal var _type:String = ""; - /** @private */ internal var _layer:int; - /** @private */ internal var _updatePrev:Entity; - /** @private */ internal var _updateNext:Entity; - /** @private */ internal var _renderPrev:Entity; - /** @private */ internal var _renderNext:Entity; - /** @private */ internal var _typePrev:Entity; - /** @private */ internal var _typeNext:Entity; - /** @private */ internal var _recycleNext:Entity; - - // Collision information. - /** @private */ private const HITBOX:Mask = new Mask; - /** @private */ private var _mask:Mask; - /** @private */ private var _x:Number; - /** @private */ private var _y:Number; - /** @private */ private var _moveX:Number = 0; - /** @private */ private var _moveY:Number = 0; - - // Rendering information. - /** @private */ internal var _graphic:Graphic; - /** @private */ private var _point:Point = FP.point; - /** @private */ private var _camera:Point = FP.point2; - } -} \ No newline at end of file diff --git a/src/net/flashpunk/FP.as b/src/net/flashpunk/FP.as deleted file mode 100644 index af22d2b..0000000 --- a/src/net/flashpunk/FP.as +++ /dev/null @@ -1,886 +0,0 @@ -package net.flashpunk -{ - import flash.display.BitmapData; - import flash.display.Sprite; - import flash.display.Stage; - import flash.geom.Matrix; - import flash.geom.Point; - import flash.geom.Rectangle; - import flash.media.SoundMixer; - import flash.media.SoundTransform; - import flash.system.System; - import flash.utils.ByteArray; - import flash.utils.getTimer; - import net.flashpunk.*; - import net.flashpunk.debug.Console; - import net.flashpunk.tweens.misc.MultiVarTween; - - /** - * Static catch-all class used to access global properties and functions. - */ - public class FP - { - /** - * The FlashPunk major version. - */ - public static const VERSION:String = "1.4"; - - /** - * Width of the game. - */ - public static var width:uint; - - /** - * Height of the game. - */ - public static var height:uint; - - /** - * If the game is running at a fixed framerate. - */ - public static var fixed:Boolean; - - /** - * The framerate assigned to the stage. - */ - public static var frameRate:Number; - - /** - * The framerate assigned to the stage. - */ - public static var assignedFrameRate:Number; - - /** - * Time elapsed since the last frame (non-fixed framerate only). - */ - public static var elapsed:Number; - - /** - * Timescale applied to FP.elapsed (non-fixed framerate only). - */ - public static var rate:Number = 1; - - /** - * The Screen object, use to transform or offset the Screen. - */ - public static var screen:Screen; - - /** - * The current screen buffer, drawn to in the render loop. - */ - public static var buffer:BitmapData; - - /** - * A rectangle representing the size of the screen. - */ - public static var bounds:Rectangle; - - /** - * Point used to determine drawing offset in the render loop. - */ - public static var camera:Point = new Point; - - /** - * Half the screen width. - */ - public static function get halfWidth():Number { return width / 2; } - - /** - * Half the screen height. - */ - public static function get halfHeight():Number { return height / 2; } - - /** - * The currently active World object. When you set this, the World is flagged - * to switch, but won't actually do so until the end of the current frame. - */ - public static function get world():World { return _world; } - public static function set world(value:World):void - { - if (_world == value) return; - _goto = value; - } - - /** - * Sets the camera position. - * @param x X position. - * @param y Y position. - */ - public static function setCamera(x:Number = 0, y:Number = 0):void - { - camera.x = x; - camera.y = y; - } - - /** - * Resets the camera position. - */ - public static function resetCamera():void - { - camera.x = camera.y = 0; - } - - /** - * Global volume factor for all sounds, a value from 0 to 1. - */ - public static function get volume():Number { return _volume; } - public static function set volume(value:Number):void - { - if (value < 0) value = 0; - if (_volume == value) return; - _soundTransform.volume = _volume = value; - SoundMixer.soundTransform = _soundTransform; - } - - /** - * Global panning factor for all sounds, a value from -1 to 1. - */ - public static function get pan():Number { return _pan; } - public static function set pan(value:Number):void - { - if (value < -1) value = -1; - if (value > 1) value = 1; - if (_pan == value) return; - _soundTransform.pan = _pan = value; - SoundMixer.soundTransform = _soundTransform; - } - - /** - * Randomly chooses and returns one of the provided values. - * @param ...objs The Objects you want to randomly choose from. Can be ints, Numbers, Points, etc. - * @return A randomly chosen one of the provided parameters. - */ - public static function choose(...objs):* - { - var c:* = (objs.length == 1 && (objs[0] is Array || objs[0] is Vector.<*>)) ? objs[0] : objs; - return c[rand(c.length)]; - } - - /** - * Finds the sign of the provided value. - * @param value The Number to evaluate. - * @return 1 if value > 0, -1 if value < 0, and 0 when value == 0. - */ - public static function sign(value:Number):int - { - return value < 0 ? -1 : (value > 0 ? 1 : 0); - } - - /** - * Approaches the value towards the target, by the specified amount, without overshooting the target. - * @param value The starting value. - * @param target The target that you want value to approach. - * @param amount How much you want the value to approach target by. - * @return The new value. - */ - public static function approach(value:Number, target:Number, amount:Number):Number - { - return value < target ? (target < value + amount ? target : value + amount) : (target > value - amount ? target : value - amount); - } - - /** - * Linear interpolation between two values. - * @param a First value. - * @param b Second value. - * @param t Interpolation factor. - * @return When t=0, returns a. When t=1, returns b. When t=0.5, will return halfway between a and b. Etc. - */ - public static function lerp(a:Number, b:Number, t:Number = 1):Number - { - return a + (b - a) * t; - } - - /** - * Linear interpolation between two colors. - * @param fromColor First color. - * @param toColor Second color. - * @param t Interpolation value. Clamped to the range [0, 1]. - * return RGB component-interpolated color value. - */ - public static function colorLerp(fromColor:uint, toColor:uint, t:Number = 1):uint - { - if (t <= 0) { return fromColor; } - if (t >= 1) { return toColor; } - var a:uint = fromColor >> 24 & 0xFF, - r:uint = fromColor >> 16 & 0xFF, - g:uint = fromColor >> 8 & 0xFF, - b:uint = fromColor & 0xFF, - dA: int = (toColor >> 24 & 0xFF) - a, - dR: int = (toColor >> 16 & 0xFF) - r, - dG: int = (toColor >> 8 & 0xFF) - g, - dB: int = (toColor & 0xFF) - b; - a += dA * t; - r += dR * t; - g += dG * t; - b += dB * t; - return a << 24 | r << 16 | g << 8 | b; - } - - /** - * Steps the object towards a point. - * @param object Object to move (must have an x and y property). - * @param x X position to step towards. - * @param y Y position to step towards. - * @param distance The distance to step (will not overshoot target). - */ - public static function stepTowards(object:Object, x:Number, y:Number, distance:Number = 1):void - { - point.x = x - object.x; - point.y = y - object.y; - if (point.length <= distance) - { - object.x = x; - object.y = y; - return; - } - point.normalize(distance); - object.x += point.x; - object.y += point.y; - } - - /** - * Anchors the object to a position. - * @param object The object to anchor. - * @param anchor The anchor object. - * @param distance The max distance object can be anchored to the anchor. - */ - public static function anchorTo(object:Object, anchor:Object, distance:Number = 0):void - { - point.x = object.x - anchor.x; - point.y = object.y - anchor.y; - if (point.length > distance) point.normalize(distance); - object.x = anchor.x + point.x; - object.y = anchor.y + point.y; - } - - /** - * Finds the angle (in degrees) from point 1 to point 2. - * @param x1 The first x-position. - * @param y1 The first y-position. - * @param x2 The second x-position. - * @param y2 The second y-position. - * @return The angle from (x1, y1) to (x2, y2). - */ - public static function angle(x1:Number, y1:Number, x2:Number, y2:Number):Number - { - var a:Number = Math.atan2(y2 - y1, x2 - x1) * DEG; - return a < 0 ? a + 360 : a; - } - - /** - * Sets the x/y values of the provided object to a vector of the specified angle and length. - * @param object The object whose x/y properties should be set. - * @param angle The angle of the vector, in degrees. - * @param length The distance to the vector from (0, 0). - * @param x X offset. - * @param y Y offset. - */ - public static function angleXY(object:Object, angle:Number, length:Number = 1, x:Number = 0, y:Number = 0):void - { - angle *= RAD; - object.x = Math.cos(angle) * length + x; - object.y = Math.sin(angle) * length + y; - } - - /** - * Rotates the object around the anchor by the specified amount. - * @param object Object to rotate around the anchor. - * @param anchor Anchor to rotate around. - * @param angle The amount of degrees to rotate by. - */ - public static function rotateAround(object:Object, anchor:Object, angle:Number = 0, relative:Boolean = true):void - { - if (relative) angle += FP.angle(anchor.x, anchor.y, object.x, object.y); - FP.angleXY(object, angle, FP.distance(anchor.x, anchor.y, object.x, object.y), anchor.x, anchor.y); - } - - /** - * Find the distance between two points. - * @param x1 The first x-position. - * @param y1 The first y-position. - * @param x2 The second x-position. - * @param y2 The second y-position. - * @return The distance. - */ - public static function distance(x1:Number, y1:Number, x2:Number = 0, y2:Number = 0):Number - { - return Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)); - } - - /** - * Find the distance between two rectangles. Will return 0 if the rectangles overlap. - * @param x1 The x-position of the first rect. - * @param y1 The y-position of the first rect. - * @param w1 The width of the first rect. - * @param h1 The height of the first rect. - * @param x2 The x-position of the second rect. - * @param y2 The y-position of the second rect. - * @param w2 The width of the second rect. - * @param h2 The height of the second rect. - * @return The distance. - */ - public static function distanceRects(x1:Number, y1:Number, w1:Number, h1:Number, x2:Number, y2:Number, w2:Number, h2:Number):Number - { - if (x1 < x2 + w2 && x2 < x1 + w1) - { - if (y1 < y2 + h2 && y2 < y1 + h1) return 0; - if (y1 > y2) return y1 - (y2 + h2); - return y2 - (y1 + h1); - } - if (y1 < y2 + h2 && y2 < y1 + h1) - { - if (x1 > x2) return x1 - (x2 + w2); - return x2 - (x1 + w1) - } - if (x1 > x2) - { - if (y1 > y2) return distance(x1, y1, (x2 + w2), (y2 + h2)); - return distance(x1, y1 + h1, x2 + w2, y2); - } - if (y1 > y2) return distance(x1 + w1, y1, x2, y2 + h2) - return distance(x1 + w1, y1 + h1, x2, y2); - } - - /** - * Find the distance between a point and a rectangle. Returns 0 if the point is within the rectangle. - * @param px The x-position of the point. - * @param py The y-position of the point. - * @param rx The x-position of the rect. - * @param ry The y-position of the rect. - * @param rw The width of the rect. - * @param rh The height of the rect. - * @return The distance. - */ - public static function distanceRectPoint(px:Number, py:Number, rx:Number, ry:Number, rw:Number, rh:Number):Number - { - if (px >= rx && px <= rx + rw) - { - if (py >= ry && py <= ry + rh) return 0; - if (py > ry) return py - (ry + rh); - return ry - py; - } - if (py >= ry && py <= ry + rh) - { - if (px > rx) return px - (rx + rw); - return rx - px; - } - if (px > rx) - { - if (py > ry) return distance(px, py, rx + rw, ry + rh); - return distance(px, py, rx + rw, ry); - } - if (py > ry) return distance(px, py, rx, ry + rh) - return distance(px, py, rx, ry); - } - - /** - * Clamps the value within the minimum and maximum values. - * @param value The Number to evaluate. - * @param min The minimum range. - * @param max The maximum range. - * @return The clamped value. - */ - public static function clamp(value:Number, min:Number, max:Number):Number - { - if (max > min) - { - value = value < max ? value : max; - return value > min ? value : min; - } - value = value < min ? value : min; - return value > max ? value : max; - } - - /** - * Clamps the object inside the rectangle. - * @param object The object to clamp (must have an x and y property). - * @param x Rectangle's x. - * @param y Rectangle's y. - * @param width Rectangle's width. - * @param height Rectangle's height. - */ - public static function clampInRect(object:Object, x:Number, y:Number, width:Number, height:Number, padding:Number = 0):void - { - object.x = clamp(object.x, x + padding, x + width - padding); - object.y = clamp(object.y, y + padding, y + height - padding); - } - - /** - * Transfers a value from one scale to another scale. For example, scale(.5, 0, 1, 10, 20) == 15, and scale(3, 0, 5, 100, 0) == 40. - * @param value The value on the first scale. - * @param min The minimum range of the first scale. - * @param max The maximum range of the first scale. - * @param min2 The minimum range of the second scale. - * @param max2 The maximum range of the second scale. - * @return The scaled value. - */ - public static function scale(value:Number, min:Number, max:Number, min2:Number, max2:Number):Number - { - return min2 + ((value - min) / (max - min)) * (max2 - min2); - } - - /** - * Transfers a value from one scale to another scale, but clamps the return value within the second scale. - * @param value The value on the first scale. - * @param min The minimum range of the first scale. - * @param max The maximum range of the first scale. - * @param min2 The minimum range of the second scale. - * @param max2 The maximum range of the second scale. - * @return The scaled and clamped value. - */ - public static function scaleClamp(value:Number, min:Number, max:Number, min2:Number, max2:Number):Number - { - value = min2 + ((value - min) / (max - min)) * (max2 - min2); - if (max2 > min2) - { - value = value < max2 ? value : max2; - return value > min2 ? value : min2; - } - value = value < min2 ? value : min2; - return value > max2 ? value : max2; - } - - /** - * The random seed used by FP's random functions. - */ - public static function get randomSeed():uint { return _getSeed; } - public static function set randomSeed(value:uint):void - { - _seed = clamp(value, 1, 2147483646); - _getSeed = _seed; - } - - /** - * Randomizes the random seed using Flash's Math.random() function. - */ - public static function randomizeSeed():void - { - randomSeed = 2147483647 * Math.random(); - } - - /** - * A pseudo-random Number produced using FP's random seed, where 0 <= Number < 1. - */ - public static function get random():Number - { - _seed = (_seed * 16807) % 2147483647; - return _seed / 2147483647; - } - - /** - * Returns a pseudo-random uint. - * @param amount The returned uint will always be 0 <= uint < amount. - * @return The uint. - */ - public static function rand(amount:uint):uint - { - _seed = (_seed * 16807) % 2147483647; - return (_seed / 2147483647) * amount; - } - - /** - * Returns the next item after current in the list of options. - * @param current The currently selected item (must be one of the options). - * @param options An array of all the items to cycle through. - * @param loop If true, will jump to the first item after the last item is reached. - * @return The next item in the list. - */ - public static function next(current:*, options:Array, loop:Boolean = true):* - { - if (loop) return options[(options.indexOf(current) + 1) % options.length]; - return options[Math.max(options.indexOf(current) + 1, options.length - 1)]; - } - - /** - * Returns the item previous to the current in the list of options. - * @param current The currently selected item (must be one of the options). - * @param options An array of all the items to cycle through. - * @param loop If true, will jump to the last item after the first is reached. - * @return The previous item in the list. - */ - public static function prev(current:*, options:Array, loop:Boolean = true):* - { - if (loop) return options[((options.indexOf(current) - 1) + options.length) % options.length]; - return options[Math.max(options.indexOf(current) - 1, 0)]; - } - - /** - * Swaps the current item between a and b. Useful for quick state/string/value swapping. - * @param current The currently selected item. - * @param a Item a. - * @param b Item b. - * @return Returns a if current is b, and b if current is a. - */ - public static function swap(current:*, a:*, b:*):* - { - return current == a ? b : a; - } - - /** - * Creates a color value by combining the chosen RGB values. - * @param R The red value of the color, from 0 to 255. - * @param G The green value of the color, from 0 to 255. - * @param B The blue value of the color, from 0 to 255. - * @return The color uint. - */ - public static function getColorRGB(R:uint = 0, G:uint = 0, B:uint = 0):uint - { - return R << 16 | G << 8 | B; - } - - /** - * Creates a color value with the chosen HSV values. - * @param h The hue of the color (from 0 to 1). - * @param s The saturation of the color (from 0 to 1). - * @param v The value of the color (from 0 to 1). - * @return The color uint. - */ - public static function getColorHSV(h:Number, s:Number, v:Number):uint - { - h = int(h * 360); - var hi:int = Math.floor(h / 60) % 6, - f:Number = h / 60 - Math.floor(h / 60), - p:Number = (v * (1 - s)), - q:Number = (v * (1 - f * s)), - t:Number = (v * (1 - (1 - f) * s)); - switch (hi) - { - case 0: return int(v * 255) << 16 | int(t * 255) << 8 | int(p * 255); - case 1: return int(q * 255) << 16 | int(v * 255) << 8 | int(p * 255); - case 2: return int(p * 255) << 16 | int(v * 255) << 8 | int(t * 255); - case 3: return int(p * 255) << 16 | int(q * 255) << 8 | int(v * 255); - case 4: return int(t * 255) << 16 | int(p * 255) << 8 | int(v * 255); - case 5: return int(v * 255) << 16 | int(p * 255) << 8 | int(q * 255); - default: return 0; - } - return 0; - } - - /** - * Finds the red factor of a color. - * @param color The color to evaluate. - * @return A uint from 0 to 255. - */ - public static function getRed(color:uint):uint - { - return color >> 16 & 0xFF; - } - - /** - * Finds the green factor of a color. - * @param color The color to evaluate. - * @return A uint from 0 to 255. - */ - public static function getGreen(color:uint):uint - { - return color >> 8 & 0xFF; - } - - /** - * Finds the blue factor of a color. - * @param color The color to evaluate. - * @return A uint from 0 to 255. - */ - public static function getBlue(color:uint):uint - { - return color & 0xFF; - } - - /** - * Fetches a stored BitmapData object represented by the source. - * @param source Embedded Bitmap class. - * @return The stored BitmapData object. - */ - public static function getBitmap(source:Class):BitmapData - { - if (_bitmap[String(source)]) return _bitmap[String(source)]; - return (_bitmap[String(source)] = (new source).bitmapData); - } - - /** - * Sets a time flag. - * @return Time elapsed (in milliseconds) since the last time flag was set. - */ - public static function timeFlag():uint - { - var t:uint = getTimer(), - e:uint = t - _time; - _time = t; - return e; - } - - /** - * The global Console object. - */ - public static function get console():Console - { - if (!_console) _console = new Console; - return _console; - } - - /** - * Logs data to the console. - * @param ...data The data parameters to log, can be variables, objects, etc. Parameters will be separated by a space (" "). - */ - public static function log(...data):void - { - if (_console) - { - if (data.length > 1) - { - var i:int = 0, s:String = ""; - while (i < data.length) - { - if (i > 0) s += " "; - s += data[i ++].toString(); - } - _console.log(s); - } - else _console.log(data[0]); - } - } - - /** - * Adds properties to watch in the console's debug panel. - * @param ...properties The properties (strings) to watch. - */ - public static function watch(...properties):void - { - if (_console) - { - if (properties.length > 1) _console.watch(properties); - else _console.watch(properties[0]); - } - } - - /** - * Loads the file as an XML object. - * @param file The embedded file to load. - * @return An XML object representing the file. - */ - public static function getXML(file:Class):XML - { - var bytes:ByteArray = new file; - return XML(bytes.readUTFBytes(bytes.length)); - } - - /** - * Tweens numeric public properties of an Object. Shorthand for creating a MultiVarTween tween, starting it and adding it to a Tweener. - * @param object The object containing the properties to tween. - * @param values An object containing key/value pairs of properties and target values. - * @param duration Duration of the tween. - * @param options An object containing key/value pairs of the following optional parameters: - * type Tween type. - * complete Optional completion callback function. - * ease Optional easer function. - * tweener The Tweener to add this Tween to. - * @return The added MultiVarTween object. - * - * Example: FP.tween(object, { x: 500, y: 350 }, 2.0, { ease: easeFunction, complete: onComplete } ); - */ - public static function tween(object:Object, values:Object, duration:Number, options:Object = null):MultiVarTween - { - var type:uint = Tween.ONESHOT, - complete:Function = null, - ease:Function = null, - tweener:Tweener = FP.world; - if (object is Tweener) tweener = object as Tweener; - if (options) - { - if (options.hasOwnProperty("type")) type = options.type; - if (options.hasOwnProperty("complete")) complete = options.complete; - if (options.hasOwnProperty("ease")) ease = options.ease; - if (options.hasOwnProperty("tweener")) tweener = options.tweener; - } - var tween:MultiVarTween = new MultiVarTween(complete, type); - tween.tween(object, values, duration, ease); - tweener.addTween(tween); - return tween; - } - - /** - * Gets an array of frame indices. - * @param from Starting frame. - * @param to Ending frame. - * @param skip Skip amount every frame (eg. use 1 for every 2nd frame). - */ - public static function frames(from:int, to:int, skip:int = 0):Array - { - var a:Array = []; - skip ++; - if (from < to) - { - while (from <= to) - { - a.push(from); - from += skip; - } - } - else - { - while (from >= to) - { - a.push(from); - from -= skip; - } - } - return a; - } - - /** - * Shuffles the elements in the array. - * @param a The Object to shuffle (an Array or Vector). - */ - public static function shuffle(a:Object):void - { - if (a is Array || a is Vector.<*>) - { - var i:int = a.length, j:int, t:*; - while (-- i) - { - t = a[i]; - a[i] = a[j = FP.rand(i + 1)]; - a[j] = t; - } - } - } - - /** - * Sorts the elements in the array. - * @param object The Object to sort (an Array or Vector). - * @param ascending If it should be sorted ascending (true) or descending (false). - */ - public static function sort(object:Object, ascending:Boolean = true):void - { - if (object is Array || object is Vector.<*>) quicksort(object, 0, object.length - 1, ascending); - } - - /** - * Sorts the elements in the array by a property of the element. - * @param object The Object to sort (an Array or Vector). - * @param property The numeric property of object's elements to sort by. - * @param ascending If it should be sorted ascending (true) or descending (false). - */ - public static function sortBy(object:Object, property:String, ascending:Boolean = true):void - { - if (object is Array || object is Vector.<*>) quicksortBy(object, 0, object.length - 1, ascending, property); - } - - /** @private Quicksorts the array. */ - private static function quicksort(a:Object, left:int, right:int, ascending:Boolean):void - { - var i:int = left, j:int = right, t:Number, - p:* = a[Math.round((left + right) * .5)]; - if (ascending) - { - while (i <= j) - { - while (a[i] < p) i ++; - while (a[j] > p) j --; - if (i <= j) - { - t = a[i]; - a[i ++] = a[j]; - a[j --] = t; - } - } - } - else - { - while (i <= j) - { - while (a[i] > p) i ++; - while (a[j] < p) j --; - if (i <= j) - { - t = a[i]; - a[i ++] = a[j]; - a[j --] = t; - } - } - } - if (left < j) quicksort(a, left, j, ascending); - if (i < right) quicksort(a, i, right, ascending); - } - - /** @private Quicksorts the array by the property. */ - private static function quicksortBy(a:Object, left:int, right:int, ascending:Boolean, property:String):void - { - var i:int = left, j:int = right, t:Object, - p:* = a[Math.round((left + right) * .5)][property]; - if (ascending) - { - while (i <= j) - { - while (a[i][property] < p) i ++; - while (a[j][property] > p) j --; - if (i <= j) - { - t = a[i]; - a[i ++] = a[j]; - a[j --] = t; - } - } - } - else - { - while (i <= j) - { - while (a[i][property] > p) i ++; - while (a[j][property] < p) j --; - if (i <= j) - { - t = a[i]; - a[i ++] = a[j]; - a[j --] = t; - } - } - } - if (left < j) quicksortBy(a, left, j, ascending, property); - if (i < right) quicksortBy(a, i, right, ascending, property); - } - - // World information. - /** @private */ internal static var _world:World; - /** @private */ internal static var _goto:World; - - // Console information. - /** @private */ internal static var _console:Console; - - // Time information. - /** @private */ internal static var _time:uint; - /** @private */ public static var _updateTime:uint; - /** @private */ public static var _renderTime:uint; - /** @private */ public static var _gameTime:uint; - /** @private */ public static var _flashTime:uint; - - // Bitmap storage. - /** @private */ private static var _bitmap:Object = { }; - - // Pseudo-random number generation (the seed is set in Engine's contructor). - /** @private */ private static var _seed:uint = 0; - /** @private */ private static var _getSeed:uint; - - // Volume control. - /** @private */ private static var _volume:Number = 1; - /** @private */ private static var _pan:Number = 0; - /** @private */ private static var _soundTransform:SoundTransform = new SoundTransform; - - // Used for rad-to-deg and deg-to-rad conversion. - /** @private */ public static const DEG:Number = -180 / Math.PI; - /** @private */ public static const RAD:Number = Math.PI / -180; - - // Global Flash objects. - /** @private */ public static var stage:Stage; - /** @private */ public static var engine:Engine; - - // Global objects used for rendering, collision, etc. - /** @private */ public static var point:Point = new Point; - /** @private */ public static var point2:Point = new Point; - /** @private */ public static var zero:Point = new Point; - /** @private */ public static var rect:Rectangle = new Rectangle; - /** @private */ public static var matrix:Matrix = new Matrix; - /** @private */ public static var sprite:Sprite = new Sprite; - /** @private */ public static var entity:Entity; - } -} \ No newline at end of file diff --git a/src/net/flashpunk/Graphic.as b/src/net/flashpunk/Graphic.as deleted file mode 100644 index 18a38c3..0000000 --- a/src/net/flashpunk/Graphic.as +++ /dev/null @@ -1,85 +0,0 @@ -package net.flashpunk -{ - import flash.display.BitmapData; - import flash.geom.Point; - - /** - * Base class for all graphical types that can be drawn by Entity. - */ - public class Graphic - { - /** - * If the graphic should update. - */ - public var active:Boolean = false; - - /** - * If the graphic should render. - */ - public var visible:Boolean = true; - - /** - * X offset. - */ - public var x:Number = 0; - - /** - * Y offset. - */ - public var y:Number = 0; - - /** - * X scrollfactor, effects how much the camera offsets the drawn graphic. - * Can be used for parallax effect, eg. Set to 0 to follow the camera, - * 0.5 to move at half-speed of the camera, or 1 (default) to stay still. - */ - public var scrollX:Number = 1; - - /** - * Y scrollfactor, effects how much the camera offsets the drawn graphic. - * Can be used for parallax effect, eg. Set to 0 to follow the camera, - * 0.5 to move at half-speed of the camera, or 1 (default) to stay still. - */ - public var scrollY:Number = 1; - - /** - * If the graphic should render at its position relative to its parent Entity's position. - */ - public var relative:Boolean = true; - - /** - * Constructor. - */ - public function Graphic() - { - - } - - /** - * Updates the graphic. - */ - public function update():void - { - - } - - /** - * Renders the graphic to the screen buffer. - * @param point The position to draw the graphic. - * @param camera The camera offset. - */ - public function render(target:BitmapData, point:Point, camera:Point):void - { - - } - - /** @private Callback for when the graphic is assigned to an Entity. */ - protected function get assign():Function { return _assign; } - protected function set assign(value:Function):void { _assign = value; } - - // Graphic information. - /** @private */ internal var _assign:Function; - /** @private */ internal var _scroll:Boolean = true; - /** @private */ protected var _point:Point = new Point; - } -} \ No newline at end of file diff --git a/src/net/flashpunk/Mask.as b/src/net/flashpunk/Mask.as deleted file mode 100644 index 8edaaf7..0000000 --- a/src/net/flashpunk/Mask.as +++ /dev/null @@ -1,78 +0,0 @@ -package net.flashpunk -{ - import flash.utils.Dictionary; - import flash.utils.getDefinitionByName; - import flash.utils.getQualifiedClassName; - import net.flashpunk.masks.Hitbox; - import net.flashpunk.masks.Masklist; - - /** - * Base class for Entity collision masks. - */ - public class Mask - { - /** - * The parent Entity of this mask. - */ - public var parent:Entity; - - /** - * The parent Masklist of the mask. - */ - public var list:Masklist; - - /** - * Constructor. - */ - public function Mask() - { - _class = Class(getDefinitionByName(getQualifiedClassName(this))); - _check[Mask] = collideMask; - _check[Masklist] = collideMasklist; - } - - /** - * Checks for collision with another Mask. - * @param mask The other Mask to check against. - * @return If the Masks overlap. - */ - public function collide(mask:Mask):Boolean - { - if (_check[mask._class] != null) return _check[mask._class](mask); - if (mask._check[_class] != null) return mask._check[_class](this); - return false; - } - - /** @private Collide against an Entity. */ - private function collideMask(other:Mask):Boolean - { - return parent.x - parent.originX + parent.width > other.parent.x - other.parent.originX - && parent.y - parent.originY + parent.height > other.parent.y - other.parent.originY - && parent.x - parent.originX < other.parent.x - other.parent.originX + other.parent.width - && parent.y - parent.originY < other.parent.y - other.parent.originY + other.parent.height; - } - - /** @private Collide against a Masklist. */ - protected function collideMasklist(other:Masklist):Boolean - { - return other.collide(this); - } - - /** @private Assigns the mask to the parent. */ - internal function assignTo(parent:Entity):void - { - this.parent = parent; - if (parent) update(); - } - - /** @private Updates the parent's bounds for this mask. */ - protected function update():void - { - - } - - // Mask information. - /** @private */ private var _class:Class; - /** @private */ protected var _check:Dictionary = new Dictionary; - } -} \ No newline at end of file diff --git a/src/net/flashpunk/Screen.as b/src/net/flashpunk/Screen.as deleted file mode 100644 index e01bb41..0000000 --- a/src/net/flashpunk/Screen.as +++ /dev/null @@ -1,223 +0,0 @@ -package net.flashpunk -{ - import flash.display.Bitmap; - import flash.display.BitmapData; - import flash.display.PixelSnapping; - import flash.display.Sprite; - import flash.geom.Matrix; - import flash.geom.Point; - import flash.geom.Transform; - import net.flashpunk.graphics.Image; - - /** - * Container for the main screen buffer. Can be used to transform the screen. - */ - public class Screen - { - /** - * Constructor. - */ - public function Screen() - { - // create screen buffers - _bitmap[0] = new Bitmap(new BitmapData(FP.width, FP.height, false, 0), PixelSnapping.NEVER); - _bitmap[1] = new Bitmap(new BitmapData(FP.width, FP.height, false, 0), PixelSnapping.NEVER); - FP.engine.addChild(_sprite); - _sprite.addChild(_bitmap[0]).visible = true; - _sprite.addChild(_bitmap[1]).visible = false; - FP.buffer = _bitmap[0].bitmapData; - _width = FP.width; - _height = FP.height; - update(); - } - - /** - * Swaps screen buffers. - */ - public function swap():void - { - _current = 1 - _current; - FP.buffer = _bitmap[_current].bitmapData; - } - - /** - * Refreshes the screen. - */ - public function refresh():void - { - // refreshes the screen - FP.buffer.fillRect(FP.bounds, _color); - } - - /** - * Redraws the screen. - */ - public function redraw():void - { - // refresh the buffers - _bitmap[_current].visible = true; - _bitmap[1 - _current].visible = false; - } - - /** @private Re-applies transformation matrix. */ - public function update():void - { - _matrix.b = _matrix.c = 0; - _matrix.a = _scaleX * _scale; - _matrix.d = _scaleY * _scale; - _matrix.tx = -_originX * _matrix.a; - _matrix.ty = -_originY * _matrix.d; - if (_angle != 0) _matrix.rotate(_angle); - _matrix.tx += _originX * _scaleX * _scale + _x; - _matrix.ty += _originY * _scaleX * _scale + _y; - _sprite.transform.matrix = _matrix; - } - - /** - * Refresh color of the screen. - */ - public function get color():uint { return _color; } - public function set color(value:uint):void { _color = 0xFF000000 | value; } - - /** - * X offset of the screen. - */ - public function get x():int { return _x; } - public function set x(value:int):void - { - if (_x == value) return; - _x = value; - update(); - } - - /** - * Y offset of the screen. - */ - public function get y():int { return _y; } - public function set y(value:int):void - { - if (_y == value) return; - _y = value; - update(); - } - - /** - * X origin of transformations. - */ - public function get originX():int { return _originX; } - public function set originX(value:int):void - { - if (_originX == value) return; - _originX = value; - update(); - } - - /** - * Y origin of transformations. - */ - public function get originY():int { return _originY; } - public function set originY(value:int):void - { - if (_originY == value) return; - _originY = value; - update(); - } - - /** - * X scale of the screen. - */ - public function get scaleX():Number { return _scaleX; } - public function set scaleX(value:Number):void - { - if (_scaleX == value) return; - _scaleX = value; - update(); - } - - /** - * Y scale of the screen. - */ - public function get scaleY():Number { return _scaleY; } - public function set scaleY(value:Number):void - { - if (_scaleY == value) return; - _scaleY = value; - update(); - } - - /** - * Scale factor of the screen. Final scale is scaleX * scale by scaleY * scale, so - * you can use this factor to scale the screen both horizontally and vertically. - */ - public function get scale():Number { return _scale; } - public function set scale(value:Number):void - { - if (_scale == value) return; - _scale = value; - update(); - } - - /** - * Rotation of the screen, in degrees. - */ - public function get angle():Number { return _angle * FP.DEG; } - public function set angle(value:Number):void - { - if (_angle == value * FP.RAD) return; - _angle = value * FP.RAD; - update(); - } - - /** - * Whether screen smoothing should be used or not. - */ - public function get smoothing():Boolean { return _bitmap[0].smoothing; } - public function set smoothing(value:Boolean):void { _bitmap[0].smoothing = _bitmap[1].smoothing = value; } - - /** - * Width of the screen. - */ - public function get width():uint { return _width; } - - /** - * Height of the screen. - */ - public function get height():uint { return _height; } - - /** - * X position of the mouse on the screen. - */ - public function get mouseX():int { return (FP.stage.mouseX - _x) / (_scaleX * _scale); } - - /** - * Y position of the mouse on the screen. - */ - public function get mouseY():int { return (FP.stage.mouseY - _y) / (_scaleY * _scale); } - - /** - * Captures the current screen as an Image object. - * @return A new Image object. - */ - public function capture():Image - { - return new Image(_bitmap[_current].bitmapData.clone()); - } - - // Screen infromation. - /** @private */ private var _sprite:Sprite = new Sprite; - /** @private */ private var _bitmap:Vector. = new Vector.(2); - /** @private */ private var _current:int = 0; - /** @private */ private var _matrix:Matrix = new Matrix; - /** @private */ private var _x:int; - /** @private */ private var _y:int; - /** @private */ private var _width:uint; - /** @private */ private var _height:uint; - /** @private */ private var _originX:int; - /** @private */ private var _originY:int; - /** @private */ private var _scaleX:Number = 1; - /** @private */ private var _scaleY:Number = 1; - /** @private */ private var _scale:Number = 1; - /** @private */ private var _angle:Number = 0; - /** @private */ private var _color:uint = 0x202020; - } -} \ No newline at end of file diff --git a/src/net/flashpunk/Sfx.as b/src/net/flashpunk/Sfx.as deleted file mode 100644 index f2f2d16..0000000 --- a/src/net/flashpunk/Sfx.as +++ /dev/null @@ -1,144 +0,0 @@ -package net.flashpunk -{ - import flash.events.Event; - import flash.media.Sound; - import flash.media.SoundChannel; - import flash.media.SoundTransform; - import flash.utils.Dictionary; - - /** - * Sound effect object used to play embedded sounds. - */ - public class Sfx - { - /** - * Optional callback function for when the sound finishes playing. - */ - public var complete:Function; - - /** - * Creates a sound effect from an embedded source. Store a reference to - * this object so that you can play the sound using play() or loop(). - * @param source The embedded sound class to use. - * @param complete Optional callback function for when the sound finishes playing. - */ - public function Sfx(source:Class, complete:Function = null) - { - _sound = _sounds[source]; - if (!_sound) _sound = _sounds[source] = new source; - this.complete = complete; - } - - /** - * Plays the sound once. - * @param vol Volume factor, a value from 0 to 1. - * @param pan Panning factor, a value from -1 to 1. - */ - public function play(vol:Number = 1, pan:Number = 0):void - { - if (_channel) stop(); - _vol = _transform.volume = vol < 0 ? 0 : vol; - _pan = _transform.pan = pan < -1 ? -1 : (pan > 1 ? 1 : pan); - _channel = _sound.play(0, 0, _transform); - _channel.addEventListener(Event.SOUND_COMPLETE, onComplete); - _looping = false; - _position = 0; - } - - /** - * Plays the sound looping. Will loop continuously until you call stop(), play(), or loop() again. - * @param vol Volume factor, a value from 0 to 1. - * @param pan Panning factor, a value from -1 to 1. - */ - public function loop(vol:Number = 1, pan:Number = 0):void - { - play(vol, pan); - _looping = true; - } - - /** - * Stops the sound if it is currently playing. - * @return - */ - public function stop():Boolean - { - if (!_channel) return false; - _position = _channel.position; - _channel.removeEventListener(Event.SOUND_COMPLETE, onComplete); - _channel.stop(); - _channel = null; - return true; - } - - /** - * Resumes the sound from the position stop() was called on it. - */ - public function resume():void - { - _channel = _sound.play(_position, 0, _transform); - _channel.addEventListener(Event.SOUND_COMPLETE, onComplete); - _position = 0; - } - - /** @private Event handler for sound completion. */ - private function onComplete(e:Event = null):void - { - if (_looping) loop(_vol, _pan); - else stop(); - _position = 0; - if (complete != null) complete(); - } - - /** - * Alter the volume factor (a value from 0 to 1) of the sound during playback. - */ - public function get volume():Number { return _vol; } - public function set volume(value:Number):void - { - if (value < 0) value = 0; - if (!_channel || _vol == value) return; - _vol = _transform.volume = value; - _channel.soundTransform = _transform; - } - - /** - * Alter the panning factor (a value from -1 to 1) of the sound during playback. - */ - public function get pan():Number { return _pan; } - public function set pan(value:Number):void - { - if (value < -1) value = -1; - if (value > 1) value = 1; - if (!_channel || _pan == value) return; - _pan = _transform.pan = value; - _channel.soundTransform = _transform; - } - - /** - * If the sound is currently playing. - */ - public function get playing():Boolean { return _channel != null; } - - /** - * Position of the currently playing sound, in seconds. - */ - public function get position():Number { return (_channel ? _channel.position : _position) / 1000; } - - /** - * Length of the sound, in seconds. - */ - public function get length():Number { return _sound.length / 1000; } - - // Sound infromation. - /** @private */ private var _vol:Number = 1; - /** @private */ private var _pan:Number = 0; - /** @private */ private var _sound:Sound; - /** @private */ private var _channel:SoundChannel; - /** @private */ private var _transform:SoundTransform = new SoundTransform; - /** @private */ private var _position:Number = 0; - /** @private */ private var _looping:Boolean; - - // Stored Sound objects. - /** @private */ private static var _sounds:Dictionary = new Dictionary; - } -} \ No newline at end of file diff --git a/src/net/flashpunk/Tween.as b/src/net/flashpunk/Tween.as deleted file mode 100644 index 4b04d96..0000000 --- a/src/net/flashpunk/Tween.as +++ /dev/null @@ -1,128 +0,0 @@ -package net.flashpunk -{ - /** - * Base class for all Tween objects, can be added to any Core-extended classes. - */ - public class Tween - { - /** - * Persistent Tween type, will stop when it finishes. - */ - public static const PERSIST:uint = 0; - - /** - * Looping Tween type, will restart immediately when it finishes. - */ - public static const LOOPING:uint = 1; - - /** - * Oneshot Tween type, will stop and remove itself from its core container when it finishes. - */ - public static const ONESHOT:uint = 2; - - /** - * If the tween should update. - */ - public var active:Boolean; - - /** - * Tween completion callback. - */ - public var complete:Function; - - /** - * Constructor. Specify basic information about the Tween. - * @param duration Duration of the tween (in seconds or frames). - * @param type Tween type, one of Tween.PERSIST (default), Tween.LOOPING, or Tween.ONESHOT. - * @param complete Optional callback for when the Tween completes. - * @param ease Optional easer function to apply to the Tweened value. - */ - public function Tween(duration:Number, type:uint = 0, complete:Function = null, ease:Function = null) - { - _target = duration; - _type = type; - this.complete = complete; - _ease = ease; - } - - /** - * Updates the Tween, called by World. - */ - public function update():void - { - _time += FP.fixed ? 1 : FP.elapsed; - _t = _time / _target; - if (_ease != null && _t > 0 && _t < 1) _t = _ease(_t); - if (_time >= _target) - { - _t = 1; - _finish = true; - } - } - - /** - * Starts the Tween, or restarts it if it's currently running. - */ - public function start():void - { - _time = 0; - if (_target == 0) - { - active = false; - return; - } - active = true; - } - - /** @private Called when the Tween completes. */ - internal function finish():void - { - switch (_type) - { - case 0: - _time = _target; - active = false; - break; - case 1: - _time %= _target; - _t = _time / _target; - if (_ease != null && _t > 0 && _t < 1) _t = _ease(_t); - start(); - break; - case 2: - _time = _target; - active = false; - _parent.removeTween(this); - break; - } - _finish = false; - if (complete != null) complete(); - } - - /** - * The completion percentage of the Tween. - */ - public function get percent():Number { return _time / _target; } - public function set percent(value:Number):void { _time = _target * value; } - - /** - * The current time scale of the Tween (after easer has been applied). - */ - public function get scale():Number { return _t; } - - // Tween information. - /** @private */ private var _type:uint; - /** @private */ protected var _ease:Function; - /** @private */ protected var _t:Number = 0; - - // Timing information. - /** @private */ protected var _time:Number; - /** @private */ protected var _target:Number; - - // List information. - /** @private */ internal var _finish:Boolean; - /** @private */ internal var _parent:Tweener; - /** @private */ internal var _prev:Tween; - /** @private */ internal var _next:Tween; - } -} \ No newline at end of file diff --git a/src/net/flashpunk/Tweener.as b/src/net/flashpunk/Tweener.as deleted file mode 100644 index e318382..0000000 --- a/src/net/flashpunk/Tweener.as +++ /dev/null @@ -1,119 +0,0 @@ -package net.flashpunk -{ - /** - * Updateable Tween container. - */ - public class Tweener - { - /** - * Persistent Tween type, will stop when it finishes. - */ - public const PERSIST:uint = 0; - - /** - * Looping Tween type, will restart immediately when it finishes. - */ - public const LOOPING:uint = 1; - - /** - * Oneshot Tween type, will stop and remove itself from its core container when it finishes. - */ - public const ONESHOT:uint = 2; - - /** - * If the Tweener should update. - */ - public var active:Boolean = true; - - /** - * If the Tweener should clear on removal. For Entities, this is when they are - * removed from a World, and for World this is when the active World is switched. - */ - public var autoClear:Boolean = false; - - /** - * Constructor. - */ - public function Tweener() - { - - } - - /** - * Updates the Tween container. - */ - public function update():void - { - - } - - /** - * Adds a new Tween. - * @param t The Tween to add. - * @param start If the Tween should call start() immediately. - * @return The added Tween. - */ - public function addTween(t:Tween, start:Boolean = false):Tween - { - if (t._parent) throw new Error("Cannot add a Tween object more than once."); - t._parent = this; - t._next = _tween; - if (_tween) _tween._prev = t; - _tween = t; - if (start) _tween.start(); - return t; - } - - /** - * Removes a Tween. - * @param t The Tween to remove. - * @return The removed Tween. - */ - public function removeTween(t:Tween):Tween - { - if (t._parent != this) throw new Error("Core object does not contain Tween."); - if (t._next) t._next._prev = t._prev; - if (t._prev) t._prev._next = t._next; - else _tween = t._next; - t._next = t._prev = null; - t._parent = null; - t.active = false; - return t; - } - - /** - * Removes all Tweens. - */ - public function clearTweens():void - { - var t:Tween = _tween, - n:Tween; - while (t) - { - n = t._next; - removeTween(t); - t = n; - } - } - - /** - * Updates all contained tweens. - */ - public function updateTweens():void - { - var t:Tween = _tween; - while (t) - { - if (t.active) - { - t.update(); - if (t._finish) t.finish(); - } - t = t._next; - } - } - - // List information. - /** @private */ internal var _tween:Tween; - } -} \ No newline at end of file diff --git a/src/net/flashpunk/World.as b/src/net/flashpunk/World.as deleted file mode 100644 index 5c4f7f8..0000000 --- a/src/net/flashpunk/World.as +++ /dev/null @@ -1,1128 +0,0 @@ -package net.flashpunk -{ - import flash.geom.Point; - import flash.utils.Dictionary; - import net.flashpunk.utils.Input; - - /** - * Updated by Engine, main game container that holds all currently active Entities. - * Useful for organization, eg. "Menu", "Level1", etc. - */ - public class World extends Tweener - { - /** - * If the render() loop is performed. - */ - public var visible:Boolean = true; - - /** - * Point used to determine drawing offset in the render loop. - */ - public var camera:Point = new Point; - - /** - * Constructor. - */ - public function World() - { - - } - - /** - * Override this; called when World is switch to, and set to the currently active world. - */ - public function begin():void - { - - } - - /** - * Override this; called when World is changed, and the active world is no longer this. - */ - public function end():void - { - - } - - /** - * Performed by the game loop, updates all contained Entities. - * If you override this to give your World update code, remember - * to call super.update() or your Entities will not be updated. - */ - override public function update():void - { - // update the entities - var e:Entity = _updateFirst; - while (e) - { - if (e.active) - { - if (e._tween) e.updateTweens(); - e.update(); - } - if (e._graphic && e._graphic.active) e._graphic.update(); - e = e._updateNext; - } - } - - /** - * Performed by the game loop, renders all contained Entities. - * If you override this to give your World render code, remember - * to call super.render() or your Entities will not be rendered. - */ - public function render():void - { - // render the entities in order of depth - var e:Entity, - i:int = _layerList.length; - while (i --) - { - e = _renderLast[_layerList[i]]; - while (e) - { - if (e.visible) e.render(); - e = e._renderPrev; - } - } - } - - /** - * X position of the mouse in the World. - */ - public function get mouseX():int - { - return FP.screen.mouseX + FP.camera.x; - } - - /** - * Y position of the mouse in the world. - */ - public function get mouseY():int - { - return FP.screen.mouseY + FP.camera.y; - } - - /** - * Adds the Entity to the World at the end of the frame. - * @param e Entity object you want to add. - * @return The added Entity object. - */ - public function add(e:Entity):Entity - { - if (e._world) return e; - _add[_add.length] = e; - e._world = this; - return e; - } - - /** - * Removes the Entity from the World at the end of the frame. - * @param e Entity object you want to remove. - * @return The removed Entity object. - */ - public function remove(e:Entity):Entity - { - if (e._world !== this) return e; - _remove[_remove.length] = e; - e._world = null; - return e; - } - - /** - * Removes all Entities from the World at the end of the frame. - */ - public function removeAll():void - { - var e:Entity = _updateFirst; - while (e) - { - _remove[_remove.length] = e; - e._world = null; - e = e._updateNext; - } - } - - /** - * Adds multiple Entities to the world. - * @param ...list Several Entities (as arguments) or an Array/Vector of Entities. - */ - public function addList(...list):void - { - var e:Entity; - if (list[0] is Array || list[0] is Vector.<*>) - { - for each (e in list[0]) add(e); - return; - } - for each (e in list) add(e); - } - - /** - * Removes multiple Entities from the world. - * @param ...list Several Entities (as arguments) or an Array/Vector of Entities. - */ - public function removeList(...list):void - { - var e:Entity; - if (list[0] is Array || list[0] is Vector.<*>) - { - for each (e in list[0]) remove(e); - return; - } - for each (e in list) remove(e); - } - - /** - * Adds an Entity to the World with the Graphic object. - * @param graphic Graphic to assign the Entity. - * @param x X position of the Entity. - * @param y Y position of the Entity. - * @param layer Layer of the Entity. - * @return The Entity that was added. - */ - public function addGraphic(graphic:Graphic, layer:int = 0, x:int = 0, y:int = 0):Entity - { - var e:Entity = new Entity(x, y, graphic); - if (layer != 0) e.layer = layer; - e.active = false; - return add(e); - } - - /** - * Adds an Entity to the World with the Mask object. - * @param mask Mask to assign the Entity. - * @param type Collision type of the Entity. - * @param x X position of the Entity. - * @param y Y position of the Entity. - * @return The Entity that was added. - */ - public function addMask(mask:Mask, type:String, x:int = 0, y:int = 0):Entity - { - var e:Entity = new Entity(x, y, null, mask); - if (type) e.type = type; - e.active = e.visible = false; - return add(e); - } - - /** - * Returns a new Entity, or a stored recycled Entity if one exists. - * @param classType The Class of the Entity you want to add. - * @param addToWorld Add it to the World immediately. - * @return The new Entity object. - */ - public function create(classType:Class, addToWorld:Boolean = true):Entity - { - var e:Entity = _recycled[classType]; - if (e) - { - _recycled[classType] = e._recycleNext; - e._recycleNext = null; - } - else e = new classType; - if (addToWorld) return add(e); - return e; - } - - /** - * Removes the Entity from the World at the end of the frame and recycles it. - * The recycled Entity can then be fetched again by calling the create() function. - * @param e The Entity to recycle. - * @return The recycled Entity. - */ - public function recycle(e:Entity):Entity - { - if (e._world !== this) return e; - e._recycleNext = _recycled[e._class]; - _recycled[e._class] = e; - return remove(e); - } - - /** - * Clears stored reycled Entities of the Class type. - * @param classType The Class type to clear. - */ - public function clearRecycled(classType:Class):void - { - var e:Entity = _recycled[classType], - n:Entity; - while (e) - { - n = e._recycleNext; - e._recycleNext = null; - e = n; - } - delete _recycled[classType]; - } - - /** - * Clears stored recycled Entities of all Class types. - */ - public function clearRecycledAll():void - { - for (var classType:Object in _recycled) clearRecycled(classType as Class); - } - - /** - * Brings the Entity to the front of its contained layer. - * @param e The Entity to shift. - * @return If the Entity changed position. - */ - public function bringToFront(e:Entity):Boolean - { - if (e._world !== this || !e._renderPrev) return false; - // pull from list - e._renderPrev._renderNext = e._renderNext; - if (e._renderNext) e._renderNext._renderPrev = e._renderPrev; - else _renderLast[e._layer] = e._renderPrev; - // place at the start - e._renderNext = _renderFirst[e._layer]; - e._renderNext._renderPrev = e; - _renderFirst[e._layer] = e; - e._renderPrev = null; - return true; - } - - /** - * Sends the Entity to the back of its contained layer. - * @param e The Entity to shift. - * @return If the Entity changed position. - */ - public function sendToBack(e:Entity):Boolean - { - if (e._world !== this || !e._renderNext) return false; - // pull from list - e._renderNext._renderPrev = e._renderPrev; - if (e._renderPrev) e._renderPrev._renderNext = e._renderNext; - else _renderFirst[e._layer] = e._renderNext; - // place at the end - e._renderPrev = _renderLast[e._layer]; - e._renderPrev._renderNext = e; - _renderLast[e._layer] = e; - e._renderNext = null; - return true; - } - - /** - * Shifts the Entity one place towards the front of its contained layer. - * @param e The Entity to shift. - * @return If the Entity changed position. - */ - public function bringForward(e:Entity):Boolean - { - if (e._world !== this || !e._renderPrev) return false; - // pull from list - e._renderPrev._renderNext = e._renderNext; - if (e._renderNext) e._renderNext._renderPrev = e._renderPrev; - else _renderLast[e._layer] = e._renderPrev; - // shift towards the front - e._renderNext = e._renderPrev; - e._renderPrev = e._renderPrev._renderPrev; - e._renderNext._renderPrev = e; - if (e._renderPrev) e._renderPrev._renderNext = e; - else _renderFirst[e._layer] = e; - return true; - } - - /** - * Shifts the Entity one place towards the back of its contained layer. - * @param e The Entity to shift. - * @return If the Entity changed position. - */ - public function sendBackward(e:Entity):Boolean - { - if (e._world !== this || !e._renderNext) return false; - // pull from list - e._renderNext._renderPrev = e._renderPrev; - if (e._renderPrev) e._renderPrev._renderNext = e._renderNext; - else _renderFirst[e._layer] = e._renderNext; - // shift towards the back - e._renderPrev = e._renderNext; - e._renderNext = e._renderNext._renderNext; - e._renderPrev._renderNext = e; - if (e._renderNext) e._renderNext._renderPrev = e; - else _renderLast[e._layer] = e; - return true; - } - - /** - * If the Entity as at the front of its layer. - * @param e The Entity to check. - * @return True or false. - */ - public function isAtFront(e:Entity):Boolean - { - return e._renderPrev == null; - } - - /** - * If the Entity as at the back of its layer. - * @param e The Entity to check. - * @return True or false. - */ - public function isAtBack(e:Entity):Boolean - { - return e._renderNext == null; - } - - /** - * Returns the first Entity that collides with the rectangular area. - * @param type The Entity type to check for. - * @param rX X position of the rectangle. - * @param rY Y position of the rectangle. - * @param rWidth Width of the rectangle. - * @param rHeight Height of the rectangle. - * @return The first Entity to collide, or null if none collide. - */ - public function collideRect(type:String, rX:Number, rY:Number, rWidth:Number, rHeight:Number):Entity - { - var e:Entity = _typeFirst[type]; - while (e) - { - if (e.collideRect(e.x, e.y, rX, rY, rWidth, rHeight)) return e; - e = e._typeNext; - } - return null; - } - - /** - * Returns the first Entity found that collides with the position. - * @param type The Entity type to check for. - * @param pX X position. - * @param pY Y position. - * @return The collided Entity, or null if none collide. - */ - public function collidePoint(type:String, pX:Number, pY:Number):Entity - { - var e:Entity = _typeFirst[type]; - while (e) - { - if (e.collidePoint(e.x, e.y, pX, pY)) return e; - e = e._typeNext; - } - return null; - } - - /** - * Returns the first Entity found that collides with the line. - * @param type The Entity type to check for. - * @param fromX Start x of the line. - * @param fromY Start y of the line. - * @param toX End x of the line. - * @param toY End y of the line. - * @param precision - * @param p - * @return - */ - public function collideLine(type:String, fromX:int, fromY:int, toX:int, toY:int, precision:uint = 1, p:Point = null):Entity - { - // If the distance is less than precision, do the short sweep. - if (precision < 1) precision = 1; - if (FP.distance(fromX, fromY, toX, toY) < precision) - { - if (p) - { - if (fromX == toX && fromY == toY) - { - p.x = toX; p.y = toY; - return collidePoint(type, toX, toY); - } - return collideLine(type, fromX, fromY, toX, toY, 1, p); - } - else return collidePoint(type, fromX, toY); - } - - // Get information about the line we're about to raycast. - var xDelta:int = Math.abs(toX - fromX), - yDelta:int = Math.abs(toY - fromY), - xSign:Number = toX > fromX ? precision : -precision, - ySign:Number = toY > fromY ? precision : -precision, - x:Number = fromX, y:Number = fromY, e:Entity; - - // Do a raycast from the start to the end point. - if (xDelta > yDelta) - { - ySign *= yDelta / xDelta; - if (xSign > 0) - { - while (x < toX) - { - if ((e = collidePoint(type, x, y))) - { - if (!p) return e; - if (precision < 2) - { - p.x = x - xSign; p.y = y - ySign; - return e; - } - return collideLine(type, x - xSign, y - ySign, toX, toY, 1, p); - } - x += xSign; y += ySign; - } - } - else - { - while (x > toX) - { - if ((e = collidePoint(type, x, y))) - { - if (!p) return e; - if (precision < 2) - { - p.x = x - xSign; p.y = y - ySign; - return e; - } - return collideLine(type, x - xSign, y - ySign, toX, toY, 1, p); - } - x += xSign; y += ySign; - } - } - } - else - { - xSign *= xDelta / yDelta; - if (ySign > 0) - { - while (y < toY) - { - if ((e = collidePoint(type, x, y))) - { - if (!p) return e; - if (precision < 2) - { - p.x = x - xSign; p.y = y - ySign; - return e; - } - return collideLine(type, x - xSign, y - ySign, toX, toY, 1, p); - } - x += xSign; y += ySign; - } - } - else - { - while (y > toY) - { - if ((e = collidePoint(type, x, y))) - { - if (!p) return e; - if (precision < 2) - { - p.x = x - xSign; p.y = y - ySign; - return e; - } - return collideLine(type, x - xSign, y - ySign, toX, toY, 1, p); - } - x += xSign; y += ySign; - } - } - } - - // Check the last position. - if (precision > 1) - { - if (!p) return collidePoint(type, toX, toY); - if (collidePoint(type, toX, toY)) return collideLine(type, x - xSign, y - ySign, toX, toY, 1, p); - } - - // No collision, return the end point. - if (p) - { - p.x = toX; - p.y = toY; - } - return null; - } - - /** - * Populates an array with all Entities that collide with the rectangle. This - * function does not empty the array, that responsibility is left to the user. - * @param type The Entity type to check for. - * @param rX X position of the rectangle. - * @param rY Y position of the rectangle. - * @param rWidth Width of the rectangle. - * @param rHeight Height of the rectangle. - * @param into The Array or Vector to populate with collided Entities. - */ - public function collideRectInto(type:String, rX:Number, rY:Number, rWidth:Number, rHeight:Number, into:Object):void - { - if (into is Array || into is Vector.<*>) - { - var e:Entity = _typeFirst[type], - n:uint = into.length; - while (e) - { - if (e.collideRect(e.x, e.y, rX, rY, rWidth, rHeight)) into[n ++] = e; - e = e._typeNext; - } - } - } - - /** - * Populates an array with all Entities that collide with the position. This - * function does not empty the array, that responsibility is left to the user. - * @param type The Entity type to check for. - * @param pX X position. - * @param pY Y position. - * @param into The Array or Vector to populate with collided Entities. - * @return The provided Array. - */ - public function collidePointInto(type:String, pX:Number, pY:Number, into:Object):void - { - if (into is Array || into is Vector.<*>) - { - var e:Entity = _typeFirst[type], - n:uint = into.length; - while (e) - { - if (e.collidePoint(e.x, e.y, pX, pY)) into[n ++] = e; - e = e._typeNext; - } - } - } - - /** - * Finds the Entity nearest to the rectangle. - * @param type The Entity type to check for. - * @param x X position of the rectangle. - * @param y Y position of the rectangle. - * @param width Width of the rectangle. - * @param height Height of the rectangle. - * @return The nearest Entity to the rectangle. - */ - public function nearestToRect(type:String, x:Number, y:Number, width:Number, height:Number):Entity - { - var n:Entity = _typeFirst[type], - nearDist:Number = Number.MAX_VALUE, - near:Entity, dist:Number; - while (n) - { - dist = squareRects(x, y, width, height, n.x - n.originX, n.y - n.originY, n.width, n.height); - if (dist < nearDist) - { - nearDist = dist; - near = n; - } - n = n._typeNext; - } - return near; - } - - /** - * Finds the Entity nearest to another. - * @param type The Entity type to check for. - * @param e The Entity to find the nearest to. - * @param useHitboxes If the Entities' hitboxes should be used to determine the distance. If false, their x/y coordinates are used. - * @return The nearest Entity to e. - */ - public function nearestToEntity(type:String, e:Entity, useHitboxes:Boolean = false):Entity - { - if (useHitboxes) return nearestToRect(type, e.x - e.originX, e.y - e.originY, e.width, e.height); - var n:Entity = _typeFirst[type], - nearDist:Number = Number.MAX_VALUE, - near:Entity, dist:Number, - x:Number = e.x - e.originX, - y:Number = e.y - e.originY; - while (n) - { - dist = (x - n.x) * (x - n.x) + (y - n.y) * (y - n.y); - if (dist < nearDist) - { - nearDist = dist; - near = n; - } - n = n._typeNext; - } - return near; - } - - /** - * Finds the Entity nearest to the position. - * @param type The Entity type to check for. - * @param x X position. - * @param y Y position. - * @param useHitboxes If the Entities' hitboxes should be used to determine the distance. If false, their x/y coordinates are used. - * @return The nearest Entity to the position. - */ - public function nearestToPoint(type:String, x:Number, y:Number, useHitboxes:Boolean = false):Entity - { - var n:Entity = _typeFirst[type], - nearDist:Number = Number.MAX_VALUE, - near:Entity, dist:Number; - if (useHitboxes) - { - while (n) - { - dist = squarePointRect(x, y, n.x - n.originX, n.y - n.originY, n.width, n.height); - if (dist < nearDist) - { - nearDist = dist; - near = n; - } - n = n._typeNext; - } - return near; - } - while (n) - { - dist = (x - n.x) * (x - n.x) + (y - n.y) * (y - n.y); - if (dist < nearDist) - { - nearDist = dist; - near = n; - } - n = n._typeNext; - } - return near; - } - - /** - * How many Entities are in the World. - */ - public function get count():uint { return _count; } - - /** - * Returns the amount of Entities of the type are in the World. - * @param type The type (or Class type) to count. - * @return How many Entities of type exist in the World. - */ - public function typeCount(type:String):uint - { - return _typeCount[type] as uint; - } - - /** - * Returns the amount of Entities of the Class are in the World. - * @param c The Class type to count. - * @return How many Entities of Class exist in the World. - */ - public function classCount(c:Class):uint - { - return _classCount[c] as uint; - } - - /** - * Returns the amount of Entities are on the layer in the World. - * @param layer The layer to count Entities on. - * @return How many Entities are on the layer. - */ - public function layerCount(layer:int):uint - { - return _layerCount[layer] as uint; - } - - /** - * The first Entity in the World. - */ - public function get first():Entity { return _updateFirst; } - - /** - * How many Entity layers the World has. - */ - public function get layers():uint { return _layerList.length; } - - /** - * The first Entity of the type. - * @param type The type to check. - * @return The Entity. - */ - public function typeFirst(type:String):Entity - { - if (!_updateFirst) return null; - return _typeFirst[type] as Entity; - } - - /** - * The first Entity of the Class. - * @param c The Class type to check. - * @return The Entity. - */ - public function classFirst(c:Class):Entity - { - if (!_updateFirst) return null; - var e:Entity = _updateFirst; - while (e) - { - if (e is c) return e; - e = e._updateNext; - } - return null; - } - - /** - * The first Entity on the Layer. - * @param layer The layer to check. - * @return The Entity. - */ - public function layerFirst(layer:int):Entity - { - if (!_updateFirst) return null; - return _renderFirst[layer] as Entity; - } - - /** - * The last Entity on the Layer. - * @param layer The layer to check. - * @return The Entity. - */ - public function layerLast(layer:int):Entity - { - if (!_updateFirst) return null; - return _renderLast[layer] as Entity; - } - - /** - * The Entity that will be rendered first by the World. - */ - public function get farthest():Entity - { - if (!_updateFirst) return null; - return _renderLast[_layerList[_layerList.length - 1] as int] as Entity; - } - - /** - * The Entity that will be rendered last by the world. - */ - public function get nearest():Entity - { - if (!_updateFirst) return null; - return _renderFirst[_layerList[0] as int] as Entity; - } - - /** - * The layer that will be rendered first by the World. - */ - public function get layerFarthest():int - { - if (!_updateFirst) return 0; - return _layerList[_layerList.length - 1] as int; - } - - /** - * The layer that will be rendered last by the World. - */ - public function get layerNearest():int - { - if (!_updateFirst) return 0; - return _layerList[0] as int; - } - - /** - * How many different types have been added to the World. - */ - public function get uniqueTypes():uint - { - var i:uint = 0; - for (var type:String in _typeCount) i ++; - return i; - } - - /** - * Pushes all Entities in the World of the type into the Array or Vector. - * @param type The type to check. - * @param into The Array or Vector to populate. - * @return The same array, populated. - */ - public function getType(type:String, into:Object):void - { - if (into is Array || into is Vector.<*>) - { - var e:Entity = _typeFirst[type], - n:uint = into.length; - while (e) - { - into[n ++] = e; - e = e._typeNext; - } - } - } - - /** - * Pushes all Entities in the World of the Class into the Array or Vector. - * @param c The Class type to check. - * @param into The Array or Vector to populate. - * @return The same array, populated. - */ - public function getClass(c:Class, into:Object):void - { - if (into is Array || into is Vector.<*>) - { - var e:Entity = _updateFirst, - n:uint = into.length; - while (e) - { - if (e is c) into[n ++] = e; - e = e._updateNext; - } - } - } - - /** - * Pushes all Entities in the World on the layer into the Array or Vector. - * @param layer The layer to check. - * @param into The Array or Vector to populate. - * @return The same array, populated. - */ - public function getLayer(layer:int, into:Object):void - { - if (into is Array || into is Vector.<*>) - { - var e:Entity = _renderLast[layer], - n:uint = into.length; - while (e) - { - into[n ++] = e; - e = e._updatePrev; - } - } - } - - /** - * Pushes all Entities in the World into the array. - * @param into The Array or Vector to populate. - * @return The same array, populated. - */ - public function getAll(into:Object):void - { - if (into is Array || into is Vector.<*>) - { - var e:Entity = _updateFirst, - n:uint = into.length; - while (e) - { - into[n ++] = e; - e = e._updateNext; - } - } - } - - /** - * Updates the add/remove lists at the end of the frame. - */ - public function updateLists():void - { - var e:Entity; - - // remove entities - if (_remove.length) - { - for each (e in _remove) - { - if (e._added != true && _add.indexOf(e) >= 0) - { - _add.splice(_add.indexOf(e), 1); - continue; - } - e._added = false; - e.removed(); - removeUpdate(e); - removeRender(e); - if (e._type) removeType(e); - if (e.autoClear && e._tween) e.clearTweens(); - } - _remove.length = 0; - } - - // add entities - if (_add.length) - { - for each (e in _add) - { - e._added = true; - addUpdate(e); - addRender(e); - if (e._type) addType(e); - e.added(); - } - _add.length = 0; - } - - // sort the depth list - if (_layerSort) - { - if (_layerList.length > 1) FP.sort(_layerList, true); - _layerSort = false; - } - } - - /** @private Adds Entity to the update list. */ - private function addUpdate(e:Entity):void - { - // add to update list - if (_updateFirst) - { - _updateFirst._updatePrev = e; - e._updateNext = _updateFirst; - } - else e._updateNext = null; - e._updatePrev = null; - _updateFirst = e; - _count ++; - if (!_classCount[e._class]) _classCount[e._class] = 0; - _classCount[e._class] ++; - } - - /** @private Removes Entity from the update list. */ - private function removeUpdate(e:Entity):void - { - // remove from the update list - if (_updateFirst == e) _updateFirst = e._updateNext; - if (e._updateNext) e._updateNext._updatePrev = e._updatePrev; - if (e._updatePrev) e._updatePrev._updateNext = e._updateNext; - e._updateNext = e._updatePrev = null; - - _count --; - _classCount[e._class] --; - } - - /** @private Adds Entity to the render list. */ - internal function addRender(e:Entity):void - { - var f:Entity = _renderFirst[e._layer]; - if (f) - { - // Append entity to existing layer. - e._renderNext = f; - f._renderPrev = e; - _layerCount[e._layer] ++; - } - else - { - // Create new layer with entity. - _renderLast[e._layer] = e; - _layerList[_layerList.length] = e._layer; - _layerSort = true; - e._renderNext = null; - _layerCount[e._layer] = 1; - } - _renderFirst[e._layer] = e; - e._renderPrev = null; - } - - /** @private Removes Entity from the render list. */ - internal function removeRender(e:Entity):void - { - if (e._renderNext) e._renderNext._renderPrev = e._renderPrev; - else _renderLast[e._layer] = e._renderPrev; - if (e._renderPrev) e._renderPrev._renderNext = e._renderNext; - else - { - // Remove this entity from the layer. - _renderFirst[e._layer] = e._renderNext - if (!e._renderNext) - { - // Remove the layer from the layer list if this was the last entity. - if (_layerList.length > 1) - { - _layerList[_layerList.indexOf(e._layer)] = _layerList[_layerList.length - 1]; - _layerSort = true; - } - _layerList.length --; - } - } - _layerCount[e._layer] --; - e._renderNext = e._renderPrev = null; - } - - /** @private Adds Entity to the type list. */ - internal function addType(e:Entity):void - { - // add to type list - if (_typeFirst[e._type]) - { - _typeFirst[e._type]._typePrev = e; - e._typeNext = _typeFirst[e._type]; - _typeCount[e._type] ++; - } - else - { - e._typeNext = null; - _typeCount[e._type] = 1; - } - e._typePrev = null; - _typeFirst[e._type] = e; - } - - /** @private Removes Entity from the type list. */ - internal function removeType(e:Entity):void - { - // remove from the type list - if (_typeFirst[e._type] == e) _typeFirst[e._type] = e._typeNext; - if (e._typeNext) e._typeNext._typePrev = e._typePrev; - if (e._typePrev) e._typePrev._typeNext = e._typeNext; - e._typeNext = e._typePrev = null; - _typeCount[e._type] --; - } - - /** @private Calculates the squared distance between two rectangles. */ - private static function squareRects(x1:Number, y1:Number, w1:Number, h1:Number, x2:Number, y2:Number, w2:Number, h2:Number):Number - { - if (x1 < x2 + w2 && x2 < x1 + w1) - { - if (y1 < y2 + h2 && y2 < y1 + h1) return 0; - if (y1 > y2) return (y1 - (y2 + h2)) * (y1 - (y2 + h2)); - return (y2 - (y1 + h1)) * (y2 - (y1 + h1)); - } - if (y1 < y2 + h2 && y2 < y1 + h1) - { - if (x1 > x2) return (x1 - (x2 + w2)) * (x1 - (x2 + w2)); - return (x2 - (x1 + w1)) * (x2 - (x1 + w1)); - } - if (x1 > x2) - { - if (y1 > y2) return squarePoints(x1, y1, (x2 + w2), (y2 + h2)); - return squarePoints(x1, y1 + h1, x2 + w2, y2); - } - if (y1 > y2) return squarePoints(x1 + w1, y1, x2, y2 + h2) - return squarePoints(x1 + w1, y1 + h1, x2, y2); - } - - /** @private Calculates the squared distance between two points. */ - private static function squarePoints(x1:Number, y1:Number, x2:Number, y2:Number):Number - { - return (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2); - } - - /** @private Calculates the squared distance between a rectangle and a point. */ - private static function squarePointRect(px:Number, py:Number, rx:Number, ry:Number, rw:Number, rh:Number):Number - { - if (px >= rx && px <= rx + rw) - { - if (py >= ry && py <= ry + rh) return 0; - if (py > ry) return (py - (ry + rh)) * (py - (ry + rh)); - return (ry - py) * (ry - py); - } - if (py >= ry && py <= ry + rh) - { - if (px > rx) return (px - (rx + rw)) * (px - (rx + rw)); - return (rx - px) * (rx - px); - } - if (px > rx) - { - if (py > ry) return squarePoints(px, py, rx + rw, ry + rh); - return squarePoints(px, py, rx + rw, ry); - } - if (py > ry) return squarePoints(px, py, rx, ry + rh) - return squarePoints(px, py, rx, ry); - } - - // Adding and removal. - /** @private */ private var _add:Vector. = new Vector.; - /** @private */ private var _remove:Vector. = new Vector.; - - // Update information. - /** @private */ private var _updateFirst:Entity; - /** @private */ private var _count:uint; - - // Render information. - private var _renderFirst:Array = []; - private var _renderLast:Array = []; - private var _layerList:Array = []; - private var _layerCount:Array = []; - private var _layerSort:Boolean; - private var _tempArray:Array = []; - - /** @private */ private var _classCount:Dictionary = new Dictionary; - /** @private */ internal var _typeFirst:Object = { }; - /** @private */ private var _typeCount:Object = { }; - /** @private */ private var _recycled:Dictionary = new Dictionary; - } -} \ No newline at end of file diff --git a/src/net/flashpunk/debug/Console.as b/src/net/flashpunk/debug/Console.as deleted file mode 100644 index 3b973f1..0000000 --- a/src/net/flashpunk/debug/Console.as +++ /dev/null @@ -1,878 +0,0 @@ -package net.flashpunk.debug -{ - import flash.display.Bitmap; - import flash.display.BitmapData; - import flash.display.BlendMode; - import flash.display.Graphics; - import flash.display.Sprite; - import flash.display.Stage; - import flash.geom.ColorTransform; - import flash.geom.Rectangle; - import flash.text.TextField; - import flash.text.TextFormat; - import net.flashpunk.Entity; - import net.flashpunk.FP; - import net.flashpunk.utils.Draw; - import net.flashpunk.utils.Input; - import net.flashpunk.utils.Key; - - /** - * FlashPunk debug console; can use to log information or pause the game and view/move Entities and step the frame. - */ - public class Console - { - /** - * The key used to toggle the Console on/off. Tilde (~) by default. - */ - public var toggleKey:uint = 192; - - /** - * Constructor. - */ - public function Console() - { - Input.define("_ARROWS", Key.RIGHT, Key.LEFT, Key.DOWN, Key.UP); - } - - /** - * Logs data to the console. - * @param ...data The data parameters to log, can be variables, objects, etc. Parameters will be separated by a space (" "). - */ - public function log(...data):void - { - var s:String; - if (data.length > 1) - { - s = ""; - var i:int = 0; - while (i < data.length) - { - if (i > 0) s += " "; - s += data[i ++].toString(); - } - } - else s = data[0].toString(); - if (s.indexOf("\n") >= 0) - { - var a:Array = s.split("\n"); - for each (s in a) LOG.push(s); - } - else LOG.push(s); - if (_enabled && _sprite.visible) updateLog(); - } - - /** - * Adds properties to watch in the console's debug panel. - * @param ...properties The properties (strings) to watch. - */ - public function watch(...properties):void - { - var i:String; - if (properties.length > 1) - { - for each (i in properties) WATCH_LIST.push(i); - } - else if (properties[0] is Array || properties[0] is Vector.<*>) - { - for each (i in properties[0]) WATCH_LIST.push(i); - } - else WATCH_LIST.push(properties[0]); - } - - /** - * Enables the console. - */ - public function enable():void - { - // Quit if the console is already enabled. - if (_enabled) return; - - // Enable it and add the Sprite to the stage. - _enabled = true; - FP.engine.addChild(_sprite); - - // Used to determine some text sizing. - var big:Boolean = width >= 480; - - // The transparent FlashPunk logo overlay bitmap. - _sprite.addChild(_back); - _back.bitmapData = new BitmapData(width, height, true, 0xFFFFFFFF); - var b:BitmapData = (new CONSOLE_LOGO).bitmapData; - FP.matrix.identity(); - FP.matrix.tx = Math.max((_back.bitmapData.width - b.width) / 2, 0); - FP.matrix.ty = Math.max((_back.bitmapData.height - b.height) / 2, 0); - FP.matrix.scale(Math.min(width / _back.bitmapData.width, 1), Math.min(height / _back.bitmapData.height, 1)); - _back.bitmapData.draw(b, FP.matrix, null, BlendMode.MULTIPLY); - _back.bitmapData.draw(_back.bitmapData, null, null, BlendMode.INVERT); - _back.bitmapData.colorTransform(_back.bitmapData.rect, new ColorTransform(1, 1, 1, 0.5)); - - // The entity and selection sprites. - _sprite.addChild(_entScreen); - _entScreen.addChild(_entSelect); - - // The entity count text. - _sprite.addChild(_entRead); - _entRead.addChild(_entReadText); - _entReadText.defaultTextFormat = format(16, 0xFFFFFF, "right"); - _entReadText.embedFonts = true; - _entReadText.width = 100; - _entReadText.height = 20; - _entRead.x = width - _entReadText.width; - - // The entity count panel. - _entRead.graphics.clear(); - _entRead.graphics.beginFill(0, .5); - _entRead.graphics.drawRoundRectComplex(0, 0, _entReadText.width, 20, 0, 0, 20, 0); - - // The FPS text. - _sprite.addChild(_fpsRead); - _fpsRead.addChild(_fpsReadText); - _fpsReadText.defaultTextFormat = format(16); - _fpsReadText.embedFonts = true; - _fpsReadText.width = 70; - _fpsReadText.height = 20; - _fpsReadText.x = 2; - _fpsReadText.y = 1; - - // The FPS and frame timing panel. - _fpsRead.graphics.clear(); - _fpsRead.graphics.beginFill(0, .75); - _fpsRead.graphics.drawRoundRectComplex(0, 0, big ? 200 : 100, 20, 0, 0, 0, 20); - - // The frame timing text. - if (big) _sprite.addChild(_fpsInfo); - _fpsInfo.addChild(_fpsInfoText0); - _fpsInfo.addChild(_fpsInfoText1); - _fpsInfoText0.defaultTextFormat = format(8, 0xAAAAAA); - _fpsInfoText1.defaultTextFormat = format(8, 0xAAAAAA); - _fpsInfoText0.embedFonts = true; - _fpsInfoText1.embedFonts = true; - _fpsInfoText0.width = _fpsInfoText1.width = 60; - _fpsInfoText0.height = _fpsInfoText1.height = 20; - _fpsInfo.x = 75; - _fpsInfoText1.x = 60; - - // The output log text. - _sprite.addChild(_logRead); - _logRead.addChild(_logReadText0); - _logRead.addChild(_logReadText1); - _logReadText0.defaultTextFormat = format(16, 0xFFFFFF); - _logReadText1.defaultTextFormat = format(big ? 16 : 8, 0xFFFFFF); - _logReadText0.embedFonts = true; - _logReadText1.embedFonts = true; - _logReadText0.selectable = false; - _logReadText0.width = 80; - _logReadText0.height = 20; - _logReadText1.width = width; - _logReadText0.x = 2; - _logReadText0.y = 3; - _logReadText0.text = "OUTPUT:"; - _logHeight = height - 60; - _logBar = new Rectangle(8, 24, 16, _logHeight - 8); - _logBarGlobal = _logBar.clone(); - _logBarGlobal.y += 40; - _logLines = _logHeight / (big ? 16.5 : 8.5); - - // The debug text. - _sprite.addChild(_debRead); - _debRead.addChild(_debReadText0); - _debRead.addChild(_debReadText1); - _debReadText0.defaultTextFormat = format(16, 0xFFFFFF); - _debReadText1.defaultTextFormat = format(8, 0xFFFFFF); - _debReadText0.embedFonts = true; - _debReadText1.embedFonts = true; - _debReadText0.selectable = false; - _debReadText0.width = 80; - _debReadText0.height = 20; - _debReadText1.width = 160; - _debReadText1.height = int(height / 4); - _debReadText0.x = 2; - _debReadText0.y = 3; - _debReadText1.x = 2; - _debReadText1.y = 24; - _debReadText0.text = "DEBUG:"; - _debRead.y = height - (_debReadText1.y + _debReadText1.height); - - // The button panel buttons. - _sprite.addChild(_butRead); - _butRead.addChild(_butDebug = new CONSOLE_DEBUG); - _butRead.addChild(_butOutput = new CONSOLE_OUTPUT); - _butRead.addChild(_butPlay = new CONSOLE_PLAY).x = 20; - _butRead.addChild(_butPause = new CONSOLE_PAUSE).x = 20; - _butRead.addChild(_butStep = new CONSOLE_STEP).x = 40; - updateButtons(); - - // The button panel. - _butRead.graphics.clear(); - _butRead.graphics.beginFill(0, .75); - _butRead.graphics.drawRoundRectComplex(-20, 0, 100, 20, 0, 0, 20, 20); - - // Set the state to unpaused. - paused = false; - } - - /** - * If the console should be visible. - */ - public function get visible():Boolean { return _sprite.visible; } - public function set visible(value:Boolean):void - { - _sprite.visible = value; - if (_enabled && value) updateLog(); - } - - /** - * Console update, called by game loop. - */ - public function update():void - { - // Quit if the console isn't enabled. - if (!_enabled) return; - - // If the console is paused. - if (_paused) - { - // Update buttons. - updateButtons(); - - // While in debug mode. - if (_debug) - { - // While the game is paused. - if (FP.engine.paused) - { - // When the mouse is pressed. - if (Input.mousePressed) - { - // Mouse is within clickable area. - if (Input.mouseFlashY > 20 && (Input.mouseFlashX > _debReadText1.width || Input.mouseFlashY < _debRead.y)) - { - if (Input.check(Key.SHIFT)) - { - if (SELECT_LIST.length) startDragging(); - else startPanning(); - } - else startSelection(); - } - } - else - { - // Update mouse movement functions. - if (_selecting) updateSelection(); - if (_dragging) updateDragging(); - if (_panning) updatePanning(); - } - - // Select all Entities - if (Input.pressed(Key.A)) selectAll(); - - // If the shift key is held. - if (Input.check(Key.SHIFT)) - { - // If Entities are selected. - if (SELECT_LIST.length) - { - // Move Entities with the arrow keys. - if (Input.pressed("_ARROWS")) updateKeyMoving(); - } - else - { - // Pan the camera with the arrow keys. - if (Input.check("_ARROWS")) updateKeyPanning(); - } - } - } - else - { - // Update info while the game runs. - updateEntityLists(FP.world.count != ENTITY_LIST.length); - renderEntities(); - updateFPSRead(); - updateEntityCount(); - } - - // Update debug panel. - updateDebugRead(); - } - else - { - // log scrollbar - if (_scrolling) updateScrolling(); - else if (Input.mousePressed) startScrolling(); - } - } - else - { - // Update info while the game runs. - updateFPSRead(); - updateEntityCount(); - } - - // Console toggle. - if (Input.pressed(toggleKey)) paused = !_paused; - } - - /** - * If the Console is currently in paused mode. - */ - public function get paused():Boolean { return _paused; } - public function set paused(value:Boolean):void - { - // Quit if the console isn't enabled. - if (!_enabled) return; - - // Set the console to paused. - _paused = value; - FP.engine.paused = value; - - // Panel visibility. - _back.visible = value; - _entScreen.visible = value; - _butRead.visible = value; - - // If the console is paused. - if (value) - { - // Set the console to paused mode. - if (_debug) debug = true; - else updateLog(); - } - else - { - // Set the console to running mode. - _debRead.visible = false; - _logRead.visible = true; - updateLog(); - ENTITY_LIST.length = 0; - SCREEN_LIST.length = 0; - SELECT_LIST.length = 0; - } - } - - /** - * If the Console is currently in debug mode. - */ - public function get debug():Boolean { return _debug; } - public function set debug(value:Boolean):void - { - // Quit if the console isn't enabled. - if (!_enabled) return; - - // Set the console to debug mode. - _debug = value; - _debRead.visible = value; - _logRead.visible = !value; - - // Update console state. - if (value) updateEntityLists(); - else updateLog(); - renderEntities(); - } - - /** @private Steps the frame ahead. */ - private function stepFrame():void - { - FP.engine.update(); - FP.engine.render(); - updateEntityCount(); - updateEntityLists(); - renderEntities(); - } - - /** @private Starts Entity dragging. */ - private function startDragging():void - { - _dragging = true; - _entRect.x = Input.mouseX; - _entRect.y = Input.mouseY; - } - - /** @private Updates Entity dragging. */ - private function updateDragging():void - { - moveSelected(Input.mouseX - _entRect.x, Input.mouseY - _entRect.y); - _entRect.x = Input.mouseX; - _entRect.y = Input.mouseY; - if (Input.mouseReleased) _dragging = false; - } - - /** @private Move the selected Entitites by the amount. */ - private function moveSelected(xDelta:int, yDelta:int):void - { - for each (var e:Entity in SELECT_LIST) - { - e.x += xDelta; - e.y += yDelta; - } - FP.engine.render(); - renderEntities(); - updateEntityLists(true); - } - - /** @private Starts camera panning. */ - private function startPanning():void - { - _panning = true; - _entRect.x = Input.mouseX; - _entRect.y = Input.mouseY; - } - - /** @private Updates camera panning. */ - private function updatePanning():void - { - if (Input.mouseReleased) _panning = false; - panCamera(_entRect.x - Input.mouseX, _entRect.y - Input.mouseY); - _entRect.x = Input.mouseX; - _entRect.y = Input.mouseY; - } - - /** @private Pans the camera. */ - private function panCamera(xDelta:int, yDelta:int):void - { - FP.camera.x += xDelta; - FP.camera.y += yDelta; - FP.engine.render(); - updateEntityLists(true); - renderEntities(); - } - - /** @private Sets the camera position. */ - private function setCamera(x:int, y:int):void - { - FP.camera.x = x; - FP.camera.y = y; - FP.engine.render(); - updateEntityLists(true); - renderEntities(); - } - - /** @private Starts Entity selection. */ - private function startSelection():void - { - _selecting = true; - _entRect.x = Input.mouseFlashX; - _entRect.y = Input.mouseFlashY; - _entRect.width = 0; - _entRect.height = 0; - } - - /** @private Updates Entity selection. */ - private function updateSelection():void - { - _entRect.width = Input.mouseFlashX - _entRect.x; - _entRect.height = Input.mouseFlashY - _entRect.y; - if (Input.mouseReleased) - { - selectEntities(_entRect); - renderEntities(); - _selecting = false; - _entSelect.graphics.clear(); - } - else - { - _entSelect.graphics.clear(); - _entSelect.graphics.lineStyle(1, 0xFFFFFF); - _entSelect.graphics.drawRect(_entRect.x, _entRect.y, _entRect.width, _entRect.height); - } - } - - /** @private Selects the Entitites in the rectangle. */ - private function selectEntities(rect:Rectangle):void - { - if (rect.width < 0) rect.x -= (rect.width = -rect.width); - else if (!rect.width) rect.width = 1; - if (rect.height < 0) rect.y -= (rect.height = -rect.height); - else if (!rect.height) rect.height = 1; - - FP.rect.width = FP.rect.height = 6; - var sx:Number = FP.screen.scaleX * FP.screen.scale, - sy:Number = FP.screen.scaleY * FP.screen.scale, - e:Entity; - - if (Input.check(Key.CONTROL)) - { - // Append selected Entitites with new selections. - for each (e in SCREEN_LIST) - { - if (SELECT_LIST.indexOf(e) < 0) - { - FP.rect.x = (e.x - FP.camera.x) * sx - 3; - FP.rect.y = (e.y - FP.camera.y) * sy - 3; - if (rect.intersects(FP.rect)) SELECT_LIST.push(e); - } - } - } - else - { - // Replace selections with new selections. - SELECT_LIST.length = 0; - for each (e in SCREEN_LIST) - { - FP.rect.x = (e.x - FP.camera.x) * sx - 3; - FP.rect.y = (e.y - FP.camera.y) * sy - 3; - if (rect.intersects(FP.rect)) SELECT_LIST.push(e); - } - } - } - - /** @private Selects all entities on screen. */ - private function selectAll():void - { - SELECT_LIST.length = 0; - for each (var e:Entity in SCREEN_LIST) SELECT_LIST.push(e); - renderEntities(); - } - - /** @private Starts log text scrolling. */ - private function startScrolling():void - { - if (LOG.length > _logLines) _scrolling = _logBarGlobal.contains(Input.mouseFlashX, Input.mouseFlashY); - } - - /** @private Updates log text scrolling. */ - private function updateScrolling():void - { - _scrolling = Input.mouseDown; - _logScroll = FP.scaleClamp(Input.mouseFlashY, _logBarGlobal.y, _logBarGlobal.bottom, 0, 1); - updateLog(); - } - - /** @private Moves Entities with the arrow keys. */ - private function updateKeyMoving():void - { - FP.point.x = (Input.pressed(Key.RIGHT) ? 1 : 0) - (Input.pressed(Key.LEFT) ? 1 : 0); - FP.point.y = (Input.pressed(Key.DOWN) ? 1 : 0) - (Input.pressed(Key.UP) ? 1 : 0); - if (FP.point.x != 0 || FP.point.y != 0) moveSelected(FP.point.x, FP.point.y); - } - - /** @private Pans the camera with the arrow keys. */ - private function updateKeyPanning():void - { - FP.point.x = (Input.check(Key.RIGHT) ? 1 : 0) - (Input.check(Key.LEFT) ? 1 : 0); - FP.point.y = (Input.check(Key.DOWN) ? 1 : 0) - (Input.check(Key.UP) ? 1 : 0); - if (FP.point.x != 0 || FP.point.y != 0) panCamera(FP.point.x, FP.point.y); - } - - /** @private Update the Entity list information. */ - private function updateEntityLists(fetchList:Boolean = true):void - { - // If the list should be re-populated. - if (fetchList) - { - ENTITY_LIST.length = 0; - FP.world.getAll(ENTITY_LIST); - } - - // Update the list of Entities on screen. - SCREEN_LIST.length = 0; - for each (var e:Entity in ENTITY_LIST) - { - if (e.collideRect(e.x, e.y, FP.camera.x, FP.camera.y, FP.width, FP.height)) - SCREEN_LIST.push(e); - } - } - - /** @private Renders the Entities positions and hitboxes. */ - private function renderEntities():void - { - // If debug mode is on. - _entScreen.visible = _debug; - if (_debug) - { - var g:Graphics = _entScreen.graphics, - sx:Number = FP.screen.scaleX * FP.screen.scale, - sy:Number = FP.screen.scaleY * FP.screen.scale; - g.clear(); - for each (var e:Entity in SCREEN_LIST) - { - // If the Entity is not selected. - if (SELECT_LIST.indexOf(e) < 0) - { - // Draw the normal hitbox and position. - if (e.width && e.height) - { - g.lineStyle(1, 0xFF0000); - g.drawRect((e.x - e.originX - FP.camera.x) * sx, (e.y - e.originY - FP.camera.y) * sy, e.width * sx, e.height * sy); - } - g.lineStyle(1, 0x00FF00); - g.drawRect((e.x - FP.camera.x) * sx - 3, (e.y - FP.camera.y) * sy - 3, 6, 6); - } - else - { - // Draw the selected hitbox and position. - if (e.width && e.height) - { - g.lineStyle(1, 0xFFFFFF); - g.drawRect((e.x - e.originX - FP.camera.x) * sx, (e.y - e.originY - FP.camera.y) * sy, e.width * sx, e.height * sy); - } - g.lineStyle(1, 0xFFFFFF); - g.drawRect((e.x - FP.camera.x) * sx - 3, (e.y - FP.camera.y) * sy - 3, 6, 6); - } - } - } - } - - /** @private Updates the log window. */ - private function updateLog():void - { - // If the console is paused. - if (_paused) - { - // Draw the log panel. - _logRead.y = 40; - _logRead.graphics.clear(); - _logRead.graphics.beginFill(0, .75); - _logRead.graphics.drawRoundRectComplex(0, 0, _logReadText0.width, 20, 0, 20, 0, 0); - _logRead.graphics.drawRect(0, 20, width, _logHeight); - - // Draw the log scrollbar. - _logRead.graphics.beginFill(0x202020, 1); - _logRead.graphics.drawRoundRectComplex(_logBar.x, _logBar.y, _logBar.width, _logBar.height, 8, 8, 8, 8); - - // If the log has more lines than the display limit. - if (LOG.length > _logLines) - { - // Draw the log scrollbar handle. - _logRead.graphics.beginFill(0xFFFFFF, 1); - var h:uint = FP.clamp(_logBar.height * (_logLines / LOG.length), 12, _logBar.height - 4), - y:uint = _logBar.y + 2 + (_logBar.height - 16) * _logScroll; - _logRead.graphics.drawRoundRectComplex(_logBar.x + 2, y, 12, 12, 6, 6, 6, 6); - } - - // Display the log text lines. - if (LOG.length) - { - var i:int = LOG.length > _logLines ? Math.round((LOG.length - _logLines) * _logScroll) : 0, - n:int = i + Math.min(_logLines, LOG.length), - s:String = ""; - while (i < n) s += LOG[i ++] + "\n"; - _logReadText1.text = s; - } - else _logReadText1.text = ""; - - // Indent the text for the scrollbar and size it to the log panel. - _logReadText1.height = _logHeight; - _logReadText1.x = 32; - _logReadText1.y = 24; - - // Make text selectable in paused mode. - _fpsReadText.selectable = true; - _fpsInfoText0.selectable = true; - _fpsInfoText1.selectable = true; - _entReadText.selectable = true; - _debReadText1.selectable = true; - } - else - { - // Draw the single-line log panel. - _logRead.y = height - 40; - _logReadText1.height = 20; - _logRead.graphics.clear(); - _logRead.graphics.beginFill(0, .75); - _logRead.graphics.drawRoundRectComplex(0, 0, _logReadText0.width, 20, 0, 20, 0, 0); - _logRead.graphics.drawRect(0, 20, width, 20); - - // Draw the single-line log text with the latests logged text. - _logReadText1.text = LOG.length ? LOG[LOG.length - 1] : ""; - _logReadText1.x = 2; - _logReadText1.y = 21; - - // Make text non-selectable while running. - _logReadText1.selectable = false; - _fpsReadText.selectable = false; - _fpsInfoText0.selectable = false; - _fpsInfoText1.selectable = false; - _entReadText.selectable = false; - _debReadText0.selectable = false; - _debReadText1.selectable = false; - } - } - - /** @private Update the FPS/frame timing panel text. */ - private function updateFPSRead():void - { - _fpsReadText.text = "FPS: " + FP.frameRate.toFixed(); - _fpsInfoText0.text = - "Update: " + String(FP._updateTime) + "ms\n" + - "Render: " + String(FP._renderTime) + "ms"; - _fpsInfoText1.text = - "Game: " + String(FP._gameTime) + "ms\n" + - "Flash: " + String(FP._flashTime) + "ms"; - } - - /** @private Update the debug panel text. */ - private function updateDebugRead():void - { - // Find out the screen size and set the text. - var big:Boolean = width >= 480; - - // Update the Debug read text. - var s:String = - "Mouse: " + String(FP.world.mouseX) + ", " + String(FP.world.mouseY) + - "\nCamera: " + String(FP.camera.x) + ", " + String(FP.camera.y); - if (SELECT_LIST.length) - { - if (SELECT_LIST.length > 1) - { - s += "\n\nSelected: " + String(SELECT_LIST.length); - } - else - { - var e:Entity = SELECT_LIST[0]; - s += "\n\n- " + String(e) + " -\n"; - for each (var i:String in WATCH_LIST) - { - if (e.hasOwnProperty(i)) s += "\n" + i + ": " + e[i].toString(); - } - } - } - - // Set the text and format. - _debReadText1.text = s; - _debReadText1.setTextFormat(format(big ? 16 : 8)); - _debReadText1.width = Math.max(_debReadText1.textWidth + 4, _debReadText0.width); - _debReadText1.height = _debReadText1.y + _debReadText1.textHeight + 4; - - // The debug panel. - _debRead.y = int(height - _debReadText1.height); - _debRead.graphics.clear(); - _debRead.graphics.beginFill(0, .75); - _debRead.graphics.drawRoundRectComplex(0, 0, _debReadText0.width, 20, 0, 20, 0, 0); - _debRead.graphics.drawRoundRectComplex(0, 20, _debReadText1.width + 20, height - _debRead.y - 20, 0, 20, 0, 0); - } - - /** @private Updates the Entity count text. */ - private function updateEntityCount():void - { - _entReadText.text = String(FP.world.count) + " Entities"; - } - - /** @private Updates the Button panel. */ - private function updateButtons():void - { - // Button visibility. - _butRead.x = _fpsInfo.x + _fpsInfo.width + int((_entRead.x - (_fpsInfo.x + _fpsInfo.width)) / 2) - 30; - _butDebug.visible = !_debug; - _butOutput.visible = _debug; - _butPlay.visible = FP.engine.paused; - _butPause.visible = !FP.engine.paused; - - // Debug/Output button. - if (_butDebug.bitmapData.rect.contains(_butDebug.mouseX, _butDebug.mouseY)) - { - _butDebug.alpha = _butOutput.alpha = 1; - if (Input.mousePressed) debug = !_debug; - } - else _butDebug.alpha = _butOutput.alpha = .5; - - // Play/Pause button. - if (_butPlay.bitmapData.rect.contains(_butPlay.mouseX, _butPlay.mouseY)) - { - _butPlay.alpha = _butPause.alpha = 1; - if (Input.mousePressed) - { - FP.engine.paused = !FP.engine.paused; - renderEntities(); - } - } - else _butPlay.alpha = _butPause.alpha = .5; - - // Frame step button. - if (_butStep.bitmapData.rect.contains(_butStep.mouseX, _butStep.mouseY)) - { - _butStep.alpha = 1; - if (Input.mousePressed) stepFrame(); - } - else _butStep.alpha = .5; - } - - /** @private Gets a TextFormat object with the formatting. */ - private function format(size:uint = 16, color:uint = 0xFFFFFF, align:String = "left"):TextFormat - { - _format.size = size; - _format.color = color; - _format.align = align; - return _format; - } - - /** - * Get the unscaled screen size for the Console. - */ - private function get width():uint { return FP.width * FP.screen.scaleX * FP.screen.scale; } - private function get height():uint { return FP.height * FP.screen.scaleY * FP.screen.scale; } - - // Console state information. - /** @private */ private var _enabled:Boolean; - /** @private */ private var _paused:Boolean; - /** @private */ private var _debug:Boolean; - /** @private */ private var _scrolling:Boolean; - /** @private */ private var _selecting:Boolean; - /** @private */ private var _dragging:Boolean; - /** @private */ private var _panning:Boolean; - - // Console display objects. - /** @private */ private var _sprite:Sprite = new Sprite; - /** @private */ private var _format:TextFormat = new TextFormat("console"); - /** @private */ private var _back:Bitmap = new Bitmap; - - // FPS panel information. - /** @private */ private var _fpsRead:Sprite = new Sprite; - /** @private */ private var _fpsReadText:TextField = new TextField; - /** @private */ private var _fpsInfo:Sprite = new Sprite; - /** @private */ private var _fpsInfoText0:TextField = new TextField; - /** @private */ private var _fpsInfoText1:TextField = new TextField; - - // Output panel information. - /** @private */ private var _logRead:Sprite = new Sprite; - /** @private */ private var _logReadText0:TextField = new TextField; - /** @private */ private var _logReadText1:TextField = new TextField; - /** @private */ private var _logHeight:uint; - /** @private */ private var _logBar:Rectangle; - /** @private */ private var _logBarGlobal:Rectangle; - /** @private */ private var _logScroll:Number = 0; - - // Entity count panel information. - /** @private */ private var _entRead:Sprite = new Sprite; - /** @private */ private var _entReadText:TextField = new TextField; - - // Debug panel information. - /** @private */ private var _debRead:Sprite = new Sprite; - /** @private */ private var _debReadText0:TextField = new TextField; - /** @private */ private var _debReadText1:TextField = new TextField; - /** @private */ private var _debWidth:uint; - - // Button panel information - /** @private */ private var _butRead:Sprite = new Sprite; - /** @private */ private var _butDebug:Bitmap; - /** @private */ private var _butOutput:Bitmap; - /** @private */ private var _butPlay:Bitmap; - /** @private */ private var _butPause:Bitmap; - /** @private */ private var _butStep:Bitmap; - - // Entity selection information. - /** @private */ private var _entScreen:Sprite = new Sprite; - /** @private */ private var _entSelect:Sprite = new Sprite; - /** @private */ private var _entRect:Rectangle = new Rectangle; - - // Log information. - /** @private */ private var _logLines:uint = 33; - /** @private */ private const LOG:Vector. = new Vector.; - - // Entity lists. - /** @private */ private const ENTITY_LIST:Vector. = new Vector.; - /** @private */ private const SCREEN_LIST:Vector. = new Vector.; - /** @private */ private const SELECT_LIST:Vector. = new Vector.; - - // Watch information. - /** @private */ private const WATCH_LIST:Vector. = Vector.(["x", "y"]); - - // Embedded assets. - [Embed(source = '../graphics/04B_03__.TTF', embedAsCFF="false", fontFamily = 'console')] private const FONT_SMALL:Class; - [Embed(source = 'console_logo.png')] private const CONSOLE_LOGO:Class; - [Embed(source = 'console_debug.png')] private const CONSOLE_DEBUG:Class; - [Embed(source = 'console_output.png')] private const CONSOLE_OUTPUT:Class; - [Embed(source = 'console_play.png')] private const CONSOLE_PLAY:Class; - [Embed(source = 'console_pause.png')] private const CONSOLE_PAUSE:Class; - [Embed(source = 'console_step.png')] private const CONSOLE_STEP:Class; - } -} \ No newline at end of file diff --git a/src/net/flashpunk/debug/console_debug.png b/src/net/flashpunk/debug/console_debug.png deleted file mode 100644 index 9e49800..0000000 Binary files a/src/net/flashpunk/debug/console_debug.png and /dev/null differ diff --git a/src/net/flashpunk/debug/console_logo.png b/src/net/flashpunk/debug/console_logo.png deleted file mode 100644 index 430f5c3..0000000 Binary files a/src/net/flashpunk/debug/console_logo.png and /dev/null differ diff --git a/src/net/flashpunk/debug/console_output.png b/src/net/flashpunk/debug/console_output.png deleted file mode 100644 index 7d011ac..0000000 Binary files a/src/net/flashpunk/debug/console_output.png and /dev/null differ diff --git a/src/net/flashpunk/debug/console_pause.png b/src/net/flashpunk/debug/console_pause.png deleted file mode 100644 index 3e7f58b..0000000 Binary files a/src/net/flashpunk/debug/console_pause.png and /dev/null differ diff --git a/src/net/flashpunk/debug/console_play.png b/src/net/flashpunk/debug/console_play.png deleted file mode 100644 index a34d201..0000000 Binary files a/src/net/flashpunk/debug/console_play.png and /dev/null differ diff --git a/src/net/flashpunk/debug/console_step.png b/src/net/flashpunk/debug/console_step.png deleted file mode 100644 index 4292f85..0000000 Binary files a/src/net/flashpunk/debug/console_step.png and /dev/null differ diff --git a/src/net/flashpunk/graphics/04B_03__.TTF b/src/net/flashpunk/graphics/04B_03__.TTF deleted file mode 100644 index fe4328b..0000000 Binary files a/src/net/flashpunk/graphics/04B_03__.TTF and /dev/null differ diff --git a/src/net/flashpunk/graphics/Anim.as b/src/net/flashpunk/graphics/Anim.as deleted file mode 100644 index 5d38116..0000000 --- a/src/net/flashpunk/graphics/Anim.as +++ /dev/null @@ -1,66 +0,0 @@ -package net.flashpunk.graphics -{ - /** - * Template used by Spritemap to define animations. Don't create - * these yourself, instead you can fetch them with Spritemap's add(). - */ - public class Anim - { - /** - * Constructor. - * @param name Animation name. - * @param frames Array of frame indices to animate. - * @param frameRate Animation speed. - * @param loop If the animation should loop. - */ - public function Anim(name:String, frames:Array, frameRate:Number = 0, loop:Boolean = true) - { - _name = name; - _frames = frames; - _frameRate = frameRate; - _loop = loop; - _frameCount = frames.length; - } - - /** - * Plays the animation. - * @param reset If the animation should force-restart if it is already playing. - */ - public function play(reset:Boolean = false):void - { - _parent.play(_name, reset); - } - - /** - * Name of the animation. - */ - public function get name():String { return _name; } - - /** - * Array of frame indices to animate. - */ - public function get frames():Array { return _frames; } - - /** - * Animation speed. - */ - public function get frameRate():Number { return _frameRate; } - - /** - * Amount of frames in the animation. - */ - public function get frameCount():uint { return _frameCount; } - - /** - * If the animation loops. - */ - public function get loop():Boolean { return _loop; } - - /** @private */ internal var _parent:Spritemap; - /** @private */ internal var _name:String; - /** @private */ internal var _frames:Array; - /** @private */ internal var _frameRate:Number; - /** @private */ internal var _frameCount:uint; - /** @private */ internal var _loop:Boolean; - } -} \ No newline at end of file diff --git a/src/net/flashpunk/graphics/Backdrop.as b/src/net/flashpunk/graphics/Backdrop.as deleted file mode 100644 index 18cef72..0000000 --- a/src/net/flashpunk/graphics/Backdrop.as +++ /dev/null @@ -1,69 +0,0 @@ -package net.flashpunk.graphics -{ - import flash.display.BitmapData; - import flash.geom.Point; - import net.flashpunk.FP; - import net.flashpunk.Graphic; - - /** - * A background texture that can be repeated horizontally and vertically - * when drawn. Really useful for parallax backgrounds, textures, etc. - */ - public class Backdrop extends Canvas - { - /** - * Constructor. - * @param texture Source texture. - * @param repeatX Repeat horizontally. - * @param repeatY Repeat vertically. - */ - public function Backdrop(texture:*, repeatX:Boolean = true, repeatY:Boolean = true) - { - if (texture is Class) _texture = FP.getBitmap(texture); - else if (texture is BitmapData) _texture = texture; - if (!_texture) _texture = new BitmapData(FP.width, FP.height, true, 0); - _repeatX = repeatX; - _repeatY = repeatY; - _textWidth = _texture.width; - _textHeight = _texture.height; - super(FP.width * uint(repeatX) + _textWidth, FP.height * uint(repeatY) + _textHeight); - FP.rect.x = FP.rect.y = 0; - FP.rect.width = _width; - FP.rect.height = _height; - fillTexture(FP.rect, _texture); - } - - /** @private Renders the Backdrop. */ - override public function render(target:BitmapData, point:Point, camera:Point):void - { - _point.x = point.x + x - camera.x * scrollX; - _point.y = point.y + y - camera.y * scrollY; - - if (_repeatX) - { - _point.x %= _textWidth; - if (_point.x > 0) _point.x -= _textWidth; - } - - if (_repeatY) - { - _point.y %= _textHeight; - if (_point.y > 0) _point.y -= _textHeight; - } - - _x = x; _y = y; - x = y = 0; - super.render(target, _point, FP.zero); - x = _x; y = _y; - } - - // Backdrop information. - /** @private */ private var _texture:BitmapData; - /** @private */ private var _textWidth:uint; - /** @private */ private var _textHeight:uint; - /** @private */ private var _repeatX:Boolean; - /** @private */ private var _repeatY:Boolean; - /** @private */ private var _x:Number; - /** @private */ private var _y:Number; - } -} \ No newline at end of file diff --git a/src/net/flashpunk/graphics/Canvas.as b/src/net/flashpunk/graphics/Canvas.as deleted file mode 100644 index b17b79d..0000000 --- a/src/net/flashpunk/graphics/Canvas.as +++ /dev/null @@ -1,313 +0,0 @@ -package net.flashpunk.graphics -{ - import flash.display.BitmapData; - import flash.display.Graphics; - import flash.geom.ColorTransform; - import flash.geom.Matrix; - import flash.geom.Point; - import flash.geom.Rectangle; - import net.flashpunk.FP; - import net.flashpunk.Graphic; - - /** - * A multi-purpose drawing canvas, can be sized beyond the normal Flash BitmapData limits. - */ - public class Canvas extends Graphic - { - /** - * Optional blend mode to use (see flash.display.BlendMode for blending modes). - */ - public var blend:String; - - /** - * Constructor. - * @param width Width of the canvas. - * @param height Height of the canvas. - */ - public function Canvas(width:uint, height:uint) - { - _width = width; - _height = height; - _refWidth = Math.ceil(width / _maxWidth); - _refHeight = Math.ceil(height / _maxHeight); - _ref = new BitmapData(_refWidth, _refHeight, false, 0); - var x:uint, y:uint, w:uint, h:uint, i:uint, - ww:uint = _width % _maxWidth, - hh:uint = _height % _maxHeight; - if (!ww) ww = _maxWidth; - if (!hh) hh = _maxHeight; - while (y < _refHeight) - { - h = y < _refHeight - 1 ? _maxHeight : hh; - while (x < _refWidth) - { - w = x < _refWidth - 1 ? _maxWidth : ww; - _ref.setPixel(x, y, i); - _buffers[i] = new BitmapData(w, h, true, 0); - i ++; x ++; - } - x = 0; y ++; - } - } - - /** @private Renders the canvas. */ - override public function render(target:BitmapData, point:Point, camera:Point):void - { - // determine drawing location - _point.x = point.x + x - camera.x * scrollX; - _point.y = point.y + y - camera.y * scrollY; - - // render the buffers - var xx:int, yy:int, buffer:BitmapData, px:Number = _point.x; - while (yy < _refHeight) - { - while (xx < _refWidth) - { - buffer = _buffers[_ref.getPixel(xx, yy)]; - if (_tint || blend) - { - _matrix.tx = _point.x; - _matrix.ty = _point.y; - target.draw(buffer, _matrix, _tint, blend); - } - else target.copyPixels(buffer, buffer.rect, _point, null, null, true); - _point.x += _maxWidth; - xx ++; - } - _point.x = px; - _point.y += _maxHeight; - xx = 0; - yy ++; - } - } - - /** - * Draws to the canvas. - * @param x X position to draw. - * @param y Y position to draw. - * @param source Source BitmapData. - * @param rect Optional area of the source image to draw from. If null, the entire BitmapData will be drawn. - */ - public function draw(x:int, y:int, source:BitmapData, rect:Rectangle = null):void - { - var xx:int, yy:int; - for each (var buffer:BitmapData in _buffers) - { - _point.x = x - xx; - _point.y = y - yy; - buffer.copyPixels(source, rect ? rect : source.rect, _point, null, null, true); - xx += _maxWidth; - if (xx >= _width) - { - xx = 0; - yy += _maxHeight; - } - } - } - - /** - * Fills the rectangular area of the canvas. The previous contents of that area are completely removed. - * @param rect Fill rectangle. - * @param color Fill color. - * @param alpha Fill alpha. - */ - public function fill(rect:Rectangle, color:uint = 0, alpha:Number = 1):void - { - var xx:int, yy:int, buffer:BitmapData; - _rect.width = rect.width; - _rect.height = rect.height; - if (alpha >= 1) color |= 0xFF000000; - else if (alpha <= 0) color = 0; - else color = (uint(alpha * 255) << 24) | (0xFFFFFF & color); - for each (buffer in _buffers) - { - _rect.x = rect.x - xx; - _rect.y = rect.y - yy; - buffer.fillRect(_rect, color); - xx += _maxWidth; - if (xx >= _width) - { - xx = 0; - yy += _maxHeight; - } - } - } - - /** - * Draws over a rectangular area of the canvas. - * @param rect Drawing rectangle. - * @param color Draw color. - * @param alpha Draw alpha. If < 1, this rectangle will blend with existing contents of the canvas. - */ - public function drawRect(rect:Rectangle, color:uint = 0, alpha:Number = 1):void - { - var xx:int, yy:int, buffer:BitmapData; - if (alpha >= 1) - { - _rect.width = rect.width; - _rect.height = rect.height; - - for each (buffer in _buffers) - { - _rect.x = rect.x - xx; - _rect.y = rect.y - yy; - buffer.fillRect(_rect, 0xFF000000 | color); - xx += _maxWidth; - if (xx >= _width) - { - xx = 0; - yy += _maxHeight; - } - } - return; - } - for each (buffer in _buffers) - { - _graphics.clear(); - _graphics.beginFill(color, alpha); - _graphics.drawRect(rect.x - xx, rect.y - yy, rect.width, rect.height); - buffer.draw(FP.sprite); - xx += _maxWidth; - if (xx >= _width) - { - xx = 0; - yy += _maxHeight; - } - } - _graphics.endFill(); - } - - /** - * Fills the rectangle area of the canvas with the texture. - * @param rect Fill rectangle. - * @param texture Fill texture. - */ - public function fillTexture(rect:Rectangle, texture:BitmapData):void - { - var xx:int, yy:int; - for each (var buffer:BitmapData in _buffers) - { - _graphics.clear(); - _graphics.beginBitmapFill(texture); - _graphics.drawRect(rect.x - xx, rect.y - yy, rect.width, rect.height); - buffer.draw(FP.sprite); - xx += _maxWidth; - if (xx >= _width) - { - xx = 0; - yy += _maxHeight; - } - } - _graphics.endFill(); - } - - /** - * Draws the Graphic object to the canvas. - * @param x X position to draw. - * @param y Y position to draw. - * @param source Graphic to draw. - */ - public function drawGraphic(x:int, y:int, source:Graphic):void - { - var xx:int, yy:int; - for each (var buffer:BitmapData in _buffers) - { - _point.x = x - xx; - _point.y = y - yy; - source.render(buffer, _point, FP.zero); - xx += _maxWidth; - if (xx >= _width) - { - xx = 0; - yy += _maxHeight; - } - } - } - - /** - * The tinted color of the Canvas. Use 0xFFFFFF to draw the it normally. - */ - public function get color():uint { return _color; } - public function set color(value:uint):void - { - value %= 0xFFFFFF; - if (_color == value) return; - _color = value; - if (_alpha == 1 && _color == 0xFFFFFF) - { - _tint = null; - return; - } - _tint = _colorTransform; - _tint.redMultiplier = (_color >> 16 & 0xFF) / 255; - _tint.greenMultiplier = (_color >> 8 & 0xFF) / 255; - _tint.blueMultiplier = (_color & 0xFF) / 255; - _tint.alphaMultiplier = _alpha; - } - - /** - * Change the opacity of the Canvas, a value from 0 to 1. - */ - public function get alpha():Number { return _alpha; } - public function set alpha(value:Number):void - { - if (value < 0) value = 0; - if (value > 1) value = 1; - if (_alpha == value) return; - _alpha = value; - if (_alpha == 1 && _color == 0xFFFFFF) - { - _tint = null; - return; - } - _tint = _colorTransform; - _tint.redMultiplier = (_color >> 16 & 0xFF) / 255; - _tint.greenMultiplier = (_color >> 8 & 0xFF) / 255; - _tint.blueMultiplier = (_color & 0xFF) / 255; - _tint.alphaMultiplier = _alpha; - } - - /** - * Shifts the canvas' pixels by the offset. - * @param x Horizontal shift. - * @param y Vertical shift. - */ - public function shift(x:int = 0, y:int = 0):void - { - drawGraphic(x, y, this); - } - - /** - * Width of the canvas. - */ - public function get width():uint { return _width; } - - /** - * Height of the canvas. - */ - public function get height():uint { return _height; } - - // Buffer information. - /** @private */ private var _buffers:Vector. = new Vector.; - /** @private */ protected var _width:uint; - /** @private */ protected var _height:uint; - /** @private */ protected var _maxWidth:uint = 4000; - /** @private */ protected var _maxHeight:uint = 4000; - - // Color tinting information. - /** @private */ private var _color:uint = 0xFFFFFF; - /** @private */ private var _alpha:Number = 1; - /** @private */ private var _tint:ColorTransform; - /** @private */ private var _colorTransform:ColorTransform = new ColorTransform; - /** @private */ private var _matrix:Matrix = new Matrix; - - // Canvas reference information. - /** @private */ private var _ref:BitmapData; - /** @private */ private var _refWidth:uint; - /** @private */ private var _refHeight:uint; - - // Global objects. - /** @private */ private var _rect:Rectangle = new Rectangle; - /** @private */ private var _graphics:Graphics = FP.sprite.graphics; - } -} \ No newline at end of file diff --git a/src/net/flashpunk/graphics/Emitter.as b/src/net/flashpunk/graphics/Emitter.as deleted file mode 100644 index 409940f..0000000 --- a/src/net/flashpunk/graphics/Emitter.as +++ /dev/null @@ -1,260 +0,0 @@ -package net.flashpunk.graphics -{ - import flash.display.BitmapData; - import flash.geom.ColorTransform; - import flash.geom.Point; - import flash.geom.Rectangle; - import net.flashpunk.FP; - import net.flashpunk.Graphic; - import net.flashpunk.utils.Input; - import net.flashpunk.utils.Key; - - /** - * Particle emitter used for emitting and rendering particle sprites. - * Good rendering performance with large amounts of particles. - */ - public class Emitter extends Graphic - { - /** - * Constructor. Sets the source image to use for newly added particle types. - * @param source Source image. - * @param frameWidth Frame width. - * @param frameHeight Frame height. - */ - public function Emitter(source:*, frameWidth:uint = 0, frameHeight:uint = 0) - { - setSource(source, frameWidth, frameHeight); - active = true; - } - - /** - * Changes the source image to use for newly added particle types. - * @param source Source image. - * @param frameWidth Frame width. - * @param frameHeight Frame height. - */ - public function setSource(source:*, frameWidth:uint = 0, frameHeight:uint = 0):void - { - if (source is Class) _source = FP.getBitmap(source); - else if (source is BitmapData) _source = source; - if (!_source) throw new Error("Invalid source image."); - _width = _source.width; - _height = _source.height; - _frameWidth = frameWidth ? frameWidth : _width; - _frameHeight = frameHeight ? frameHeight : _height; - _frameCount = uint(_width / _frameWidth) * uint(_height / _frameHeight); - } - - override public function update():void - { - // quit if there are no particles - if (!_particle) return; - - // particle info - var e:Number = FP.fixed ? 1 : FP.elapsed, - p:Particle = _particle, - n:Particle, t:Number; - - // loop through the particles - while (p) - { - // update time scale - p._time += e; - t = p._time / p._duration; - - // remove on time-out - if (p._time >= p._duration) - { - if (p._next) p._next._prev = p._prev; - if (p._prev) p._prev._next = p._next; - else _particle = p._next; - n = p._next; - p._next = _cache; - p._prev = null; - _cache = p; - p = n; - _particleCount --; - continue; - } - - // get next particle - p = p._next; - } - } - - /** @private Renders the particles. */ - override public function render(target:BitmapData, point:Point, camera:Point):void - { - // quit if there are no particles - if (!_particle) return; - - // get rendering position - _point.x = point.x + x - camera.x * scrollX; - _point.y = point.y + y - camera.y * scrollY; - - // particle info - var t:Number, td:Number, - p:Particle = _particle, - type:ParticleType, - rect:Rectangle; - - // loop through the particles - while (p) - { - // get time scale - t = p._time / p._duration; - - // get particle type - type = p._type; - rect = type._frame; - - // get position - td = (type._ease == null) ? t : type._ease(t); - _p.x = _point.x + p._x + p._moveX * td; - _p.y = _point.y + p._y + p._moveY * td; - - // get frame - rect.x = rect.width * type._frames[uint(td * type._frameCount)]; - rect.y = uint(rect.x / type._width) * rect.height; - rect.x %= type._width; - - // draw particle - if (type._buffer) - { - // get alpha - _tint.alphaMultiplier = type._alpha + type._alphaRange * ((type._alphaEase == null) ? t : type._alphaEase(t)); - - // get color - td = (type._colorEase == null) ? t : type._colorEase(t); - _tint.redMultiplier = type._red + type._redRange * td; - _tint.greenMultiplier = type._green + type._greenRange * td; - _tint.blueMultiplier = type._blue + type._blueRange * td; - type._buffer.fillRect(type._bufferRect, 0); - type._buffer.copyPixels(type._source, rect, FP.zero); - type._buffer.colorTransform(type._bufferRect, _tint); - - // draw particle - target.copyPixels(type._buffer, type._bufferRect, _p, null, null, true); - } - else target.copyPixels(type._source, rect, _p, null, null, true); - - // get next particle - p = p._next; - } - } - - /** - * Creates a new Particle type for this Emitter. - * @param name Name of the particle type. - * @param frames Array of frame indices for the particles to animate. - * @return A new ParticleType object. - */ - public function newType(name:String, frames:Array = null):ParticleType - { - if (_types[name]) throw new Error("Cannot add multiple particle types of the same name"); - return (_types[name] = new ParticleType(name, frames, _source, _frameWidth, _frameHeight)); - } - - /** - * Defines the motion range for a particle type. - * @param name The particle type. - * @param angle Launch Direction. - * @param distance Distance to travel. - * @param duration Particle duration. - * @param angleRange Random amount to add to the particle's direction. - * @param distanceRange Random amount to add to the particle's distance. - * @param durationRange Random amount to add to the particle's duration. - * @param ease Optional easer function. - * @return This ParticleType object. - */ - public function setMotion(name:String, angle:Number, distance:Number, duration:Number, angleRange:Number = 0, distanceRange:Number = 0, durationRange:Number = 0, ease:Function = null):ParticleType - { - return (_types[name] as ParticleType).setMotion(angle, distance, duration, angleRange, distanceRange, durationRange, ease); - } - - /** - * Sets the alpha range of the particle type. - * @param name The particle type. - * @param start The starting alpha. - * @param finish The finish alpha. - * @param ease Optional easer function. - * @return This ParticleType object. - */ - public function setAlpha(name:String, start:Number = 1, finish:Number = 0, ease:Function = null):ParticleType - { - return (_types[name] as ParticleType).setAlpha(start, finish, ease); - } - - /** - * Sets the color range of the particle type. - * @param name The particle type. - * @param start The starting color. - * @param finish The finish color. - * @param ease Optional easer function. - * @return This ParticleType object. - */ - public function setColor(name:String, start:uint = 0xFFFFFF, finish:uint = 0, ease:Function = null):ParticleType - { - return (_types[name] as ParticleType).setColor(start, finish, ease); - } - - /** - * Emits a particle. - * @param name Particle type to emit. - * @param x X point to emit from. - * @param y Y point to emit from. - * @return - */ - public function emit(name:String, x:Number, y:Number):Particle - { - if (!_types[name]) throw new Error("Particle type \"" + name + "\" does not exist."); - var p:Particle, type:ParticleType = _types[name]; - - if (_cache) - { - p = _cache; - _cache = p._next; - } - else p = new Particle; - p._next = _particle; - p._prev = null; - if (p._next) p._next._prev = p; - - p._type = type; - p._time = 0; - p._duration = type._duration + type._durationRange * FP.random; - var a:Number = type._angle + type._angleRange * FP.random, - d:Number = type._distance + type._distanceRange * FP.random; - p._moveX = Math.cos(a) * d; - p._moveY = Math.sin(a) * d; - p._x = x; - p._y = y; - _particleCount ++; - return (_particle = p); - } - - /** - * Amount of currently existing particles. - */ - public function get particleCount():uint { return _particleCount; } - - // Particle infromation. - /** @private */ private var _types:Object = { }; - /** @private */ private var _particle:Particle; - /** @private */ private var _cache:Particle; - /** @private */ private var _particleCount:uint; - - // Source information. - /** @private */ private var _source:BitmapData; - /** @private */ private var _width:uint; - /** @private */ private var _height:uint; - /** @private */ private var _frameWidth:uint; - /** @private */ private var _frameHeight:uint; - /** @private */ private var _frameCount:uint; - - // Drawing information. - /** @private */ private var _p:Point = new Point; - /** @private */ private var _tint:ColorTransform = new ColorTransform; - /** @private */ private static const SIN:Number = Math.PI / 2; - } -} \ No newline at end of file diff --git a/src/net/flashpunk/graphics/Graphiclist.as b/src/net/flashpunk/graphics/Graphiclist.as deleted file mode 100644 index d0b389e..0000000 --- a/src/net/flashpunk/graphics/Graphiclist.as +++ /dev/null @@ -1,142 +0,0 @@ -package net.flashpunk.graphics -{ - import flash.display.BitmapData; - import flash.geom.Point; - import flash.utils.Dictionary; - import net.flashpunk.*; - - /** - * A Graphic that can contain multiple Graphics of one or various types. - * Useful for drawing sprites with multiple different parts, etc. - */ - public class Graphiclist extends Graphic - { - /** - * Constructor. - * @param ...graphic Graphic objects to add to the list. - */ - public function Graphiclist(...graphic) - { - for each (var g:Graphic in graphic) add(g); - } - - /** @private Updates the graphics in the list. */ - override public function update():void - { - for each (var g:Graphic in _graphics) - { - if (g.active) g.update(); - } - } - - /** @private Renders the Graphics in the list. */ - override public function render(target:BitmapData, point:Point, camera:Point):void - { - point.x += x; - point.y += y; - camera.x *= scrollX; - camera.y *= scrollY; - for each (var g:Graphic in _graphics) - { - if (g.visible) - { - if (g.relative) - { - _point.x = point.x; - _point.y = point.y; - } - else _point.x = _point.y = 0; - _camera.x = camera.x; - _camera.y = camera.y; - g.render(target, _point, _camera); - } - } - } - - /** - * Adds the Graphic to the list. - * @param graphic The Graphic to add. - * @return The added Graphic. - */ - public function add(graphic:Graphic):Graphic - { - _graphics[_count ++] = graphic; - if (!active) active = graphic.active; - return graphic; - } - - /** - * Removes the Graphic from the list. - * @param graphic The Graphic to remove. - * @return The removed Graphic. - */ - public function remove(graphic:Graphic):Graphic - { - if (_graphics.indexOf(graphic) < 0) return graphic; - _temp.length = 0; - for each (var g:Graphic in _graphics) - { - if (g == graphic) _count --; - else _temp[_temp.length] = g; - } - var temp:Vector. = _graphics; - _graphics = _temp; - _temp = temp; - updateCheck(); - return graphic; - } - - /** - * Removes the Graphic from the position in the list. - * @param index Index to remove. - */ - public function removeAt(index:uint = 0):void - { - if (!_graphics.length) return; - index %= _graphics.length; - remove(_graphics[index % _graphics.length]); - updateCheck(); - } - - /** - * Removes all Graphics from the list. - */ - public function removeAll():void - { - _graphics.length = _temp.length = _count = 0; - active = false; - } - - /** - * All Graphics in this list. - */ - public function get children():Vector. { return _graphics; } - - /** - * Amount of Graphics in this list. - */ - public function get count():uint { return _count; } - - /** - * Check if the Graphiclist should update. - */ - private function updateCheck():void - { - active = false; - for each (var g:Graphic in _graphics) - { - if (g.active) - { - active = true; - return; - } - } - } - - // List information. - /** @private */ private var _graphics:Vector. = new Vector.; - /** @private */ private var _temp:Vector. = new Vector.; - /** @private */ private var _count:uint; - /** @private */ private var _camera:Point = new Point; - } -} \ No newline at end of file diff --git a/src/net/flashpunk/graphics/Image.as b/src/net/flashpunk/graphics/Image.as deleted file mode 100644 index 1be9ad9..0000000 --- a/src/net/flashpunk/graphics/Image.as +++ /dev/null @@ -1,312 +0,0 @@ -package net.flashpunk.graphics -{ - import flash.display.Bitmap; - import flash.display.BitmapData; - import flash.geom.ColorTransform; - import flash.geom.Matrix; - import flash.geom.Point; - import flash.geom.Rectangle; - import net.flashpunk.*; - - /** - * Performance-optimized non-animated image. Can be drawn to the screen with transformations. - */ - public class Image extends Graphic - { - /** - * Rotation of the image, in degrees. - */ - public var angle:Number = 0; - - /** - * Scale of the image, effects both x and y scale. - */ - public var scale:Number = 1; - - /** - * X scale of the image. - */ - public var scaleX:Number = 1; - - /** - * Y scale of the image. - */ - public var scaleY:Number = 1; - - /** - * X origin of the image, determines transformation point. - */ - public var originX:int; - - /** - * Y origin of the image, determines transformation point. - */ - public var originY:int; - - /** - * Optional blend mode to use when drawing this image. - * Use constants from the flash.display.BlendMode class. - */ - public var blend:String; - - /** - * If the image should be drawn transformed with pixel smoothing. - * This will affect drawing performance, but look less pixelly. - */ - public var smooth:Boolean; - - /** - * Constructor. - * @param source Source image. - * @param clipRect Optional rectangle defining area of the source image to draw. - */ - public function Image(source:* = null, clipRect:Rectangle = null) - { - if (source is Class) - { - _source = FP.getBitmap(source); - _class = String(source); - } - else if (source is BitmapData) _source = source; - if (!_source) throw new Error("Invalid source image."); - _sourceRect = _source.rect; - if (clipRect) - { - if (!clipRect.width) clipRect.width = _sourceRect.width; - if (!clipRect.height) clipRect.height = _sourceRect.height; - _sourceRect = clipRect; - } - createBuffer(); - updateBuffer(); - } - - /** @private Creates the buffer. */ - protected function createBuffer():void - { - _buffer = new BitmapData(_sourceRect.width, _sourceRect.height, true, 0); - _bufferRect = _buffer.rect; - _bitmap.bitmapData = _buffer; - } - - /** @private Renders the image. */ - override public function render(target:BitmapData, point:Point, camera:Point):void - { - // quit if no graphic is assigned - if (!_buffer) return; - - // determine drawing location - _point.x = point.x + x - camera.x * scrollX; - _point.y = point.y + y - camera.y * scrollY; - - // render without transformation - if (angle == 0 && scaleX * scale == 1 && scaleY * scale == 1 && !blend) - { - target.copyPixels(_buffer, _bufferRect, _point, null, null, true); - return; - } - - // render with transformation - _matrix.b = _matrix.c = 0; - _matrix.a = scaleX * scale; - _matrix.d = scaleY * scale; - _matrix.tx = -originX * _matrix.a; - _matrix.ty = -originY * _matrix.d; - if (angle != 0) _matrix.rotate(angle * FP.RAD); - _matrix.tx += originX + _point.x; - _matrix.ty += originY + _point.y; - target.draw(_bitmap, _matrix, null, blend, null, smooth); - } - - /** - * Creates a new rectangle Image. - * @param width Width of the rectangle. - * @param height Height of the rectangle. - * @param color Color of the rectangle. - * @return A new Image object. - */ - public static function createRect(width:uint, height:uint, color:uint = 0xFFFFFF):Image - { - var source:BitmapData = new BitmapData(width, height, true, 0xFF000000 | color); - return new Image(source); - } - - /** - * Creates a new circle Image. - * @param radius Radius of the circle. - * @param color Color of the circle. - * @return A new Circle object. - */ - public static function createCircle(radius:uint, color:uint = 0xFFFFFF):Image - { - FP.sprite.graphics.clear(); - FP.sprite.graphics.beginFill(color); - FP.sprite.graphics.drawCircle(radius, radius, radius); - var data:BitmapData = new BitmapData(radius * 2, radius * 2, true, 0); - data.draw(FP.sprite); - return new Image(data); - } - - /** - * Updates the image buffer. - */ - public function updateBuffer(clearBefore:Boolean = false):void - { - if (!_source) return; - if (clearBefore) _buffer.fillRect(_bufferRect, 0); - _buffer.copyPixels(_source, _sourceRect, FP.zero); - if (_tint) _buffer.colorTransform(_bufferRect, _tint); - } - - /** - * Clears the image buffer. - */ - public function clear():void - { - _buffer.fillRect(_bufferRect, 0); - } - - /** - * Change the opacity of the Image, a value from 0 to 1. - */ - public function get alpha():Number { return _alpha; } - public function set alpha(value:Number):void - { - value = value < 0 ? 0 : (value > 1 ? 1 : value); - if (_alpha == value) return; - _alpha = value; - if (_alpha == 1 && _color == 0xFFFFFF) - { - _tint = null; - return updateBuffer(); - } - _tint = _colorTransform; - _tint.redMultiplier = (_color >> 16 & 0xFF) / 255; - _tint.greenMultiplier = (_color >> 8 & 0xFF) / 255; - _tint.blueMultiplier = (_color & 0xFF) / 255; - _tint.alphaMultiplier = _alpha; - updateBuffer(); - } - - /** - * The tinted color of the Image. Use 0xFFFFFF to draw the Image normally. - */ - public function get color():uint { return _color; } - public function set color(value:uint):void - { - value &= 0xFFFFFF; - if (_color == value) return; - _color = value; - if (_alpha == 1 && _color == 0xFFFFFF) - { - _tint = null; - return updateBuffer(); - } - _tint = _colorTransform; - _tint.redMultiplier = (_color >> 16 & 0xFF) / 255; - _tint.greenMultiplier = (_color >> 8 & 0xFF) / 255; - _tint.blueMultiplier = (_color & 0xFF) / 255; - _tint.alphaMultiplier = _alpha; - updateBuffer(); - } - - /** - * If you want to draw the Image horizontally flipped. This is - * faster than setting scaleX to -1 if your image isn't transformed. - */ - public function get flipped():Boolean { return _flipped; } - public function set flipped(value:Boolean):void - { - if (_flipped == value || !_class) return; - _flipped = value; - var temp:BitmapData = _source; - if (!value || _flip) - { - _source = _flip; - _flip = temp; - return updateBuffer(); - } - if (_flips[_class]) - { - _source = _flips[_class]; - _flip = temp; - return updateBuffer(); - } - _source = _flips[_class] = new BitmapData(_source.width, _source.height, true, 0); - _flip = temp; - FP.matrix.identity(); - FP.matrix.a = -1; - FP.matrix.tx = _source.width; - _source.draw(temp, FP.matrix); - updateBuffer(); - } - - /** - * Centers the Image's originX/Y to its center. - */ - public function centerOrigin():void - { - originX = _bufferRect.width / 2; - originY = _bufferRect.height / 2; - } - - /** - * Centers the Image's originX/Y to its center, and negates the offset by the same amount. - */ - public function centerOO():void - { - x += originX; - y += originY; - centerOrigin(); - x -= originX; - y -= originY; - } - - /** - * Width of the image. - */ - public function get width():uint { return _bufferRect.width; } - - /** - * Height of the image. - */ - public function get height():uint { return _bufferRect.height; } - - /** - * The scaled width of the image. - */ - public function get scaledWidth():uint { return _bufferRect.width * scaleX * scale; } - - /** - * The scaled height of the image. - */ - public function get scaledHeight():uint { return _bufferRect.height * scaleY * scale; } - - /** - * Clipping rectangle for the image. - */ - public function get clipRect():Rectangle { return _sourceRect; } - - /** @private Source BitmapData image. */ - protected function get source():BitmapData { return _source; } - - // Source and buffer information. - /** @private */ protected var _source:BitmapData; - /** @private */ protected var _sourceRect:Rectangle; - /** @private */ protected var _buffer:BitmapData; - /** @private */ protected var _bufferRect:Rectangle; - /** @private */ protected var _bitmap:Bitmap = new Bitmap; - - // Color and alpha information. - /** @private */ private var _alpha:Number = 1; - /** @private */ private var _color:uint = 0x00FFFFFF; - /** @private */ protected var _tint:ColorTransform; - /** @private */ private var _colorTransform:ColorTransform = new ColorTransform; - /** @private */ private var _matrix:Matrix = FP.matrix; - - // Flipped image information. - /** @private */ private var _class:String; - /** @private */ protected var _flipped:Boolean; - /** @private */ private var _flip:BitmapData; - /** @private */ private static var _flips:Object = { }; - } -} \ No newline at end of file diff --git a/src/net/flashpunk/graphics/Particle.as b/src/net/flashpunk/graphics/Particle.as deleted file mode 100644 index 5201274..0000000 --- a/src/net/flashpunk/graphics/Particle.as +++ /dev/null @@ -1,35 +0,0 @@ -package net.flashpunk.graphics -{ - import flash.display.BitmapData; - import flash.geom.Point; - import flash.geom.Rectangle; - - /** - * Used by the Emitter class to track an existing Particle. - */ - public class Particle - { - /** - * Constructor. - */ - public function Particle() - { - - } - - // Particle information. - /** @private */ internal var _type:ParticleType; - /** @private */ internal var _time:Number; - /** @private */ internal var _duration:Number; - - // Motion information. - /** @private */ internal var _x:Number; - /** @private */ internal var _y:Number; - /** @private */ internal var _moveX:Number; - /** @private */ internal var _moveY:Number; - - // List information. - /** @private */ internal var _prev:Particle; - /** @private */ internal var _next:Particle; - } -} \ No newline at end of file diff --git a/src/net/flashpunk/graphics/ParticleType.as b/src/net/flashpunk/graphics/ParticleType.as deleted file mode 100644 index 076cec2..0000000 --- a/src/net/flashpunk/graphics/ParticleType.as +++ /dev/null @@ -1,157 +0,0 @@ -package net.flashpunk.graphics -{ - import flash.display.BitmapData; - import flash.geom.Rectangle; - import net.flashpunk.FP; - - /** - * Template used to define a particle type used by the Emitter class. Instead - * of creating this object yourself, fetch one with Emitter's add() function. - */ - public class ParticleType - { - /** - * Constructor. - * @param name Name of the particle type. - * @param frames Array of frame indices to animate through. - * @param source Source image. - * @param frameWidth Frame width. - * @param frameHeight Frame height. - * @param frameCount Frame count. - */ - public function ParticleType(name:String, frames:Array, source:BitmapData, frameWidth:uint, frameHeight:uint) - { - _name = name; - _source = source; - _width = source.width; - _frame = new Rectangle(0, 0, frameWidth, frameHeight); - _frames = frames; - _frameCount = frames.length; - } - - /** - * Defines the motion range for this particle type. - * @param angle Launch Direction. - * @param distance Distance to travel. - * @param duration Particle duration. - * @param angleRange Random amount to add to the particle's direction. - * @param distanceRange Random amount to add to the particle's distance. - * @param durationRange Random amount to add to the particle's duration. - * @param ease Optional easer function. - * @return This ParticleType object. - */ - public function setMotion(angle:Number, distance:Number, duration:Number, angleRange:Number = 0, distanceRange:Number = 0, durationRange:Number = 0, ease:Function = null):ParticleType - { - _angle = angle * FP.RAD; - _distance = distance; - _duration = duration; - _angleRange = angleRange * FP.RAD; - _distanceRange = distanceRange; - _durationRange = durationRange; - _ease = ease; - return this; - } - - /** - * Defines the motion range for this particle type based on the vector. - * @param x X distance to move. - * @param y Y distance to move. - * @param duration Particle duration. - * @param durationRange Random amount to add to the particle's duration. - * @param ease Optional easer function. - * @return This ParticleType object. - */ - public function setMotionVector(x:Number, y:Number, duration:Number, durationRange:Number = 0, ease:Function = null):ParticleType - { - _angle = Math.atan2(y, x); - _angleRange = 0; - _duration = duration; - _durationRange = durationRange; - _ease = ease; - return this; - } - - /** - * Sets the alpha range of this particle type. - * @param start The starting alpha. - * @param finish The finish alpha. - * @param ease Optional easer function. - * @return This ParticleType object. - */ - public function setAlpha(start:Number = 1, finish:Number = 0, ease:Function = null):ParticleType - { - start = start < 0 ? 0 : (start > 1 ? 1 : start); - finish = finish < 0 ? 0 : (finish > 1 ? 1 : finish); - _alpha = start; - _alphaRange = finish - start; - _alphaEase = ease; - createBuffer(); - return this; - } - - /** - * Sets the color range of this particle type. - * @param start The starting color. - * @param finish The finish color. - * @param ease Optional easer function. - * @return This ParticleType object. - */ - public function setColor(start:uint = 0xFFFFFF, finish:uint = 0, ease:Function = null):ParticleType - { - start &= 0xFFFFFF; - finish &= 0xFFFFFF; - _red = (start >> 16 & 0xFF) / 255; - _green = (start >> 8 & 0xFF) / 255; - _blue = (start & 0xFF) / 255; - _redRange = (finish >> 16 & 0xFF) / 255 - _red; - _greenRange = (finish >> 8 & 0xFF) / 255 - _green; - _blueRange = (finish & 0xFF) / 255 - _blue; - _colorEase = ease; - createBuffer(); - return this; - } - - /** @private Creates the buffer if it doesn't exist. */ - private function createBuffer():void - { - if (_buffer) return; - _buffer = new BitmapData(_frame.width, _frame.height, true, 0); - _bufferRect = _buffer.rect; - } - - // Particle information. - /** @private */ internal var _name:String; - /** @private */ internal var _source:BitmapData; - /** @private */ internal var _width:uint; - /** @private */ internal var _frame:Rectangle; - /** @private */ internal var _frames:Array; - /** @private */ internal var _frameCount:uint; - - // Motion information. - /** @private */ internal var _angle:Number; - /** @private */ internal var _angleRange:Number; - /** @private */ internal var _distance:Number; - /** @private */ internal var _distanceRange:Number; - /** @private */ internal var _duration:Number; - /** @private */ internal var _durationRange:Number; - /** @private */ internal var _ease:Function; - - // Alpha information. - /** @private */ internal var _alpha:Number = 1; - /** @private */ internal var _alphaRange:Number = 0; - /** @private */ internal var _alphaEase:Function; - - // Color information. - /** @private */ internal var _red:Number = 1; - /** @private */ internal var _redRange:Number = 0; - /** @private */ internal var _green:Number = 1; - /** @private */ internal var _greenRange:Number = 0; - /** @private */ internal var _blue:Number = 1; - /** @private */ internal var _blueRange:Number = 0; - /** @private */ internal var _colorEase:Function; - - // Buffer information. - /** @private */ internal var _buffer:BitmapData; - /** @private */ internal var _bufferRect:Rectangle; - } -} \ No newline at end of file diff --git a/src/net/flashpunk/graphics/PreRotation.as b/src/net/flashpunk/graphics/PreRotation.as deleted file mode 100644 index c368fab..0000000 --- a/src/net/flashpunk/graphics/PreRotation.as +++ /dev/null @@ -1,106 +0,0 @@ -package net.flashpunk.graphics -{ - import flash.display.Bitmap; - import flash.display.BitmapData; - import flash.geom.Matrix; - import flash.geom.Point; - import flash.geom.Rectangle; - import flash.utils.Dictionary; - import net.flashpunk.FP; - import net.flashpunk.Graphic; - - /** - * Creates a pre-rotated Image strip to increase runtime performance for rotating graphics. - */ - public class PreRotation extends Image - { - /** - * Current angle to fetch the pre-rotated frame from. - */ - public var frameAngle:Number = 0; - - /** - * Constructor. - * @param source The source image to be rotated. - * @param frameCount How many frames to use. More frames result in smoother rotations. - * @param smooth Make the rotated graphic appear less pixelly. - */ - public function PreRotation(source:Class, frameCount:uint = 36, smooth:Boolean = false) - { - var r:BitmapData = _rotated[source]; - _frame = new Rectangle(0, 0, _size[source], _size[source]); - if (!r) - { - // produce a rotated bitmap strip - var temp:BitmapData = (new source).bitmapData, - size:uint = _size[source] = Math.ceil(FP.distance(0, 0, temp.width, temp.height)); - _frame.width = _frame.height = size; - var width:uint = _frame.width * frameCount, - height:uint = _frame.height; - if (width > _MAX_WIDTH) - { - width = _MAX_WIDTH - (_MAX_WIDTH % _frame.width); - height = Math.ceil(frameCount / (width / _frame.width)) * _frame.height; - } - r = new BitmapData(width, height, true, 0); - var m:Matrix = FP.matrix, - a:Number = 0, - aa:Number = (Math.PI * 2) / -frameCount, - ox:uint = temp.width / 2, - oy:uint = temp.height / 2, - o:uint = _frame.width / 2, - x:uint = 0, - y:uint = 0; - while (y < height) - { - while (x < width) - { - m.identity(); - m.translate(-ox, -oy); - m.rotate(a); - m.translate(o + x, o + y); - r.draw(temp, m, null, null, null, smooth); - x += _frame.width; - a += aa; - } - x = 0; - y += _frame.height; - } - } - _source = r; - _width = r.width; - _frameCount = frameCount; - super(_source, _frame); - } - - /** @private Renders the PreRotated graphic. */ - override public function render(target:BitmapData, point:Point, camera:Point):void - { - frameAngle %= 360; - if (frameAngle < 0) frameAngle += 360; - _current = uint(_frameCount * (frameAngle / 360)); - if (_last != _current) - { - _last = _current; - _frame.x = _frame.width * _last; - _frame.y = uint(_frame.x / _width) * _frame.height; - _frame.x %= _width; - updateBuffer(); - } - super.render(target, point, camera); - } - - // Rotation information. - /** @private */ private var _width:uint; - /** @private */ private var _frame:Rectangle; - /** @private */ private var _frameCount:uint; - /** @private */ private var _last:int = -1; - /** @private */ private var _current:int = -1; - - // Global information. - /** @private */ private static var _rotated:Dictionary = new Dictionary; - /** @private */ private static var _size:Dictionary = new Dictionary; - /** @private */ private static const _MAX_WIDTH:uint = 4000; - /** @private */ private static const _MAX_HEIGHT:uint = 4000; - } -} \ No newline at end of file diff --git a/src/net/flashpunk/graphics/Spritemap.as b/src/net/flashpunk/graphics/Spritemap.as deleted file mode 100644 index 2fd2d3c..0000000 --- a/src/net/flashpunk/graphics/Spritemap.as +++ /dev/null @@ -1,252 +0,0 @@ -package net.flashpunk.graphics -{ - import flash.display.BitmapData; - import flash.display.BlendMode; - import flash.display.SpreadMethod; - import flash.geom.Point; - import flash.geom.Rectangle; - import net.flashpunk.FP; - - /** - * Performance-optimized animated Image. Can have multiple animations, - * which draw frames from the provided source image to the screen. - */ - public class Spritemap extends Image - { - /** - * If the animation has stopped. - */ - public var complete:Boolean = true; - - /** - * Optional callback function for animation end. - */ - public var callback:Function; - - /** - * Animation speed factor, alter this to speed up/slow down all animations. - */ - public var rate:Number = 1; - - /** - * Constructor. - * @param source Source image. - * @param frameWidth Frame width. - * @param frameHeight Frame height. - * @param callback Optional callback function for animation end. - */ - public function Spritemap(source:*, frameWidth:uint = 0, frameHeight:uint = 0, callback:Function = null) - { - _rect = new Rectangle(0, 0, frameWidth, frameHeight); - super(source, _rect); - if (!frameWidth) _rect.width = this.source.width; - if (!frameHeight) _rect.height = this.source.height; - _width = this.source.width; - _height = this.source.height; - _columns = _width / _rect.width; - _rows = _height / _rect.height; - _frameCount = _columns * _rows; - this.callback = callback; - updateBuffer(); - active = true; - } - - /** - * Updates the spritemap's buffer. - */ - override public function updateBuffer(clearBefore:Boolean = false):void - { - // get position of the current frame - _rect.x = _rect.width * _frame; - _rect.y = uint(_rect.x / _width) * _rect.height; - _rect.x %= _width; - if (_flipped) _rect.x = (_width - _rect.width) - _rect.x; - - // update the buffer - super.updateBuffer(clearBefore); - } - - /** @private Updates the animation. */ - override public function update():void - { - if (_anim && !complete) - { - _timer += (FP.fixed ? _anim._frameRate : _anim._frameRate * FP.elapsed) * rate; - if (_timer >= 1) - { - while (_timer >= 1) - { - _timer --; - _index ++; - if (_index == _anim._frameCount) - { - if (_anim._loop) - { - _index = 0; - if (callback != null) callback(); - } - else - { - _index = _anim._frameCount - 1; - complete = true; - if (callback != null) callback(); - break; - } - } - } - if (_anim) _frame = uint(_anim._frames[_index]); - updateBuffer(); - } - } - } - - /** - * Add an Animation. - * @param name Name of the animation. - * @param frames Array of frame indices to animate through. - * @param frameRate Animation speed. - * @param loop If the animation should loop. - * @return A new Anim object for the animation. - */ - public function add(name:String, frames:Array, frameRate:Number = 0, loop:Boolean = true):Anim - { - if (_anims[name]) throw new Error("Cannot have multiple animations with the same name"); - (_anims[name] = new Anim(name, frames, frameRate, loop))._parent = this; - return _anims[name]; - } - - /** - * Plays an animation. - * @param name Name of the animation to play. - * @param reset If the animation should force-restart if it is already playing. - * @return Anim object representing the played animation. - */ - public function play(name:String = "", reset:Boolean = false):Anim - { - if (!reset && _anim && _anim._name == name) return _anim; - _anim = _anims[name]; - if (!_anim) - { - _frame = _index = 0; - complete = true; - updateBuffer(); - return null; - } - _index = 0; - _timer = 0; - _frame = uint(_anim._frames[0]); - complete = false; - updateBuffer(); - return _anim; - } - - /** - * Gets the frame index based on the column and row of the source image. - * @param column Frame column. - * @param row Frame row. - * @return Frame index. - */ - public function getFrame(column:uint = 0, row:uint = 0):uint - { - return (row % _rows) * _columns + (column % _columns); - } - - /** - * Sets the current display frame based on the column and row of the source image. - * When you set the frame, any animations playing will be stopped to force the frame. - * @param column Frame column. - * @param row Frame row. - */ - public function setFrame(column:uint = 0, row:uint = 0):void - { - _anim = null; - var frame:uint = (row % _rows) * _columns + (column % _columns); - if (_frame == frame) return; - _frame = frame; - updateBuffer(); - } - - /** - * Assigns the Spritemap to a random frame. - */ - public function randFrame():void - { - frame = FP.rand(_frameCount); - } - - /** - * Sets the frame to the frame index of an animation. - * @param name Animation to draw the frame frame. - * @param index Index of the frame of the animation to set to. - */ - public function setAnimFrame(name:String, index:int):void - { - var frames:Array = _anims[name]._frames; - index %= frames.length; - if (index < 0) index += frames.length; - frame = frames[index]; - } - - /** - * Sets the current frame index. When you set this, any - * animations playing will be stopped to force the frame. - */ - public function get frame():int { return _frame; } - public function set frame(value:int):void - { - _anim = null; - value %= _frameCount; - if (value < 0) value = _frameCount + value; - if (_frame == value) return; - _frame = value; - updateBuffer(); - } - - /** - * Current index of the playing animation. - */ - public function get index():uint { return _anim ? _index : 0; } - public function set index(value:uint):void - { - if (!_anim) return; - value %= _anim._frameCount; - if (_index == value) return; - _index = value; - _frame = uint(_anim._frames[_index]); - updateBuffer(); - } - - /** - * The amount of frames in the Spritemap. - */ - public function get frameCount():uint { return _frameCount; } - - /** - * Columns in the Spritemap. - */ - public function get columns():uint { return _columns; } - - /** - * Rows in the Spritemap. - */ - public function get rows():uint { return _rows; } - - /** - * The currently playing animation. - */ - public function get currentAnim():String { return _anim ? _anim._name : ""; } - - // Spritemap information. - /** @private */ protected var _rect:Rectangle; - /** @private */ protected var _width:uint; - /** @private */ protected var _height:uint; - /** @private */ private var _columns:uint; - /** @private */ private var _rows:uint; - /** @private */ private var _frameCount:uint; - /** @private */ private var _anims:Object = { }; - /** @private */ private var _anim:Anim; - /** @private */ private var _index:uint; - /** @private */ protected var _frame:uint; - /** @private */ private var _timer:Number = 0; - } -} \ No newline at end of file diff --git a/src/net/flashpunk/graphics/Stamp.as b/src/net/flashpunk/graphics/Stamp.as deleted file mode 100644 index a20aa43..0000000 --- a/src/net/flashpunk/graphics/Stamp.as +++ /dev/null @@ -1,56 +0,0 @@ -package net.flashpunk.graphics -{ - import flash.display.BitmapData; - import flash.display.DisplayObject; - import flash.geom.Point; - import flash.geom.Rectangle; - import net.flashpunk.*; - - /** - * A simple non-transformed, non-animated graphic. - */ - public class Stamp extends Graphic - { - /** - * Constructor. - * @param source Source image. - * @param x X offset. - * @param y Y offset. - */ - public function Stamp(source:*, x:int = 0, y:int = 0) - { - // set the origin - this.x = x; - this.y = y; - - // set the graphic - if (!source) return; - if (source is Class) _source = FP.getBitmap(source); - else if (source is BitmapData) _source = source; - if (_source) _sourceRect = _source.rect; - } - - /** @private Renders the Graphic. */ - override public function render(target:BitmapData, point:Point, camera:Point):void - { - if (!_source) return; - _point.x = point.x + x - camera.x * scrollX; - _point.y = point.y + y - camera.y * scrollY; - target.copyPixels(_source, _sourceRect, _point, null, null, true); - } - - /** - * Source BitmapData image. - */ - public function get source():BitmapData { return _source; } - public function set source(value:BitmapData):void - { - _source = value; - if (_source) _sourceRect = _source.rect; - } - - // Stamp information. - /** @private */ private var _source:BitmapData; - /** @private */ private var _sourceRect:Rectangle; - } -} \ No newline at end of file diff --git a/src/net/flashpunk/graphics/Text.as b/src/net/flashpunk/graphics/Text.as deleted file mode 100644 index 4e3160a..0000000 --- a/src/net/flashpunk/graphics/Text.as +++ /dev/null @@ -1,126 +0,0 @@ -package net.flashpunk.graphics -{ - import flash.display.BitmapData; - import flash.geom.Point; - import flash.geom.Rectangle; - import flash.text.TextField; - import flash.text.TextFormat; - import flash.text.TextLineMetrics; - import net.flashpunk.FP; - import net.flashpunk.Graphic; - - /** - * Used for drawing text using embedded fonts. - */ - public class Text extends Image - { - /** - * The font to assign to new Text objects. - */ - public static var font:String = "default"; - - /** - * The font size to assign to new Text objects. - */ - public static var size:uint = 16; - - /** - * Constructor. - * @param text Text to display. - * @param x X offset. - * @param y Y offset. - * @param width Image width (leave as 0 to size to the starting text string). - * @param height Image height (leave as 0 to size to the starting text string). - */ - public function Text(text:String, x:Number = 0, y:Number = 0, width:uint = 0, height:uint = 0) - { - _field.embedFonts = true; - _field.defaultTextFormat = _form = new TextFormat(Text.font, Text.size, 0xFFFFFF); - _field.text = _text = text; - if (!width) width = _field.textWidth + 4; - if (!height) height = _field.textHeight + 4; - _source = new BitmapData(width, height, true, 0); - super(_source); - updateBuffer(); - this.x = x; - this.y = y; - } - - /** @private Updates the drawing buffer. */ - override public function updateBuffer(clearBefore:Boolean = false):void - { - _field.setTextFormat(_form); - _field.width = _width = _field.textWidth + 4; - _field.height = _height = _field.textHeight + 4; - _source.fillRect(_sourceRect, 0); - _source.draw(_field); - super.updateBuffer(clearBefore); - } - - /** @private Centers the Text's originX/Y to its center. */ - override public function centerOrigin():void - { - originX = _width / 2; - originY = _height / 2; - } - - /** - * Text string. - */ - public function get text():String { return _text; } - public function set text(value:String):void - { - if (_text == value) return; - _field.text = _text = value; - updateBuffer(); - } - - /** - * Font family. - */ - public function get font():String { return _font; } - public function set font(value:String):void - { - if (_font == value) return; - _form.font = _font = value; - updateBuffer(); - } - - /** - * Font size. - */ - public function get size():uint { return _size; } - public function set size(value:uint):void - { - if (_size == value) return; - _form.size = _size = value; - updateBuffer(); - } - - /** - * Width of the text image. - */ - override public function get width():uint { return _width; } - - /** - * Height of the text image. - */ - override public function get height():uint { return _height; } - - // Text information. - /** @private */ private var _field:TextField = new TextField; - /** @private */ private var _width:uint; - /** @private */ private var _height:uint; - /** @private */ private var _form:TextFormat; - /** @private */ private var _text:String; - /** @private */ private var _font:String; - /** @private */ private var _size:uint; - - // Default font family. - // Use this option when compiling with Flex SDK 3 or lower - // [Embed(source = '04B_03__.TTF', fontFamily = 'default')] - // Use this option when compiling with Flex SDK 4 - [Embed(source = '04B_03__.TTF', embedAsCFF="false", fontFamily = 'default')] - /** @private */ private static var _FONT_DEFAULT:Class; - } -} \ No newline at end of file diff --git a/src/net/flashpunk/graphics/TiledImage.as b/src/net/flashpunk/graphics/TiledImage.as deleted file mode 100644 index 0560ce3..0000000 --- a/src/net/flashpunk/graphics/TiledImage.as +++ /dev/null @@ -1,102 +0,0 @@ -package net.flashpunk.graphics -{ - import flash.display.BitmapData; - import flash.display.Graphics; - import flash.geom.Rectangle; - import net.flashpunk.FP; - - /** - * Special Image object that can display blocks of tiles. - */ - public class TiledImage extends Image - { - /** - * Constructs the TiledImage. - * @param texture Source texture. - * @param width The width of the image (the texture will be drawn to fill this area). - * @param height The height of the image (the texture will be drawn to fill this area). - * @param clipRect An optional area of the source texture to use (eg. a tile from a tileset). - */ - public function TiledImage(texture:*, width:uint = 0, height:uint = 0, clipRect:Rectangle = null) - { - _width = width; - _height = height; - super(texture, clipRect); - } - - /** @private Creates the buffer. */ - override protected function createBuffer():void - { - if (!_width) _width = _sourceRect.width; - if (!_height) _height = _sourceRect.height; - _buffer = new BitmapData(_width, _height, true, 0); - _bufferRect = _buffer.rect; - } - - /** @private Updates the buffer. */ - override public function updateBuffer(clearBefore:Boolean = false):void - { - if (!_source) return; - if (!_texture) - { - _texture = new BitmapData(_sourceRect.width, _sourceRect.height, true, 0); - _texture.copyPixels(_source, _sourceRect, FP.zero); - } - _buffer.fillRect(_bufferRect, 0); - _graphics.clear(); - if (_offsetX != 0 || _offsetY != 0) - { - FP.matrix.identity(); - FP.matrix.tx = Math.round(_offsetX); - FP.matrix.ty = Math.round(_offsetY); - _graphics.beginBitmapFill(_texture, FP.matrix); - } - else _graphics.beginBitmapFill(_texture); - _graphics.drawRect(0, 0, _width, _height); - _buffer.draw(FP.sprite, null, _tint); - } - - /** - * The x-offset of the texture. - */ - public function get offsetX():Number { return _offsetX; } - public function set offsetX(value:Number):void - { - if (_offsetX == value) return; - _offsetX = value; - updateBuffer(); - } - - /** - * The y-offset of the texture. - */ - public function get offsetY():Number { return _offsetY; } - public function set offsetY(value:Number):void - { - if (_offsetY == value) return; - _offsetY = value; - updateBuffer(); - } - - /** - * Sets the texture offset. - * @param x The x-offset. - * @param y The y-offset. - */ - public function setOffset(x:Number, y:Number):void - { - if (_offsetX == x && _offsetY == y) return; - _offsetX = x; - _offsetY = y; - updateBuffer(); - } - - // Drawing information. - /** @private */ private var _graphics:Graphics = FP.sprite.graphics; - /** @private */ private var _texture:BitmapData; - /** @private */ private var _width:uint; - /** @private */ private var _height:uint; - /** @private */ private var _offsetX:Number = 0; - /** @private */ private var _offsetY:Number = 0; - } -} \ No newline at end of file diff --git a/src/net/flashpunk/graphics/TiledSpritemap.as b/src/net/flashpunk/graphics/TiledSpritemap.as deleted file mode 100644 index 1950e7f..0000000 --- a/src/net/flashpunk/graphics/TiledSpritemap.as +++ /dev/null @@ -1,109 +0,0 @@ -package net.flashpunk.graphics -{ - import flash.display.BitmapData; - import flash.display.Graphics; - import net.flashpunk.FP; - - /** - * Special Spritemap object that can display blocks of animated sprites. - */ - public class TiledSpritemap extends Spritemap - { - /** - * Constructs the tiled spritemap. - * @param source Source image. - * @param frameWidth Frame width. - * @param frameHeight Frame height. - * @param width Width of the block to render. - * @param height Height of the block to render. - * @param callback Optional callback function for animation end. - */ - public function TiledSpritemap(source:*, frameWidth:uint = 0, frameHeight:uint = 0, width:uint = 0, height:uint = 0, callback:Function = null) - { - _imageWidth = width; - _imageHeight = height; - super(source, frameWidth, frameHeight, callback); - } - - /** @private Creates the buffer. */ - override protected function createBuffer():void - { - if (!_imageWidth) _imageWidth = _sourceRect.width; - if (!_imageHeight) _imageHeight = _sourceRect.height; - _buffer = new BitmapData(_imageWidth, _imageHeight, true, 0); - _bufferRect = _buffer.rect; - } - - /** @private Updates the buffer. */ - override public function updateBuffer(clearBefore:Boolean = false):void - { - // get position of the current frame - _rect.x = _rect.width * _frame; - _rect.y = uint(_rect.x / _width) * _rect.height; - _rect.x %= _width; - if (_flipped) _rect.x = (_width - _rect.width) - _rect.x; - - // render it repeated to the buffer - var xx:int = int(_offsetX) % _imageWidth, - yy:int = int(_offsetY) % _imageHeight; - if (xx >= 0) xx -= _imageWidth; - if (yy >= 0) yy -= _imageHeight; - FP.point.x = xx; - FP.point.y = yy; - while (FP.point.y < _imageHeight) - { - while (FP.point.x < _imageWidth) - { - _buffer.copyPixels(_source, _sourceRect, FP.point); - FP.point.x += _sourceRect.width; - } - FP.point.x = xx; - FP.point.y += _sourceRect.height; - } - - // tint the buffer - if (_tint) _buffer.colorTransform(_bufferRect, _tint); - } - - /** - * The x-offset of the texture. - */ - public function get offsetX():Number { return _offsetX; } - public function set offsetX(value:Number):void - { - if (_offsetX == value) return; - _offsetX = value; - updateBuffer(); - } - - /** - * The y-offset of the texture. - */ - public function get offsetY():Number { return _offsetY; } - public function set offsetY(value:Number):void - { - if (_offsetY == value) return; - _offsetY = value; - updateBuffer(); - } - - /** - * Sets the texture offset. - * @param x The x-offset. - * @param y The y-offset. - */ - public function setOffset(x:Number, y:Number):void - { - if (_offsetX == x && _offsetY == y) return; - _offsetX = x; - _offsetY = y; - updateBuffer(); - } - - /** @private */ private var _graphics:Graphics = FP.sprite.graphics; - /** @private */ private var _imageWidth:uint; - /** @private */ private var _imageHeight:uint; - /** @private */ private var _offsetX:Number = 0; - /** @private */ private var _offsetY:Number = 0; - } -} \ No newline at end of file diff --git a/src/net/flashpunk/graphics/Tilemap.as b/src/net/flashpunk/graphics/Tilemap.as deleted file mode 100644 index 2cac146..0000000 --- a/src/net/flashpunk/graphics/Tilemap.as +++ /dev/null @@ -1,360 +0,0 @@ -package net.flashpunk.graphics -{ - import flash.display.BitmapData; - import flash.geom.Point; - import flash.geom.Rectangle; - import net.flashpunk.Graphic; - import net.flashpunk.FP; - import net.flashpunk.masks.Grid; - - /** - * A canvas to which Tiles can be drawn for fast multiple tile rendering. - */ - public class Tilemap extends Canvas - { - /** - * If x/y positions should be used instead of columns/rows. - */ - public var usePositions:Boolean; - - /** - * Constructor. - * @param tileset The source tileset image. - * @param width Width of the tilemap, in pixels. - * @param height Height of the tilemap, in pixels. - * @param tileWidth Tile width. - * @param tileHeight Tile height. - */ - public function Tilemap(tileset:*, width:uint, height:uint, tileWidth:uint, tileHeight:uint) - { - // set some tilemap information - _width = width - (width % tileWidth); - _height = height - (height % tileHeight); - _columns = _width / tileWidth; - _rows = _height / tileHeight; - _map = new BitmapData(_columns, _rows, false, 0); - _temp = _map.clone(); - _tile = new Rectangle(0, 0, tileWidth, tileHeight); - - // create the canvas - _maxWidth -= _maxWidth % tileWidth; - _maxHeight -= _maxHeight % tileHeight; - super(_width, _height); - - // load the tileset graphic - if (tileset is Class) _set = FP.getBitmap(tileset); - else if (tileset is BitmapData) _set = tileset; - if (!_set) throw new Error("Invalid tileset graphic provided."); - _setColumns = uint(_set.width / tileWidth); - _setRows = uint(_set.height / tileHeight); - _setCount = _setColumns * _setRows; - } - - /** - * Sets the index of the tile at the position. - * @param column Tile column. - * @param row Tile row. - * @param index Tile index. - */ - public function setTile(column:uint, row:uint, index:uint = 0):void - { - if (usePositions) - { - column /= _tile.width; - row /= _tile.height; - } - index %= _setCount; - column %= _columns; - row %= _rows; - _tile.x = (index % _setColumns) * _tile.width; - _tile.y = uint(index / _setColumns) * _tile.height; - _map.setPixel(column, row, index); - draw(column * _tile.width, row * _tile.height, _set, _tile); - } - - /** - * Clears the tile at the position. - * @param column Tile column. - * @param row Tile row. - */ - public function clearTile(column:uint, row:uint):void - { - if (usePositions) - { - column /= _tile.width; - row /= _tile.height; - } - column %= _columns; - row %= _rows; - _tile.x = column * _tile.width; - _tile.y = row * _tile.height; - fill(_tile, 0, 0); - } - - /** - * Gets the tile index at the position. - * @param column Tile column. - * @param row Tile row. - * @return The tile index. - */ - public function getTile(column:uint, row:uint):uint - { - if (usePositions) - { - column /= _tile.width; - row /= _tile.height; - } - return _map.getPixel(column % _columns, row % _rows); - } - - /** - * Sets a rectangular region of tiles to the index. - * @param column First tile column. - * @param row First tile row. - * @param width Width in tiles. - * @param height Height in tiles. - * @param index Tile index. - */ - public function setRect(column:uint, row:uint, width:uint = 1, height:uint = 1, index:uint = 0):void - { - if (usePositions) - { - column /= _tile.width; - row /= _tile.height; - width /= _tile.width; - height /= _tile.height; - } - column %= _columns; - row %= _rows; - var c:uint = column, - r:uint = column + width, - b:uint = row + height, - u:Boolean = usePositions; - usePositions = false; - while (row < b) - { - while (column < r) - { - setTile(column, row, index); - column ++; - } - column = c; - row ++; - } - usePositions = u; - } - - /** - * Clears the rectangular region of tiles. - * @param column First tile column. - * @param row First tile row. - * @param width Width in tiles. - * @param height Height in tiles. - */ - public function clearRect(column:uint, row:uint, width:uint = 1, height:uint = 1):void - { - if (usePositions) - { - column /= _tile.width; - row /= _tile.height; - width /= _tile.width; - height /= _tile.height; - } - column %= _columns; - row %= _rows; - var c:uint = column, - r:uint = column + width, - b:uint = row + height, - u:Boolean = usePositions; - usePositions = false; - while (row < b) - { - while (column < r) - { - clearTile(column, row); - column ++; - } - column = c; - row ++; - } - usePositions = u; - } - - /** - * Loads the Tilemap tile index data from a string. - * @param str The string data, which is a set of tile values separated by the columnSep and rowSep strings. - * @param columnSep The string that separates each tile value on a row, default is ",". - * @param rowSep The string that separates each row of tiles, default is "\n". - */ - public function loadFromString(str:String, columnSep:String = ",", rowSep:String = "\n"):void - { - var row:Array = str.split(rowSep), - rows:int = row.length, - col:Array, cols:int, x:int, y:int; - for (y = 0; y < rows; y ++) - { - if (row[y] == '') continue; - col = row[y].split(columnSep), - cols = col.length; - for (x = 0; x < cols; x ++) - { - if (col[x] == '') continue; - setTile(x, y, uint(col[x])); - } - } - } - - /** - * Saves the Tilemap tile index data to a string. - * @param columnSep The string that separates each tile value on a row, default is ",". - * @param rowSep The string that separates each row of tiles, default is "\n". - */ - public function saveToString(columnSep:String = ",", rowSep:String = "\n"): String - { - var s:String = '', - x:int, y:int; - for (y = 0; y < _rows; y ++) - { - for (x = 0; x < _columns; x ++) - { - s += String(getTile(x, y)); - if (x != _columns - 1) s += columnSep; - } - if (y != _rows - 1) s += rowSep; - } - return s; - } - - /** - * Gets the index of a tile, based on its column and row in the tileset. - * @param tilesColumn Tileset column. - * @param tilesRow Tileset row. - * @return Index of the tile. - */ - public function getIndex(tilesColumn:uint, tilesRow:uint):uint - { - return (tilesRow % _setRows) * _setColumns + (tilesColumn % _setColumns); - } - - /** - * Shifts all the tiles in the tilemap. - * @param columns Horizontal shift. - * @param rows Vertical shift. - * @param wrap If tiles shifted off the canvas should wrap around to the other side. - */ - public function shiftTiles(columns:int, rows:int, wrap:Boolean = false):void - { - if (usePositions) - { - columns /= _tile.width; - rows /= _tile.height; - } - - if (!wrap) _temp.fillRect(_temp.rect, 0); - - if (columns != 0) - { - shift(columns * _tile.width, 0); - if (wrap) _temp.copyPixels(_map, _map.rect, FP.zero); - _map.scroll(columns, 0); - _point.y = 0; - _point.x = columns > 0 ? columns - _columns : columns + _columns; - _map.copyPixels(_temp, _temp.rect, _point); - - _rect.x = columns > 0 ? 0 : _columns + columns; - _rect.y = 0; - _rect.width = Math.abs(columns); - _rect.height = _rows; - updateRect(_rect, !wrap); - } - - if (rows != 0) - { - shift(0, rows * _tile.height); - if (wrap) _temp.copyPixels(_map, _map.rect, FP.zero); - _map.scroll(0, rows); - _point.x = 0; - _point.y = rows > 0 ? rows - _rows : rows + _rows; - _map.copyPixels(_temp, _temp.rect, _point); - - _rect.x = 0; - _rect.y = rows > 0 ? 0 : _rows + rows; - _rect.width = _columns; - _rect.height = Math.abs(rows); - updateRect(_rect, !wrap); - } - } - - /** @private Used by shiftTiles to update a rectangle of tiles from the tilemap. */ - private function updateRect(rect:Rectangle, clear:Boolean):void - { - var x:int = rect.x, - y:int = rect.y, - w:int = x + rect.width, - h:int = y + rect.height, - u:Boolean = usePositions; - usePositions = false; - if (clear) - { - while (y < h) - { - while (x < w) clearTile(x ++, y); - x = rect.x; - y ++; - } - } - else - { - while (y < h) - { - while (x < w) updateTile(x ++, y); - x = rect.x; - y ++; - } - } - usePositions = u; - } - - /** @private Used by shiftTiles to update a tile from the tilemap. */ - private function updateTile(column:uint, row:uint):void - { - setTile(column, row, _map.getPixel(column % _columns, row % _rows)); - } - - /** - * The tile width. - */ - public function get tileWidth():uint { return _tile.width; } - - /** - * The tile height. - */ - public function get tileHeight():uint { return _tile.height; } - - /** - * How many columns the tilemap has. - */ - public function get columns():uint { return _columns; } - - /** - * How many rows the tilemap has. - */ - public function get rows():uint { return _rows; } - - // Tilemap information. - /** @private */ private var _map:BitmapData; - /** @private */ private var _temp:BitmapData; - /** @private */ private var _columns:uint; - /** @private */ private var _rows:uint; - - // Tileset information. - /** @private */ private var _set:BitmapData; - /** @private */ private var _setColumns:uint; - /** @private */ private var _setRows:uint; - /** @private */ private var _setCount:uint; - /** @private */ private var _tile:Rectangle; - - // Global objects. - /** @private */ private var _rect:Rectangle = FP.rect; - } -} \ No newline at end of file diff --git a/src/net/flashpunk/masks/Grid.as b/src/net/flashpunk/masks/Grid.as deleted file mode 100644 index 71a5069..0000000 --- a/src/net/flashpunk/masks/Grid.as +++ /dev/null @@ -1,268 +0,0 @@ -package net.flashpunk.masks -{ - import flash.display.Bitmap; - import flash.display.BitmapData; - import flash.geom.Point; - import flash.geom.Rectangle; - import net.flashpunk.*; - - /** - * Uses a hash grid to determine collision, faster than - * using hundreds of Entities for tiled levels, etc. - */ - public class Grid extends Hitbox - { - /** - * If x/y positions should be used instead of columns/rows. - */ - public var usePositions:Boolean; - - /** - * Constructor. - * @param width Width of the grid, in pixels. - * @param height Height of the grid, in pixels. - * @param tileWidth Width of a grid tile, in pixels. - * @param tileHeight Height of a grid tile, in pixels. - * @param x X offset of the grid. - * @param y Y offset of the grid. - */ - public function Grid(width:uint, height:uint, tileWidth:uint, tileHeight:uint, x:int = 0, y:int = 0) - { - // check for illegal grid size - if (!width || !height || !tileWidth || !tileHeight) throw new Error("Illegal Grid, sizes cannot be 0."); - - // set grid properties - _columns = width / tileWidth; - _rows = height / tileHeight; - _data = new BitmapData(_columns, _rows, true, 0); - _tile = new Rectangle(0, 0, tileWidth, tileHeight); - _x = x; - _y = y; - _width = width; - _height = height; - - // set callback functions - _check[Mask] = collideMask; - _check[Hitbox] = collideHitbox; - _check[Pixelmask] = collidePixelmask; - } - - /** - * Sets the value of the tile. - * @param column Tile column. - * @param row Tile row. - * @param solid If the tile should be solid. - */ - public function setTile(column:uint = 0, row:uint = 0, solid:Boolean = true):void - { - if (usePositions) - { - column /= _tile.width; - row /= _tile.height; - } - _data.setPixel32(column, row, solid ? 0xFFFFFFFF : 0); - } - - /** - * Makes the tile non-solid. - * @param column Tile column. - * @param row Tile row. - */ - public function clearTile(column:uint = 0, row:uint = 0):void - { - setTile(column, row, false); - } - - /** - * Gets the value of a tile. - * @param column Tile column. - * @param row Tile row. - * @return tile value. - */ - public function getTile(column:uint = 0, row:uint = 0):Boolean - { - if (usePositions) - { - column /= _tile.width; - row /= _tile.height; - } - return _data.getPixel32(column, row) > 0; - } - - /** - * Sets the value of a rectangle region of tiles. - * @param column First column. - * @param row First row. - * @param width Columns to fill. - * @param height Rows to fill. - * @param fill Value to fill. - */ - public function setRect(column:uint = 0, row:uint = 0, width:int = 1, height:int = 1, solid:Boolean = true):void - { - if (usePositions) - { - column /= _tile.width; - row /= _tile.height; - width /= _tile.width; - height /= _tile.height; - } - _rect.x = column; - _rect.y = row; - _rect.width = width; - _rect.height = height; - _data.fillRect(_rect, solid ? 0xFFFFFFFF : 0); - } - - /** - * Makes the rectangular region of tiles non-solid. - * @param column First column. - * @param row First row. - * @param width Columns to fill. - * @param height Rows to fill. - */ - public function clearRect(column:uint = 0, row:uint = 0, width:int = 1, height:int = 1):void - { - setRect(column, row, width, height, false); - } - - /** - * Loads the grid data from a string. - * @param str The string data, which is a set of tile values (0 or 1) separated by the columnSep and rowSep strings. - * @param columnSep The string that separates each tile value on a row, default is ",". - * @param rowSep The string that separates each row of tiles, default is "\n". - */ - public function loadFromString(str:String, columnSep:String = ",", rowSep:String = "\n"):void - { - var row:Array = str.split(rowSep), - rows:int = row.length, - col:Array, cols:int, x:int, y:int; - for (y = 0; y < rows; y ++) - { - if (row[y] == '') continue; - col = row[y].split(columnSep), - cols = col.length; - for (x = 0; x < cols; x ++) - { - if (col[x] == '') continue; - setTile(x, y, uint(col[x]) > 0); - } - } - } - - /** - * Saves the grid data to a string. - * @param columnSep The string that separates each tile value on a row, default is ",". - * @param rowSep The string that separates each row of tiles, default is "\n". - */ - public function saveToString(columnSep:String = ",", rowSep:String = "\n"): String - { - var s:String = '', - x:int, y:int; - for (y = 0; y < _rows; y ++) - { - for (x = 0; x < _columns; x ++) - { - s += String(getTile(x, y)); - if (x != _columns - 1) s += columnSep; - } - if (y != _rows - 1) s += rowSep; - } - return s; - } - - /** - * The tile width. - */ - public function get tileWidth():uint { return _tile.width; } - - /** - * The tile height. - */ - public function get tileHeight():uint { return _tile.height; } - - /** - * How many columns the grid has - */ - public function get columns():uint { return _columns; } - - /** - * How many rows the grid has. - */ - public function get rows():uint { return _rows; } - - /** - * The grid data. - */ - public function get data():BitmapData { return _data; } - - /** @private Collides against an Entity. */ - private function collideMask(other:Mask):Boolean - { - _rect.x = other.parent.x - other.parent.originX - parent.x + parent.originX; - _rect.y = other.parent.y - other.parent.originY - parent.y + parent.originY; - _point.x = int((_rect.x + other.parent.width - 1) / _tile.width) + 1; - _point.y = int((_rect.y + other.parent.height -1) / _tile.height) + 1; - _rect.x = int(_rect.x / _tile.width); - _rect.y = int(_rect.y / _tile.height); - _rect.width = _point.x - _rect.x; - _rect.height = _point.y - _rect.y; - return _data.hitTest(FP.zero, 1, _rect); - } - - /** @private Collides against a Hitbox. */ - private function collideHitbox(other:Hitbox):Boolean - { - _rect.x = other.parent.x + other._x - parent.x - _x; - _rect.y = other.parent.y + other._y - parent.y - _y; - _point.x = int((_rect.x + other._width - 1) / _tile.width) + 1; - _point.y = int((_rect.y + other._height -1) / _tile.height) + 1; - _rect.x = int(_rect.x / _tile.width); - _rect.y = int(_rect.y / _tile.height); - _rect.width = _point.x - _rect.x; - _rect.height = _point.y - _rect.y; - return _data.hitTest(FP.zero, 1, _rect); - } - - /** @private Collides against a Pixelmask. */ - private function collidePixelmask(other:Pixelmask):Boolean - { - var x1:int = other.parent.x + other._x - parent.x - _x, - y1:int = other.parent.y + other._y - parent.y - _y, - x2:int = ((x1 + other._width - 1) / _tile.width), - y2:int = ((y1 + other._height - 1) / _tile.height); - _point.x = x1; - _point.y = y1; - x1 /= _tile.width; - y1 /= _tile.height; - _tile.x = x1 * _tile.width; - _tile.y = y1 * _tile.height; - var xx:int = x1; - while (y1 <= y2) - { - while (x1 <= x2) - { - if (_data.getPixel32(x1, y1)) - { - if (other._data.hitTest(_point, 1, _tile)) return true; - } - x1 ++; - _tile.x += _tile.width; - } - x1 = xx; - y1 ++; - _tile.x = x1 * _tile.width; - _tile.y += _tile.height; - } - return false; - } - - // Grid information. - /** @private */ private var _data:BitmapData; - /** @private */ private var _columns:uint; - /** @private */ private var _rows:uint; - /** @private */ private var _tile:Rectangle; - /** @private */ private var _rect:Rectangle = FP.rect; - /** @private */ private var _point:Point = FP.point; - /** @private */ private var _point2:Point = FP.point2; - } -} \ No newline at end of file diff --git a/src/net/flashpunk/masks/Hitbox.as b/src/net/flashpunk/masks/Hitbox.as deleted file mode 100644 index 41982e6..0000000 --- a/src/net/flashpunk/masks/Hitbox.as +++ /dev/null @@ -1,114 +0,0 @@ -package net.flashpunk.masks -{ - import net.flashpunk.*; - - /** - * Uses parent's hitbox to determine collision. This class is used - * internally by FlashPunk, you don't need to use this class because - * this is the default behaviour of Entities without a Mask object. - */ - public class Hitbox extends Mask - { - /** - * Constructor. - * @param width Width of the hitbox. - * @param height Height of the hitbox. - * @param x X offset of the hitbox. - * @param y Y offset of the hitbox. - */ - public function Hitbox(width:uint = 1, height:uint = 1, x:int = 0, y:int = 0) - { - _width = width; - _height = height; - _x = x; - _y = y; - _check[Mask] = collideMask; - _check[Hitbox] = collideHitbox; - } - - /** @private Collides against an Entity. */ - private function collideMask(other:Mask):Boolean - { - return parent.x + _x + _width > other.parent.x - other.parent.originX - && parent.y + _y + _height > other.parent.y - other.parent.originY - && parent.x + _x < other.parent.x - other.parent.originX + other.parent.width - && parent.y + _y < other.parent.y - other.parent.originY + other.parent.height; - } - - /** @private Collides against a Hitbox. */ - private function collideHitbox(other:Hitbox):Boolean - { - return parent.x + _x + _width > other.parent.x + other._x - && parent.y + _y + _height > other.parent.y + other._y - && parent.x + _x < other.parent.x + other._x + other._width - && parent.y + _y < other.parent.y + other._y + other._height; - } - - /** - * X offset. - */ - public function get x():int { return _x; } - public function set x(value:int):void - { - if (_x == value) return; - _x = value; - if (list) list.update(); - else if (parent) update(); - } - - /** - * Y offset. - */ - public function get y():int { return _y; } - public function set y(value:int):void - { - if (_y == value) return; - _y = value; - if (list) list.update(); - else if (parent) update(); - } - - /** - * Width. - */ - public function get width():int { return _width; } - public function set width(value:int):void - { - if (_width == value) return; - _width = value; - if (list) list.update(); - else if (parent) update(); - } - - /** - * Height. - */ - public function get height():int { return _height; } - public function set height(value:int):void - { - if (_height == value) return; - _height = value; - if (list) list.update(); - else if (parent) update(); - } - - /** @private Updates the parent's bounds for this mask. */ - override protected function update():void - { - // update entity bounds - parent.originX = -_x; - parent.originY = -_y; - parent.width = _width; - parent.height = _height; - - // update parent list - if (list) list.update(); - } - - // Hitbox information. - /** @private */ internal var _width:uint; - /** @private */ internal var _height:uint; - /** @private */ internal var _x:int; - /** @private */ internal var _y:int; - } -} \ No newline at end of file diff --git a/src/net/flashpunk/masks/Masklist.as b/src/net/flashpunk/masks/Masklist.as deleted file mode 100644 index ce38ebe..0000000 --- a/src/net/flashpunk/masks/Masklist.as +++ /dev/null @@ -1,159 +0,0 @@ -package net.flashpunk.masks -{ - import net.flashpunk.*; - import net.flashpunk.masks.Masklist; - - /** - * A Mask that can contain multiple Masks of one or various types. - */ - public class Masklist extends Hitbox - { - /** - * Constructor. - * @param ...mask Masks to add to the list. - */ - public function Masklist(...mask) - { - for each (var m:Mask in mask) add(m); - } - - /** @private Collide against a mask. */ - override public function collide(mask:Mask):Boolean - { - for each (var m:Mask in _masks) - { - if (m.collide(mask)) return true; - } - return false; - } - - /** @private Collide against a Masklist. */ - override protected function collideMasklist(other:Masklist):Boolean - { - for each (var a:Mask in _masks) - { - for each (var b:Mask in other._masks) - { - if (a.collide(b)) return true; - } - } - return true; - } - - /** - * Adds a Mask to the list. - * @param mask The Mask to add. - * @return The added Mask. - */ - public function add(mask:Mask):Mask - { - _masks[_count ++] = mask; - mask.list = this; - update(); - return mask; - } - - /** - * Removes the Mask from the list. - * @param mask The Mask to remove. - * @return The removed Mask. - */ - public function remove(mask:Mask):Mask - { - if (_masks.indexOf(mask) < 0) return mask; - _temp.length = 0; - for each (var m:Mask in _masks) - { - if (m == mask) - { - mask.list = null; - _count --; - update(); - } - else _temp[_temp.length] = m; - } - var temp:Vector. = _masks; - _masks = _temp; - _temp = temp; - return mask; - } - - /** - * Removes the Mask at the index. - * @param index The Mask index. - */ - public function removeAt(index:uint = 0):void - { - _temp.length = 0; - var i:int = _masks.length; - index %= i; - while (i --) - { - if (i == index) - { - _masks[index].list = null; - _count --; - update(); - } - else _temp[_temp.length] = _masks[index]; - } - var temp:Vector. = _masks; - _masks = _temp; - _temp = temp; - } - - /** - * Removes all Masks from the list. - */ - public function removeAll():void - { - for each (var m:Mask in _masks) m.list = null; - _masks.length = _temp.length = _count = 0; - update(); - } - - /** - * Gets a Mask from the list. - * @param index The Mask index. - * @return The Mask at the index. - */ - public function getMask(index:uint = 0):Mask - { - return _masks[index % _masks.length]; - } - - /** @private Updates the parent's bounds for this mask. */ - override protected function update():void - { - // find bounds of the contained masks - var t:int, l:int, r:int, b:int, h:Hitbox, i:int = _count; - while (i --) - { - if ((h = _masks[i] as Hitbox)) - { - if (h._x < l) l = h._x; - if (h._y < t) t = h._y; - if (h._x + h._width > r) r = h._x + h._width; - if (h._y + h._height > b) b = h._y + h._height; - } - } - - // update hitbox bounds - _x = l; - _y = t; - _width = r - l; - _height = b - t; - super.update(); - } - - /** - * Amount of Masks in the list. - */ - public function get count():uint { return _count; } - - // List information. - /** @private */ private var _masks:Vector. = new Vector.; - /** @private */ private var _temp:Vector. = new Vector.; - /** @private */ private var _count:uint; - } -} \ No newline at end of file diff --git a/src/net/flashpunk/masks/Pixelmask.as b/src/net/flashpunk/masks/Pixelmask.as deleted file mode 100644 index a25a851..0000000 --- a/src/net/flashpunk/masks/Pixelmask.as +++ /dev/null @@ -1,97 +0,0 @@ -package net.flashpunk.masks -{ - import flash.display.BitmapData; - import flash.geom.Point; - import flash.geom.Rectangle; - import net.flashpunk.*; - - /** - * A bitmap mask used for pixel-perfect collision. - */ - public class Pixelmask extends Hitbox - { - /** - * Alpha threshold of the bitmap used for collision. - */ - public var threshold:uint = 1; - - /** - * Constructor. - * @param source The image to use as a mask. - * @param x X offset of the mask. - * @param y Y offset of the mask. - */ - public function Pixelmask(source:*, x:int = 0, y:int = 0) - { - // fetch mask data - if (source is BitmapData) _data = source; - if (source is Class) _data = FP.getBitmap(source); - if (!_data) throw new Error("Invalid Pixelmask source image."); - - // set mask properties - _width = data.width; - _height = data.height; - _x = x; - _y = y; - - // set callback functions - _check[Mask] = collideMask; - _check[Pixelmask] = collidePixelmask; - _check[Hitbox] = collideHitbox; - } - - /** @private Collide against an Entity. */ - private function collideMask(other:Mask):Boolean - { - _point.x = parent.x + _x; - _point.y = parent.y + _y; - _rect.x = other.parent.x - other.parent.originX; - _rect.y = other.parent.y - other.parent.originY; - _rect.width = other.parent.width; - _rect.height = other.parent.height; - return _data.hitTest(_point, threshold, _rect); - } - - /** @private Collide against a Hitbox. */ - private function collideHitbox(other:Hitbox):Boolean - { - _point.x = parent.x + _x; - _point.y = parent.y + _y; - _rect.x = other.parent.x + other._x; - _rect.y = other.parent.y + other._y; - _rect.width = other._width; - _rect.height = other._height; - return _data.hitTest(_point, threshold, _rect); - } - - /** @private Collide against a Pixelmask. */ - private function collidePixelmask(other:Pixelmask):Boolean - { - _point.x = parent.x + _x; - _point.y = parent.y + _y; - _point2.x = other.parent.x + other._x; - _point2.y = other.parent.y + other._y; - return _data.hitTest(_point, threshold, other._data, _point2, other.threshold); - } - - /** - * Current BitmapData mask. - */ - public function get data():BitmapData { return _data; } - public function set data(value:BitmapData):void - { - _data = value; - _width = value.width; - _height = value.height; - update(); - } - - // Pixelmask information. - /** @private */ internal var _data:BitmapData; - - // Global objects. - /** @private */ private var _rect:Rectangle = FP.rect; - /** @private */ private var _point:Point = FP.point; - /** @private */ private var _point2:Point = FP.point2; - } -} \ No newline at end of file diff --git a/src/net/flashpunk/tweens/misc/Alarm.as b/src/net/flashpunk/tweens/misc/Alarm.as deleted file mode 100644 index dc7adf2..0000000 --- a/src/net/flashpunk/tweens/misc/Alarm.as +++ /dev/null @@ -1,46 +0,0 @@ -package net.flashpunk.tweens.misc -{ - import net.flashpunk.Tween; - - /** - * A simple alarm, useful for timed events, etc. - */ - public class Alarm extends Tween - { - /** - * Constructor. - * @param duration Duration of the alarm. - * @param complete Optional completion callback. - * @param type Tween type. - */ - public function Alarm(duration:Number, complete:Function = null, type:uint = 0) - { - super(duration, type, complete, null); - } - - /** - * Sets the alarm. - * @param duration Duration of the alarm. - */ - public function reset(duration:Number):void - { - _target = duration; - start(); - } - - /** - * How much time has passed since reset. - */ - public function get elapsed():Number { return _time; } - - /** - * Current alarm duration. - */ - public function get duration():Number { return _target; } - - /** - * Time remaining on the alarm. - */ - public function get remaining():Number { return _target - _time; } - } -} \ No newline at end of file diff --git a/src/net/flashpunk/tweens/misc/AngleTween.as b/src/net/flashpunk/tweens/misc/AngleTween.as deleted file mode 100644 index 11ef91e..0000000 --- a/src/net/flashpunk/tweens/misc/AngleTween.as +++ /dev/null @@ -1,58 +0,0 @@ -package net.flashpunk.tweens.misc -{ - import net.flashpunk.FP; - import net.flashpunk.Tween; - - /** - * Tweens from one angle to another. - */ - public class AngleTween extends Tween - { - /** - * The current value. - */ - public var angle:Number = 0; - - /** - * Constructor. - * @param complete Optional completion callback. - * @param type Tween type. - */ - public function AngleTween(complete:Function = null, type:uint = 0) - { - super(0, type, complete); - } - - /** - * Tweens the value from one angle to another. - * @param fromAngle Start angle. - * @param toAngle End angle. - * @param duration Duration of the tween. - * @param ease Optional easer function. - */ - public function tween(fromAngle:Number, toAngle:Number, duration:Number, ease:Function = null):void - { - _start = angle = fromAngle; - var d:Number = toAngle - angle, - a:Number = Math.abs(d); - if (a > 181) _range = (360 - a) * (d > 0 ? -1 : 1); - else if (a < 179) _range = d; - else _range = FP.choose(180, -180); - _target = duration; - _ease = ease; - start(); - } - - /** @private Updates the Tween. */ - override public function update():void - { - super.update(); - angle = (_start + _range * _t) % 360; - if (angle < 0) angle += 360; - } - - // Tween information. - /** @private */ private var _start:Number; - /** @private */ private var _range:Number; - } -} \ No newline at end of file diff --git a/src/net/flashpunk/tweens/misc/ColorTween.as b/src/net/flashpunk/tweens/misc/ColorTween.as deleted file mode 100644 index 0af04a5..0000000 --- a/src/net/flashpunk/tweens/misc/ColorTween.as +++ /dev/null @@ -1,100 +0,0 @@ -package net.flashpunk.tweens.misc -{ - import net.flashpunk.Tween; - - /** - * Tweens a color's red, green, and blue properties - * independently. Can also tween an alpha value. - */ - public class ColorTween extends Tween - { - /** - * The current color. - */ - public var color:uint; - - /** - * The current alpha. - */ - public var alpha:Number = 1; - - /** - * Constructor. - * @param complete Optional completion callback. - * @param type Tween type. - */ - public function ColorTween(complete:Function = null, type:uint = 0) - { - super(0, type, complete); - } - - /** - * Tweens the color to a new color and an alpha to a new alpha. - * @param duration Duration of the tween. - * @param fromColor Start color. - * @param toColor End color. - * @param fromAlpha Start alpha - * @param toAlpha End alpha. - * @param ease Optional easer function. - */ - public function tween(duration:Number, fromColor:uint, toColor:uint, fromAlpha:Number = 1, toAlpha:Number = 1, ease:Function = null):void - { - fromColor &= 0xFFFFFF; - toColor &= 0xFFFFFF; - color = fromColor; - _r = fromColor >> 16 & 0xFF; - _g = fromColor >> 8 & 0xFF; - _b = fromColor & 0xFF; - _startR = _r / 255; - _startG = _g / 255; - _startB = _b / 255; - _rangeR = ((toColor >> 16 & 0xFF) / 255) - _startR; - _rangeG = ((toColor >> 8 & 0xFF) / 255) - _startG; - _rangeB = ((toColor & 0xFF) / 255) - _startB; - _startA = alpha = fromAlpha; - _rangeA = toAlpha - alpha; - _target = duration; - _ease = ease; - start(); - } - - /** @private Updates the Tween. */ - override public function update():void - { - super.update(); - alpha = _startA + _rangeA * _t; - _r = uint((_startR + _rangeR * _t) * 255); - _g = uint((_startG + _rangeG * _t) * 255); - _b = uint((_startB + _rangeB * _t) * 255); - color = _r << 16 | _g << 8 | _b; - } - - /** - * Red value of the current color, from 0 to 255. - */ - public function get red():uint { return _r; } - - /** - * Green value of the current color, from 0 to 255. - */ - public function get green():uint { return _g; } - - /** - * Blue value of the current color, from 0 to 255. - */ - public function get blue():uint { return _b; } - - // Color information. - /** @private */ private var _r:uint; - /** @private */ private var _g:uint; - /** @private */ private var _b:uint; - /** @private */ private var _startA:Number; - /** @private */ private var _startR:Number; - /** @private */ private var _startG:Number; - /** @private */ private var _startB:Number; - /** @private */ private var _rangeA:Number; - /** @private */ private var _rangeR:Number; - /** @private */ private var _rangeG:Number; - /** @private */ private var _rangeB:Number; - } -} \ No newline at end of file diff --git a/src/net/flashpunk/tweens/misc/MultiVarTween.as b/src/net/flashpunk/tweens/misc/MultiVarTween.as deleted file mode 100644 index 8aa532b..0000000 --- a/src/net/flashpunk/tweens/misc/MultiVarTween.as +++ /dev/null @@ -1,61 +0,0 @@ -package net.flashpunk.tweens.misc -{ - import net.flashpunk.Tween; - - /** - * Tweens multiple numeric public properties of an Object simultaneously. - */ - public class MultiVarTween extends Tween - { - /** - * Constructor. - * @param complete Optional completion callback. - * @param type Tween type. - */ - public function MultiVarTween(complete:Function = null, type:uint = 0) - { - super(0, type, complete); - } - - /** - * Tweens multiple numeric public properties. - * @param object The object containing the properties. - * @param values An object containing key/value pairs of properties and target values. - * @param duration Duration of the tween. - * @param ease Optional easer function. - */ - public function tween(object:Object, values:Object, duration:Number, ease:Function = null):void - { - _object = object; - _vars.length = 0; - _start.length = 0; - _range.length = 0; - _target = duration; - _ease = ease; - for (var p:String in values) - { - if (!object.hasOwnProperty(p)) throw new Error("The Object does not have the property\"" + p + "\", or it is not accessible."); - var a:* = _object[p] as Number; - if (a == null) throw new Error("The property \"" + p + "\" is not numeric."); - _vars.push(p); - _start.push(a); - _range.push(values[p] - a); - } - start(); - } - - /** @private Updates the Tween. */ - override public function update():void - { - super.update(); - var i:int = _vars.length; - while (i --) _object[_vars[i]] = _start[i] + _range[i] * _t; - } - - // Tween information. - /** @private */ private var _object:Object; - /** @private */ private var _vars:Vector. = new Vector.; - /** @private */ private var _start:Vector. = new Vector.; - /** @private */ private var _range:Vector. = new Vector.; - } -} \ No newline at end of file diff --git a/src/net/flashpunk/tweens/misc/NumTween.as b/src/net/flashpunk/tweens/misc/NumTween.as deleted file mode 100644 index 88a21d1..0000000 --- a/src/net/flashpunk/tweens/misc/NumTween.as +++ /dev/null @@ -1,52 +0,0 @@ -package net.flashpunk.tweens.misc -{ - import net.flashpunk.Tween; - - /** - * Tweens a numeric value. - */ - public class NumTween extends Tween - { - /** - * The current value. - */ - public var value:Number = 0; - - /** - * Constructor. - * @param complete Optional completion callback. - * @param type Tween type. - */ - public function NumTween(complete:Function = null, type:uint = 0) - { - super(0, type, complete); - } - - /** - * Tweens the value from one value to another. - * @param fromValue Start value. - * @param toValue End value. - * @param duration Duration of the tween. - * @param ease Optional easer function. - */ - public function tween(fromValue:Number, toValue:Number, duration:Number, ease:Function = null):void - { - _start = value = fromValue; - _range = toValue - value; - _target = duration; - _ease = ease; - start(); - } - - /** @private Updates the Tween. */ - override public function update():void - { - super.update(); - value = _start + _range * _t; - } - - // Tween information. - /** @private */ private var _start:Number; - /** @private */ private var _range:Number; - } -} \ No newline at end of file diff --git a/src/net/flashpunk/tweens/misc/VarTween.as b/src/net/flashpunk/tweens/misc/VarTween.as deleted file mode 100644 index 841d8e1..0000000 --- a/src/net/flashpunk/tweens/misc/VarTween.as +++ /dev/null @@ -1,55 +0,0 @@ -package net.flashpunk.tweens.misc -{ - import net.flashpunk.Tween; - - /** - * Tweens a numeric public property of an Object. - */ - public class VarTween extends Tween - { - /** - * Constructor. - * @param complete Optional completion callback. - * @param type Tween type. - */ - public function VarTween(complete:Function = null, type:uint = 0) - { - super(0, type, complete); - } - - /** - * Tweens a numeric public property. - * @param object The object containing the property. - * @param property The name of the property (eg. "x"). - * @param to Value to tween to. - * @param duration Duration of the tween. - * @param ease Optional easer function. - */ - public function tween(object:Object, property:String, to:Number, duration:Number, ease:Function = null):void - { - _object = object; - _property = property; - _ease = ease; - if (!object.hasOwnProperty(property)) throw new Error("The Object does not have the property\"" + property + "\", or it is not accessible."); - var a:* = _object[property] as Number; - if (a == null) throw new Error("The property \"" + property + "\" is not numeric."); - _start = _object[property]; - _range = to - _start; - _target = duration; - start(); - } - - /** @private Updates the Tween. */ - override public function update():void - { - super.update(); - _object[_property] = _start + _range * _t; - } - - // Tween information. - /** @private */ private var _object:Object; - /** @private */ private var _property:String; - /** @private */ private var _start:Number; - /** @private */ private var _range:Number; - } -} \ No newline at end of file diff --git a/src/net/flashpunk/tweens/motion/CircularMotion.as b/src/net/flashpunk/tweens/motion/CircularMotion.as deleted file mode 100644 index efa5cde..0000000 --- a/src/net/flashpunk/tweens/motion/CircularMotion.as +++ /dev/null @@ -1,94 +0,0 @@ -package net.flashpunk.tweens.motion -{ - import flash.geom.Point; - import net.flashpunk.FP; - import net.flashpunk.utils.Ease; - - /** - * Determines a circular motion. - */ - public class CircularMotion extends Motion - { - /** - * Constructor. - * @param complete Optional completion callback. - * @param type Tween type. - */ - public function CircularMotion(complete:Function = null, type:uint = 0) - { - super(0, complete, type, null); - } - - /** - * Starts moving along a circle. - * @param centerX X position of the circle's center. - * @param centerY Y position of the circle's center. - * @param radius Radius of the circle. - * @param angle Starting position on the circle. - * @param clockwise If the motion is clockwise. - * @param duration Duration of the movement. - * @param ease Optional easer function. - */ - public function setMotion(centerX:Number, centerY:Number, radius:Number, angle:Number, clockwise:Boolean, duration:Number, ease:Function = null):void - { - _centerX = centerX; - _centerY = centerY; - _radius = radius; - _angle = _angleStart = angle * FP.RAD; - _angleFinish = _CIRC * (clockwise ? 1 : -1); - _target = duration; - _ease = ease; - start(); - } - - /** - * Starts moving along a circle at the speed. - * @param centerX X position of the circle's center. - * @param centerY Y position of the circle's center. - * @param radius Radius of the circle. - * @param angle Starting position on the circle. - * @param clockwise If the motion is clockwise. - * @param speed Speed of the movement. - * @param ease Optional easer function. - */ - public function setMotionSpeed(centerX:Number, centerY:Number, radius:Number, angle:Number, clockwise:Boolean, speed:Number, ease:Function = null):void - { - _centerX = centerX; - _centerY = centerY; - _radius = radius; - _angle = _angleStart = angle * FP.RAD; - _angleFinish = _CIRC * (clockwise ? 1 : -1); - _target = (_radius * _CIRC) / speed; - _ease = ease; - start(); - } - - /** @private Updates the Tween. */ - override public function update():void - { - super.update(); - _angle = _angleStart + _angleFinish * _t; - x = _centerX + Math.cos(_angle) * _radius; - y = _centerY + Math.sin(_angle) * _radius; - } - - /** - * The current position on the circle. - */ - public function get angle():Number { return _angle; } - - /** - * The circumference of the current circle motion. - */ - public function get circumference():Number { return _radius * _CIRC; } - - // Circle information. - /** @private */ private var _centerX:Number = 0; - /** @private */ private var _centerY:Number = 0; - /** @private */ private var _radius:Number = 0; - /** @private */ private var _angle:Number = 0; - /** @private */ private var _angleStart:Number = 0; - /** @private */ private var _angleFinish:Number = 0; - /** @private */ private static const _CIRC:Number = Math.PI * 2; - } -} \ No newline at end of file diff --git a/src/net/flashpunk/tweens/motion/CubicMotion.as b/src/net/flashpunk/tweens/motion/CubicMotion.as deleted file mode 100644 index 5162cd0..0000000 --- a/src/net/flashpunk/tweens/motion/CubicMotion.as +++ /dev/null @@ -1,69 +0,0 @@ -package net.flashpunk.tweens.motion -{ - import flash.geom.Point; - import net.flashpunk.utils.Ease; - - /** - * Determines motion along a cubic curve. - */ - public class CubicMotion extends Motion - { - /** - * Constructor. - * @param complete Optional completion callback. - * @param type Tween type. - */ - public function CubicMotion(complete:Function = null, type:uint = 0) - { - super(0, complete, type, null); - } - - /** - * Starts moving along the curve. - * @param fromX X start. - * @param fromY Y start. - * @param aX First control x. - * @param aY First control y. - * @param bX Second control x. - * @param bY Second control y. - * @param toX X finish. - * @param toY Y finish. - * @param duration Duration of the movement. - * @param ease Optional easer function. - */ - public function setMotion(fromX:Number, fromY:Number, aX:Number, aY:Number, bX:Number, bY:Number, toX:Number, toY:Number, duration:Number, ease:Function = null):void - { - x = _fromX = fromX; - y = _fromY = fromY; - _aX = aX; - _aY = aY; - _bX = bX; - _bY = bY; - _toX = toX; - _toY = toY; - _target = duration; - _ease = ease; - start(); - } - - /** @private Updates the Tween. */ - override public function update():void - { - super.update(); - x = _t * _t * _t * (_toX + 3 * (_aX - _bX) - _fromX) + 3 * _t * _t * (_fromX - 2 * _aX + _bX) + 3 * _t * (_aX - _fromX) + _fromX; - y = _t * _t * _t * (_toY + 3 * (_aY - _bY) - _fromY) + 3 * _t * _t * (_fromY - 2 * _aY + _bY) + 3 * _t * (_aY - _fromY) + _fromY; - } - - // Curve information. - /** @private */ private var _fromX:Number = 0; - /** @private */ private var _fromY:Number = 0; - /** @private */ private var _toX:Number = 0; - /** @private */ private var _toY:Number = 0; - /** @private */ private var _aX:Number = 0; - /** @private */ private var _aY:Number = 0; - /** @private */ private var _bX:Number = 0; - /** @private */ private var _bY:Number = 0; - /** @private */ private var _ttt:Number; - /** @private */ private var _tt:Number; - } -} \ No newline at end of file diff --git a/src/net/flashpunk/tweens/motion/LinearMotion.as b/src/net/flashpunk/tweens/motion/LinearMotion.as deleted file mode 100644 index 02623f0..0000000 --- a/src/net/flashpunk/tweens/motion/LinearMotion.as +++ /dev/null @@ -1,86 +0,0 @@ -package net.flashpunk.tweens.motion -{ - import flash.geom.Point; - - /** - * Determines motion along a line, from one point to another. - */ - public class LinearMotion extends Motion - { - /** - * Constructor. - * @param complete Optional completion callback. - * @param type Tween type. - */ - public function LinearMotion(complete:Function = null, type:uint = 0) - { - super(0,complete, type, null); - } - - /** - * Starts moving along a line. - * @param fromX X start. - * @param fromY Y start. - * @param toX X finish. - * @param toY Y finish. - * @param duration Duration of the movement. - * @param ease Optional easer function. - */ - public function setMotion(fromX:Number, fromY:Number, toX:Number, toY:Number, duration:Number, ease:Function = null):void - { - _distance = -1; - x = _fromX = fromX; - y = _fromY = fromY; - _moveX = toX - fromX; - _moveY = toY - fromY; - _target = duration; - _ease = ease; - start(); - } - - /** - * Starts moving along a line at the speed. - * @param fromX X start. - * @param fromY Y start. - * @param toX X finish. - * @param toY Y finish. - * @param speed Speed of the movement. - * @param ease Optional easer function. - */ - public function setMotionSpeed(fromX:Number, fromY:Number, toX:Number, toY:Number, speed:Number, ease:Function = null):void - { - _distance = -1; - x = _fromX = fromX; - y = _fromY = fromY; - _moveX = toX - fromX; - _moveY = toY - fromY; - _target = distance / speed; - _ease = ease; - start(); - } - - /** @private Updates the Tween. */ - override public function update():void - { - super.update(); - x = _fromX + _moveX * _t; - y = _fromY + _moveY * _t; - } - - /** - * Length of the current line of movement. - */ - public function get distance():Number - { - if (_distance >= 0) return _distance; - return (_distance = Math.sqrt(_moveX * _moveX + _moveY * _moveY)); - } - - // Line information. - /** @private */ private var _fromX:Number = 0; - /** @private */ private var _fromY:Number = 0; - /** @private */ private var _moveX:Number = 0; - /** @private */ private var _moveY:Number = 0; - /** @private */ private var _distance:Number = - 1; - } -} \ No newline at end of file diff --git a/src/net/flashpunk/tweens/motion/LinearPath.as b/src/net/flashpunk/tweens/motion/LinearPath.as deleted file mode 100644 index 62eaf11..0000000 --- a/src/net/flashpunk/tweens/motion/LinearPath.as +++ /dev/null @@ -1,133 +0,0 @@ -package net.flashpunk.tweens.motion -{ - import flash.geom.Point; - import net.flashpunk.FP; - - /** - * Determines linear motion along a set of points. - */ - public class LinearPath extends Motion - { - /** - * Constructor. - * @param complete Optional completion callback. - * @param type Tween type. - */ - public function LinearPath(complete:Function = null, type:uint = 0) - { - super(0, complete, type, null); - _pointD[0] = _pointT[0] = 0; - } - - /** - * Starts moving along the path. - * @param duration Duration of the movement. - * @param ease Optional easer function. - */ - public function setMotion(duration:Number, ease:Function = null):void - { - updatePath(); - _target = duration; - _speed = _distance / duration; - _ease = ease; - start(); - } - - /** - * Starts moving along the path at the speed. - * @param speed Speed of the movement. - * @param ease Optional easer function. - */ - public function setMotionSpeed(speed:Number, ease:Function = null):void - { - updatePath(); - _target = _distance / speed; - _speed = speed; - _ease = ease; - start(); - } - - /** - * Adds the point to the path. - * @param x X position. - * @param y Y position. - */ - public function addPoint(x:Number = 0, y:Number = 0):void - { - if (_last) - { - _distance += Math.sqrt((x - _last.x) * (x - _last.x) + (y - _last.y) * (y - _last.y)); - _pointD[_points.length] = _distance; - } - _points[_points.length] = _last = new Point(x, y); - } - - /** - * Gets a point on the path. - * @param index Index of the point. - * @return The Point object. - */ - public function getPoint(index:uint = 0):Point - { - if (!_points.length) throw new Error("No points have been added to the path yet."); - return _points[index % _points.length]; - } - - /** @private Starts the Tween. */ - override public function start():void - { - _index = 0; - super.start(); - } - - /** @private Updates the Tween. */ - override public function update():void - { - super.update(); - if (_index < _points.length - 1) - { - while (_t > _pointT[_index + 1]) _index ++; - } - var td:Number = _pointT[_index], - tt:Number = _pointT[_index + 1] - td; - td = (_t - td) / tt; - _prev = _points[_index]; - _next = _points[_index + 1]; - x = _prev.x + (_next.x - _prev.x) * td; - y = _prev.y + (_next.y - _prev.y) * td; - } - - /** @private Updates the path, preparing it for motion. */ - private function updatePath():void - { - if (_points.length < 2) throw new Error("A LinearPath must have at least 2 points to operate."); - if (_pointD.length == _pointT.length) return; - // evaluate t for each point - var i:int = 0; - while (i < _points.length) _pointT[i] = _pointD[i ++] / _distance; - } - - /** - * The full length of the path. - */ - public function get distance():Number { return _distance; } - - /** - * How many points are on the path. - */ - public function get pointCount():Number { return _points.length; } - - // Path information. - /** @private */ private var _points:Vector. = new Vector.; - /** @private */ private var _pointD:Vector. = new Vector.; - /** @private */ private var _pointT:Vector. = new Vector.; - /** @private */ private var _distance:Number = 0; - /** @private */ private var _speed:Number = 0; - /** @private */ private var _index:uint = 0; - - // Line information. - /** @private */ private var _last:Point; - /** @private */ private var _prev:Point; - /** @private */ private var _next:Point; - } -} \ No newline at end of file diff --git a/src/net/flashpunk/tweens/motion/Motion.as b/src/net/flashpunk/tweens/motion/Motion.as deleted file mode 100644 index 0675fa2..0000000 --- a/src/net/flashpunk/tweens/motion/Motion.as +++ /dev/null @@ -1,32 +0,0 @@ -package net.flashpunk.tweens.motion -{ - import net.flashpunk.Tween; - - /** - * Base class for motion Tweens. - */ - public class Motion extends Tween - { - /** - * Current x position of the Tween. - */ - public var x:Number = 0; - - /** - * Current y position of the Tween. - */ - public var y:Number = 0; - - /** - * Constructor. - * @param duration Duration of the Tween. - * @param complete Optional completion callback. - * @param type Tween type. - * @param ease Optional easer function. - */ - public function Motion(duration:Number, complete:Function = null, type:uint = 0, ease:Function = null) - { - super(duration, type, complete, ease); - } - } -} \ No newline at end of file diff --git a/src/net/flashpunk/tweens/motion/QuadMotion.as b/src/net/flashpunk/tweens/motion/QuadMotion.as deleted file mode 100644 index 6729c3c..0000000 --- a/src/net/flashpunk/tweens/motion/QuadMotion.as +++ /dev/null @@ -1,112 +0,0 @@ -package net.flashpunk.tweens.motion -{ - import flash.geom.Point; - import net.flashpunk.FP; - import net.flashpunk.utils.Ease; - - /** - * Determines motion along a quadratic curve. - */ - public class QuadMotion extends Motion - { - /** - * Constructor. - * @param complete Optional completion callback. - * @param type Tween type. - */ - public function QuadMotion(complete:Function = null, type:uint = 0) - { - super(0, complete, type, null); - } - - /** - * Starts moving along the curve. - * @param fromX X start. - * @param fromY Y start. - * @param controlX X control, used to determine the curve. - * @param controlY Y control, used to determine the curve. - * @param toX X finish. - * @param toY Y finish. - * @param duration Duration of the movement. - * @param ease Optional easer function. - */ - public function setMotion(fromX:Number, fromY:Number, controlX:Number, controlY:Number, toX:Number, toY:Number, duration:Number, ease:Function = null):void - { - _distance = -1; - x = _fromX = fromX; - y = _fromY = fromY; - _controlX = controlX; - _controlY = controlY; - _toX = toX; - _toY = toY; - _target = duration; - _ease = ease; - start(); - } - - /** - * Starts moving along the curve at the speed. - * @param fromX X start. - * @param fromY Y start. - * @param controlX X control, used to determine the curve. - * @param controlY Y control, used to determine the curve. - * @param toX X finish. - * @param toY Y finish. - * @param speed Speed of the movement. - * @param ease Optional easer function. - */ - public function setMotionSpeed(fromX:Number, fromY:Number, controlX:Number, controlY:Number, toX:Number, toY:Number, speed:Number, ease:Function = null):void - { - _distance = -1; - x = _fromX = fromX; - y = _fromY = fromY; - _controlX = controlX; - _controlY = controlY; - _toX = toX; - _toY = toY; - _target = distance / speed; - _ease = ease; - start(); - } - - /** @private Updates the Tween. */ - override public function update():void - { - super.update(); - x = _fromX * (1 - _t) * (1 - _t) + _controlX * 2 * (1 - _t) * _t + _toX * _t * _t; - y = _fromY * (1 - _t) * (1 - _t) + _controlY * 2 * (1 - _t) * _t + _toY * _t * _t; - } - - /** - * The distance of the entire curve. - */ - public function get distance():Number - { - if (_distance >= 0) return _distance; - var a:Point = FP.point, - b:Point = FP.point2; - a.x = x - 2 * _controlX + _toX; - a.y = y - 2 * _controlY + _toY; - b.x = 2 * _controlX - 2 * x; - b.y = 2 * _controlY - 2 * y; - var A:Number = 4 * (a.x * a.x + a.y * a.y), - B:Number = 4 * (a.x * b.x + a.y * b.y), - C:Number = b.x * b.x + b.y * b.y, - ABC:Number = 2 * Math.sqrt(A + B + C), - A2:Number = Math.sqrt(A), - A32:Number = 2 * A * A2, - C2:Number = 2 * Math.sqrt(C), - BA:Number = B / A2; - return (A32 * ABC + A2 * B * (ABC - C2) + (4 * C * A - B * B) * Math.log((2 * A2 + BA + ABC) / (BA + C2))) / (4 * A32); - } - - // Curve information. - /** @private */ private var _distance:Number = -1; - /** @private */ private var _fromX:Number = 0; - /** @private */ private var _fromY:Number = 0; - /** @private */ private var _toX:Number = 0; - /** @private */ private var _toY:Number = 0; - /** @private */ private var _controlX:Number = 0; - /** @private */ private var _controlY:Number = 0; - } -} \ No newline at end of file diff --git a/src/net/flashpunk/tweens/motion/QuadPath.as b/src/net/flashpunk/tweens/motion/QuadPath.as deleted file mode 100644 index fdb2efa..0000000 --- a/src/net/flashpunk/tweens/motion/QuadPath.as +++ /dev/null @@ -1,192 +0,0 @@ -package net.flashpunk.tweens.motion -{ - import flash.geom.Point; - import net.flashpunk.FP; - - /** - * A series of points which will determine a path from the - * beginning point to the end poing using quadratic curves. - */ - public class QuadPath extends Motion - { - /** - * Constructor. - * @param complete Optional completion callback. - * @param type Tween type. - */ - public function QuadPath(complete:Function = null, type:uint = 0) - { - super(0, complete, type, null); - _curveT[0] = 0; - } - - /** - * Starts moving along the path. - * @param duration Duration of the movement. - * @param ease Optional easer function. - */ - public function setMotion(duration:Number, ease:Function = null):void - { - updatePath(); - _target = duration; - _speed = _distance / duration; - _ease = ease; - start(); - } - - /** - * Starts moving along the path at the speed. - * @param speed Speed of the movement. - * @param ease Optional easer function. - */ - public function setMotionSpeed(speed:Number, ease:Function = null):void - { - updatePath(); - _target = _distance / speed; - _speed = speed; - _ease = ease; - start(); - } - - /** - * Adds the point to the path. - * @param x X position. - * @param y Y position. - */ - public function addPoint(x:Number = 0, y:Number = 0):void - { - _updateCurve = true; - if (!_points.length) _curve[0] = new Point(x, y); - _points[_points.length] = new Point(x, y); - } - - /** - * Gets the point on the path. - * @param index Index of the point. - * @return The Point object. - */ - public function getPoint(index:uint = 0):Point - { - if (!_points.length) throw new Error("No points have been added to the path yet."); - return _points[index % _points.length]; - } - - /** @private Starts the Tween. */ - override public function start():void - { - _index = 0; - super.start(); - } - - /** @private Updates the Tween. */ - override public function update():void - { - super.update(); - if (_index < _curve.length - 1) - { - while (_t > _curveT[_index + 1]) _index ++; - } - var td:Number = _curveT[_index], - tt:Number = _curveT[_index + 1] - td; - td = (_t - td) / tt; - _a = _curve[_index]; - _b = _points[_index + 1]; - _c = _curve[_index + 1]; - x = _a.x * (1 - td) * (1 - td) + _b.x * 2 * (1 - td) * td + _c.x * td * td; - y = _a.y * (1 - td) * (1 - td) + _b.y * 2 * (1 - td) * td + _c.y * td * td; - } - - /** @private Updates the path, preparing the curve. */ - private function updatePath():void - { - if (_points.length < 3) throw new Error("A QuadPath must have at least 3 points to operate."); - if (!_updateCurve) return; - _updateCurve = false; - - // produce the curve points - var p:Point, - c:Point, - l:Point = _points[1], - i:uint = 2; - while (i < _points.length) - { - p = _points[i]; - if (_curve.length > i - 1) c = _curve[i - 1]; - else c = _curve[i - 1] = new Point; - if (i < _points.length - 1) - { - c.x = l.x + (p.x - l.x) / 2; - c.y = l.y + (p.y - l.y) / 2; - } - else - { - c.x = p.x; - c.y = p.y; - } - l = p; - i ++; - } - - // find the total distance of the path - i = 0; - _distance = 0; - while (i < _curve.length - 1) - { - _curveD[i] = curveLength(_curve[i], _points[i + 1], _curve[i + 1]); - _distance += _curveD[i ++]; - } - - // find t for each point on the curve - i = 1; - var d:Number = 0; - while (i < _curve.length - 1) - { - d += _curveD[i]; - _curveT[i ++] = d / _distance; - } - _curveT[_curve.length - 1] = 1; - } - - /** - * Amount of points on the path. - */ - public function get pointCount():Number { return _points.length; } - - /** @private Calculates the lenght of the curve. */ - private function curveLength(start:Point, control:Point, finish:Point):Number - { - var a:Point = FP.point, - b:Point = FP.point2; - a.x = start.x - 2 * control.x + finish.x; - a.y = start.y - 2 * control.y + finish.y; - b.x = 2 * control.x - 2 * start.x; - b.y = 2 * control.y - 2 * start.y; - var A:Number = 4 * (a.x * a.x + a.y * a.y), - B:Number = 4 * (a.x * b.x + a.y * b.y), - C:Number = b.x * b.x + b.y * b.y, - ABC:Number = 2 * Math.sqrt(A + B + C), - A2:Number = Math.sqrt(A), - A32:Number = 2 * A * A2, - C2:Number = 2 * Math.sqrt(C), - BA:Number = B / A2; - return (A32 * ABC + A2 * B * (ABC - C2) + (4 * C * A - B * B) * Math.log((2 * A2 + BA + ABC) / (BA + C2))) / (4 * A32); - } - - // Path information. - /** @private */ private var _points:Vector. = new Vector.; - /** @private */ private var _distance:Number = 0; - /** @private */ private var _speed:Number = 0; - /** @private */ private var _index:uint = 0; - - // Curve information. - /** @private */ private var _updateCurve:Boolean = true; - /** @private */ private var _curve:Vector. = new Vector.; - /** @private */ private var _curveT:Vector. = new Vector.; - /** @private */ private var _curveD:Vector. = new Vector.; - - // Curve points. - /** @private */ private var _a:Point; - /** @private */ private var _b:Point; - /** @private */ private var _c:Point; - } -} \ No newline at end of file diff --git a/src/net/flashpunk/tweens/sound/Fader.as b/src/net/flashpunk/tweens/sound/Fader.as deleted file mode 100644 index 02b4281..0000000 --- a/src/net/flashpunk/tweens/sound/Fader.as +++ /dev/null @@ -1,48 +0,0 @@ -package net.flashpunk.tweens.sound -{ - import net.flashpunk.FP; - import net.flashpunk.Tween; - - /** - * Global volume fader. - */ - public class Fader extends Tween - { - /** - * Constructor. - * @param complete Optional completion callback. - * @param type Tween type. - */ - public function Fader(complete:Function = null, type:uint = 0) - { - super(0, type, complete); - } - - /** - * Fades FP.volume to the target volume. - * @param volume The volume to fade to. - * @param duration Duration of the fade. - * @param ease Optional easer function. - */ - public function fadeTo(volume:Number, duration:Number, ease:Function = null):void - { - if (volume < 0) volume = 0; - _start = FP.volume; - _range = volume - _start; - _target = duration; - _ease = ease; - start(); - } - - /** @private Updates the Tween. */ - override public function update():void - { - super.update(); - FP.volume = _start + _range * _t; - } - - // Fader information. - /** @private */ private var _start:Number; - /** @private */ private var _range:Number; - } -} \ No newline at end of file diff --git a/src/net/flashpunk/tweens/sound/SfxFader.as b/src/net/flashpunk/tweens/sound/SfxFader.as deleted file mode 100644 index 67f0548..0000000 --- a/src/net/flashpunk/tweens/sound/SfxFader.as +++ /dev/null @@ -1,94 +0,0 @@ -package net.flashpunk.tweens.sound -{ - import net.flashpunk.Sfx; - import net.flashpunk.Tween; - - /** - * Sound effect fader. - */ - public class SfxFader extends Tween - { - /** - * Constructor. - * @param sfx The Sfx object to alter. - * @param complete Optional completion callback. - * @param type Tween type. - */ - public function SfxFader(sfx:Sfx, complete:Function = null, type:uint = 0) - { - super(0, type, finish); - _complete = complete; - _sfx = sfx; - } - - /** - * Fades the Sfx to the target volume. - * @param volume The volume to fade to. - * @param duration Duration of the fade. - * @param ease Optional easer function. - */ - public function fadeTo(volume:Number, duration:Number, ease:Function = null):void - { - if (volume < 0) volume = 0; - _start = _sfx.volume; - _range = volume - _start; - _target = duration; - _ease = ease; - start(); - } - - /** - * Fades out the Sfx, while also playing and fading in a replacement Sfx. - * @param play The Sfx to play and fade in. - * @param loop If the new Sfx should loop. - * @param duration Duration of the crossfade. - * @param volume The volume to fade in the new Sfx to. - * @param ease Optional easer function. - */ - public function crossFade(play:Sfx, loop:Boolean, duration:Number, volume:Number = 1, ease:Function = null):void - { - _crossSfx = play; - _crossRange = volume; - _start = _sfx.volume; - _range = -_start; - _target = duration; - _ease = ease; - if (loop) _crossSfx.loop(0); - else _crossSfx.play(0); - start(); - } - - /** @private Updates the Tween. */ - override public function update():void - { - super.update(); - if (_sfx) _sfx.volume = _start + _range * _t; - if (_crossSfx) _crossSfx.volume = _crossRange * _t; - } - - /** @private When the tween completes. */ - private function finish():void - { - if (_crossSfx) - { - if (_sfx) _sfx.stop(); - _sfx = _crossSfx; - _crossSfx = null; - } - if (_complete != null) _complete(); - } - - /** - * The current Sfx this object is effecting. - */ - public function get sfx():Sfx { return _sfx; } - - // Fader information. - /** @private */ private var _sfx:Sfx; - /** @private */ private var _start:Number; - /** @private */ private var _range:Number; - /** @private */ private var _crossSfx:Sfx; - /** @private */ private var _crossRange:Number; - /** @private */ private var _complete:Function; - } -} \ No newline at end of file diff --git a/src/net/flashpunk/utils/Data.as b/src/net/flashpunk/utils/Data.as deleted file mode 100644 index ce69c2b..0000000 --- a/src/net/flashpunk/utils/Data.as +++ /dev/null @@ -1,146 +0,0 @@ -package net.flashpunk.utils -{ - import flash.net.SharedObject; - - /** - * Static helper class used for saving and loading data from stored cookies. - */ - public class Data - { - /** - * If you want to share data between different SWFs on the same host, use this id. - */ - public static var id:String = ""; - - /** - * Overwrites the current data with the file. - * @param file The filename to load. - */ - public static function load(file:String = ""):void - { - var data:Object = loadData(file); - _data = { }; - for (var i:String in data) _data[i] = data[i]; - } - - /** - * Overwrites the file with the current data. The current data will not be saved until this function is called. - * @param file The filename to save. - */ - public static function save(file:String = ""):void - { - if (_shared) _shared.clear(); - var data:Object = loadData(file); - for (var i:String in _data) data[i] = _data[i]; - _shared.flush(SIZE); - } - - /** - * Reads an int from the current data. - * @param name Property to read. - * @param defaultValue Default value. - * @return The property value, or defaultValue if the property is not assigned. - */ - public static function readInt(name:String, defaultValue:int = 0):int - { - return int(read(name, defaultValue)); - } - - /** - * Reads a uint from the current data. - * @param name Property to read. - * @param defaultValue Default value. - * @return The property value, or defaultValue if the property is not assigned. - */ - public static function readUint(name:String, defaultValue:uint = 0):uint - { - return uint(read(name, defaultValue)); - } - - /** - * Reads a Boolean from the current data. - * @param name Property to read. - * @param defaultValue Default value. - * @return The property value, or defaultValue if the property is not assigned. - */ - public static function readBool(name:String, defaultValue:Boolean = true):Boolean - { - return Boolean(read(name, defaultValue)); - } - - /** - * Reads a String from the current data. - * @param name Property to read. - * @param defaultValue Default value. - * @return The property value, or defaultValue if the property is not assigned. - */ - public static function readString(name:String, defaultValue:String = ""):String - { - return String(read(name, defaultValue)); - } - - /** - * Writes an int to the current data. - * @param name Property to write. - * @param value Value to write. - */ - public static function writeInt(name:String, value:int = 0):void - { - _data[name] = value; - } - - /** - * Writes a uint to the current data. - * @param name Property to write. - * @param value Value to write. - */ - public static function writeUint(name:String, value:uint = 0):void - { - _data[name] = value; - } - - /** - * Writes a Boolean to the current data. - * @param name Property to write. - * @param value Value to write. - */ - public static function writeBool(name:String, value:Boolean = true):void - { - _data[name] = value; - } - - /** - * Writes a String to the current data. - * @param name Property to write. - * @param value Value to write. - */ - public static function writeString(name:String, value:String = ""):void - { - _data[name] = value; - } - - /** @private Reads a property from the data object. */ - private static function read(name:String, defaultValue:*):* - { - if (_data.hasOwnProperty(name)) return _data[name]; - return defaultValue; - } - - /** @private Loads the data file, or return it if you're loading the same one. */ - private static function loadData(file:String):Object - { - if (!file) file = DEFAULT_FILE; - if (id) _shared = SharedObject.getLocal(PREFIX + "/" + id + "/" + file, "/"); - else _shared = SharedObject.getLocal(PREFIX + "/" + file); - return _shared.data; - } - - // Data information. - /** @private */ private static var _shared:SharedObject; - /** @private */ private static var _dir:String; - /** @private */ private static var _data:Object = { }; - /** @private */ private static const PREFIX:String = "FlashPunk"; - /** @private */ private static const DEFAULT_FILE:String = "_file"; - /** @private */ private static const SIZE:uint = 10000; - } -} \ No newline at end of file diff --git a/src/net/flashpunk/utils/Draw.as b/src/net/flashpunk/utils/Draw.as deleted file mode 100644 index 6b20162..0000000 --- a/src/net/flashpunk/utils/Draw.as +++ /dev/null @@ -1,375 +0,0 @@ -package net.flashpunk.utils -{ - import flash.display.BitmapData; - import flash.display.Graphics; - import flash.display.LineScaleMode; - import flash.geom.Matrix; - import flash.geom.Point; - import flash.geom.Rectangle; - import net.flashpunk.Entity; - import net.flashpunk.FP; - import net.flashpunk.Graphic; - - /** - * Static class with access to miscellanious drawing functions. - * These functions are not meant to replace Graphic components - * for Entities, but rather to help with testing and debugging. - */ - public class Draw - { - /** - * The blending mode used by Draw functions. This will not - * apply to Draw.line(), but will apply to Draw.linePlus(). - */ - public static var blend:String; - - /** - * Sets the drawing target for Draw functions. - * @param target The buffer to draw to. - * @param camera The camera offset (use null for none). - * @param blend The blend mode to use. - */ - public static function setTarget(target:BitmapData, camera:Point = null, blend:String = null):void - { - _target = target; - _camera = camera ? camera : FP.zero; - Draw.blend = blend; - } - - /** - * Resets the drawing target to the default. The same as calling Draw.setTarget(FP.buffer, FP.camera). - */ - public static function resetTarget():void - { - _target = FP.buffer; - _camera = FP.camera; - Draw.blend = null; - } - - /** - * Draws a pixelated, non-antialiased line. - * @param x1 Starting x position. - * @param y1 Starting y position. - * @param x2 Ending x position. - * @param y2 Ending y position. - * @param color Color of the line. - */ - public static function line(x1:int, y1:int, x2:int, y2:int, color:uint = 0xFFFFFF):void - { - if (color < 0xFF000000) color = 0xFF000000 | color; - - // get the drawing positions - x1 -= _camera.x; - y1 -= _camera.y; - x2 -= _camera.x; - y2 -= _camera.y; - - // get the drawing difference - var screen:BitmapData = _target, - X:Number = Math.abs(x2 - x1), - Y:Number = Math.abs(y2 - y1), - xx:int, - yy:int; - - // draw a single pixel - if (X == 0) - { - if (Y == 0) - { - screen.setPixel32(x1, y1, color); - return; - } - // draw a straight vertical line - yy = y2 > y1 ? 1 : -1; - while (y1 != y2) - { - screen.setPixel32(x1, y1, color); - y1 += yy; - } - screen.setPixel32(x2, y2, color); - return; - } - - if (Y == 0) - { - // draw a straight horizontal line - xx = x2 > x1 ? 1 : -1; - while (x1 != x2) - { - screen.setPixel32(x1, y1, color); - x1 += xx; - } - screen.setPixel32(x2, y2, color); - return; - } - - xx = x2 > x1 ? 1 : -1; - yy = y2 > y1 ? 1 : -1; - var c:Number = 0, - slope:Number; - - if (X > Y) - { - slope = Y / X; - c = .5; - while (x1 != x2) - { - screen.setPixel32(x1, y1, color); - x1 += xx; - c += slope; - if (c >= 1) - { - y1 += yy; - c -= 1; - } - } - screen.setPixel32(x2, y2, color); - } - else - { - slope = X / Y; - c = .5; - while (y1 != y2) - { - screen.setPixel32(x1, y1, color); - y1 += yy; - c += slope; - if (c >= 1) - { - x1 += xx; - c -= 1; - } - } - screen.setPixel32(x2, y2, color); - } - } - - /** - * Draws a smooth, antialiased line with optional alpha and thickness. - * @param x1 Starting x position. - * @param y1 Starting y position. - * @param x2 Ending x position. - * @param y2 Ending y position. - * @param color Color of the line. - * @param alpha Alpha of the line. - * @param thick The thickness of the line. - */ - public static function linePlus(x1:int, y1:int, x2:int, y2:int, color:uint = 0xFF000000, alpha:Number = 1, thick:Number = 1):void - { - _graphics.clear(); - _graphics.lineStyle(thick, color, alpha, false, LineScaleMode.NONE); - _graphics.moveTo(x1 - _camera.x, y1 - _camera.y); - _graphics.lineTo(x2 - _camera.x, y2 - _camera.y); - _target.draw(FP.sprite, null, null, blend); - } - - /** - * Draws a filled rectangle. - * @param x X position of the rectangle. - * @param y Y position of the rectangle. - * @param width Width of the rectangle. - * @param height Height of the rectangle. - * @param color Color of the rectangle. - * @param alpha Alpha of the rectangle. - */ - public static function rect(x:int, y:int, width:uint, height:uint, color:uint = 0xFFFFFF, alpha:Number = 1):void - { - if (alpha >= 1 && !blend) - { - if (color < 0xFF000000) color = 0xFF000000 | color; - _rect.x = x - _camera.x; - _rect.y = y - _camera.y; - _rect.width = width; - _rect.height = height; - _target.fillRect(_rect, color); - return; - } - if (color >= 0xFF000000) color = 0xFFFFFF & color; - _graphics.clear(); - _graphics.beginFill(color, alpha); - _graphics.drawRect(x - _camera.x, y - _camera.y, width, height); - _target.draw(FP.sprite, null, null, blend); - } - - /** - * Draws a non-filled, pixelated circle. - * @param x Center x position. - * @param y Center y position. - * @param radius Radius of the circle. - * @param color Color of the circle. - */ - public static function circle(x:int, y:int, radius:int, color:uint = 0xFFFFFF):void - { - if (color < 0xFF000000) color = 0xFF000000 | color; - x -= _camera.x; - y -= _camera.y; - var f:int = 1 - radius, - fx:int = 1, - fy:int = -2 * radius, - xx:int = 0, - yy:int = radius; - _target.setPixel32(x, y + radius, color); - _target.setPixel32(x, y - radius, color); - _target.setPixel32(x + radius, y, color); - _target.setPixel32(x - radius, y, color); - while (xx < yy) - { - if (f >= 0) - { - yy --; - fy += 2; - f += fy; - } - xx ++; - fx += 2; - f += fx; - _target.setPixel32(x + xx, y + yy, color); - _target.setPixel32(x - xx, y + yy, color); - _target.setPixel32(x + xx, y - yy, color); - _target.setPixel32(x - xx, y - yy, color); - _target.setPixel32(x + yy, y + xx, color); - _target.setPixel32(x - yy, y + xx, color); - _target.setPixel32(x + yy, y - xx, color); - _target.setPixel32(x - yy, y - xx, color); - } - } - - /** - * Draws a circle to the screen. - * @param x X position of the circle's center. - * @param y Y position of the circle's center. - * @param radius Radius of the circle. - * @param color Color of the circle. - * @param alpha Alpha of the circle. - * @param fill If the circle should be filled with the color (true) or just an outline (false). - * @param thick How thick the outline should be (only applicable when fill = false). - */ - public static function circlePlus(x:int, y:int, radius:Number, color:uint = 0xFFFFFF, alpha:Number = 1, fill:Boolean = true, thick:int = 1):void - { - _graphics.clear(); - if (fill) - { - _graphics.beginFill(color & 0xFFFFFF, alpha); - _graphics.drawCircle(x - _camera.x, y - _camera.y, radius); - _graphics.endFill(); - } - else - { - _graphics.lineStyle(thick, color & 0xFFFFFF, alpha); - _graphics.drawCircle(x - _camera.x, y - _camera.y, radius); - } - _target.draw(FP.sprite, null, null, blend); - } - - /** - * Draws the Entity's hitbox. - * @param e The Entity whose hitbox is to be drawn. - * @param outline If just the hitbox's outline should be drawn. - * @param color Color of the hitbox. - * @param alpha Alpha of the hitbox. - */ - public static function hitbox(e:Entity, outline:Boolean = true, color:uint = 0xFFFFFF, alpha:Number = 1):void - { - if (outline) - { - if (color < 0xFF000000) color = 0xFF000000 | color; - var x:int = e.x - e.originX - _camera.x, - y:int = e.y - e.originY - _camera.y; - _rect.x = x; - _rect.y = y; - _rect.width = e.width; - _rect.height = 1; - _target.fillRect(_rect, color); - _rect.y += e.height - 1; - _target.fillRect(_rect, color); - _rect.y = y; - _rect.width = 1; - _rect.height = e.height; - _target.fillRect(_rect, color); - _rect.x += e.width - 1; - _target.fillRect(_rect, color); - return; - } - if (alpha >= 1 && !blend) - { - if (color < 0xFF000000) color = 0xFF000000 | color; - _rect.x = e.x - e.originX - _camera.x; - _rect.y = e.y - e.originY - _camera.y; - _rect.width = e.width; - _rect.height = e.height; - _target.fillRect(_rect, color); - return; - } - if (color >= 0xFF000000) color = 0xFFFFFF & color; - _graphics.clear(); - _graphics.beginFill(color, alpha); - _graphics.drawRect(e.x - e.originX - _camera.x, e.y - e.originY - _camera.y, e.width, e.height); - _target.draw(FP.sprite, null, null, blend); - } - - /** - * Draws a quadratic curve. - * @param x1 X start. - * @param y1 Y start. - * @param x2 X control point, used to determine the curve. - * @param y2 Y control point, used to determine the curve. - * @param x3 X finish. - * @param y3 Y finish. - * @param color Color of the curve - * @param alpha Alpha transparency. - */ - public static function curve(x1:int, y1:int, x2:int, y2:int, x3:int, y3:int, thick:Number = 1, color:uint = 0, alpha:Number = 1):void - { - _graphics.clear(); - _graphics.lineStyle(thick, color, alpha); - _graphics.moveTo(x1 - _camera.x, y1 - _camera.y); - _graphics.curveTo(x2 - _camera.x, y2 - _camera.y, x3 - _camera.x, y3 - _camera.y); - _target.draw(FP.sprite, null, null, blend); - } - - /** - * Draws a graphic object. - * @param g The Graphic to draw. - * @param x X position. - * @param y Y position. - */ - public static function graphic(g:Graphic, x:int = 0, y:int = 0):void - { - if (g.visible) - { - if (g.relative) - { - FP.point.x = x; - FP.point.y = y; - } - else FP.point.x = FP.point.y = 0; - FP.point2.x = FP.camera.x; - FP.point2.y = FP.camera.y; - g.render(_target, FP.point, FP.point2); - } - } - - /** - * Draws an Entity object. - * @param e The Entity to draw. - * @param x X position. - * @param y Y position. - * @param addEntityPosition Adds the Entity's x and y position to the target position. - */ - public static function entity(e:Entity, x:int = 0, y:int = 0, addEntityPosition:Boolean = false):void - { - if (e.visible && e.graphic) - { - if (addEntityPosition) graphic(e.graphic, x + e.x, y + e.y); - else graphic(e.graphic, x, y); - } - } - - // Drawing information. - /** @private */ private static var _target:BitmapData; - /** @private */ private static var _camera:Point; - /** @private */ private static var _graphics:Graphics = FP.sprite.graphics; - /** @private */ private static var _rect:Rectangle = FP.rect; - /** @private */ private static var _matrix:Matrix = new Matrix; - } -} \ No newline at end of file diff --git a/src/net/flashpunk/utils/Ease.as b/src/net/flashpunk/utils/Ease.as deleted file mode 100644 index b3c690d..0000000 --- a/src/net/flashpunk/utils/Ease.as +++ /dev/null @@ -1,214 +0,0 @@ -package net.flashpunk.utils -{ - /** - * Static class with useful easer functions that can be used by Tweens. - */ - public class Ease - { - /** Quadratic in. */ - public static function quadIn(t:Number):Number - { - return t * t; - } - - /** Quadratic out. */ - public static function quadOut(t:Number):Number - { - return -t * (t - 2); - } - - /** Quadratic in and out. */ - public static function quadInOut(t:Number):Number - { - return t <= .5 ? t * t * 2 : 1 - (--t) * t * 2; - } - - /** Cubic in. */ - public static function cubeIn(t:Number):Number - { - return t * t * t; - } - - /** Cubic out. */ - public static function cubeOut(t:Number):Number - { - return 1 + (--t) * t * t; - } - - /** Cubic in and out. */ - public static function cubeInOut(t:Number):Number - { - return t <= .5 ? t * t * t * 4 : 1 + (--t) * t * t * 4; - } - - /** Quart in. */ - public static function quartIn(t:Number):Number - { - return t * t * t * t; - } - - /** Quart out. */ - public static function quartOut(t:Number):Number - { - return 1 - (t-=1) * t * t * t; - } - - /** Quart in and out. */ - public static function quartInOut(t:Number):Number - { - return t <= .5 ? t * t * t * t * 8 : (1 - (t = t * 2 - 2) * t * t * t) / 2 + .5; - } - - /** Quint in. */ - public static function quintIn(t:Number):Number - { - return t * t * t * t * t; - } - - /** Quint out. */ - public static function quintOut(t:Number):Number - { - return (t = t - 1) * t * t * t * t + 1; - } - - /** Quint in and out. */ - public static function quintInOut(t:Number):Number - { - return ((t *= 2) < 1) ? (t * t * t * t * t) / 2 : ((t -= 2) * t * t * t * t + 2) / 2; - } - - /** Sine in. */ - public static function sineIn(t:Number):Number - { - return -Math.cos(PI2 * t) + 1; - } - - /** Sine out. */ - public static function sineOut(t:Number):Number - { - return Math.sin(PI2 * t); - } - - /** Sine in and out. */ - public static function sineInOut(t:Number):Number - { - return -Math.cos(PI * t) / 2 + .5; - } - - /** Bounce in. */ - public static function bounceIn(t:Number):Number - { - t = 1 - t; - if (t < B1) return 1 - 7.5625 * t * t; - if (t < B2) return 1 - (7.5625 * (t - B3) * (t - B3) + .75); - if (t < B4) return 1 - (7.5625 * (t - B5) * (t - B5) + .9375); - return 1 - (7.5625 * (t - B6) * (t - B6) + .984375); - } - - /** Bounce out. */ - public static function bounceOut(t:Number):Number - { - if (t < B1) return 7.5625 * t * t; - if (t < B2) return 7.5625 * (t - B3) * (t - B3) + .75; - if (t < B4) return 7.5625 * (t - B5) * (t - B5) + .9375; - return 7.5625 * (t - B6) * (t - B6) + .984375; - } - - /** Bounce in and out. */ - public static function bounceInOut(t:Number):Number - { - if (t < .5) - { - t = 1 - t * 2; - if (t < B1) return (1 - 7.5625 * t * t) / 2; - if (t < B2) return (1 - (7.5625 * (t - B3) * (t - B3) + .75)) / 2; - if (t < B4) return (1 - (7.5625 * (t - B5) * (t - B5) + .9375)) / 2; - return (1 - (7.5625 * (t - B6) * (t - B6) + .984375)) / 2; - } - t = t * 2 - 1; - if (t < B1) return (7.5625 * t * t) / 2 + .5; - if (t < B2) return (7.5625 * (t - B3) * (t - B3) + .75) / 2 + .5; - if (t < B4) return (7.5625 * (t - B5) * (t - B5) + .9375) / 2 + .5; - return (7.5625 * (t - B6) * (t - B6) + .984375) / 2 + .5; - } - - /** Circle in. */ - public static function circIn(t:Number):Number - { - return -(Math.sqrt(1 - t * t) - 1); - } - - /** Circle out. */ - public static function circOut(t:Number):Number - { - return Math.sqrt(1 - (t - 1) * (t - 1)); - } - - /** Circle in and out. */ - public static function circInOut(t:Number):Number - { - return t <= .5 ? (Math.sqrt(1 - t * t * 4) - 1) / -2 : (Math.sqrt(1 - (t * 2 - 2) * (t * 2 - 2)) + 1) / 2; - } - - /** Exponential in. */ - public static function expoIn(t:Number):Number - { - return Math.pow(2, 10 * (t - 1)); - } - - /** Exponential out. */ - public static function expoOut(t:Number):Number - { - return -Math.pow(2, -10 * t) + 1; - } - - /** Exponential in and out. */ - public static function expoInOut(t:Number):Number - { - return t < .5 ? Math.pow(2, 10 * (t * 2 - 1)) / 2 : (-Math.pow(2, -10 * (t * 2 - 1)) + 2) / 2; - } - - /** Back in. */ - public static function backIn(t:Number):Number - { - return t * t * (2.70158 * t - 1.70158); - } - - /** Back out. */ - public static function backOut(t:Number):Number - { - return 1 - (--t) * (t) * (-2.70158 * t - 1.70158); - } - - /** Back in and out. */ - public static function backInOut(t:Number):Number - { - t *= 2; - if (t < 1) return t * t * (2.70158 * t - 1.70158) / 2; - t --; - return (1 - (--t) * (t) * (-2.70158 * t - 1.70158)) / 2 + .5; - } - - // Easing constants. - /** @private */ private static const PI:Number = Math.PI; - /** @private */ private static const PI2:Number = Math.PI / 2; - /** @private */ private static const EL:Number = 2 * PI / .45; - /** @private */ private static const B1:Number = 1 / 2.75; - /** @private */ private static const B2:Number = 2 / 2.75; - /** @private */ private static const B3:Number = 1.5 / 2.75; - /** @private */ private static const B4:Number = 2.5 / 2.75; - /** @private */ private static const B5:Number = 2.25 / 2.75; - /** @private */ private static const B6:Number = 2.625 / 2.75; - - /** - * Operation of in/out easers: - * - * in(t) - * return t; - * out(t) - * return 1 - in(1 - t); - * inOut(t) - * return (t <= .5) ? in(t * 2) / 2 : out(t * 2 - 1) / 2 + .5; - */ - } -} \ No newline at end of file diff --git a/src/net/flashpunk/utils/Input.as b/src/net/flashpunk/utils/Input.as deleted file mode 100644 index 6589a2f..0000000 --- a/src/net/flashpunk/utils/Input.as +++ /dev/null @@ -1,296 +0,0 @@ -package net.flashpunk.utils -{ - import flash.display.Stage; - import flash.events.KeyboardEvent; - import flash.events.MouseEvent; - import flash.ui.Keyboard; - import net.flashpunk.*; - - /** - * Static class updated by Engine. Use for defining and checking keyboard/mouse input. - */ - public class Input - { - /** - * An updated string containing the last 100 characters pressed on the keyboard. - * Useful for creating text input fields, such as highscore entries, etc. - */ - public static var keyString:String = ""; - - /** - * The last key pressed. - */ - public static var lastKey:int; - - /** - * If the mouse button is down. - */ - public static var mouseDown:Boolean = false; - - /** - * If the mouse button is up. - */ - public static var mouseUp:Boolean = true; - - /** - * If the mouse button was pressed this frame. - */ - public static var mousePressed:Boolean = false; - - /** - * If the mouse button was released this frame. - */ - public static var mouseReleased:Boolean = false; - - /** - * If the mouse wheel was moved this frame. - */ - public static var mouseWheel:Boolean = false; - - /** - * If the mouse wheel was moved this frame, this was the delta. - */ - public static function get mouseWheelDelta():int - { - if (mouseWheel) - { - mouseWheel = false; - return _mouseWheelDelta; - } - return 0; - } - - /** - * X position of the mouse on the screen. - */ - public static function get mouseX():int - { - return FP.screen.mouseX; - } - - /** - * Y position of the mouse on the screen. - */ - public static function get mouseY():int - { - return FP.screen.mouseY; - } - - /** - * The absolute mouse x position on the screen (unscaled). - */ - public static function get mouseFlashX():int - { - return FP.stage.mouseX; - } - - /** - * The absolute mouse y position on the screen (unscaled). - */ - public static function get mouseFlashY():int - { - return FP.stage.mouseY; - } - - /** - * Defines a new input. - * @param name String to map the input to. - * @param ...keys The keys to use for the Input. - */ - public static function define(name:String, ...keys):void - { - _control[name] = Vector.(keys); - } - - /** - * If the input or key is held down. - * @param input An input name or key to check for. - * @return True or false. - */ - public static function check(input:*):Boolean - { - if (input is String) - { - var v:Vector. = _control[input], - i:int = v.length; - while (i --) - { - if (v[i] < 0) - { - if (_keyNum > 0) return true; - continue; - } - if (_key[v[i]]) return true; - } - return false; - } - return input < 0 ? _keyNum > 0 : _key[input]; - } - - /** - * If the input or key was pressed this frame. - * @param input An input name or key to check for. - * @return True or false. - */ - public static function pressed(input:*):Boolean - { - if (input is String) - { - var v:Vector. = _control[input], - i:int = v.length; - while (i --) - { - if ((v[i] < 0) ? _pressNum : _press.indexOf(v[i]) >= 0) return true; - } - return false; - } - return (input < 0) ? _pressNum : _press.indexOf(input) >= 0; - } - - /** - * If the input or key was released this frame. - * @param input An input name or key to check for. - * @return True or false. - */ - public static function released(input:*):Boolean - { - if (input is String) - { - var v:Vector. = _control[input], - i:int = v.length; - while (i --) - { - if ((v[i] < 0) ? _releaseNum : _release.indexOf(v[i]) >= 0) return true; - } - return false; - } - return (input < 0) ? _releaseNum : _release.indexOf(input) >= 0; - } - - /** - * Returns the keys mapped to the input name. - * @param name The input name. - * @return A Vector of keys. - */ - public static function keys(name:String):Vector. - { - return _control[name] as Vector.; - } - - /** @private Called by Engine to enable keyboard input on the stage. */ - public static function enable():void - { - if (!_enabled && FP.stage) - { - FP.stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown); - FP.stage.addEventListener(KeyboardEvent.KEY_UP, onKeyUp); - FP.stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown); - FP.stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp); - FP.stage.addEventListener(MouseEvent.MOUSE_WHEEL, onMouseWheel); - _enabled = true; - } - } - - /** @private Called by Engine to update the input. */ - public static function update():void - { - while (_pressNum --) _press[_pressNum] = -1; - _pressNum = 0; - while (_releaseNum --) _release[_releaseNum] = -1; - _releaseNum = 0; - if (mousePressed) mousePressed = false; - if (mouseReleased) mouseReleased = false; - } - - /** - * Clears all input states. - */ - public static function clear():void - { - _press.length = _pressNum = 0; - _release.length = _releaseNum = 0; - var i:int = _key.length; - while (i --) _key[i] = false; - _keyNum = 0; - } - - /** @private Event handler for key press. */ - private static function onKeyDown(e:KeyboardEvent = null):void - { - // get the keycode - var code:int = lastKey = e.keyCode; - - // update the keystring - if (code == Key.BACKSPACE) keyString = keyString.substring(0, keyString.length - 1); - else if ((code > 47 && code < 58) || (code > 64 && code < 91) || code == 32) - { - if (keyString.length > KEYSTRING_MAX) keyString = keyString.substring(1); - var char:String = String.fromCharCode(code); - if (e.shiftKey || Keyboard.capsLock) char = char.toLocaleUpperCase(); - else char = char.toLocaleLowerCase(); - keyString += char; - } - - // update the keystate - if (!_key[code]) - { - _key[code] = true; - _keyNum ++; - _press[_pressNum ++] = code; - } - } - - /** @private Event handler for key release. */ - private static function onKeyUp(e:KeyboardEvent):void - { - // get the keycode and update the keystate - var code:int = e.keyCode; - if (_key[code]) - { - _key[code] = false; - _keyNum --; - _release[_releaseNum ++] = code; - } - } - - /** @private Event handler for mouse press. */ - private static function onMouseDown(e:MouseEvent):void - { - if (!mouseDown) - { - mouseDown = true; - mouseUp = false; - mousePressed = true; - } - } - - /** @private Event handler for mouse release. */ - private static function onMouseUp(e:MouseEvent):void - { - mouseDown = false; - mouseUp = true; - mouseReleased = true; - } - - /** @private Event handler for mouse wheel events */ - private static function onMouseWheel(e:MouseEvent):void - { - mouseWheel = true; - _mouseWheelDelta = e.delta; - } - - // Max amount of characters stored by the keystring. - /** @private */ private static const KEYSTRING_MAX:uint = 100; - - // Input information. - /** @private */ private static var _enabled:Boolean = false; - /** @private */ private static var _key:Vector. = new Vector.(256); - /** @private */ private static var _keyNum:int = 0; - /** @private */ private static var _press:Vector. = new Vector.(256); - /** @private */ private static var _release:Vector. = new Vector.(256); - /** @private */ private static var _pressNum:int = 0; - /** @private */ private static var _releaseNum:int = 0; - /** @private */ private static var _control:Object = {}; - /** @private */ private static var _mouseWheelDelta:int = 0; - } -} \ No newline at end of file diff --git a/src/net/flashpunk/utils/Key.as b/src/net/flashpunk/utils/Key.as deleted file mode 100644 index 144cc90..0000000 --- a/src/net/flashpunk/utils/Key.as +++ /dev/null @@ -1,193 +0,0 @@ -package net.flashpunk.utils -{ - /** - * Contains static key constants to be used by Input. - */ - public class Key - { - public static const ANY:int = -1; - - public static const LEFT:int = 37; - public static const UP:int = 38; - public static const RIGHT:int = 39; - public static const DOWN:int = 40; - - public static const ENTER:int = 13; - public static const CONTROL:int = 17; - public static const SPACE:int = 32; - public static const SHIFT:int = 16; - public static const BACKSPACE:int = 8; - public static const CAPS_LOCK:int = 20; - public static const DELETE:int = 46; - public static const END:int = 35; - public static const ESCAPE:int = 27; - public static const HOME:int = 36; - public static const INSERT:int = 45; - public static const TAB:int = 9; - public static const PAGE_DOWN:int = 34; - public static const PAGE_UP:int = 33; - public static const LEFT_SQUARE_BRACKET:int = 219; - public static const RIGHT_SQUARE_BRACKET:int = 221; - - public static const A:int = 65; - public static const B:int = 66; - public static const C:int = 67; - public static const D:int = 68; - public static const E:int = 69; - public static const F:int = 70; - public static const G:int = 71; - public static const H:int = 72; - public static const I:int = 73; - public static const J:int = 74; - public static const K:int = 75; - public static const L:int = 76; - public static const M:int = 77; - public static const N:int = 78; - public static const O:int = 79; - public static const P:int = 80; - public static const Q:int = 81; - public static const R:int = 82; - public static const S:int = 83; - public static const T:int = 84; - public static const U:int = 85; - public static const V:int = 86; - public static const W:int = 87; - public static const X:int = 88; - public static const Y:int = 89; - public static const Z:int = 90; - - public static const F1:int = 112; - public static const F2:int = 113; - public static const F3:int = 114; - public static const F4:int = 115; - public static const F5:int = 116; - public static const F6:int = 117; - public static const F7:int = 118; - public static const F8:int = 119; - public static const F9:int = 120; - public static const F10:int = 121; - public static const F11:int = 122; - public static const F12:int = 123; - public static const F13:int = 124; - public static const F14:int = 125; - public static const F15:int = 126; - - public static const DIGIT_0:int = 48; - public static const DIGIT_1:int = 49; - public static const DIGIT_2:int = 50; - public static const DIGIT_3:int = 51; - public static const DIGIT_4:int = 52; - public static const DIGIT_5:int = 53; - public static const DIGIT_6:int = 54; - public static const DIGIT_7:int = 55; - public static const DIGIT_8:int = 56; - public static const DIGIT_9:int = 57; - - public static const NUMPAD_0:int = 96; - public static const NUMPAD_1:int = 97; - public static const NUMPAD_2:int = 98; - public static const NUMPAD_3:int = 99; - public static const NUMPAD_4:int = 100; - public static const NUMPAD_5:int = 101; - public static const NUMPAD_6:int = 102; - public static const NUMPAD_7:int = 103; - public static const NUMPAD_8:int = 104; - public static const NUMPAD_9:int = 105; - public static const NUMPAD_ADD:int = 107; - public static const NUMPAD_DECIMAL:int = 110; - public static const NUMPAD_DIVIDE:int = 111; - public static const NUMPAD_ENTER:int = 108; - public static const NUMPAD_MULTIPLY:int = 106; - public static const NUMPAD_SUBTRACT:int = 109; - - /** - * Returns the name of the key. - * @param char The key to name. - * @return The name. - */ - public static function name(char:int):String - { - if (char >= A && char <= Z) return String.fromCharCode(char); - if (char >= F1 && char <= F15) return "F" + String(char - 111); - if (char >= 96 && char <= 105) return "NUMPAD " + String(char - 96); - switch (char) - { - case LEFT: - return "LEFT"; - - case UP: - return "UP"; - - case RIGHT: - return "RIGHT"; - - case DOWN: - return "DOWN"; - - case ENTER: - return "ENTER"; - - case CONTROL: - return "CONTROL"; - - case SPACE: - return "SPACE"; - - case SHIFT: - return "SHIFT"; - - case BACKSPACE: - return "BACKSPACE"; - - case CAPS_LOCK: - return "CAPS LOCK"; - - case DELETE: - return "DELETE"; - - case END: - return "END"; - - case ESCAPE: - return "ESCAPE"; - - case HOME: - return "HOME"; - - case INSERT: - return "INSERT"; - - case TAB: - return "TAB"; - - case PAGE_DOWN: - return "PAGE DOWN"; - - case PAGE_UP: - return "PAGE UP"; - - case NUMPAD_ADD: - return "NUMPAD ADD"; - - case NUMPAD_DECIMAL: - return "NUMPAD DECIMAL"; - - case NUMPAD_DIVIDE: - return "NUMPAD DIVIDE"; - - case NUMPAD_ENTER: - return "NUMPAD ENTER"; - - case NUMPAD_MULTIPLY: - return "NUMPAD MULTIPLY"; - - case NUMPAD_SUBTRACT: - return "NUMPAD SUBTRACT"; - - default: - return String.fromCharCode(char); - } - return String.fromCharCode(char); - } - } -} \ No newline at end of file diff --git a/src/org/flixel/FlxBasic.as b/src/org/flixel/FlxBasic.as new file mode 100644 index 0000000..7078836 --- /dev/null +++ b/src/org/flixel/FlxBasic.as @@ -0,0 +1,154 @@ +package org.flixel +{ + /** + * This is a useful "generic" Flixel object. + * Both FlxObject and FlxGroup extend this class, + * as do the plugins. Has no size, position or graphical data. + * + * @author Adam Atomic + */ + public class FlxBasic + { + static internal var _ACTIVECOUNT:uint; + static internal var _VISIBLECOUNT:uint; + + /** + * IDs seem like they could be pretty useful, huh? + * They're not actually used for anything yet though. + */ + public var ID:int; + /** + * Controls whether update() and draw() are automatically called by FlxState/FlxGroup. + */ + public var exists:Boolean; + /** + * Controls whether update() is automatically called by FlxState/FlxGroup. + */ + public var active:Boolean; + /** + * Controls whether draw() is automatically called by FlxState/FlxGroup. + */ + public var visible:Boolean; + /** + * Useful state for many game objects - "dead" (!alive) vs alive. + * kill() and revive() both flip this switch (along with exists, but you can override that). + */ + public var alive:Boolean; + /** + * An array of camera objects that this object will use during draw(). + * This value will initialize itself during the first draw to automatically + * point at the main camera list out in FlxG unless you already set it. + * You can also change it afterward too, very flexible! + */ + public var cameras:Array; + /** + * 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; + + /** + * Instantiate the basic flixel object. + */ + public function FlxBasic() + { + ID = -1; + exists = true; + active = true; + visible = true; + alive = true; + ignoreDrawDebug = false; + } + + /** + * Override this function to null out variables or manually call + * destroy() on class members if necessary. + * Don't forget to call super.destroy()! + */ + public function destroy():void {} + + /** + * Pre-update is called right before update() on each object in the game loop. + */ + public function preUpdate():void + { + _ACTIVECOUNT++; + } + + /** + * Override this function to update your class's position and appearance. + * This is where most of your game rules and behavioral code will go. + */ + public function update():void + { + } + + /** + * Post-update is called right after update() on each object in the game loop. + */ + public function postUpdate():void + { + } + + /** + * Override this function to control how the object is drawn. + * Overriding draw() is rarely necessary, but can be very useful. + */ + public function draw():void + { + if(cameras == null) + cameras = FlxG.cameras; + var camera:FlxCamera; + var i:uint = 0; + var l:uint = cameras.length; + while(i < l) + { + camera = cameras[i++]; + _VISIBLECOUNT++; + if(FlxG.visualDebug && !ignoreDrawDebug) + drawDebug(camera); + } + } + + /** + * Override this function to draw custom "debug mode" graphics to the + * specified camera while the debugger's visual mode is toggled on. + * + * @param Camera Which camera to draw the debug visuals to. + */ + public function drawDebug(Camera:FlxCamera=null):void + { + } + + /** + * Handy function for "killing" game objects. + * Default behavior is to flag them as nonexistent AND dead. + * However, if you want the "corpse" to remain in the game, + * like to animate an effect or whatever, you should override this, + * setting only alive to false, and leaving exists true. + */ + public function kill():void + { + alive = false; + exists = false; + } + + /** + * Handy function for bringing game objects "back to life". Just sets alive and exists back to true. + * In practice, this function is most often called by FlxObject.reset(). + */ + public function revive():void + { + alive = true; + exists = true; + } + + /** + * Convert object to readable string name. Useful for debugging, save games, etc. + */ + public function toString():String + { + return FlxU.getClassName(this,true); + } + } +} diff --git a/src/org/flixel/FlxButton.as b/src/org/flixel/FlxButton.as new file mode 100755 index 0000000..ad2bbf1 --- /dev/null +++ b/src/org/flixel/FlxButton.as @@ -0,0 +1,355 @@ +package org.flixel +{ + import flash.events.MouseEvent; + + /** + * A simple button class that calls a function when clicked by the mouse. + * + * @author Adam Atomic + */ + public class FlxButton extends FlxSprite + { + [Embed(source="data/button.png")] protected var ImgDefaultButton:Class; + [Embed(source="data/beep.mp3")] protected var SndBeep:Class; + + /** + * Used with public variable status, means not highlighted or pressed. + */ + static public var NORMAL:uint = 0; + /** + * Used with public variable status, means highlighted (usually from mouse over). + */ + static public var HIGHLIGHT:uint = 1; + /** + * Used with public variable status, means pressed (usually from mouse click). + */ + static public var PRESSED:uint = 2; + + /** + * The text that appears on the button. + */ + public var label:FlxText; + /** + * Controls the offset (from top left) of the text from the button. + */ + public var labelOffset:FlxPoint; + /** + * This function is called when the button is released. + * We recommend assigning your main button behavior to this function + * via the FlxButton constructor. + */ + public var onUp:Function; + /** + * This function is called when the button is pressed down. + */ + public var onDown:Function; + /** + * This function is called when the mouse goes over the button. + */ + public var onOver:Function; + /** + * This function is called when the mouse leaves the button area. + */ + public var onOut:Function; + /** + * Shows the current state of the button. + */ + public var status:uint; + /** + * Set this to play a sound when the mouse goes over the button. + * We recommend using the helper function setSounds()! + */ + public var soundOver:FlxSound; + /** + * Set this to play a sound when the mouse leaves the button. + * We recommend using the helper function setSounds()! + */ + public var soundOut:FlxSound; + /** + * Set this to play a sound when the button is pressed down. + * We recommend using the helper function setSounds()! + */ + public var soundDown:FlxSound; + /** + * Set this to play a sound when the button is released. + * We recommend using the helper function setSounds()! + */ + public var soundUp:FlxSound; + + /** + * Used for checkbox-style behavior. + */ + protected var _onToggle:Boolean; + + /** + * Tracks whether or not the button is currently pressed. + */ + protected var _pressed:Boolean; + /** + * Whether or not the button has initialized itself yet. + */ + protected var _initialized:Boolean; + + /** + * Creates a new FlxButton object with a gray background + * and a callback function on the UI thread. + * + * @param X The X position of the button. + * @param Y The Y position of the button. + * @param Label The text that you want to appear on the button. + * @param OnClick The function to call whenever the button is clicked. + */ + public function FlxButton(X:Number=0,Y:Number=0,Label:String=null,OnClick:Function=null) + { + super(X,Y); + if(Label != null) + { + label = new FlxText(0,0,80,Label); + label.setFormat(null,8,0x333333,"center"); + labelOffset = new FlxPoint(-1,3); + } + loadGraphic(ImgDefaultButton,true,false,80,20); + + onUp = OnClick; + onDown = null; + onOut = null; + onOver = null; + + soundOver = null; + soundOut = null; + soundDown = null; + soundUp = null; + + status = NORMAL; + _onToggle = false; + _pressed = false; + _initialized = false; + } + + /** + * Called by the game state when state is changed (if this object belongs to the state) + */ + override public function destroy():void + { + if(FlxG.stage != null) + FlxG.stage.removeEventListener(MouseEvent.MOUSE_UP, onMouseUp); + if(label != null) + { + label.destroy(); + label = null; + } + onUp = null; + onDown = null; + onOut = null; + onOver = null; + if(soundOver != null) + soundOver.destroy(); + if(soundOut != null) + soundOut.destroy(); + if(soundDown != null) + soundDown.destroy(); + if(soundUp != null) + soundUp.destroy(); + super.destroy(); + } + + /** + * Since button uses its own mouse handler for thread reasons, + * we run a little pre-check here to make sure that we only add + * the mouse handler when it is actually safe to do so. + */ + override public function preUpdate():void + { + super.preUpdate(); + + if(!_initialized) + { + if(FlxG.stage != null) + { + FlxG.stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp); + _initialized = true; + } + } + } + + /** + * Called by the game loop automatically, handles mouseover and click detection. + */ + override public function update():void + { + updateButton(); //Basic button logic + + //Default button appearance is to simply update + // the label appearance based on animation frame. + if(label == null) + return; + switch(frame) + { + case HIGHLIGHT: //Extra behavior to accomodate checkbox logic. + label.alpha = 1.0; + break; + case PRESSED: + label.alpha = 0.5; + label.y++; + break; + case NORMAL: + default: + label.alpha = 0.8; + break; + } + } + + /** + * Basic button update logic + */ + protected function updateButton():void + { + //Figure out if the button is highlighted or pressed or what + // (ignore checkbox behavior for now). + if(FlxG.mouse.visible) + { + if(cameras == null) + cameras = FlxG.cameras; + var camera:FlxCamera; + var i:uint = 0; + var l:uint = cameras.length; + var offAll:Boolean = true; + while(i < l) + { + camera = cameras[i++] as FlxCamera; + FlxG.mouse.getWorldPosition(camera,_point); + if(overlapsPoint(_point,true,camera)) + { + offAll = false; + if(FlxG.mouse.justPressed()) + { + status = PRESSED; + if(onDown != null) + onDown(); + if(soundDown != null) + soundDown.play(true); + } + if(status == NORMAL) + { + status = HIGHLIGHT; + if(onOver != null) + onOver(); + if(soundOver != null) + soundOver.play(true); + } + } + } + if(offAll) + { + if(status != NORMAL) + { + if(onOut != null) + onOut(); + if(soundOut != null) + soundOut.play(true); + } + status = NORMAL; + } + } + + //Then if the label and/or the label offset exist, + // position them to match the button. + if(label != null) + { + label.x = x; + label.y = y; + } + if(labelOffset != null) + { + label.x += labelOffset.x; + label.y += labelOffset.y; + } + + //Then pick the appropriate frame of animation + if((status == HIGHLIGHT) && _onToggle) + frame = NORMAL; + else + frame = status; + } + + /** + * Just draws the button graphic and text label to the screen. + */ + override public function draw():void + { + super.draw(); + if(label != null) + { + label.scrollFactor = scrollFactor; + label.cameras = cameras; + label.draw(); + } + } + + /** + * Updates the size of the text field to match the button. + */ + override protected function resetHelpers():void + { + super.resetHelpers(); + if(label != null) + label.width = width; + } + + /** + * Set sounds to play during mouse-button interactions. + * These operations can be done manually as well, and the public + * sound variables can be used after this for more fine-tuning, + * such as positional audio, etc. + * + * @param SoundOver What embedded sound effect to play when the mouse goes over the button. Default is null, or no sound. + * @param SoundOverVolume How load the that sound should be. + * @param SoundOut What embedded sound effect to play when the mouse leaves the button area. Default is null, or no sound. + * @param SoundOutVolume How load the that sound should be. + * @param SoundDown What embedded sound effect to play when the mouse presses the button down. Default is null, or no sound. + * @param SoundDownVolume How load the that sound should be. + * @param SoundUp What embedded sound effect to play when the mouse releases the button. Default is null, or no sound. + * @param SoundUpVolume How load the that sound should be. + */ + public function setSounds(SoundOver:Class=null, SoundOverVolume:Number=1.0, SoundOut:Class=null, SoundOutVolume:Number=1.0, SoundDown:Class=null, SoundDownVolume:Number=1.0, SoundUp:Class=null, SoundUpVolume:Number=1.0):void + { + if(SoundOver != null) + soundOver = FlxG.loadSound(SoundOver, SoundOverVolume); + if(SoundOut != null) + soundOut = FlxG.loadSound(SoundOut, SoundOutVolume); + if(SoundDown != null) + soundDown = FlxG.loadSound(SoundDown, SoundDownVolume); + if(SoundUp != null) + soundUp = FlxG.loadSound(SoundUp, SoundUpVolume); + } + + /** + * Use this to toggle checkbox-style behavior. + */ + public function get on():Boolean + { + return _onToggle; + } + + /** + * @private + */ + public function set on(On:Boolean):void + { + _onToggle = On; + } + + /** + * Internal function for handling the actual callback call (for UI thread dependent calls like FlxU.openURL()). + */ + protected function onMouseUp(event:MouseEvent):void + { + if(!exists || !visible || !active || (status != PRESSED)) + return; + if(onUp != null) + onUp(); + if(soundUp != null) + soundUp.play(true); + } + } +} diff --git a/src/org/flixel/FlxCamera.as b/src/org/flixel/FlxCamera.as new file mode 100644 index 0000000..c69222d --- /dev/null +++ b/src/org/flixel/FlxCamera.as @@ -0,0 +1,706 @@ +package org.flixel +{ + import flash.display.Bitmap; + import flash.display.BitmapData; + import flash.display.Sprite; + import flash.geom.ColorTransform; + import flash.geom.Point; + import flash.geom.Rectangle; + + /** + * The camera class is used to display the game's visuals in the Flash player. + * By default one camera is created automatically, that is the same size as the Flash player. + * You can add more cameras or even replace the main camera using utilities in FlxG. + * + * @author Adam Atomic + */ + public class FlxCamera extends FlxBasic + { + /** + * Camera "follow" style preset: camera has no deadzone, just tracks the focus object directly. + */ + static public const STYLE_LOCKON:uint = 0; + /** + * Camera "follow" style preset: camera deadzone is narrow but tall. + */ + static public const STYLE_PLATFORMER:uint = 1; + /** + * Camera "follow" style preset: camera deadzone is a medium-size square around the focus object. + */ + static public const STYLE_TOPDOWN:uint = 2; + /** + * Camera "follow" style preset: camera deadzone is a small square around the focus object. + */ + static public const STYLE_TOPDOWN_TIGHT:uint = 3; + + /** + * Camera "shake" effect preset: shake camera on both the X and Y axes. + */ + static public const SHAKE_BOTH_AXES:uint = 0; + /** + * Camera "shake" effect preset: shake camera on the X axis only. + */ + static public const SHAKE_HORIZONTAL_ONLY:uint = 1; + /** + * Camera "shake" effect preset: shake camera on the Y axis only. + */ + static public const SHAKE_VERTICAL_ONLY:uint = 2; + + /** + * While you can alter the zoom of each camera after the fact, + * this variable determines what value the camera will start at when created. + */ + static public var defaultZoom:Number; + + /** + * The X position of this camera's display. Zoom does NOT affect this number. + * Measured in pixels from the left side of the flash window. + */ + public var x:Number; + /** + * The Y position of this camera's display. Zoom does NOT affect this number. + * Measured in pixels from the top of the flash window. + */ + public var y:Number; + /** + * How wide the camera display is, in game pixels. + */ + public var width:uint; + /** + * How tall the camera display is, in game pixels. + */ + public var height:uint; + /** + * Tells the camera to follow this FlxObject object around. + */ + public var target:FlxObject; + /** + * You can assign a "dead zone" to the camera in order to better control its movement. + * The camera will always keep the focus object inside the dead zone, + * unless it is bumping up against the bounds rectangle's edges. + * The deadzone's coordinates are measured from the camera's upper left corner in game pixels. + * For rapid prototyping, you can use the preset deadzones (e.g. STYLE_PLATFORMER) with follow(). + */ + public var deadzone:FlxRect; + /** + * The edges of the camera's range, i.e. where to stop scrolling. + * Measured in game pixels and world coordinates. + */ + public var bounds:FlxRect; + + /** + * Stores the basic parallax scrolling values. + */ + public var scroll:FlxPoint; + /** + * The actual bitmap data of the camera display itself. + */ + public var buffer:BitmapData; + /** + * The natural background color of the camera. Defaults to FlxG.bgColor. + * NOTE: can be transparent for crazy FX! + */ + public var bgColor:uint; + /** + * Sometimes it's easier to just work with a FlxSprite than it is to work + * directly with the BitmapData buffer. This sprite reference will + * allow you to do exactly that. + */ + public var screen:FlxSprite; + + /** + * Indicates how far the camera is zoomed in. + */ + protected var _zoom:Number; + /** + * Internal, to help avoid costly allocations. + */ + protected var _point:FlxPoint; + /** + * Internal, help with color transforming the flash bitmap. + */ + protected var _color:uint; + + /** + * Internal, used to render buffer to screen space. + */ + protected var _flashBitmap:Bitmap; + /** + * Internal, used to render buffer to screen space. + */ + internal var _flashSprite:Sprite; + /** + * Internal, used to render buffer to screen space. + */ + internal var _flashOffsetX:Number; + /** + * Internal, used to render buffer to screen space. + */ + internal var _flashOffsetY:Number; + /** + * Internal, used to render buffer to screen space. + */ + protected var _flashRect:Rectangle; + /** + * Internal, used to render buffer to screen space. + */ + protected var _flashPoint:Point; + /** + * Internal, used to control the "flash" special effect. + */ + protected var _fxFlashColor:uint; + /** + * Internal, used to control the "flash" special effect. + */ + protected var _fxFlashDuration:Number; + /** + * Internal, used to control the "flash" special effect. + */ + protected var _fxFlashComplete:Function; + /** + * Internal, used to control the "flash" special effect. + */ + protected var _fxFlashAlpha:Number; + /** + * Internal, used to control the "fade" special effect. + */ + protected var _fxFadeColor:uint; + /** + * Internal, used to control the "fade" special effect. + */ + protected var _fxFadeDuration:Number; + /** + * Internal, used to control the "fade" special effect. + */ + protected var _fxFadeComplete:Function; + /** + * Internal, used to control the "fade" special effect. + */ + protected var _fxFadeAlpha:Number; + /** + * Internal, used to control the "shake" special effect. + */ + protected var _fxShakeIntensity:Number; + /** + * Internal, used to control the "shake" special effect. + */ + protected var _fxShakeDuration:Number; + /** + * Internal, used to control the "shake" special effect. + */ + protected var _fxShakeComplete:Function; + /** + * Internal, used to control the "shake" special effect. + */ + protected var _fxShakeOffset:FlxPoint; + /** + * Internal, used to control the "shake" special effect. + */ + protected var _fxShakeDirection:uint; + /** + * Internal helper variable for doing better wipes/fills between renders. + */ + protected var _fill:BitmapData; + + /** + * Instantiates a new camera at the specified location, with the specified size and zoom level. + * + * @param X X location of the camera's display in pixels. Uses native, 1:1 resolution, ignores zoom. + * @param Y Y location of the camera's display in pixels. Uses native, 1:1 resolution, ignores zoom. + * @param Width The width of the camera display in pixels. + * @param Height The height of the camera display in pixels. + * @param Zoom The initial zoom level of the camera. A zoom level of 2 will make all pixels display at 2x resolution. + */ + public function FlxCamera(X:int,Y:int,Width:int,Height:int,Zoom:Number=0) + { + x = X; + y = Y; + width = Width; + height = Height; + target = null; + deadzone = null; + scroll = new FlxPoint(); + _point = new FlxPoint(); + bounds = null; + screen = new FlxSprite(); + screen.makeGraphic(width,height,0,true); + screen.setOriginToCorner(); + buffer = screen.pixels; + bgColor = FlxG.bgColor; + _color = 0xffffff; + + _flashBitmap = new Bitmap(buffer); + _flashBitmap.x = -width*0.5; + _flashBitmap.y = -height*0.5; + _flashSprite = new Sprite(); + zoom = Zoom; //sets the scale of flash sprite, which in turn loads flashoffset values + _flashOffsetX = width*0.5*zoom; + _flashOffsetY = height*0.5*zoom; + _flashSprite.x = x + _flashOffsetX; + _flashSprite.y = y + _flashOffsetY; + _flashSprite.addChild(_flashBitmap); + _flashRect = new Rectangle(0,0,width,height); + _flashPoint = new Point(); + + _fxFlashColor = 0; + _fxFlashDuration = 0.0; + _fxFlashComplete = null; + _fxFlashAlpha = 0.0; + + _fxFadeColor = 0; + _fxFadeDuration = 0.0; + _fxFadeComplete = null; + _fxFadeAlpha = 0.0; + + _fxShakeIntensity = 0.0; + _fxShakeDuration = 0.0; + _fxShakeComplete = null; + _fxShakeOffset = new FlxPoint(); + _fxShakeDirection = 0; + + _fill = new BitmapData(width,height,true,0); + } + + /** + * Clean up memory. + */ + override public function destroy():void + { + screen.destroy(); + screen = null; + target = null; + scroll = null; + deadzone = null; + bounds = null; + buffer = null; + _flashBitmap = null; + _flashRect = null; + _flashPoint = null; + _fxFlashComplete = null; + _fxFadeComplete = null; + _fxShakeComplete = null; + _fxShakeOffset = null; + _fill = null; + } + + /** + * Updates the camera scroll as well as special effects like screen-shake or fades. + */ + override public function update():void + { + //Either follow the object closely, + //or doublecheck our deadzone and update accordingly. + if(target != null) + { + if(deadzone == null) + focusOn(target.getMidpoint(_point)); + else + { + var edge:Number; + var targetX:Number = target.x + ((target.x > 0)?0.0000001:-0.0000001); + var targetY:Number = target.y + ((target.y > 0)?0.0000001:-0.0000001); + + edge = targetX - deadzone.x; + if(scroll.x > edge) + scroll.x = edge; + edge = targetX + target.width - deadzone.x - deadzone.width; + if(scroll.x < edge) + scroll.x = edge; + + edge = targetY - deadzone.y; + if(scroll.y > edge) + scroll.y = edge; + edge = targetY + target.height - deadzone.y - deadzone.height; + if(scroll.y < edge) + scroll.y = edge; + } + } + + //Make sure we didn't go outside the camera's bounds + if(bounds != null) + { + if(scroll.x < bounds.left) + scroll.x = bounds.left; + if(scroll.x > bounds.right - width) + scroll.x = bounds.right - width; + if(scroll.y < bounds.top) + scroll.y = bounds.top; + if(scroll.y > bounds.bottom - height) + scroll.y = bounds.bottom - height; + } + + //Update the "flash" special effect + if(_fxFlashAlpha > 0.0) + { + _fxFlashAlpha -= FlxG.elapsed/_fxFlashDuration; + if((_fxFlashAlpha <= 0) && (_fxFlashComplete != null)) + _fxFlashComplete(); + } + + //Update the "fade" special effect + if((_fxFadeAlpha > 0.0) && (_fxFadeAlpha < 1.0)) + { + _fxFadeAlpha += FlxG.elapsed/_fxFadeDuration; + if(_fxFadeAlpha >= 1.0) + { + _fxFadeAlpha = 1.0; + if(_fxFadeComplete != null) + _fxFadeComplete(); + } + } + + //Update the "shake" special effect + if(_fxShakeDuration > 0) + { + _fxShakeDuration -= FlxG.elapsed; + if(_fxShakeDuration <= 0) + { + _fxShakeOffset.make(); + if(_fxShakeComplete != null) + _fxShakeComplete(); + } + else + { + if((_fxShakeDirection == SHAKE_BOTH_AXES) || (_fxShakeDirection == SHAKE_HORIZONTAL_ONLY)) + _fxShakeOffset.x = (FlxG.random()*_fxShakeIntensity*width*2-_fxShakeIntensity*width)*_zoom; + if((_fxShakeDirection == SHAKE_BOTH_AXES) || (_fxShakeDirection == SHAKE_VERTICAL_ONLY)) + _fxShakeOffset.y = (FlxG.random()*_fxShakeIntensity*height*2-_fxShakeIntensity*height)*_zoom; + } + } + } + + /** + * Tells this camera object what FlxObject to track. + * + * @param Target The object you want the camera to track. Set to null to not follow anything. + * @param Style Leverage one of the existing "deadzone" presets. If you use a custom deadzone, ignore this parameter and manually specify the deadzone after calling follow(). + */ + public function follow(Target:FlxObject, Style:uint=STYLE_LOCKON):void + { + target = Target; + var helper:Number; + switch(Style) + { + case STYLE_PLATFORMER: + var w:Number = width/8; + var h:Number = height/3; + deadzone = new FlxRect((width-w)/2,(height-h)/2 - h*0.25,w,h); + break; + case STYLE_TOPDOWN: + helper = FlxU.max(width,height)/4; + deadzone = new FlxRect((width-helper)/2,(height-helper)/2,helper,helper); + break; + case STYLE_TOPDOWN_TIGHT: + helper = FlxU.max(width,height)/8; + deadzone = new FlxRect((width-helper)/2,(height-helper)/2,helper,helper); + break; + case STYLE_LOCKON: + default: + deadzone = null; + break; + } + } + + /** + * Move the camera focus to this location instantly. + * + * @param Point Where you want the camera to focus. + */ + public function focusOn(Point:FlxPoint):void + { + Point.x += (Point.x > 0)?0.0000001:-0.0000001; + Point.y += (Point.y > 0)?0.0000001:-0.0000001; + scroll.make(Point.x - width*0.5,Point.y - height*0.5); + } + + /** + * Specify the boundaries of the level or where the camera is allowed to move. + * + * @param X The smallest X value of your level (usually 0). + * @param Y The smallest Y value of your level (usually 0). + * @param Width The largest X value of your level (usually the level width). + * @param Height The largest Y value of your level (usually the level height). + * @param UpdateWorld Whether the global quad-tree's dimensions should be updated to match (default: false). + */ + public function setBounds(X:Number=0, Y:Number=0, Width:Number=0, Height:Number=0, UpdateWorld:Boolean=false):void + { + if(bounds == null) + bounds = new FlxRect(); + bounds.make(X,Y,Width,Height); + if(UpdateWorld) + FlxG.worldBounds.copyFrom(bounds); + update(); + } + + /** + * The screen is filled with this color and gradually returns to normal. + * + * @param Color The color you want to use. + * @param Duration How long it takes for the flash to fade. + * @param OnComplete A function you want to run when the flash finishes. + * @param Force Force the effect to reset. + */ + public function flash(Color:uint=0xffffffff, Duration:Number=1, OnComplete:Function=null, Force:Boolean=false):void + { + if(!Force && (_fxFlashAlpha > 0.0)) + return; + _fxFlashColor = Color; + if(Duration <= 0) + Duration = Number.MIN_VALUE; + _fxFlashDuration = Duration; + _fxFlashComplete = OnComplete; + _fxFlashAlpha = 1.0; + } + + /** + * The screen is gradually filled with this color. + * + * @param Color The color you want to use. + * @param Duration How long it takes for the fade to finish. + * @param OnComplete A function you want to run when the fade finishes. + * @param Force Force the effect to reset. + */ + public function fade(Color:uint=0xff000000, Duration:Number=1, OnComplete:Function=null, Force:Boolean=false):void + { + if(!Force && (_fxFadeAlpha > 0.0)) + return; + _fxFadeColor = Color; + if(Duration <= 0) + Duration = Number.MIN_VALUE; + _fxFadeDuration = Duration; + _fxFadeComplete = OnComplete; + _fxFadeAlpha = Number.MIN_VALUE; + } + + /** + * A simple screen-shake effect. + * + * @param Intensity Percentage of screen size representing the maximum distance that the screen can move while shaking. + * @param Duration The length in seconds that the shaking effect should last. + * @param OnComplete A function you want to run when the shake effect finishes. + * @param Force Force the effect to reset (default = true, unlike flash() and fade()!). + * @param Direction Whether to shake on both axes, just up and down, or just side to side (use class constants SHAKE_BOTH_AXES, SHAKE_VERTICAL_ONLY, or SHAKE_HORIZONTAL_ONLY). + */ + public function shake(Intensity:Number=0.05, Duration:Number=0.5, OnComplete:Function=null, Force:Boolean=true, Direction:uint=SHAKE_BOTH_AXES):void + { + if(!Force && ((_fxShakeOffset.x != 0) || (_fxShakeOffset.y != 0))) + return; + _fxShakeIntensity = Intensity; + _fxShakeDuration = Duration; + _fxShakeComplete = OnComplete; + _fxShakeDirection = Direction; + _fxShakeOffset.make(); + } + + /** + * Just turns off all the camera effects instantly. + */ + public function stopFX():void + { + _fxFlashAlpha = 0.0; + _fxFadeAlpha = 0.0; + _fxShakeDuration = 0; + _flashSprite.x = x + width*0.5; + _flashSprite.y = y + height*0.5; + } + + /** + * Copy the bounds, focus object, and deadzone info from an existing camera. + * + * @param Camera The camera you want to copy from. + * + * @return A reference to this FlxCamera object. + */ + public function copyFrom(Camera:FlxCamera):FlxCamera + { + if(Camera.bounds == null) + bounds = null; + else + { + if(bounds == null) + bounds = new FlxRect(); + bounds.copyFrom(Camera.bounds); + } + target = Camera.target; + if(target != null) + { + if(Camera.deadzone == null) + deadzone = null; + else + { + if(deadzone == null) + deadzone = new FlxRect(); + deadzone.copyFrom(Camera.deadzone); + } + } + return this; + } + + /** + * The zoom level of this camera. 1 = 1:1, 2 = 2x zoom, etc. + */ + public function get zoom():Number + { + return _zoom; + } + + /** + * @private + */ + public function set zoom(Zoom:Number):void + { + if(Zoom == 0) + _zoom = defaultZoom; + else + _zoom = Zoom; + setScale(_zoom,_zoom); + } + + /** + * The alpha value of this camera display (a Number between 0.0 and 1.0). + */ + public function get alpha():Number + { + return _flashBitmap.alpha; + } + + /** + * @private + */ + public function set alpha(Alpha:Number):void + { + _flashBitmap.alpha = Alpha; + } + + /** + * The angle of the camera display (in degrees). + * Currently yields weird display results, + * since cameras aren't nested in an extra display object yet. + */ + public function get angle():Number + { + return _flashSprite.rotation; + } + + /** + * @private + */ + public function set angle(Angle:Number):void + { + _flashSprite.rotation = Angle; + } + + /** + * The color tint of the camera display. + */ + public function get color():uint + { + return _color; + } + + /** + * @private + */ + public function set color(Color:uint):void + { + _color = Color; + var colorTransform:ColorTransform = _flashBitmap.transform.colorTransform; + colorTransform.redMultiplier = (_color>>16)*0.00392; + colorTransform.greenMultiplier = (_color>>8&0xff)*0.00392; + colorTransform.blueMultiplier = (_color&0xff)*0.00392; + _flashBitmap.transform.colorTransform = colorTransform; + } + + /** + * Whether the camera display is smooth and filtered, or chunky and pixelated. + * Default behavior is chunky-style. + */ + public function get antialiasing():Boolean + { + return _flashBitmap.smoothing; + } + + /** + * @private + */ + public function set antialiasing(Antialiasing:Boolean):void + { + _flashBitmap.smoothing = Antialiasing; + } + + /** + * The scale of the camera object, irrespective of zoom. + * Currently yields weird display results, + * since cameras aren't nested in an extra display object yet. + */ + public function getScale():FlxPoint + { + return _point.make(_flashSprite.scaleX,_flashSprite.scaleY); + } + + /** + * @private + */ + public function setScale(X:Number,Y:Number):void + { + _flashSprite.scaleX = X; + _flashSprite.scaleY = Y; + } + + /** + * Fetches a reference to the Flash Sprite object + * that contains the camera display in the Flash display list. + * Uses include 3D projection, advanced display list modification, and more. + * NOTE: We don't recommend modifying this directly unless you are + * fairly experienced. For simple changes to the camera display, + * like scaling, rotation, and color tinting, we recommend + * using the existing FlxCamera variables. + * + * @return A Flash Sprite object containing the camera display. + */ + public function getContainerSprite():Sprite + { + return _flashSprite; + } + + /** + * Fill the camera with the specified color. + * + * @param Color The color to fill with in 0xAARRGGBB hex format. + * @param BlendAlpha Whether to blend the alpha value or just wipe the previous contents. Default is true. + */ + public function fill(Color:uint,BlendAlpha:Boolean=true):void + { + _fill.fillRect(_flashRect,Color); + buffer.copyPixels(_fill,_flashRect,_flashPoint,null,null,BlendAlpha); + } + + /** + * Internal helper function, handles the actual drawing of all the special effects. + */ + internal function drawFX():void + { + var alphaComponent:Number; + + //Draw the "flash" special effect onto the buffer + if(_fxFlashAlpha > 0.0) + { + alphaComponent = _fxFlashColor>>24; + fill((uint(((alphaComponent <= 0)?0xff:alphaComponent)*_fxFlashAlpha)<<24)+(_fxFlashColor&0x00ffffff)); + } + + //Draw the "fade" special effect onto the buffer + if(_fxFadeAlpha > 0.0) + { + alphaComponent = _fxFadeColor>>24; + fill((uint(((alphaComponent <= 0)?0xff:alphaComponent)*_fxFadeAlpha)<<24)+(_fxFadeColor&0x00ffffff)); + } + + if((_fxShakeOffset.x != 0) || (_fxShakeOffset.y != 0)) + { + _flashSprite.x = x + _flashOffsetX + _fxShakeOffset.x; + _flashSprite.y = y + _flashOffsetY + _fxShakeOffset.y; + } + } + } +} diff --git a/src/org/flixel/FlxEmitter.as b/src/org/flixel/FlxEmitter.as new file mode 100644 index 0000000..f39ee31 --- /dev/null +++ b/src/org/flixel/FlxEmitter.as @@ -0,0 +1,381 @@ +package org.flixel +{ + + /** + * FlxEmitter is a lightweight particle emitter. + * It can be used for one-time explosions or for + * continuous fx like rain and fire. FlxEmitter + * is not optimized or anything; all it does is launch + * FlxParticle objects out at set intervals + * by setting their positions and velocities accordingly. + * It is easy to use and relatively efficient, + * relying on FlxGroup's RECYCLE POWERS. + * + * @author Adam Atomic + */ + public class FlxEmitter extends FlxGroup + { + /** + * The X position of the top left corner of the emitter in world space. + */ + public var x:Number; + /** + * The Y position of the top left corner of emitter in world space. + */ + public var y:Number; + /** + * The width of the emitter. Particles can be randomly generated from anywhere within this box. + */ + public var width:Number; + /** + * The height of the emitter. Particles can be randomly generated from anywhere within this box. + */ + public var height:Number; + /** + * The minimum possible velocity of a particle. + * The default value is (-100,-100). + */ + public var minParticleSpeed:FlxPoint; + /** + * The maximum possible velocity of a particle. + * The default value is (100,100). + */ + public var maxParticleSpeed:FlxPoint; + /** + * The X and Y drag component of particles launched from the emitter. + */ + public var particleDrag:FlxPoint; + /** + * The minimum possible angular velocity of a particle. The default value is -360. + * NOTE: rotating particles are more expensive to draw than non-rotating ones! + */ + public var minRotation:Number; + /** + * The maximum possible angular velocity of a particle. The default value is 360. + * NOTE: rotating particles are more expensive to draw than non-rotating ones! + */ + public var maxRotation:Number; + /** + * Sets the acceleration.y member of each particle to this value on launch. + */ + public var gravity:Number; + /** + * Determines whether the emitter is currently emitting particles. + * It is totally safe to directly toggle this. + */ + public var on:Boolean; + /** + * How often a particle is emitted (if emitter is started with Explode == false). + */ + public var frequency:Number; + /** + * How long each particle lives once it is emitted. + * Set lifespan to 'zero' for particles to live forever. + */ + public var lifespan:Number; + /** + * How much each particle should bounce. 1 = full bounce, 0 = no bounce. + */ + public var bounce:Number; + /** + * Set your own particle class type here. + * Default is FlxParticle. + */ + public var particleClass:Class; + /** + * Internal helper for deciding how many particles to launch. + */ + protected var _quantity:uint; + /** + * Internal helper for the style of particle emission (all at once, or one at a time). + */ + protected var _explode:Boolean; + /** + * Internal helper for deciding when to launch particles or kill them. + */ + protected var _timer:Number; + /** + * Internal counter for figuring out how many particles to launch. + */ + protected var _counter:uint; + /** + * Internal point object, handy for reusing for memory mgmt purposes. + */ + protected var _point:FlxPoint; + + /** + * Creates a new FlxEmitter object at a specific position. + * Does NOT automatically generate or attach particles! + * + * @param X The X position of the emitter. + * @param Y The Y position of the emitter. + * @param Size Optional, specifies a maximum capacity for this emitter. + */ + public function FlxEmitter(X:Number=0, Y:Number=0, Size:Number=0) + { + super(Size); + x = X; + y = Y; + width = 0; + height = 0; + minParticleSpeed = new FlxPoint(-100,-100); + maxParticleSpeed = new FlxPoint(100,100); + minRotation = -360; + maxRotation = 360; + gravity = 0; + particleClass = null; + particleDrag = new FlxPoint(); + frequency = 0.1; + lifespan = 3; + bounce = 0; + _quantity = 0; + _counter = 0; + _explode = true; + on = false; + _point = new FlxPoint(); + } + + /** + * Clean up memory. + */ + override public function destroy():void + { + minParticleSpeed = null; + maxParticleSpeed = null; + particleDrag = null; + particleClass = null; + _point = null; + super.destroy(); + } + + /** + * This function generates a new array of particle sprites to attach to the emitter. + * + * @param Graphics If you opted to not pre-configure an array of FlxSprite objects, you can simply pass in a particle image or sprite sheet. + * @param Quantity The number of particles to generate when using the "create from image" option. + * @param BakedRotations How many frames of baked rotation to use (boosts performance). Set to zero to not use baked rotations. + * @param Multiple Whether the image in the Graphics param is a single particle or a bunch of particles (if it's a bunch, they need to be square!). + * @param Collide Whether the particles should be flagged as not 'dead' (non-colliding particles are higher performance). 0 means no collisions, 0-1 controls scale of particle's bounding box. + * + * @return This FlxEmitter instance (nice for chaining stuff together, if you're into that). + */ + public function makeParticles(Graphics:Class, Quantity:uint=50, BakedRotations:uint=16, Multiple:Boolean=false, Collide:Number=0.8):FlxEmitter + { + maxSize = Quantity; + + var totalFrames:uint = 1; + if(Multiple) + { + var sprite:FlxSprite = new FlxSprite(); + sprite.loadGraphic(Graphics,true); + totalFrames = sprite.frames; + sprite.destroy(); + } + + var randomFrame:uint; + var particle:FlxParticle; + var i:uint = 0; + while(i < Quantity) + { + if(particleClass == null) + particle = new FlxParticle(); + else + particle = new particleClass(); + if(Multiple) + { + randomFrame = FlxG.random()*totalFrames; + if(BakedRotations > 0) + particle.loadRotatedGraphic(Graphics,BakedRotations,randomFrame); + else + { + particle.loadGraphic(Graphics,true); + particle.frame = randomFrame; + } + } + else + { + if(BakedRotations > 0) + particle.loadRotatedGraphic(Graphics,BakedRotations); + else + particle.loadGraphic(Graphics); + } + if(Collide > 0) + { + particle.width *= Collide; + particle.height *= Collide; + particle.centerOffsets(); + } + else + particle.allowCollisions = FlxObject.NONE; + particle.exists = false; + add(particle); + i++; + } + return this; + } + + /** + * Called automatically by the game loop, decides when to launch particles and when to "die". + */ + override public function update():void + { + if(on) + { + if(_explode) + { + on = false; + var i:uint = 0; + var l:uint = _quantity; + if((l <= 0) || (l > length)) + l = length; + while(i < l) + { + emitParticle(); + i++; + } + _quantity = 0; + } + else + { + _timer += FlxG.elapsed; + while((frequency > 0) && (_timer > frequency) && on) + { + _timer -= frequency; + emitParticle(); + if((_quantity > 0) && (++_counter >= _quantity)) + { + on = false; + _quantity = 0; + } + } + } + } + super.update(); + } + + /** + * Call this function to turn off all the particles and the emitter. + */ + override public function kill():void + { + on = false; + super.kill(); + } + + /** + * Call this function to start emitting particles. + * + * @param Explode Whether the particles should all burst out at once. + * @param Lifespan How long each particle lives once emitted. 0 = forever. + * @param Frequency Ignored if Explode is set to true. Frequency is how often to emit a particle. 0 = never emit, 0.1 = 1 particle every 0.1 seconds, 5 = 1 particle every 5 seconds. + * @param Quantity How many particles to launch. 0 = "all of the particles". + */ + public function start(Explode:Boolean=true,Lifespan:Number=0,Frequency:Number=0.1,Quantity:uint=0):void + { + revive(); + visible = true; + on = true; + + _explode = Explode; + lifespan = Lifespan; + frequency = Frequency; + _quantity += Quantity; + + _counter = 0; + _timer = 0; + } + + /** + * This function can be used both internally and externally to emit the next particle. + */ + public function emitParticle():void + { + var particle:FlxParticle = recycle(FlxParticle) as FlxParticle; + particle.lifespan = lifespan; + particle.elasticity = bounce; + particle.reset(x - (particle.width>>1) + FlxG.random()*width, y - (particle.height>>1) + FlxG.random()*height); + particle.visible = true; + + if(minParticleSpeed.x != maxParticleSpeed.x) + particle.velocity.x = minParticleSpeed.x + FlxG.random()*(maxParticleSpeed.x-minParticleSpeed.x); + else + particle.velocity.x = minParticleSpeed.x; + if(minParticleSpeed.y != maxParticleSpeed.y) + particle.velocity.y = minParticleSpeed.y + FlxG.random()*(maxParticleSpeed.y-minParticleSpeed.y); + else + particle.velocity.y = minParticleSpeed.y; + particle.acceleration.y = gravity; + + if(minRotation != maxRotation) + particle.angularVelocity = minRotation + FlxG.random()*(maxRotation-minRotation); + else + particle.angularVelocity = minRotation; + if(particle.angularVelocity != 0) + particle.angle = FlxG.random()*360-180; + + particle.drag.x = particleDrag.x; + particle.drag.y = particleDrag.y; + particle.onEmit(); + } + + /** + * A more compact way of setting the width and height of the emitter. + * + * @param Width The desired width of the emitter (particles are spawned randomly within these dimensions). + * @param Height The desired height of the emitter. + */ + public function setSize(Width:uint,Height:uint):void + { + width = Width; + height = Height; + } + + /** + * A more compact way of setting the X velocity range of the emitter. + * + * @param Min The minimum value for this range. + * @param Max The maximum value for this range. + */ + public function setXSpeed(Min:Number=0,Max:Number=0):void + { + minParticleSpeed.x = Min; + maxParticleSpeed.x = Max; + } + + /** + * A more compact way of setting the Y velocity range of the emitter. + * + * @param Min The minimum value for this range. + * @param Max The maximum value for this range. + */ + public function setYSpeed(Min:Number=0,Max:Number=0):void + { + minParticleSpeed.y = Min; + maxParticleSpeed.y = Max; + } + + /** + * A more compact way of setting the angular velocity constraints of the emitter. + * + * @param Min The minimum value for this range. + * @param Max The maximum value for this range. + */ + public function setRotation(Min:Number=0,Max:Number=0):void + { + minRotation = Min; + maxRotation = Max; + } + + /** + * Change the emitter's midpoint to match the midpoint of a FlxObject. + * + * @param Object The FlxObject that you want to sync up with. + */ + public function at(Object:FlxObject):void + { + Object.getMidpoint(_point); + x = _point.x - (width>>1); + y = _point.y - (height>>1); + } + } +} diff --git a/src/org/flixel/FlxG.as b/src/org/flixel/FlxG.as new file mode 100755 index 0000000..e62ffac --- /dev/null +++ b/src/org/flixel/FlxG.as @@ -0,0 +1,1256 @@ +package org.flixel +{ + import flash.display.BitmapData; + import flash.display.Graphics; + import flash.display.Sprite; + import flash.display.Stage; + import flash.geom.Matrix; + import flash.geom.Point; + import flash.geom.Rectangle; + + import org.flixel.plugin.DebugPathDisplay; + import org.flixel.plugin.TimerManager; + import org.flixel.system.FlxDebugger; + import org.flixel.system.FlxQuadTree; + import org.flixel.system.input.*; + + /** + * This is a global helper class full of useful functions for audio, + * input, basic info, and the camera system among other things. + * Utilities for maths and color and things can be found in FlxU. + * FlxG is specifically for Flixel-specific properties. + * + * @author Adam Atomic + */ + public class FlxG + { + /** + * If you build and maintain your own version of flixel, + * you can give it your own name here. + */ + static public var LIBRARY_NAME:String = "flixel"; + /** + * Assign a major version to your library. + * Appears before the decimal in the console. + */ + static public var LIBRARY_MAJOR_VERSION:uint = 2; + /** + * Assign a minor version to your library. + * Appears after the decimal in the console. + */ + static public var LIBRARY_MINOR_VERSION:uint = 55; + + /** + * Debugger overlay layout preset: Wide but low windows at the bottom of the screen. + */ + static public const DEBUGGER_STANDARD:uint = 0; + /** + * Debugger overlay layout preset: Tiny windows in the screen corners. + */ + static public const DEBUGGER_MICRO:uint = 1; + /** + * Debugger overlay layout preset: Large windows taking up bottom half of screen. + */ + static public const DEBUGGER_BIG:uint = 2; + /** + * Debugger overlay layout preset: Wide but low windows at the top of the screen. + */ + static public const DEBUGGER_TOP:uint = 3; + /** + * Debugger overlay layout preset: Large windows taking up left third of screen. + */ + static public const DEBUGGER_LEFT:uint = 4; + /** + * Debugger overlay layout preset: Large windows taking up right third of screen. + */ + static public const DEBUGGER_RIGHT:uint = 5; + + /** + * Some handy color presets. Less glaring than pure RGB full values. + * Primarily used in the visual debugger mode for bounding box displays. + * Red is used to indicate an active, movable, solid object. + */ + static public const RED:uint = 0xffff0012; + /** + * Green is used to indicate solid but immovable objects. + */ + static public const GREEN:uint = 0xff00f225; + /** + * Blue is used to indicate non-solid objects. + */ + static public const BLUE:uint = 0xff0090e9; + /** + * Pink is used to indicate objects that are only partially solid, like one-way platforms. + */ + static public const PINK:uint = 0xfff01eff; + /** + * White... for white stuff. + */ + static public const WHITE:uint = 0xffffffff; + /** + * And black too. + */ + static public const BLACK:uint = 0xff000000; + + /** + * Internal tracker for game object. + */ + static internal var _game:FlxGame; + /** + * Handy shared variable for implementing your own pause behavior. + */ + static public var paused:Boolean; + /** + * Whether you are running in Debug or Release mode. + * Set automatically by FlxPreloader during startup. + */ + static public var debug:Boolean; + + /** + * Represents the amount of time in seconds that passed since last frame. + */ + static public var elapsed:Number; + /** + * How fast or slow time should pass in the game; default is 1.0. + */ + static public var timeScale:Number; + /** + * The width of the screen in game pixels. + */ + static public var width:uint; + /** + * The height of the screen in game pixels. + */ + static public var height:uint; + /** + * The dimensions of the game world, used by the quad tree for collisions and overlap checks. + */ + static public var worldBounds:FlxRect; + /** + * How many times the quad tree should divide the world on each axis. + * Generally, sparse collisions can have fewer divisons, + * while denser collision activity usually profits from more. + * Default value is 6. + */ + static public var worldDivisions:uint; + /** + * Whether to show visual debug displays or not. + * Default = false. + */ + static public var visualDebug:Boolean; + /** + * Setting this to true will disable/skip stuff that isn't necessary for mobile platforms like Android. [BETA] + */ + static public var mobile:Boolean; + /** + * The global random number generator seed (for deterministic behavior in recordings and saves). + */ + static public var globalSeed:Number; + /** + * FlxG.levels and FlxG.scores are generic + * global variables that can be used for various cross-state stuff. + */ + static public var levels:Array; + static public var level:int; + static public var scores:Array; + static public var score:int; + /** + * FlxG.saves is a generic bucket for storing + * FlxSaves so you can access them whenever you want. + */ + static public var saves:Array; + static public var save:int; + + /** + * A reference to a FlxMouse object. Important for input! + */ + static public var mouse:Mouse; + /** + * A reference to a FlxKeyboard object. Important for input! + */ + static public var keys:Keyboard; + + /** + * A handy container for a background music object. + */ + static public var music:FlxSound; + /** + * A list of all the sounds being played in the game. + */ + static public var sounds:FlxGroup; + /** + * Whether or not the game sounds are muted. + */ + static public var mute:Boolean; + /** + * Internal volume level, used for global sound control. + */ + static protected var _volume:Number; + + /** + * An array of FlxCamera objects that are used to draw stuff. + * By default flixel creates one camera the size of the screen. + */ + static public var cameras:Array; + /** + * By default this just refers to the first entry in the cameras array + * declared above, but you can do what you like with it. + */ + static public var camera:FlxCamera; + /** + * Allows you to possibly slightly optimize the rendering process IF + * you are not doing any pre-processing in your game state's draw() call. + * @default false + */ + static public var useBufferLocking:Boolean; + /** + * Internal helper variable for clearing the cameras each frame. + */ + static protected var _cameraRect:Rectangle; + + /** + * An array container for plugins. + * By default flixel uses a couple of plugins: + * DebugPathDisplay, and TimerManager. + */ + static public var plugins:Array; + + /** + * Set this hook to get a callback whenever the volume changes. + * Function should take the form myVolumeHandler(Volume:Number). + */ + static public var volumeHandler:Function; + + /** + * Useful helper objects for doing Flash-specific rendering. + * Primarily used for "debug visuals" like drawing bounding boxes directly to the screen buffer. + */ + static public var flashGfxSprite:Sprite; + static public var flashGfx:Graphics; + + /** + * Internal storage system to prevent graphics from being used repeatedly in memory. + */ + static protected var _cache:Object; + + static public function getLibraryName():String + { + return FlxG.LIBRARY_NAME + " v" + FlxG.LIBRARY_MAJOR_VERSION + "." + FlxG.LIBRARY_MINOR_VERSION; + } + + /** + * Log data to the debugger. + * + * @param Data Anything you want to log to the console. + */ + static public function log(Data:Object):void + { + if((_game != null) && (_game._debugger != null)) + _game._debugger.log.add((Data == null)?"ERROR: null object":Data.toString()); + } + + /** + * Add a variable to the watch list in the debugger. + * This lets you see the value of the variable all the time. + * + * @param AnyObject A reference to any object in your game, e.g. Player or Robot or this. + * @param VariableName The name of the variable you want to watch, in quotes, as a string: e.g. "speed" or "health". + * @param DisplayName Optional, display your own string instead of the class name + variable name: e.g. "enemy count". + */ + static public function watch(AnyObject:Object,VariableName:String,DisplayName:String=null):void + { + if((_game != null) && (_game._debugger != null)) + _game._debugger.watch.add(AnyObject,VariableName,DisplayName); + } + + /** + * Remove a variable from the watch list in the debugger. + * Don't pass a Variable Name to remove all watched variables for the specified object. + * + * @param AnyObject A reference to any object in your game, e.g. Player or Robot or this. + * @param VariableName The name of the variable you want to watch, in quotes, as a string: e.g. "speed" or "health". + */ + static public function unwatch(AnyObject:Object,VariableName:String=null):void + { + if((_game != null) && (_game._debugger != null)) + _game._debugger.watch.remove(AnyObject,VariableName); + } + + /** + * How many times you want your game to update each second. + * More updates usually means better collisions and smoother motion. + * NOTE: This is NOT the same thing as the Flash Player framerate! + */ + static public function get framerate():Number + { + return 1000/_game._step; + } + + /** + * @private + */ + static public function set framerate(Framerate:Number):void + { + _game._step = 1000/Framerate; + if(_game._maxAccumulation < _game._step) + _game._maxAccumulation = _game._step; + } + + /** + * How many times you want your game to update each second. + * More updates usually means better collisions and smoother motion. + * NOTE: This is NOT the same thing as the Flash Player framerate! + */ + static public function get flashFramerate():Number + { + if(_game.root != null) + return _game.stage.frameRate; + else + return 0; + } + + /** + * @private + */ + static public function set flashFramerate(Framerate:Number):void + { + _game._flashFramerate = Framerate; + if(_game.root != null) + _game.stage.frameRate = _game._flashFramerate; + _game._maxAccumulation = 2000/_game._flashFramerate - 1; + if(_game._maxAccumulation < _game._step) + _game._maxAccumulation = _game._step; + } + + /** + * Generates a random number. Deterministic, meaning safe + * to use if you want to record replays in random environments. + * + * @return A Number between 0 and 1. + */ + static public function random():Number + { + return globalSeed = FlxU.srand(globalSeed); + } + + /** + * Shuffles the entries in an array into a new random order. + * FlxG.shuffle() is deterministic and safe for use with replays/recordings. + * HOWEVER, FlxU.shuffle() is NOT deterministic and unsafe for use with replays/recordings. + * + * @param A A Flash Array object containing...stuff. + * @param HowManyTimes How many swaps to perform during the shuffle operation. Good rule of thumb is 2-4 times as many objects are in the list. + * + * @return The same Flash Array object that you passed in in the first place. + */ + static public function shuffle(Objects:Array,HowManyTimes:uint):Array + { + var i:uint = 0; + var index1:uint; + var index2:uint; + var object:Object; + while(i < HowManyTimes) + { + index1 = FlxG.random()*Objects.length; + index2 = FlxG.random()*Objects.length; + object = Objects[index2]; + Objects[index2] = Objects[index1]; + Objects[index1] = object; + i++; + } + return Objects; + } + + /** + * Fetch a random entry from the given array. + * Will return null if random selection is missing, or array has no entries. + * FlxG.getRandom() is deterministic and safe for use with replays/recordings. + * HOWEVER, FlxU.getRandom() is NOT deterministic and unsafe for use with replays/recordings. + * + * @param Objects A Flash array of objects. + * @param StartIndex Optional offset off the front of the array. Default value is 0, or the beginning of the array. + * @param Length Optional restriction on the number of values you want to randomly select from. + * + * @return The random object that was selected. + */ + static public function getRandom(Objects:Array,StartIndex:uint=0,Length:uint=0):Object + { + if(Objects != null) + { + var l:uint = Length; + if((l == 0) || (l > Objects.length - StartIndex)) + l = Objects.length - StartIndex; + if(l > 0) + return Objects[StartIndex + uint(FlxG.random()*l)]; + } + return null; + } + + /** + * Load replay data from a string and play it back. + * + * @param Data The replay that you want to load. + * @param State Optional parameter: if you recorded a state-specific demo or cutscene, pass a new instance of that state here. + * @param CancelKeys Optional parameter: an array of string names of keys (see FlxKeyboard) that can be pressed to cancel the playback, e.g. ["ESCAPE","ENTER"]. Also accepts 2 custom key names: "ANY" and "MOUSE" (fairly self-explanatory I hope!). + * @param Timeout Optional parameter: set a time limit for the replay. CancelKeys will override this if pressed. + * @param Callback Optional parameter: if set, called when the replay finishes. Running to the end, CancelKeys, and Timeout will all trigger Callback(), but only once, and CancelKeys and Timeout will NOT call FlxG.stopReplay() if Callback is set! + */ + static public function loadReplay(Data:String,State:FlxState=null,CancelKeys:Array=null,Timeout:Number=0,Callback:Function=null):void + { + _game._replay.load(Data); + if(State == null) + FlxG.resetGame(); + else + FlxG.switchState(State); + _game._replayCancelKeys = CancelKeys; + _game._replayTimer = Timeout*1000; + _game._replayCallback = Callback; + _game._replayRequested = true; + } + + /** + * Resets the game or state and replay requested flag. + * + * @param StandardMode If true, reload entire game, else just reload current game state. + */ + static public function reloadReplay(StandardMode:Boolean=true):void + { + if(StandardMode) + FlxG.resetGame(); + else + FlxG.resetState(); + if(_game._replay.frameCount > 0) + _game._replayRequested = true; + } + + /** + * Stops the current replay. + */ + static public function stopReplay():void + { + _game._replaying = false; + if(_game._debugger != null) + _game._debugger.vcr.stopped(); + resetInput(); + } + + /** + * Resets the game or state and requests a new recording. + * + * @param StandardMode If true, reset the entire game, else just reset the current state. + */ + static public function recordReplay(StandardMode:Boolean=true):void + { + if(StandardMode) + FlxG.resetGame(); + else + FlxG.resetState(); + _game._recordingRequested = true; + } + + /** + * Stop recording the current replay and return the replay data. + * + * @return The replay data in simple ASCII format (see FlxReplay.save()). + */ + static public function stopRecording():String + { + _game._recording = false; + if(_game._debugger != null) + _game._debugger.vcr.stopped(); + return _game._replay.save(); + } + + /** + * Request a reset of the current game state. + */ + static public function resetState():void + { + _game._requestedState = new (FlxU.getClass(FlxU.getClassName(_game._state,false)))(); + } + + /** + * Like hitting the reset button on a game console, this will re-launch the game as if it just started. + */ + static public function resetGame():void + { + _game._requestedReset = true; + } + + /** + * Reset the input helper objects (useful when changing screens or states) + */ + static public function resetInput():void + { + keys.reset(); + mouse.reset(); + } + + /** + * Set up and play a looping background soundtrack. + * + * @param Music The sound file you want to loop in the background. + * @param Volume How loud the sound should be, from 0 to 1. + */ + static public function playMusic(Music:Class,Volume:Number=1.0):void + { + if(music == null) + music = new FlxSound(); + else if(music.active) + music.stop(); + music.loadEmbedded(Music,true); + music.volume = Volume; + music.survive = true; + music.play(); + } + + /** + * Creates a new sound object. + * + * @param EmbeddedSound The embedded sound resource you want to play. To stream, use the optional URL parameter instead. + * @param Volume How loud to play it (0 to 1). + * @param Looped Whether to loop this sound. + * @param AutoDestroy Whether to destroy this sound when it finishes playing. Leave this value set to "false" if you want to re-use this FlxSound instance. + * @param AutoPlay Whether to play the sound. + * @param URL Load a sound from an external web resource instead. Only used if EmbeddedSound = null. + * + * @return A FlxSound object. + */ + static public function loadSound(EmbeddedSound:Class=null,Volume:Number=1.0,Looped:Boolean=false,AutoDestroy:Boolean=false,AutoPlay:Boolean=false,URL:String=null):FlxSound + { + if((EmbeddedSound == null) && (URL == null)) + { + FlxG.log("WARNING: FlxG.loadSound() requires either\nan embedded sound or a URL to work."); + return null; + } + var sound:FlxSound = sounds.recycle(FlxSound) as FlxSound; + if(EmbeddedSound != null) + sound.loadEmbedded(EmbeddedSound,Looped,AutoDestroy); + else + sound.loadStream(URL,Looped,AutoDestroy); + sound.volume = Volume; + if(AutoPlay) + sound.play(); + return sound; + } + + /** + * Creates a new sound object from an embedded Class object. + * NOTE: Just calls FlxG.loadSound() with AutoPlay == true. + * + * @param EmbeddedSound The sound you want to play. + * @param Volume How loud to play it (0 to 1). + * @param Looped Whether to loop this sound. + * @param AutoDestroy Whether to destroy this sound when it finishes playing. Leave this value set to "false" if you want to re-use this FlxSound instance. + * + * @return A FlxSound object. + */ + static public function play(EmbeddedSound:Class,Volume:Number=1.0,Looped:Boolean=false,AutoDestroy:Boolean=true):FlxSound + { + return FlxG.loadSound(EmbeddedSound,Volume,Looped,AutoDestroy,true); + } + + /** + * Creates a new sound object from a URL. + * NOTE: Just calls FlxG.loadSound() with AutoPlay == true. + * + * @param URL The URL of the sound you want to play. + * @param Volume How loud to play it (0 to 1). + * @param Looped Whether or not to loop this sound. + * @param AutoDestroy Whether to destroy this sound when it finishes playing. Leave this value set to "false" if you want to re-use this FlxSound instance. + * + * @return A FlxSound object. + */ + static public function stream(URL:String,Volume:Number=1.0,Looped:Boolean=false,AutoDestroy:Boolean=true):FlxSound + { + return FlxG.loadSound(null,Volume,Looped,AutoDestroy,true,URL); + } + + /** + * Set volume to a number between 0 and 1 to change the global volume. + * + * @default 0.5 + */ + static public function get volume():Number + { + return _volume; + } + + /** + * @private + */ + static public function set volume(Volume:Number):void + { + _volume = Volume; + if(_volume < 0) + _volume = 0; + else if(_volume > 1) + _volume = 1; + if(volumeHandler != null) + volumeHandler(FlxG.mute?0:_volume); + } + + /** + * Called by FlxGame on state changes to stop and destroy sounds. + * + * @param ForceDestroy Kill sounds even if they're flagged survive. + */ + static internal function destroySounds(ForceDestroy:Boolean=false):void + { + if((music != null) && (ForceDestroy || !music.survive)) + { + music.destroy(); + music = null; + } + var i:uint = 0; + var sound:FlxSound; + var l:uint = sounds.members.length; + while(i < l) + { + sound = sounds.members[i++] as FlxSound; + if((sound != null) && (ForceDestroy || !sound.survive)) + sound.destroy(); + } + } + + /** + * Called by the game loop to make sure the sounds get updated each frame. + */ + static internal function updateSounds():void + { + if((music != null) && music.active) + music.update(); + if((sounds != null) && sounds.active) + sounds.update(); + } + + /** + * Pause all sounds currently playing. + */ + static public function pauseSounds():void + { + if((music != null) && music.exists && music.active) + music.pause(); + var i:uint = 0; + var sound:FlxSound; + var l:uint = sounds.length; + while(i < l) + { + sound = sounds.members[i++] as FlxSound; + if((sound != null) && sound.exists && sound.active) + sound.pause(); + } + } + + /** + * Resume playing existing sounds. + */ + static public function resumeSounds():void + { + if((music != null) && music.exists) + music.play(); + var i:uint = 0; + var sound:FlxSound; + var l:uint = sounds.length; + while(i < l) + { + sound = sounds.members[i++] as FlxSound; + if((sound != null) && sound.exists) + sound.resume(); + } + } + + /** + * Check the local bitmap cache to see if a bitmap with this key has been loaded already. + * + * @param Key The string key identifying the bitmap. + * + * @return Whether or not this file can be found in the cache. + */ + static public function checkBitmapCache(Key:String):Boolean + { + return (_cache[Key] != undefined) && (_cache[Key] != null); + } + + /** + * Generates a new BitmapData object (a colored square) and caches it. + * + * @param Width How wide the square should be. + * @param Height How high the square should be. + * @param Color What color the square should be (0xAARRGGBB) + * @param Unique Ensures that the bitmap data uses a new slot in the cache. + * @param Key Force the cache to use a specific Key to index the bitmap. + * + * @return The BitmapData we just created. + */ + static public function createBitmap(Width:uint, Height:uint, Color:uint, Unique:Boolean=false, Key:String=null):BitmapData + { + if(Key == null) + { + Key = Width+"x"+Height+":"+Color; + if(Unique && checkBitmapCache(Key)) + { + var inc:uint = 0; + var ukey:String; + do + { + ukey = Key + inc++; + } while(checkBitmapCache(ukey)); + Key = ukey; + } + } + if(!checkBitmapCache(Key)) + _cache[Key] = new BitmapData(Width,Height,true,Color); + return _cache[Key]; + } + + /** + * Loads a bitmap from a file, caches it, and generates a horizontally flipped version if necessary. + * + * @param Graphic The image file that you want to load. + * @param Reverse Whether to generate a flipped version. + * @param Unique Ensures that the bitmap data uses a new slot in the cache. + * @param Key Force the cache to use a specific Key to index the bitmap. + * + * @return The BitmapData we just created. + */ + static public function addBitmap(Graphic:Class, Reverse:Boolean=false, Unique:Boolean=false, Key:String=null):BitmapData + { + var needReverse:Boolean = false; + if(Key == null) + { + Key = String(Graphic)+(Reverse?"_REVERSE_":""); + if(Unique && checkBitmapCache(Key)) + { + var inc:uint = 0; + var ukey:String; + do + { + ukey = Key + inc++; + } while(checkBitmapCache(ukey)); + Key = ukey; + } + } + + //If there is no data for this key, generate the requested graphic + if(!checkBitmapCache(Key)) + { + _cache[Key] = (new Graphic).bitmapData; + if(Reverse) + needReverse = true; + } + var pixels:BitmapData = _cache[Key]; + if(!needReverse && Reverse && (pixels.width == (new Graphic).bitmapData.width)) + needReverse = true; + if(needReverse) + { + var newPixels:BitmapData = new BitmapData(pixels.width<<1,pixels.height,true,0x00000000); + newPixels.draw(pixels); + var mtx:Matrix = new Matrix(); + mtx.scale(-1,1); + mtx.translate(newPixels.width,0); + newPixels.draw(pixels,mtx); + pixels = newPixels; + _cache[Key] = pixels; + } + return pixels; + } + + /** + * Dumps the cache's image references. + */ + static public function clearBitmapCache():void + { + _cache = new Object(); + } + + /** + * Read-only: retrieves the Flash stage object (required for event listeners) + * Will be null if it's not safe/useful yet. + */ + static public function get stage():Stage + { + if(_game.root != null) + return _game.stage; + return null; + } + + /** + * Read-only: access the current game state from anywhere. + */ + static public function get state():FlxState + { + return _game._state; + } + + /** + * Switch from the current game state to the one specified here. + */ + static public function switchState(State:FlxState):void + { + _game._requestedState = State; + } + + /** + * Change the way the debugger's windows are laid out. + * + * @param Layout See the presets above (e.g. DEBUGGER_MICRO, etc). + */ + static public function setDebuggerLayout(Layout:uint):void + { + if(_game._debugger != null) + _game._debugger.setLayout(Layout); + } + + /** + * Just resets the debugger windows to whatever the last selected layout was (DEBUGGER_STANDARD by default). + */ + static public function resetDebuggerLayout():void + { + if(_game._debugger != null) + _game._debugger.resetLayout(); + } + + /** + * Add a new camera object to the game. + * Handy for PiP, split-screen, etc. + * + * @param NewCamera The camera you want to add. + * + * @return This FlxCamera instance. + */ + static public function addCamera(NewCamera:FlxCamera):FlxCamera + { + FlxG._game.addChildAt(NewCamera._flashSprite,FlxG._game.getChildIndex(FlxG._game._mouse)); + FlxG.cameras.push(NewCamera); + return NewCamera; + } + + /** + * Remove a camera from the game. + * + * @param Camera The camera you want to remove. + * @param Destroy Whether to call destroy() on the camera, default value is true. + */ + static public function removeCamera(Camera:FlxCamera,Destroy:Boolean=true):void + { + try + { + FlxG._game.removeChild(Camera._flashSprite); + } + catch(E:Error) + { + FlxG.log("Error removing camera, not part of game."); + } + if(Destroy) + Camera.destroy(); + } + + /** + * Dumps all the current cameras and resets to just one camera. + * Handy for doing split-screen especially. + * + * @param NewCamera Optional; specify a specific camera object to be the new main camera. + */ + static public function resetCameras(NewCamera:FlxCamera=null):void + { + var cam:FlxCamera; + var i:uint = 0; + var l:uint = cameras.length; + while(i < l) + { + cam = FlxG.cameras[i++] as FlxCamera; + FlxG._game.removeChild(cam._flashSprite); + cam.destroy(); + } + FlxG.cameras.length = 0; + + if(NewCamera == null) + NewCamera = new FlxCamera(0,0,FlxG.width,FlxG.height) + FlxG.camera = FlxG.addCamera(NewCamera); + } + + /** + * All screens are filled with this color and gradually return to normal. + * + * @param Color The color you want to use. + * @param Duration How long it takes for the flash to fade. + * @param OnComplete A function you want to run when the flash finishes. + * @param Force Force the effect to reset. + */ + static public function flash(Color:uint=0xffffffff, Duration:Number=1, OnComplete:Function=null, Force:Boolean=false):void + { + var i:uint = 0; + var l:uint = FlxG.cameras.length; + while(i < l) + (FlxG.cameras[i++] as FlxCamera).flash(Color,Duration,OnComplete,Force); + } + + /** + * The screen is gradually filled with this color. + * + * @param Color The color you want to use. + * @param Duration How long it takes for the fade to finish. + * @param OnComplete A function you want to run when the fade finishes. + * @param Force Force the effect to reset. + */ + static public function fade(Color:uint=0xff000000, Duration:Number=1, OnComplete:Function=null, Force:Boolean=false):void + { + var i:uint = 0; + var l:uint = FlxG.cameras.length; + while(i < l) + (FlxG.cameras[i++] as FlxCamera).fade(Color,Duration,OnComplete,Force); + } + + /** + * A simple screen-shake effect. + * + * @param Intensity Percentage of screen size representing the maximum distance that the screen can move while shaking. + * @param Duration The length in seconds that the shaking effect should last. + * @param OnComplete A function you want to run when the shake effect finishes. + * @param Force Force the effect to reset (default = true, unlike flash() and fade()!). + * @param Direction Whether to shake on both axes, just up and down, or just side to side (use class constants SHAKE_BOTH_AXES, SHAKE_VERTICAL_ONLY, or SHAKE_HORIZONTAL_ONLY). Default value is SHAKE_BOTH_AXES (0). + */ + static public function shake(Intensity:Number=0.05, Duration:Number=0.5, OnComplete:Function=null, Force:Boolean=true, Direction:uint=0):void + { + var i:uint = 0; + var l:uint = FlxG.cameras.length; + while(i < l) + (FlxG.cameras[i++] as FlxCamera).shake(Intensity,Duration,OnComplete,Force,Direction); + } + + /** + * Get and set the background color of the game. + * Get functionality is equivalent to FlxG.camera.bgColor. + * Set functionality sets the background color of all the current cameras. + */ + static public function get bgColor():uint + { + if(FlxG.camera == null) + return 0xff000000; + else + return FlxG.camera.bgColor; + } + + static public function set bgColor(Color:uint):void + { + var i:uint = 0; + var l:uint = FlxG.cameras.length; + while(i < l) + (FlxG.cameras[i++] as FlxCamera).bgColor = Color; + } + + /** + * Call this function to see if one FlxObject overlaps another. + * Can be called with one object and one group, or two groups, or two objects, + * whatever floats your boat! For maximum performance try bundling a lot of objects + * together using a FlxGroup (or even bundling groups together!). + * + *

NOTE: does NOT take objects' scrollfactor into account, all overlaps are checked in world space.

+ * + * @param ObjectOrGroup1 The first object or group you want to check. + * @param ObjectOrGroup2 The second object or group you want to check. If it is the same as the first, flixel knows to just do a comparison within that group. + * @param NotifyCallback A function with two FlxObject parameters - e.g. myOverlapFunction(Object1:FlxObject,Object2:FlxObject) - that is called if those two objects overlap. + * @param ProcessCallback A function with two FlxObject parameters - e.g. myOverlapFunction(Object1:FlxObject,Object2:FlxObject) - that is called if those two objects overlap. If a ProcessCallback is provided, then NotifyCallback will only be called if ProcessCallback returns true for those objects! + * + * @return Whether any oevrlaps were detected. + */ + static public function overlap(ObjectOrGroup1:FlxBasic=null,ObjectOrGroup2:FlxBasic=null,NotifyCallback:Function=null,ProcessCallback:Function=null):Boolean + { + if(ObjectOrGroup1 == null) + ObjectOrGroup1 = FlxG.state; + if(ObjectOrGroup2 === ObjectOrGroup1) + ObjectOrGroup2 = null; + FlxQuadTree.divisions = FlxG.worldDivisions; + var quadTree:FlxQuadTree = new FlxQuadTree(FlxG.worldBounds.x,FlxG.worldBounds.y,FlxG.worldBounds.width,FlxG.worldBounds.height); + quadTree.load(ObjectOrGroup1,ObjectOrGroup2,NotifyCallback,ProcessCallback); + var result:Boolean = quadTree.execute(); + quadTree.destroy(); + return result; + } + + /** + * Call this function to see if one FlxObject collides with another. + * Can be called with one object and one group, or two groups, or two objects, + * whatever floats your boat! For maximum performance try bundling a lot of objects + * together using a FlxGroup (or even bundling groups together!). + * + *

This function just calls FlxG.overlap and presets the ProcessCallback parameter to FlxObject.separate. + * To create your own collision logic, write your own ProcessCallback and use FlxG.overlap to set it up.

+ * + *

NOTE: does NOT take objects' scrollfactor into account, all overlaps are checked in world space.

+ * + * @param ObjectOrGroup1 The first object or group you want to check. + * @param ObjectOrGroup2 The second object or group you want to check. If it is the same as the first, flixel knows to just do a comparison within that group. + * @param NotifyCallback A function with two FlxObject parameters - e.g. myOverlapFunction(Object1:FlxObject,Object2:FlxObject) - that is called if those two objects overlap. + * + * @return Whether any objects were successfully collided/separated. + */ + static public function collide(ObjectOrGroup1:FlxBasic=null, ObjectOrGroup2:FlxBasic=null, NotifyCallback:Function=null):Boolean + { + return overlap(ObjectOrGroup1,ObjectOrGroup2,NotifyCallback,FlxObject.separate); + } + + /** + * Adds a new plugin to the global plugin array. + * + * @param Plugin Any object that extends FlxBasic. Useful for managers and other things. See org.flixel.plugin for some examples! + * + * @return The same FlxBasic-based plugin you passed in. + */ + static public function addPlugin(Plugin:FlxBasic):FlxBasic + { + //Don't add repeats + var pluginList:Array = FlxG.plugins; + var i:uint = 0; + var l:uint = pluginList.length; + while(i < l) + { + if(pluginList[i++].toString() == Plugin.toString()) + return Plugin; + } + + //no repeats! safe to add a new instance of this plugin + pluginList.push(Plugin); + return Plugin; + } + + /** + * Retrieves a plugin based on its class name from the global plugin array. + * + * @param ClassType The class name of the plugin you want to retrieve. See the FlxPath or FlxTimer constructors for example usage. + * + * @return The plugin object, or null if no matching plugin was found. + */ + static public function getPlugin(ClassType:Class):FlxBasic + { + var pluginList:Array = FlxG.plugins; + var i:uint = 0; + var l:uint = pluginList.length; + while(i < l) + { + if(pluginList[i] is ClassType) + return plugins[i]; + i++; + } + return null; + } + + /** + * Removes an instance of a plugin from the global plugin array. + * + * @param Plugin The plugin instance you want to remove. + * + * @return The same FlxBasic-based plugin you passed in. + */ + static public function removePlugin(Plugin:FlxBasic):FlxBasic + { + //Don't add repeats + var pluginList:Array = FlxG.plugins; + var i:int = pluginList.length-1; + while(i >= 0) + { + if(pluginList[i] == Plugin) + pluginList.splice(i,1); + i--; + } + return Plugin; + } + + /** + * Removes an instance of a plugin from the global plugin array. + * + * @param ClassType The class name of the plugin type you want removed from the array. + * + * @return Whether or not at least one instance of this plugin type was removed. + */ + static public function removePluginType(ClassType:Class):Boolean + { + //Don't add repeats + var results:Boolean = false; + var pluginList:Array = FlxG.plugins; + var i:int = pluginList.length-1; + while(i >= 0) + { + if(pluginList[i] is ClassType) + { + pluginList.splice(i,1); + results = true; + } + i--; + } + return results; + } + + /** + * Called by FlxGame to set up FlxG during FlxGame's constructor. + */ + static internal function init(Game:FlxGame,Width:uint,Height:uint,Zoom:Number):void + { + FlxG._game = Game; + FlxG.width = Width; + FlxG.height = Height; + + FlxG.mute = false; + FlxG._volume = 0.5; + FlxG.sounds = new FlxGroup(); + FlxG.volumeHandler = null; + + FlxG.clearBitmapCache(); + + if(flashGfxSprite == null) + { + flashGfxSprite = new Sprite(); + flashGfx = flashGfxSprite.graphics; + } + + FlxCamera.defaultZoom = Zoom; + FlxG._cameraRect = new Rectangle(); + FlxG.cameras = new Array(); + useBufferLocking = false; + + plugins = new Array(); + addPlugin(new DebugPathDisplay()); + addPlugin(new TimerManager()); + + FlxG.mouse = new Mouse(FlxG._game._mouse); + FlxG.keys = new Keyboard(); + FlxG.mobile = false; + + FlxG.levels = new Array(); + FlxG.scores = new Array(); + FlxG.visualDebug = false; + } + + /** + * Called whenever the game is reset, doesn't have to do quite as much work as the basic initialization stuff. + */ + static internal function reset():void + { + FlxG.clearBitmapCache(); + FlxG.resetInput(); + FlxG.destroySounds(true); + FlxG.levels.length = 0; + FlxG.scores.length = 0; + FlxG.level = 0; + FlxG.score = 0; + FlxG.paused = false; + FlxG.timeScale = 1.0; + FlxG.elapsed = 0; + FlxG.globalSeed = Math.random(); + FlxG.worldBounds = new FlxRect(-10,-10,FlxG.width+20,FlxG.height+20); + FlxG.worldDivisions = 6; + var debugPathDisplay:DebugPathDisplay = FlxG.getPlugin(DebugPathDisplay) as DebugPathDisplay; + if(debugPathDisplay != null) + debugPathDisplay.clear(); + } + + /** + * Called by the game object to update the keyboard and mouse input tracking objects. + */ + static internal function updateInput():void + { + FlxG.keys.update(); + if(!_game._debuggerUp || !_game._debugger.hasMouse) + FlxG.mouse.update(FlxG._game.mouseX,FlxG._game.mouseY); + } + + /** + * Called by the game object to lock all the camera buffers and clear them for the next draw pass. + */ + static internal function lockCameras():void + { + var cam:FlxCamera; + var cams:Array = FlxG.cameras; + var i:uint = 0; + var l:uint = cams.length; + while(i < l) + { + cam = cams[i++] as FlxCamera; + if((cam == null) || !cam.exists || !cam.visible) + continue; + if(useBufferLocking) + cam.buffer.lock(); + cam.fill(cam.bgColor); + cam.screen.dirty = true; + } + } + + /** + * Called by the game object to draw the special FX and unlock all the camera buffers. + */ + static internal function unlockCameras():void + { + var cam:FlxCamera; + var cams:Array = FlxG.cameras; + var i:uint = 0; + var l:uint = cams.length; + while(i < l) + { + cam = cams[i++] as FlxCamera; + if((cam == null) || !cam.exists || !cam.visible) + continue; + cam.drawFX(); + if(useBufferLocking) + cam.buffer.unlock(); + } + } + + /** + * Called by the game object to update the cameras and their tracking/special effects logic. + */ + static internal function updateCameras():void + { + var cam:FlxCamera; + var cams:Array = FlxG.cameras; + var i:uint = 0; + var l:uint = cams.length; + while(i < l) + { + cam = cams[i++] as FlxCamera; + if((cam != null) && cam.exists) + { + if(cam.active) + cam.update(); + cam._flashSprite.x = cam.x + cam._flashOffsetX; + cam._flashSprite.y = cam.y + cam._flashOffsetY; + cam._flashSprite.visible = cam.visible; + } + } + } + + /** + * Used by the game object to call update() on all the plugins. + */ + static internal function updatePlugins():void + { + var plugin:FlxBasic; + var pluginList:Array = FlxG.plugins; + var i:uint = 0; + var l:uint = pluginList.length; + while(i < l) + { + plugin = pluginList[i++] as FlxBasic; + if(plugin.exists && plugin.active) + plugin.update(); + } + } + + /** + * Used by the game object to call draw() on all the plugins. + */ + static internal function drawPlugins():void + { + var plugin:FlxBasic; + var pluginList:Array = FlxG.plugins; + var i:uint = 0; + var l:uint = pluginList.length; + while(i < l) + { + plugin = pluginList[i++] as FlxBasic; + if(plugin.exists && plugin.visible) + plugin.draw(); + } + } + } +} diff --git a/src/org/flixel/FlxGame.as b/src/org/flixel/FlxGame.as new file mode 100755 index 0000000..3dc1b28 --- /dev/null +++ b/src/org/flixel/FlxGame.as @@ -0,0 +1,801 @@ +package org.flixel +{ + import flash.display.Bitmap; + import flash.display.BitmapData; + import flash.display.Graphics; + import flash.display.Sprite; + import flash.display.StageAlign; + import flash.display.StageScaleMode; + import flash.events.*; + import flash.geom.Point; + import flash.text.AntiAliasType; + import flash.text.GridFitType; + import flash.text.TextField; + import flash.text.TextFormat; + import flash.ui.Mouse; + import flash.utils.Timer; + import flash.utils.getTimer; + + import org.flixel.plugin.TimerManager; + import org.flixel.system.FlxDebugger; + import org.flixel.system.FlxReplay; + + /** + * FlxGame is the heart of all flixel games, and contains a bunch of basic game loops and things. + * It is a long and sloppy file that you shouldn't have to worry about too much! + * It is basically only used to create your game object in the first place, + * after that FlxG and FlxState have all the useful stuff you actually need. + * + * @author Adam Atomic + */ + public class FlxGame extends Sprite + { + [Embed(source="data/nokiafc22.ttf",fontFamily="system",embedAsCFF="false")] protected var junk:String; + [Embed(source="data/beep.mp3")] protected var SndBeep:Class; + [Embed(source="data/logo.png")] protected var ImgLogo:Class; + + /** + * Sets 0, -, and + to control the global volume sound volume. + * @default true + */ + public var useSoundHotKeys:Boolean; + /** + * Tells flixel to use the default system mouse cursor instead of custom Flixel mouse cursors. + * @default false + */ + public var useSystemCursor:Boolean; + /** + * Initialize and allow the flixel debugger overlay even in release mode. + * Also useful if you don't use FlxPreloader! + * @default false + */ + public var forceDebugger:Boolean; + + /** + * Current game state. + */ + internal var _state:FlxState; + /** + * Mouse cursor. + */ + internal var _mouse:Sprite; + + /** + * Class type of the initial/first game state for the game, usually MenuState or something like that. + */ + protected var _iState:Class; + /** + * Whether the game object's basic initialization has finished yet. + */ + protected var _created:Boolean; + + /** + * Total number of milliseconds elapsed since game start. + */ + protected var _total:uint; + /** + * Total number of milliseconds elapsed since last update loop. + * Counts down as we step through the game loop. + */ + protected var _accumulator:int; + /** + * Whether the Flash player lost focus. + */ + protected var _lostFocus:Boolean; + /** + * Milliseconds of time per step of the game loop. FlashEvent.g. 60 fps = 16ms. + */ + internal var _step:uint; + /** + * Framerate of the Flash player (NOT the game loop). Default = 30. + */ + internal var _flashFramerate:uint; + /** + * Max allowable accumulation (see _accumulator). + * Should always (and automatically) be set to roughly 2x the flash player framerate. + */ + internal var _maxAccumulation:uint; + /** + * If a state change was requested, the new state object is stored here until we switch to it. + */ + internal var _requestedState:FlxState; + /** + * A flag for keeping track of whether a game reset was requested or not. + */ + internal var _requestedReset:Boolean; + + /** + * The "focus lost" screen (see createFocusScreen()). + */ + protected var _focus:Sprite; + /** + * The sound tray display container (see createSoundTray()). + */ + protected var _soundTray:Sprite; + /** + * Helps us auto-hide the sound tray after a volume change. + */ + protected var _soundTrayTimer:Number; + /** + * Helps display the volume bars on the sound tray. + */ + protected var _soundTrayBars:Array; + /** + * The debugger overlay object. + */ + internal var _debugger:FlxDebugger; + /** + * A handy boolean that keeps track of whether the debugger exists and is currently visible. + */ + internal var _debuggerUp:Boolean; + + /** + * Container for a game replay object. + */ + internal var _replay:FlxReplay; + /** + * Flag for whether a playback of a recording was requested. + */ + internal var _replayRequested:Boolean; + /** + * Flag for whether a new recording was requested. + */ + internal var _recordingRequested:Boolean; + /** + * Flag for whether a replay is currently playing. + */ + internal var _replaying:Boolean; + /** + * Flag for whether a new recording is being made. + */ + internal var _recording:Boolean; + /** + * Array that keeps track of keypresses that can cancel a replay. + * Handy for skipping cutscenes or getting out of attract modes! + */ + internal var _replayCancelKeys:Array; + /** + * Helps time out a replay if necessary. + */ + internal var _replayTimer:int; + /** + * This function, if set, is triggered when the callback stops playing. + */ + internal var _replayCallback:Function; + + /** + * Instantiate a new game object. + * + * @param GameSizeX The width of your game in game pixels, not necessarily final display pixels (see Zoom). + * @param GameSizeY The height of your game in game pixels, not necessarily final display pixels (see Zoom). + * @param InitialState The class name of the state you want to create and switch to first (e.g. MenuState). + * @param Zoom The default level of zoom for the game's cameras (e.g. 2 = all pixels are now drawn at 2x). Default = 1. + * @param GameFramerate How frequently the game should update (default is 60 times per second). + * @param FlashFramerate Sets the actual display framerate for Flash player (default is 30 times per second). + * @param UseSystemCursor Whether to use the default OS mouse pointer, or to use custom flixel ones. + */ + public function FlxGame(GameSizeX:uint,GameSizeY:uint,InitialState:Class,Zoom:Number=1,GameFramerate:uint=60,FlashFramerate:uint=30,UseSystemCursor:Boolean=false) + { + //super high priority init stuff (focus, mouse, etc) + _lostFocus = false; + _focus = new Sprite(); + _focus.visible = false; + _soundTray = new Sprite(); + _mouse = new Sprite() + + //basic display and update setup stuff + FlxG.init(this,GameSizeX,GameSizeY,Zoom); + FlxG.framerate = GameFramerate; + FlxG.flashFramerate = FlashFramerate; + _accumulator = _step; + _total = 0; + _state = null; + useSoundHotKeys = true; + useSystemCursor = UseSystemCursor; + if(!useSystemCursor) + flash.ui.Mouse.hide(); + forceDebugger = false; + _debuggerUp = false; + + //replay data + _replay = new FlxReplay(); + _replayRequested = false; + _recordingRequested = false; + _replaying = false; + _recording = false; + + //then get ready to create the game object for real + _iState = InitialState; + _requestedState = null; + _requestedReset = true; + _created = false; + addEventListener(Event.ENTER_FRAME, create); + } + + /** + * Makes the little volume tray slide out. + * + * @param Silent Whether or not it should beep. + */ + internal function showSoundTray(Silent:Boolean=false):void + { + if(!Silent) + FlxG.play(SndBeep); + _soundTrayTimer = 1; + _soundTray.y = 0; + _soundTray.visible = true; + var globalVolume:uint = Math.round(FlxG.volume*10); + if(FlxG.mute) + globalVolume = 0; + for (var i:uint = 0; i < _soundTrayBars.length; i++) + { + if(i < globalVolume) _soundTrayBars[i].alpha = 1; + else _soundTrayBars[i].alpha = 0.5; + } + } + + /** + * Internal event handler for input and focus. + * + * @param FlashEvent Flash keyboard event. + */ + protected function onKeyUp(FlashEvent:KeyboardEvent):void + { + if(_debuggerUp && _debugger.watch.editing) + return; + if(!FlxG.mobile) + { + if((_debugger != null) && ((FlashEvent.keyCode == 192) || (FlashEvent.keyCode == 220))) + { + _debugger.visible = !_debugger.visible; + _debuggerUp = _debugger.visible; + if(_debugger.visible) + flash.ui.Mouse.show(); + else if(!useSystemCursor) + flash.ui.Mouse.hide(); + //_console.toggle(); + return; + } + if(useSoundHotKeys) + { + var c:int = FlashEvent.keyCode; + var code:String = String.fromCharCode(FlashEvent.charCode); + switch(c) + { + case 48: + case 96: + FlxG.mute = !FlxG.mute; + if(FlxG.volumeHandler != null) + FlxG.volumeHandler(FlxG.mute?0:FlxG.volume); + showSoundTray(); + return; + case 109: + case 189: + FlxG.mute = false; + FlxG.volume = FlxG.volume - 0.1; + showSoundTray(); + return; + case 107: + case 187: + FlxG.mute = false; + FlxG.volume = FlxG.volume + 0.1; + showSoundTray(); + return; + default: + break; + } + } + } + if(_replaying) + return; + FlxG.keys.handleKeyUp(FlashEvent); + } + + /** + * Internal event handler for input and focus. + * + * @param FlashEvent Flash keyboard event. + */ + protected function onKeyDown(FlashEvent:KeyboardEvent):void + { + if(_debuggerUp && _debugger.watch.editing) + return; + if(_replaying && (_replayCancelKeys != null) && (_debugger == null) && (FlashEvent.keyCode != 192) && (FlashEvent.keyCode != 220)) + { + var cancel:Boolean = false; + var replayCancelKey:String; + var i:uint = 0; + var l:uint = _replayCancelKeys.length; + while(i < l) + { + replayCancelKey = _replayCancelKeys[i++]; + if((replayCancelKey == "ANY") || (FlxG.keys.getKeyCode(replayCancelKey) == FlashEvent.keyCode)) + { + if(_replayCallback != null) + { + _replayCallback(); + _replayCallback = null; + } + else + FlxG.stopReplay(); + break; + } + } + return; + } + FlxG.keys.handleKeyDown(FlashEvent); + } + + /** + * Internal event handler for input and focus. + * + * @param FlashEvent Flash mouse event. + */ + protected function onMouseDown(FlashEvent:MouseEvent):void + { + if(_debuggerUp) + { + if(_debugger.hasMouse) + return; + if(_debugger.watch.editing) + _debugger.watch.submit(); + } + if(_replaying && (_replayCancelKeys != null)) + { + var replayCancelKey:String; + var i:uint = 0; + var l:uint = _replayCancelKeys.length; + while(i < l) + { + replayCancelKey = _replayCancelKeys[i++] as String; + if((replayCancelKey == "MOUSE") || (replayCancelKey == "ANY")) + { + if(_replayCallback != null) + { + _replayCallback(); + _replayCallback = null; + } + else + FlxG.stopReplay(); + break; + } + } + return; + } + FlxG.mouse.handleMouseDown(FlashEvent); + } + + /** + * Internal event handler for input and focus. + * + * @param FlashEvent Flash mouse event. + */ + protected function onMouseUp(FlashEvent:MouseEvent):void + { + if((_debuggerUp && _debugger.hasMouse) || _replaying) + return; + FlxG.mouse.handleMouseUp(FlashEvent); + } + + /** + * Internal event handler for input and focus. + * + * @param FlashEvent Flash mouse event. + */ + protected function onMouseWheel(FlashEvent:MouseEvent):void + { + if((_debuggerUp && _debugger.hasMouse) || _replaying) + return; + FlxG.mouse.handleMouseWheel(FlashEvent); + } + + /** + * Internal event handler for input and focus. + * + * @param FlashEvent Flash event. + */ + protected function onFocus(FlashEvent:Event=null):void + { + if(!_debuggerUp && !useSystemCursor) + flash.ui.Mouse.hide(); + FlxG.resetInput(); + _lostFocus = _focus.visible = false; + stage.frameRate = _flashFramerate; + FlxG.resumeSounds(); + } + + /** + * Internal event handler for input and focus. + * + * @param FlashEvent Flash event. + */ + protected function onFocusLost(FlashEvent:Event=null):void + { + if((x != 0) || (y != 0)) + { + x = 0; + y = 0; + } + flash.ui.Mouse.show(); + _lostFocus = _focus.visible = true; + stage.frameRate = 10; + FlxG.pauseSounds(); + } + + /** + * Handles the onEnterFrame call and figures out how many updates and draw calls to do. + * + * @param FlashEvent Flash event. + */ + protected function onEnterFrame(FlashEvent:Event=null):void + { + var mark:uint = getTimer(); + var elapsedMS:uint = mark-_total; + _total = mark; + updateSoundTray(elapsedMS); + if(!_lostFocus) + { + if((_debugger != null) && _debugger.vcr.paused) + { + if(_debugger.vcr.stepRequested) + { + _debugger.vcr.stepRequested = false; + step(); + } + } + else + { + _accumulator += elapsedMS; + if(_accumulator > _maxAccumulation) + _accumulator = _maxAccumulation; + while(_accumulator >= _step) + { + step(); + _accumulator = _accumulator - _step; + } + } + + FlxBasic._VISIBLECOUNT = 0; + draw(); + + if(_debuggerUp) + { + _debugger.perf.flash(elapsedMS); + _debugger.perf.visibleObjects(FlxBasic._VISIBLECOUNT); + _debugger.perf.update(); + _debugger.watch.update(); + } + } + } + + /** + * If there is a state change requested during the update loop, + * this function handles actual destroying the old state and related processes, + * and calls creates on the new state and plugs it into the game object. + */ + protected function switchState():void + { + //Basic reset stuff + FlxG.resetCameras(); + FlxG.resetInput(); + FlxG.destroySounds(); + FlxG.clearBitmapCache(); + + //Clear the debugger overlay's Watch window + if(_debugger != null) + _debugger.watch.removeAll(); + + //Clear any timers left in the timer manager + var timerManager:TimerManager = FlxTimer.manager; + if(timerManager != null) + timerManager.clear(); + + //Destroy the old state (if there is an old state) + if(_state != null) + _state.destroy(); + + //Finally assign and create the new state + _state = _requestedState; + _state.create(); + } + + /** + * This is the main game update logic section. + * The onEnterFrame() handler is in charge of calling this + * the appropriate number of times each frame. + * This block handles state changes, replays, all that good stuff. + */ + protected function step():void + { + //handle game reset request + if(_requestedReset) + { + _requestedReset = false; + _requestedState = new _iState(); + _replayTimer = 0; + _replayCancelKeys = null; + FlxG.reset(); + } + + //handle replay-related requests + if(_recordingRequested) + { + _recordingRequested = false; + _replay.create(FlxG.globalSeed); + _recording = true; + if(_debugger != null) + { + _debugger.vcr.recording(); + FlxG.log("FLIXEL: starting new flixel gameplay record."); + } + } + else if(_replayRequested) + { + _replayRequested = false; + _replay.rewind(); + FlxG.globalSeed = _replay.seed; + if(_debugger != null) + _debugger.vcr.playing(); + _replaying = true; + } + + //handle state switching requests + if(_state != _requestedState) + switchState(); + + //finally actually step through the game physics + FlxBasic._ACTIVECOUNT = 0; + if(_replaying) + { + _replay.playNextFrame(); + if(_replayTimer > 0) + { + _replayTimer -= _step; + if(_replayTimer <= 0) + { + if(_replayCallback != null) + { + _replayCallback(); + _replayCallback = null; + } + else + FlxG.stopReplay(); + } + } + if(_replaying && _replay.finished) + { + FlxG.stopReplay(); + if(_replayCallback != null) + { + _replayCallback(); + _replayCallback = null; + } + } + if(_debugger != null) + _debugger.vcr.updateRuntime(_step); + } + else + FlxG.updateInput(); + if(_recording) + { + _replay.recordFrame(); + if(_debugger != null) + _debugger.vcr.updateRuntime(_step); + } + update(); + FlxG.mouse.wheel = 0; + if(_debuggerUp) + _debugger.perf.activeObjects(FlxBasic._ACTIVECOUNT); + } + + /** + * This function just updates the soundtray object. + */ + protected function updateSoundTray(MS:Number):void + { + //animate stupid sound tray thing + + if(_soundTray != null) + { + if(_soundTrayTimer > 0) + _soundTrayTimer -= MS/1000; + else if(_soundTray.y > -_soundTray.height) + { + _soundTray.y -= (MS/1000)*FlxG.height*2; + if(_soundTray.y <= -_soundTray.height) + { + _soundTray.visible = false; + + //Save sound preferences + var soundPrefs:FlxSave = new FlxSave(); + if(soundPrefs.bind("flixel")) + { + if(soundPrefs.data.sound == null) + soundPrefs.data.sound = new Object; + soundPrefs.data.sound.mute = FlxG.mute; + soundPrefs.data.sound.volume = FlxG.volume; + soundPrefs.close(); + } + } + } + } + } + + /** + * This function is called by step() and updates the actual game state. + * May be called multiple times per "frame" or draw call. + */ + protected function update():void + { + var mark:uint = getTimer(); + + FlxG.elapsed = FlxG.timeScale*(_step/1000); + FlxG.updateSounds(); + FlxG.updatePlugins(); + _state.update(); + FlxG.updateCameras(); + + if(_debuggerUp) + _debugger.perf.flixelUpdate(getTimer()-mark); + } + + /** + * Goes through the game state and draws all the game objects and special effects. + */ + protected function draw():void + { + var mark:uint = getTimer(); + FlxG.lockCameras(); + _state.draw(); + FlxG.drawPlugins(); + FlxG.unlockCameras(); + if(_debuggerUp) + _debugger.perf.flixelDraw(getTimer()-mark); + } + + /** + * Used to instantiate the guts of the flixel game object once we have a valid reference to the root. + * + * @param FlashEvent Just a Flash system event, not too important for our purposes. + */ + protected function create(FlashEvent:Event):void + { + if(root == null) + return; + removeEventListener(Event.ENTER_FRAME, create); + _total = getTimer(); + + //Set up the view window and double buffering + stage.scaleMode = StageScaleMode.NO_SCALE; + stage.align = StageAlign.TOP_LEFT; + stage.frameRate = _flashFramerate; + + //Add basic input event listeners and mouse container + stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown); + stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp); + stage.addEventListener(MouseEvent.MOUSE_WHEEL, onMouseWheel); + stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown); + stage.addEventListener(KeyboardEvent.KEY_UP, onKeyUp); + addChild(_mouse); + + //Let mobile devs opt out of unnecessary overlays. + if(!FlxG.mobile) + { + //Debugger overlay + if(FlxG.debug || forceDebugger) + { + _debugger = new FlxDebugger(FlxG.width*FlxCamera.defaultZoom,FlxG.height*FlxCamera.defaultZoom); + addChild(_debugger); + } + + //Volume display tab + createSoundTray(); + + //Focus gained/lost monitoring + stage.addEventListener(Event.DEACTIVATE, onFocusLost); + stage.addEventListener(Event.ACTIVATE, onFocus); + createFocusScreen(); + } + + //Finally, set up an event for the actual game loop stuff. + addEventListener(Event.ENTER_FRAME, onEnterFrame); + } + + /** + * Sets up the "sound tray", the little volume meter that pops down sometimes. + */ + protected function createSoundTray():void + { + _soundTray.visible = false; + _soundTray.scaleX = 2; + _soundTray.scaleY = 2; + var tmp:Bitmap = new Bitmap(new BitmapData(80,30,true,0x7F000000)); + _soundTray.x = (FlxG.width/2)*FlxCamera.defaultZoom-(tmp.width/2)*_soundTray.scaleX; + _soundTray.addChild(tmp); + + var text:TextField = new TextField(); + text.width = tmp.width; + text.height = tmp.height; + text.multiline = true; + text.wordWrap = true; + text.selectable = false; + text.embedFonts = true; + text.antiAliasType = AntiAliasType.NORMAL; + text.gridFitType = GridFitType.PIXEL; + text.defaultTextFormat = new TextFormat("system",8,0xffffff,null,null,null,null,null,"center");; + _soundTray.addChild(text); + text.text = "VOLUME"; + text.y = 16; + + var bx:uint = 10; + var by:uint = 14; + _soundTrayBars = new Array(); + var i:uint = 0; + while(i < 10) + { + tmp = new Bitmap(new BitmapData(4,++i,false,0xffffff)); + tmp.x = bx; + tmp.y = by; + _soundTrayBars.push(_soundTray.addChild(tmp)); + bx += 6; + by--; + } + + _soundTray.y = -_soundTray.height; + _soundTray.visible = false; + addChild(_soundTray); + + //load saved sound preferences for this game if they exist + var soundPrefs:FlxSave = new FlxSave(); + if(soundPrefs.bind("flixel") && (soundPrefs.data.sound != null)) + { + if(soundPrefs.data.sound.volume != null) + FlxG.volume = soundPrefs.data.sound.volume; + if(soundPrefs.data.sound.mute != null) + FlxG.mute = soundPrefs.data.sound.mute; + soundPrefs.destroy(); + } + } + + /** + * Sets up the darkened overlay with the big white "play" button that appears when a flixel game loses focus. + */ + protected function createFocusScreen():void + { + var gfx:Graphics = _focus.graphics; + var screenWidth:uint = FlxG.width*FlxCamera.defaultZoom; + var screenHeight:uint = FlxG.height*FlxCamera.defaultZoom; + + //draw transparent black backdrop + gfx.moveTo(0,0); + gfx.beginFill(0,0.5); + gfx.lineTo(screenWidth,0); + gfx.lineTo(screenWidth,screenHeight); + gfx.lineTo(0,screenHeight); + gfx.lineTo(0,0); + gfx.endFill(); + + //draw white arrow + var halfWidth:uint = screenWidth/2; + var halfHeight:uint = screenHeight/2; + var helper:uint = FlxU.min(halfWidth,halfHeight)/3; + gfx.moveTo(halfWidth-helper,halfHeight-helper); + gfx.beginFill(0xffffff,0.65); + gfx.lineTo(halfWidth+helper,halfHeight); + gfx.lineTo(halfWidth-helper,halfHeight+helper); + gfx.lineTo(halfWidth-helper,halfHeight-helper); + gfx.endFill(); + + var logo:Bitmap = new ImgLogo(); + logo.scaleX = int(helper/10); + if(logo.scaleX < 1) + logo.scaleX = 1; + logo.scaleY = logo.scaleX; + logo.x -= logo.scaleX; + logo.alpha = 0.35; + _focus.addChild(logo); + + addChild(_focus); + } + } +} diff --git a/src/org/flixel/FlxGroup.as b/src/org/flixel/FlxGroup.as new file mode 100644 index 0000000..222659b --- /dev/null +++ b/src/org/flixel/FlxGroup.as @@ -0,0 +1,575 @@ +package org.flixel +{ + /** + * This is an organizational class that can update and render a bunch of FlxBasics. + * NOTE: Although FlxGroup extends FlxBasic, it will not automatically + * add itself to the global collisions quad tree, it will only add its members. + * + * @author Adam Atomic + */ + public class FlxGroup extends FlxBasic + { + /** + * Use with sort() to sort in ascending order. + */ + static public const ASCENDING:int = -1; + /** + * Use with sort() to sort in descending order. + */ + static public const DESCENDING:int = 1; + + /** + * Array of all the FlxBasics that exist in this group. + */ + public var members:Array; + /** + * The number of entries in the members array. + * For performance and safety you should check this variable + * instead of members.length unless you really know what you're doing! + */ + public var length:Number; + + /** + * Internal tracker for the maximum capacity of the group. + * Default is 0, or no max capacity. + */ + protected var _maxSize:uint; + /** + * Internal helper variable for recycling objects a la FlxEmitter. + */ + protected var _marker:uint; + + /** + * Helper for sort. + */ + protected var _sortIndex:String; + /** + * Helper for sort. + */ + protected var _sortOrder:int; + + /** + * Constructor + */ + public function FlxGroup(MaxSize:uint=0) + { + super(); + members = new Array(); + length = 0; + _maxSize = MaxSize; + _marker = 0; + _sortIndex = null; + } + + /** + * Override this function to handle any deleting or "shutdown" type operations you might need, + * such as removing traditional Flash children like Sprite objects. + */ + override public function destroy():void + { + if(members != null) + { + var basic:FlxBasic; + var i:uint = 0; + while(i < length) + { + basic = members[i++] as FlxBasic; + if(basic != null) + basic.destroy(); + } + members.length = 0; + members = null; + } + _sortIndex = null; + } + + /** + * Just making sure we don't increment the active objects count. + */ + override public function preUpdate():void + { + } + + /** + * Automatically goes through and calls update on everything you added. + */ + override public function update():void + { + var basic:FlxBasic; + var i:uint = 0; + while(i < length) + { + basic = members[i++] as FlxBasic; + if((basic != null) && basic.exists && basic.active) + { + basic.preUpdate(); + basic.update(); + basic.postUpdate(); + } + } + } + + /** + * Automatically goes through and calls render on everything you added. + */ + override public function draw():void + { + var basic:FlxBasic; + var i:uint = 0; + while(i < length) + { + basic = members[i++] as FlxBasic; + if((basic != null) && basic.exists && basic.visible) + basic.draw(); + } + } + + /** + * The maximum capacity of this group. Default is 0, meaning no max capacity, and the group can just grow. + */ + public function get maxSize():uint + { + return _maxSize; + } + + /** + * @private + */ + public function set maxSize(Size:uint):void + { + _maxSize = Size; + if(_marker >= _maxSize) + _marker = 0; + if((_maxSize == 0) || (members == null) || (_maxSize >= members.length)) + return; + + //If the max size has shrunk, we need to get rid of some objects + var basic:FlxBasic; + var i:uint = _maxSize; + var l:uint = members.length; + while(i < l) + { + basic = members[i++] as FlxBasic; + if(basic != null) + basic.destroy(); + } + length = members.length = _maxSize; + } + + /** + * Adds a new FlxBasic subclass (FlxBasic, FlxSprite, Enemy, etc) to the group. + * FlxGroup will try to replace a null member of the array first. + * Failing that, FlxGroup will add it to the end of the member array, + * assuming there is room for it, and doubling the size of the array if necessary. + * + *

WARNING: If the group has a maxSize that has already been met, + * the object will NOT be added to the group!

+ * + * @param Object The object you want to add to the group. + * + * @return The same FlxBasic object that was passed in. + */ + public function add(Object:FlxBasic):FlxBasic + { + //Don't bother adding an object twice. + if(members.indexOf(Object) >= 0) + return Object; + + //First, look for a null entry where we can add the object. + var i:uint = 0; + var l:uint = members.length; + while(i < l) + { + if(members[i] == null) + { + members[i] = Object; + if(i >= length) + length = i+1; + return Object; + } + i++; + } + + //Failing that, expand the array (if we can) and add the object. + if(_maxSize > 0) + { + if(members.length >= _maxSize) + return Object; + else if(members.length * 2 <= _maxSize) + members.length *= 2; + else + members.length = _maxSize; + } + else + members.length *= 2; + + //If we made it this far, then we successfully grew the group, + //and we can go ahead and add the object at the first open slot. + members[i] = Object; + length = i+1; + return Object; + } + + /** + * Recycling is designed to help you reuse game objects without always re-allocating or "newing" them. + * + *

If you specified a maximum size for this group (like in FlxEmitter), + * then recycle will employ what we're calling "rotating" recycling. + * Recycle() will first check to see if the group is at capacity yet. + * If group is not yet at capacity, recycle() returns a new object. + * If the group IS at capacity, then recycle() just returns the next object in line.

+ * + *

If you did NOT specify a maximum size for this group, + * then recycle() will employ what we're calling "grow-style" recycling. + * Recycle() will return either the first object with exists == false, + * or, finding none, add a new object to the array, + * doubling the size of the array if necessary.

+ * + *

WARNING: If this function needs to create a new object, + * and no object class was provided, it will return null + * instead of a valid object!

+ * + * @param ObjectClass The class type you want to recycle (e.g. FlxSprite, EvilRobot, etc). Do NOT "new" the class in the parameter! + * + * @return A reference to the object that was created. Don't forget to cast it back to the Class you want (e.g. myObject = myGroup.recycle(myObjectClass) as myObjectClass;). + */ + public function recycle(ObjectClass:Class=null):FlxBasic + { + var basic:FlxBasic; + if(_maxSize > 0) + { + if(length < _maxSize) + { + if(ObjectClass == null) + return null; + return add(new ObjectClass() as FlxBasic); + } + else + { + basic = members[_marker++]; + if(_marker >= _maxSize) + _marker = 0; + return basic; + } + } + else + { + basic = getFirstAvailable(ObjectClass); + if(basic != null) + return basic; + if(ObjectClass == null) + return null; + return add(new ObjectClass() as FlxBasic); + } + } + + /** + * Removes an object from the group. + * + * @param Object The FlxBasic you want to remove. + * @param Splice Whether the object should be cut from the array entirely or not. + * + * @return The removed object. + */ + public function remove(Object:FlxBasic,Splice:Boolean=false):FlxBasic + { + var index:int = members.indexOf(Object); + if((index < 0) || (index >= members.length)) + return null; + if(Splice) + { + members.splice(index,1); + length--; + } + else + members[index] = null; + return Object; + } + + /** + * Replaces an existing FlxBasic with a new one. + * + * @param OldObject The object you want to replace. + * @param NewObject The new object you want to use instead. + * + * @return The new object. + */ + public function replace(OldObject:FlxBasic,NewObject:FlxBasic):FlxBasic + { + var index:int = members.indexOf(OldObject); + if((index < 0) || (index >= members.length)) + return null; + members[index] = NewObject; + return NewObject; + } + + /** + * Call this function to sort the group according to a particular value and order. + * For example, to sort game objects for Zelda-style overlaps you might call + * myGroup.sort("y",ASCENDING) at the bottom of your + * FlxState.update() override. To sort all existing objects after + * a big explosion or bomb attack, you might call myGroup.sort("exists",DESCENDING). + * + * @param Index The String name of the member variable you want to sort on. Default value is "y". + * @param Order A FlxGroup constant that defines the sort order. Possible values are ASCENDING and DESCENDING. Default value is ASCENDING. + */ + public function sort(Index:String="y",Order:int=ASCENDING):void + { + _sortIndex = Index; + _sortOrder = Order; + members.sort(sortHandler); + } + + /** + * Go through and set the specified variable to the specified value on all members of the group. + * + * @param VariableName The string representation of the variable name you want to modify, for example "visible" or "scrollFactor". + * @param Value The value you want to assign to that variable. + * @param Recurse Default value is true, meaning if setAll() encounters a member that is a group, it will call setAll() on that group rather than modifying its variable. + */ + public function setAll(VariableName:String,Value:Object,Recurse:Boolean=true):void + { + var basic:FlxBasic; + var i:uint = 0; + while(i < length) + { + basic = members[i++] as FlxBasic; + if(basic != null) + { + if(Recurse && (basic is FlxGroup)) + (basic as FlxGroup).setAll(VariableName,Value,Recurse); + else + basic[VariableName] = Value; + } + } + } + + /** + * Go through and call the specified function on all members of the group. + * Currently only works on functions that have no required parameters. + * + * @param FunctionName The string representation of the function you want to call on each object, for example "kill()" or "init()". + * @param Recurse Default value is true, meaning if callAll() encounters a member that is a group, it will call callAll() on that group rather than calling the group's function. + */ + public function callAll(FunctionName:String,Recurse:Boolean=true):void + { + var basic:FlxBasic; + var i:uint = 0; + while(i < length) + { + basic = members[i++] as FlxBasic; + if(basic != null) + { + if(Recurse && (basic is FlxGroup)) + (basic as FlxGroup).callAll(FunctionName,Recurse); + else + basic[FunctionName](); + } + } + } + + /** + * Call this function to retrieve the first object with exists == false in the group. + * This is handy for recycling in general, e.g. respawning enemies. + * + * @param ObjectClass An optional parameter that lets you narrow the results to instances of this particular class. + * + * @return A FlxBasic currently flagged as not existing. + */ + public function getFirstAvailable(ObjectClass:Class=null):FlxBasic + { + var basic:FlxBasic; + var i:uint = 0; + while(i < length) + { + basic = members[i++] as FlxBasic; + if((basic != null) && !basic.exists && ((ObjectClass == null) || (basic is ObjectClass))) + return basic; + } + return null; + } + + /** + * Call this function to retrieve the first index set to 'null'. + * Returns -1 if no index stores a null object. + * + * @return An int indicating the first null slot in the group. + */ + public function getFirstNull():int + { + var basic:FlxBasic; + var i:uint = 0; + var l:uint = members.length; + while(i < l) + { + if(members[i] == null) + return i; + else + i++; + } + return -1; + } + + /** + * Call this function to retrieve the first object with exists == true in the group. + * This is handy for checking if everything's wiped out, or choosing a squad leader, etc. + * + * @return A FlxBasic currently flagged as existing. + */ + public function getFirstExtant():FlxBasic + { + var basic:FlxBasic; + var i:uint = 0; + while(i < length) + { + basic = members[i++] as FlxBasic; + if((basic != null) && basic.exists) + return basic; + } + return null; + } + + /** + * Call this function to retrieve the first object with dead == false in the group. + * This is handy for checking if everything's wiped out, or choosing a squad leader, etc. + * + * @return A FlxBasic currently flagged as not dead. + */ + public function getFirstAlive():FlxBasic + { + var basic:FlxBasic; + var i:uint = 0; + while(i < length) + { + basic = members[i++] as FlxBasic; + if((basic != null) && basic.exists && basic.alive) + return basic; + } + return null; + } + + /** + * Call this function to retrieve the first object with dead == true in the group. + * This is handy for checking if everything's wiped out, or choosing a squad leader, etc. + * + * @return A FlxBasic currently flagged as dead. + */ + public function getFirstDead():FlxBasic + { + var basic:FlxBasic; + var i:uint = 0; + while(i < length) + { + basic = members[i++] as FlxBasic; + if((basic != null) && !basic.alive) + return basic; + } + return null; + } + + /** + * Call this function to find out how many members of the group are not dead. + * + * @return The number of FlxBasics flagged as not dead. Returns -1 if group is empty. + */ + public function countLiving():int + { + var count:int = -1; + var basic:FlxBasic; + var i:uint = 0; + while(i < length) + { + basic = members[i++] as FlxBasic; + if(basic != null) + { + if(count < 0) + count = 0; + if(basic.exists && basic.alive) + count++; + } + } + return count; + } + + /** + * Call this function to find out how many members of the group are dead. + * + * @return The number of FlxBasics flagged as dead. Returns -1 if group is empty. + */ + public function countDead():int + { + var count:int = -1; + var basic:FlxBasic; + var i:uint = 0; + while(i < length) + { + basic = members[i++] as FlxBasic; + if(basic != null) + { + if(count < 0) + count = 0; + if(!basic.alive) + count++; + } + } + return count; + } + + /** + * Returns a member at random from the group. + * + * @param StartIndex Optional offset off the front of the array. Default value is 0, or the beginning of the array. + * @param Length Optional restriction on the number of values you want to randomly select from. + * + * @return A FlxBasic from the members list. + */ + public function getRandom(StartIndex:uint=0,Length:uint=0):FlxBasic + { + if(Length == 0) + Length = length; + return FlxG.getRandom(members,StartIndex,Length) as FlxBasic; + } + + /** + * Remove all instances of FlxBasic subclass (FlxSprite, FlxBlock, etc) from the list. + * WARNING: does not destroy() or kill() any of these objects! + */ + public function clear():void + { + length = members.length = 0; + } + + /** + * Calls kill on the group's members and then on the group itself. + */ + override public function kill():void + { + var basic:FlxBasic; + var i:uint = 0; + while(i < length) + { + basic = members[i++] as FlxBasic; + if((basic != null) && basic.exists) + basic.kill(); + } + super.kill(); + } + + /** + * Helper function for the sort process. + * + * @param Obj1 The first object being sorted. + * @param Obj2 The second object being sorted. + * + * @return An integer value: -1 (Obj1 before Obj2), 0 (same), or 1 (Obj1 after Obj2). + */ + protected function sortHandler(Obj1:FlxBasic,Obj2:FlxBasic):int + { + if(Obj1[_sortIndex] < Obj2[_sortIndex]) + return _sortOrder; + else if(Obj1[_sortIndex] > Obj2[_sortIndex]) + return -_sortOrder; + return 0; + } + } +} diff --git a/src/org/flixel/FlxObject.as b/src/org/flixel/FlxObject.as new file mode 100644 index 0000000..665c8a3 --- /dev/null +++ b/src/org/flixel/FlxObject.as @@ -0,0 +1,1173 @@ +package org.flixel +{ + import flash.display.Graphics; + import flash.display.Sprite; + import flash.geom.Point; + + import org.flixel.FlxBasic; + + /** + * This is the base class for most of the display objects (FlxSprite, FlxText, etc). + * It includes some basic attributes about game objects, including retro-style flickering, + * basic state information, sizes, scrolling, and basic physics and motion. + * + * @author Adam Atomic + */ + public class FlxObject extends FlxBasic + { + /** + * Generic value for "left" Used by facing, allowCollisions, and touching. + */ + static public const LEFT:uint = 0x0001; + /** + * Generic value for "right" Used by facing, allowCollisions, and touching. + */ + static public const RIGHT:uint = 0x0010; + /** + * Generic value for "up" Used by facing, allowCollisions, and touching. + */ + static public const UP:uint = 0x0100; + /** + * Generic value for "down" Used by facing, allowCollisions, and touching. + */ + static public const DOWN:uint = 0x1000; + + /** + * Special-case constant meaning no collisions, used mainly by allowCollisions and touching. + */ + static public const NONE:uint = 0; + /** + * Special-case constant meaning up, used mainly by allowCollisions and touching. + */ + static public const CEILING:uint= UP; + /** + * Special-case constant meaning down, used mainly by allowCollisions and touching. + */ + static public const FLOOR:uint = DOWN; + /** + * Special-case constant meaning only the left and right sides, used mainly by allowCollisions and touching. + */ + static public const WALL:uint = LEFT | RIGHT; + /** + * Special-case constant meaning any direction, used mainly by allowCollisions and touching. + */ + static public const ANY:uint = LEFT | RIGHT | UP | DOWN; + + /** + * Handy constant used during collision resolution (see separateX() and separateY()). + */ + static public const OVERLAP_BIAS:Number = 4; + + /** + * Path behavior controls: move from the start of the path to the end then stop. + */ + static public const PATH_FORWARD:uint = 0x000000; + /** + * Path behavior controls: move from the end of the path to the start then stop. + */ + static public const PATH_BACKWARD:uint = 0x000001; + /** + * Path behavior controls: move from the start of the path to the end then directly back to the start, and start over. + */ + static public const PATH_LOOP_FORWARD:uint = 0x000010; + /** + * Path behavior controls: move from the end of the path to the start then directly back to the end, and start over. + */ + static public const PATH_LOOP_BACKWARD:uint = 0x000100; + /** + * 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. + */ + static public const PATH_YOYO:uint = 0x001000; + /** + * Path behavior controls: ignores any vertical component to the path data, only follows side to side. + */ + static public const PATH_HORIZONTAL_ONLY:uint = 0x010000; + /** + * Path behavior controls: ignores any horizontal component to the path data, only follows up and down. + */ + static public const PATH_VERTICAL_ONLY:uint = 0x100000; + + /** + * X position of the upper left corner of this object in world space. + */ + public var x:Number; + /** + * Y position of the upper left corner of this object in world space. + */ + public var y:Number; + /** + * The width of this object. + */ + public var width:Number; + /** + * The height of this object. + */ + public var height:Number; + + /** + * Whether an object will move/alter position after a collision. + */ + public var immovable:Boolean; + + /** + * The basic speed of this object. + */ + public var velocity:FlxPoint; + /** + * The virtual mass of the object. Default value is 1. + * Currently only used with elasticity during collision resolution. + * Change at your own risk; effects seem crazy unpredictable so far! + */ + public var mass:Number; + /** + * The bounciness of this object. Only affects collisions. Default value is 0, or "not bouncy at all." + */ + public var elasticity:Number; + /** + * How fast the speed of this object is changing. + * Useful for smooth movement and gravity. + */ + public var acceleration:FlxPoint; + /** + * This isn't drag exactly, more like deceleration that is only applied + * when acceleration is not affecting the sprite. + */ + public var drag:FlxPoint; + /** + * If you are using acceleration, you can use maxVelocity with it + * to cap the speed automatically (very useful!). + */ + public var maxVelocity:FlxPoint; + /** + * Set the angle of a sprite to rotate it. + * WARNING: rotating sprites decreases rendering + * performance for this sprite by a factor of 10x! + */ + public var angle:Number; + /** + * This is how fast you want this sprite to spin. + */ + public var angularVelocity:Number; + /** + * How fast the spin speed should change. + */ + public var angularAcceleration:Number; + /** + * Like drag but for spinning. + */ + public var angularDrag:Number; + /** + * Use in conjunction with angularAcceleration for fluid spin speed control. + */ + public var maxAngular:Number; + /** + * Should always represent (0,0) - useful for different things, for avoiding unnecessary new calls. + */ + static protected const _pZero:FlxPoint = new FlxPoint(); + + /** + * A point that can store numbers from 0 to 1 (for X and Y independently) + * that governs how much this object is affected by the camera subsystem. + * 0 means it never moves, like a HUD element or far background graphic. + * 1 means it scrolls along a the same speed as the foreground layer. + * scrollFactor is initialized as (1,1) by default. + */ + public var scrollFactor:FlxPoint; + /** + * Internal helper used for retro-style flickering. + */ + protected var _flicker:Boolean; + /** + * Internal helper used for retro-style flickering. + */ + protected var _flickerTimer:Number; + /** + * Handy for storing health percentage or armor points or whatever. + */ + public var health:Number; + /** + * This is just a pre-allocated x-y point container to be used however you like + */ + protected var _point:FlxPoint; + /** + * This is just a pre-allocated rectangle container to be used however you like + */ + protected var _rect:FlxRect; + /** + * Set this to false if you want to skip the automatic motion/movement stuff (see updateMotion()). + * FlxObject and FlxSprite default to true. + * FlxText, FlxTileblock, FlxTilemap and FlxSound default to false. + */ + public var moves:Boolean; + /** + * Bit field of flags (use with UP, DOWN, LEFT, RIGHT, etc) indicating surface contacts. + * Use bitwise operators to check the values stored here, or use touching(), justStartedTouching(), etc. + * You can even use them broadly as boolean values if you're feeling saucy! + */ + public var touching:uint; + /** + * Bit field of flags (use with UP, DOWN, LEFT, RIGHT, etc) indicating surface contacts from the previous game loop step. + * Use bitwise operators to check the values stored here, or use touching(), justStartedTouching(), etc. + * You can even use them broadly as boolean values if you're feeling saucy! + */ + public var wasTouching:uint; + /** + * Bit field of flags (use with UP, DOWN, LEFT, RIGHT, etc) indicating collision directions. + * Use bitwise operators to check the values stored here. + * Useful for things like one-way platforms (e.g. allowCollisions = UP;) + * The accessor "solid" just flips this variable between NONE and ANY. + */ + public var allowCollisions:uint; + + /** + * Important variable for collision processing. + * By default this value is set automatically during preUpdate(). + */ + public var last:FlxPoint; + + /** + * A reference to a path object. Null by default, assigned by followPath(). + */ + public var path:FlxPath; + /** + * The speed at which the object is moving on the path. + * When an object completes a non-looping path circuit, + * the pathSpeed will be zeroed out, but the path reference + * will NOT be nulled out. So pathSpeed is a good way + * to check if this object is currently following a path or not. + */ + public var pathSpeed:Number; + /** + * The angle in degrees between this object and the next node, where 0 is directly upward, and 90 is to the right. + */ + public var pathAngle:Number; + /** + * Internal helper, tracks which node of the path this object is moving toward. + */ + protected var _pathNodeIndex:int; + /** + * Internal tracker for path behavior flags (like looping, horizontal only, etc). + */ + protected var _pathMode:uint; + /** + * Internal helper for node navigation, specifically yo-yo and backwards movement. + */ + protected var _pathInc:int; + /** + * Internal flag for whether hte object's angle should be adjusted to the path angle during path follow behavior. + */ + protected var _pathRotate:Boolean; + + /** + * Instantiates a FlxObject. + * + * @param X The X-coordinate of the point in space. + * @param Y The Y-coordinate of the point in space. + * @param Width Desired width of the rectangle. + * @param Height Desired height of the rectangle. + */ + public function FlxObject(X:Number=0,Y:Number=0,Width:Number=0,Height:Number=0) + { + x = X; + y = Y; + last = new FlxPoint(x,y); + width = Width; + height = Height; + mass = 1.0; + elasticity = 0.0; + + immovable = false; + moves = true; + + touching = NONE; + wasTouching = NONE; + allowCollisions = ANY; + + velocity = new FlxPoint(); + acceleration = new FlxPoint(); + drag = new FlxPoint(); + maxVelocity = new FlxPoint(10000,10000); + + angle = 0; + angularVelocity = 0; + angularAcceleration = 0; + angularDrag = 0; + maxAngular = 10000; + + scrollFactor = new FlxPoint(1.0,1.0); + _flicker = false; + _flickerTimer = 0; + + _point = new FlxPoint(); + _rect = new FlxRect(); + + path = null; + pathSpeed = 0; + pathAngle = 0; + } + + /** + * Override this function to null out variables or + * manually call destroy() on class members if necessary. + * Don't forget to call super.destroy()! + */ + override public function destroy():void + { + velocity = null; + acceleration = null; + drag = null; + maxVelocity = null; + scrollFactor = null; + _point = null; + _rect = null; + last = null; + cameras = null; + if(path != null) + path.destroy(); + path = null; + } + + /** + * Pre-update is called right before update() on each object in the game loop. + * In FlxObject it controls the flicker timer, + * tracking the last coordinates for collision purposes, + * and checking if the object is moving along a path or not. + */ + override public function preUpdate():void + { + _ACTIVECOUNT++; + + if(_flickerTimer != 0) + { + if(_flickerTimer > 0) + { + _flickerTimer = _flickerTimer - FlxG.elapsed; + if(_flickerTimer <= 0) + { + _flickerTimer = 0; + _flicker = false; + } + } + } + + last.x = x; + last.y = y; + + if((path != null) && (pathSpeed != 0) && (path.nodes[_pathNodeIndex] != null)) + updatePathMotion(); + } + + /** + * Post-update is called right after update() on each object in the game loop. + * In FlxObject this function handles integrating the objects motion + * based on the velocity and acceleration settings, and tracking/clearing the touching flags. + */ + override public function postUpdate():void + { + if(moves) + updateMotion(); + + wasTouching = touching; + touching = NONE; + } + + /** + * Internal function for updating the position and speed of this object. + * Useful for cases when you need to update this but are buried down in too many supers. + * Does a slightly fancier-than-normal integration to help with higher fidelity framerate-independenct motion. + */ + protected function updateMotion():void + { + var delta:Number; + var velocityDelta:Number; + + velocityDelta = (FlxU.computeVelocity(angularVelocity,angularAcceleration,angularDrag,maxAngular) - angularVelocity)/2; + angularVelocity += velocityDelta; + angle += angularVelocity*FlxG.elapsed; + angularVelocity += velocityDelta; + + velocityDelta = (FlxU.computeVelocity(velocity.x,acceleration.x,drag.x,maxVelocity.x) - velocity.x)/2; + velocity.x += velocityDelta; + delta = velocity.x*FlxG.elapsed; + velocity.x += velocityDelta; + x += delta; + + velocityDelta = (FlxU.computeVelocity(velocity.y,acceleration.y,drag.y,maxVelocity.y) - velocity.y)/2; + velocity.y += velocityDelta; + delta = velocity.y*FlxG.elapsed; + velocity.y += velocityDelta; + y += delta; + } + + /** + * Rarely called, and in this case just increments the visible objects count and calls drawDebug() if necessary. + */ + override public function draw():void + { + if(cameras == null) + cameras = FlxG.cameras; + var camera:FlxCamera; + var i:uint = 0; + var l:uint = cameras.length; + while(i < l) + { + camera = cameras[i++]; + if(!onScreen(camera)) + continue; + _VISIBLECOUNT++; + if(FlxG.visualDebug && !ignoreDrawDebug) + drawDebug(camera); + } + } + + /** + * Override this function to draw custom "debug mode" graphics to the + * specified camera while the debugger's visual mode is toggled on. + * + * @param Camera Which camera to draw the debug visuals to. + */ + override public function drawDebug(Camera:FlxCamera=null):void + { + if(Camera == null) + Camera = FlxG.camera; + + //get bounding box coordinates + var boundingBoxX:Number = x - int(Camera.scroll.x*scrollFactor.x); //copied from getScreenXY() + var boundingBoxY:Number = y - int(Camera.scroll.y*scrollFactor.y); + boundingBoxX = int(boundingBoxX + ((boundingBoxX > 0)?0.0000001:-0.0000001)); + boundingBoxY = int(boundingBoxY + ((boundingBoxY > 0)?0.0000001:-0.0000001)); + var boundingBoxWidth:int = (width != int(width))?width:width-1; + var boundingBoxHeight:int = (height != int(height))?height:height-1; + + //fill static graphics object with square shape + var gfx:Graphics = FlxG.flashGfx; + gfx.clear(); + gfx.moveTo(boundingBoxX,boundingBoxY); + var boundingBoxColor:uint; + if(allowCollisions) + { + if(allowCollisions != ANY) + boundingBoxColor = FlxG.PINK; + if(immovable) + boundingBoxColor = FlxG.GREEN; + else + boundingBoxColor = FlxG.RED; + } + else + boundingBoxColor = FlxG.BLUE; + gfx.lineStyle(1,boundingBoxColor,0.5); + gfx.lineTo(boundingBoxX+boundingBoxWidth,boundingBoxY); + gfx.lineTo(boundingBoxX+boundingBoxWidth,boundingBoxY+boundingBoxHeight); + gfx.lineTo(boundingBoxX,boundingBoxY+boundingBoxHeight); + gfx.lineTo(boundingBoxX,boundingBoxY); + + //draw graphics shape to camera buffer + Camera.buffer.draw(FlxG.flashGfxSprite); + } + + /** + * Call this function to give this object a path to follow. + * If the path does not have at least one node in it, this function + * will log a warning message and return. + * + * @param Path The FlxPath you want this object to follow. + * @param Speed How fast to travel along the path in pixels per second. + * @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. + * @param AutoRotate Automatically point the object toward the next node. Assumes the graphic is pointing upward. Default behavior is false, or no automatic rotation. + */ + public function followPath(Path:FlxPath,Speed:Number=100,Mode:uint=PATH_FORWARD,AutoRotate:Boolean=false):void + { + if(Path.nodes.length <= 0) + { + FlxG.log("WARNING: Paths need at least one node in them to be followed."); + return; + } + + path = Path; + pathSpeed = FlxU.abs(Speed); + _pathMode = Mode; + _pathRotate = AutoRotate; + + //get starting node + if((_pathMode == PATH_BACKWARD) || (_pathMode == PATH_LOOP_BACKWARD)) + { + _pathNodeIndex = path.nodes.length-1; + _pathInc = -1; + } + else + { + _pathNodeIndex = 0; + _pathInc = 1; + } + } + + /** + * Tells this object to stop following the path its on. + * + * @param DestroyPath Tells this function whether to call destroy on the path object. Default value is false. + */ + public function stopFollowingPath(DestroyPath:Boolean=false):void + { + pathSpeed = 0; + if(DestroyPath && (path != null)) + { + path.destroy(); + path = null; + } + } + + /** + * Internal function that decides what node in the path to aim for next based on the behavior flags. + * + * @return The node (a FlxPoint object) we are aiming for next. + */ + protected function advancePath(Snap:Boolean=true):FlxPoint + { + if(Snap) + { + var oldNode:FlxPoint = path.nodes[_pathNodeIndex]; + if(oldNode != null) + { + if((_pathMode & PATH_VERTICAL_ONLY) == 0) + x = oldNode.x - width*0.5; + if((_pathMode & PATH_HORIZONTAL_ONLY) == 0) + y = oldNode.y - height*0.5; + } + } + + _pathNodeIndex += _pathInc; + + if((_pathMode & PATH_BACKWARD) > 0) + { + if(_pathNodeIndex < 0) + { + _pathNodeIndex = 0; + pathSpeed = 0; + } + } + else if((_pathMode & PATH_LOOP_FORWARD) > 0) + { + if(_pathNodeIndex >= path.nodes.length) + _pathNodeIndex = 0; + } + else if((_pathMode & PATH_LOOP_BACKWARD) > 0) + { + if(_pathNodeIndex < 0) + { + _pathNodeIndex = path.nodes.length-1; + if(_pathNodeIndex < 0) + _pathNodeIndex = 0; + } + } + else if((_pathMode & PATH_YOYO) > 0) + { + if(_pathInc > 0) + { + if(_pathNodeIndex >= path.nodes.length) + { + _pathNodeIndex = path.nodes.length-2; + if(_pathNodeIndex < 0) + _pathNodeIndex = 0; + _pathInc = -_pathInc; + } + } + else if(_pathNodeIndex < 0) + { + _pathNodeIndex = 1; + if(_pathNodeIndex >= path.nodes.length) + _pathNodeIndex = path.nodes.length-1; + if(_pathNodeIndex < 0) + _pathNodeIndex = 0; + _pathInc = -_pathInc; + } + } + else + { + if(_pathNodeIndex >= path.nodes.length) + { + _pathNodeIndex = path.nodes.length-1; + pathSpeed = 0; + } + } + + return path.nodes[_pathNodeIndex]; + } + + /** + * Internal function for moving the object along the path. + * Generally this function is called automatically by preUpdate(). + * The first half of the function decides if the object can advance to the next node in the path, + * while the second half handles actually picking a velocity toward the next node. + */ + protected function updatePathMotion():void + { + //first check if we need to be pointing at the next node yet + _point.x = x + width*0.5; + _point.y = y + height*0.5; + var node:FlxPoint = path.nodes[_pathNodeIndex]; + var deltaX:Number = node.x - _point.x; + var deltaY:Number = node.y - _point.y; + + var horizontalOnly:Boolean = (_pathMode & PATH_HORIZONTAL_ONLY) > 0; + var verticalOnly:Boolean = (_pathMode & PATH_VERTICAL_ONLY) > 0; + + if(horizontalOnly) + { + if(((deltaX>0)?deltaX:-deltaX) < pathSpeed*FlxG.elapsed) + node = advancePath(); + } + else if(verticalOnly) + { + if(((deltaY>0)?deltaY:-deltaY) < pathSpeed*FlxG.elapsed) + node = advancePath(); + } + else + { + if(Math.sqrt(deltaX*deltaX + deltaY*deltaY) < pathSpeed*FlxG.elapsed) + node = advancePath(); + } + + //then just move toward the current node at the requested speed + if(pathSpeed != 0) + { + //set velocity based on path mode + _point.x = x + width*0.5; + _point.y = y + height*0.5; + if(horizontalOnly || (_point.y == node.y)) + { + velocity.x = (_point.x < node.x)?pathSpeed:-pathSpeed; + if(velocity.x < 0) + pathAngle = -90; + else + pathAngle = 90; + if(!horizontalOnly) + velocity.y = 0; + } + else if(verticalOnly || (_point.x == node.x)) + { + velocity.y = (_point.y < node.y)?pathSpeed:-pathSpeed; + if(velocity.y < 0) + pathAngle = 0; + else + pathAngle = 180; + if(!verticalOnly) + velocity.x = 0; + } + else + { + pathAngle = FlxU.getAngle(_point,node); + FlxU.rotatePoint(0,pathSpeed,0,0,pathAngle,velocity); + } + + //then set object rotation if necessary + if(_pathRotate) + { + angularVelocity = 0; + angularAcceleration = 0; + angle = pathAngle; + } + } + } + + /** + * Checks to see if some FlxObject overlaps this FlxObject or FlxGroup. + * If the group has a LOT of things in it, it might be faster to use FlxG.overlaps(). + * WARNING: Currently tilemaps do NOT support screen space overlap checks! + * + * @param ObjectOrGroup The object or group being tested. + * @param InScreenSpace Whether to take scroll factors into account when checking for overlap. Default is false, or "only compare in world space." + * @param Camera Specify which game camera you want. If null getScreenXY() will just grab the first global camera. + * + * @return Whether or not the two objects overlap. + */ + public function overlaps(ObjectOrGroup:FlxBasic,InScreenSpace:Boolean=false,Camera:FlxCamera=null):Boolean + { + if(ObjectOrGroup is FlxGroup) + { + var results:Boolean = false; + var i:uint = 0; + var members:Array = (ObjectOrGroup as FlxGroup).members; + while(i < length) + { + if(overlaps(members[i++],InScreenSpace,Camera)) + results = true; + } + return results; + } + + if(ObjectOrGroup is FlxTilemap) + { + //Since tilemap's have to be the caller, not the target, to do proper tile-based collisions, + // we redirect the call to the tilemap overlap here. + return (ObjectOrGroup as FlxTilemap).overlaps(this,InScreenSpace,Camera); + } + + var object:FlxObject = ObjectOrGroup as FlxObject; + if(!InScreenSpace) + { + return (object.x + object.width > x) && (object.x < x + width) && + (object.y + object.height > y) && (object.y < y + height); + } + + if(Camera == null) + Camera = FlxG.camera; + var objectScreenPos:FlxPoint = object.getScreenXY(null,Camera); + getScreenXY(_point,Camera); + return (objectScreenPos.x + object.width > _point.x) && (objectScreenPos.x < _point.x + width) && + (objectScreenPos.y + object.height > _point.y) && (objectScreenPos.y < _point.y + height); + } + + /** + * Checks to see if this FlxObject were located at the given position, would it overlap the FlxObject or FlxGroup? + * This is distinct from overlapsPoint(), which just checks that point, rather than taking the object's size into account. + * WARNING: Currently tilemaps do NOT support screen space overlap checks! + * + * @param X The X position you want to check. Pretends this object (the caller, not the parameter) is located here. + * @param Y The Y position you want to check. Pretends this object (the caller, not the parameter) is located here. + * @param ObjectOrGroup The object or group being tested. + * @param InScreenSpace Whether to take scroll factors into account when checking for overlap. Default is false, or "only compare in world space." + * @param Camera Specify which game camera you want. If null getScreenXY() will just grab the first global camera. + * + * @return Whether or not the two objects overlap. + */ + public function overlapsAt(X:Number,Y:Number,ObjectOrGroup:FlxBasic,InScreenSpace:Boolean=false,Camera:FlxCamera=null):Boolean + { + if(ObjectOrGroup is FlxGroup) + { + var results:Boolean = false; + var basic:FlxBasic; + var i:uint = 0; + var members:Array = (ObjectOrGroup as FlxGroup).members; + while(i < length) + { + if(overlapsAt(X,Y,members[i++],InScreenSpace,Camera)) + results = true; + } + return results; + } + + if(ObjectOrGroup is FlxTilemap) + { + //Since tilemap's have to be the caller, not the target, to do proper tile-based collisions, + // we redirect the call to the tilemap overlap here. + //However, since this is overlapsAt(), we also have to invent the appropriate position for the tilemap. + //So we calculate the offset between the player and the requested position, and subtract that from the tilemap. + var tilemap:FlxTilemap = ObjectOrGroup as FlxTilemap; + return tilemap.overlapsAt(tilemap.x - (X - x),tilemap.y - (Y - y),this,InScreenSpace,Camera); + } + + var object:FlxObject = ObjectOrGroup as FlxObject; + if(!InScreenSpace) + { + return (object.x + object.width > X) && (object.x < X + width) && + (object.y + object.height > Y) && (object.y < Y + height); + } + + if(Camera == null) + Camera = FlxG.camera; + var objectScreenPos:FlxPoint = object.getScreenXY(null,Camera); + _point.x = X - int(Camera.scroll.x*scrollFactor.x); //copied from getScreenXY() + _point.y = Y - int(Camera.scroll.y*scrollFactor.y); + _point.x += (_point.x > 0)?0.0000001:-0.0000001; + _point.y += (_point.y > 0)?0.0000001:-0.0000001; + return (objectScreenPos.x + object.width > _point.x) && (objectScreenPos.x < _point.x + width) && + (objectScreenPos.y + object.height > _point.y) && (objectScreenPos.y < _point.y + height); + } + + /** + * Checks to see if a point in 2D world space overlaps this FlxObject object. + * + * @param Point The point in world space you want to check. + * @param InScreenSpace Whether to take scroll factors into account when checking for overlap. + * @param Camera Specify which game camera you want. If null getScreenXY() will just grab the first global camera. + * + * @return Whether or not the point overlaps this object. + */ + public function overlapsPoint(Point:FlxPoint,InScreenSpace:Boolean=false,Camera:FlxCamera=null):Boolean + { + if(!InScreenSpace) + return (Point.x > x) && (Point.x < x + width) && (Point.y > y) && (Point.y < y + height); + + if(Camera == null) + Camera = FlxG.camera; + var X:Number = Point.x - Camera.scroll.x; + var Y:Number = Point.y - Camera.scroll.y; + getScreenXY(_point,Camera); + return (X > _point.x) && (X < _point.x+width) && (Y > _point.y) && (Y < _point.y+height); + } + + /** + * Check and see if this object is currently on screen. + * + * @param Camera Specify which game camera you want. If null getScreenXY() will just grab the first global camera. + * + * @return Whether the object is on screen or not. + */ + public function onScreen(Camera:FlxCamera=null):Boolean + { + if(Camera == null) + Camera = FlxG.camera; + getScreenXY(_point,Camera); + return (_point.x + width > 0) && (_point.x < Camera.width) && (_point.y + height > 0) && (_point.y < Camera.height); + } + + /** + * Call this function to figure out the on-screen position of the object. + * + * @param Camera Specify which game camera you want. If null getScreenXY() will just grab the first global camera. + * @param Point Takes a FlxPoint object and assigns the post-scrolled X and Y values of this object to it. + * + * @return The Point you passed in, or a new Point if you didn't pass one, containing the screen X and Y position of this object. + */ + public function getScreenXY(Point:FlxPoint=null,Camera:FlxCamera=null):FlxPoint + { + if(Point == null) + Point = new FlxPoint(); + if(Camera == null) + Camera = FlxG.camera; + Point.x = x - int(Camera.scroll.x*scrollFactor.x); + Point.y = y - int(Camera.scroll.y*scrollFactor.y); + Point.x += (Point.x > 0)?0.0000001:-0.0000001; + Point.y += (Point.y > 0)?0.0000001:-0.0000001; + return Point; + } + + /** + * Tells this object to flicker, retro-style. + * Pass a negative value to flicker forever. + * + * @param Duration How many seconds to flicker for. + */ + public function flicker(Duration:Number=1):void + { + _flickerTimer = Duration; + if(_flickerTimer == 0) + _flicker = false; + } + + /** + * Check to see if the object is still flickering. + * + * @return Whether the object is flickering or not. + */ + public function get flickering():Boolean + { + return _flickerTimer != 0; + } + + /** + * Whether the object collides or not. For more control over what directions + * the object will collide from, use collision constants (like LEFT, FLOOR, etc) + * to set the value of allowCollisions directly. + */ + public function get solid():Boolean + { + return (allowCollisions & ANY) > NONE; + } + + /** + * @private + */ + public function set solid(Solid:Boolean):void + { + if(Solid) + allowCollisions = ANY; + else + allowCollisions = NONE; + } + + /** + * Retrieve the midpoint of this object in world coordinates. + * + * @Point Allows you to pass in an existing FlxPoint object if you're so inclined. Otherwise a new one is created. + * + * @return A FlxPoint object containing the midpoint of this object in world coordinates. + */ + public function getMidpoint(Point:FlxPoint=null):FlxPoint + { + if(Point == null) + Point = new FlxPoint(); + Point.x = x + width*0.5; + Point.y = y + height*0.5; + return Point; + } + + /** + * Handy function for reviving game objects. + * Resets their existence flags and position. + * + * @param X The new X position of this object. + * @param Y The new Y position of this object. + */ + public function reset(X:Number,Y:Number):void + { + revive(); + touching = NONE; + wasTouching = NONE; + x = X; + y = Y; + last.x = x; + last.y = y; + velocity.x = 0; + velocity.y = 0; + } + + /** + * Handy function for checking if this object is touching a particular surface. + * For slightly better performance you can just & the value directly into touching. + * However, this method is good for readability and accessibility. + * + * @param Direction Any of the collision flags (e.g. LEFT, FLOOR, etc). + * + * @return Whether the object is touching an object in (any of) the specified direction(s) this frame. + */ + public function isTouching(Direction:uint):Boolean + { + return (touching & Direction) > NONE; + } + + /** + * Handy function for checking if this object is just landed on a particular surface. + * + * @param Direction Any of the collision flags (e.g. LEFT, FLOOR, etc). + * + * @return Whether the object just landed on (any of) the specified surface(s) this frame. + */ + public function justTouched(Direction:uint):Boolean + { + return ((touching & Direction) > NONE) && ((wasTouching & Direction) <= NONE); + } + + /** + * Reduces the "health" variable of this sprite by the amount specified in Damage. + * Calls kill() if health drops to or below zero. + * + * @param Damage How much health to take away (use a negative number to give a health bonus). + */ + public function hurt(Damage:Number):void + { + health = health - Damage; + if(health <= 0) + kill(); + } + + /** + * The main collision resolution function in flixel. + * + * @param Object1 Any FlxObject. + * @param Object2 Any other FlxObject. + * + * @return Whether the objects in fact touched and were separated. + */ + static public function separate(Object1:FlxObject, Object2:FlxObject):Boolean + { + var separatedX:Boolean = separateX(Object1,Object2); + var separatedY:Boolean = separateY(Object1,Object2); + return separatedX || separatedY; + } + + /** + * The X-axis component of the object separation process. + * + * @param Object1 Any FlxObject. + * @param Object2 Any other FlxObject. + * + * @return Whether the objects in fact touched and were separated along the X axis. + */ + static public function separateX(Object1:FlxObject, Object2:FlxObject):Boolean + { + //can't separate two immovable objects + var obj1immovable:Boolean = Object1.immovable; + var obj2immovable:Boolean = Object2.immovable; + if(obj1immovable && obj2immovable) + return false; + + //If one of the objects is a tilemap, just pass it off. + if(Object1 is FlxTilemap) + return (Object1 as FlxTilemap).overlapsWithCallback(Object2,separateX); + if(Object2 is FlxTilemap) + return (Object2 as FlxTilemap).overlapsWithCallback(Object1,separateX,true); + + //First, get the two object deltas + var overlap:Number = 0; + var obj1delta:Number = Object1.x - Object1.last.x; + var obj2delta:Number = Object2.x - Object2.last.x; + if(obj1delta != obj2delta) + { + //Check if the X hulls actually overlap + var obj1deltaAbs:Number = (obj1delta > 0)?obj1delta:-obj1delta; + var obj2deltaAbs:Number = (obj2delta > 0)?obj2delta:-obj2delta; + var obj1rect:FlxRect = new FlxRect(Object1.x-((obj1delta > 0)?obj1delta:0),Object1.last.y,Object1.width+((obj1delta > 0)?obj1delta:-obj1delta),Object1.height); + var obj2rect:FlxRect = new FlxRect(Object2.x-((obj2delta > 0)?obj2delta:0),Object2.last.y,Object2.width+((obj2delta > 0)?obj2delta:-obj2delta),Object2.height); + 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)) + { + var maxOverlap:Number = obj1deltaAbs + obj2deltaAbs + OVERLAP_BIAS; + + //If they did overlap (and can), figure out by how much and flip the corresponding flags + if(obj1delta > obj2delta) + { + overlap = Object1.x + Object1.width - Object2.x; + if((overlap > maxOverlap) || !(Object1.allowCollisions & RIGHT) || !(Object2.allowCollisions & LEFT)) + overlap = 0; + else + { + Object1.touching |= RIGHT; + Object2.touching |= LEFT; + } + } + else if(obj1delta < obj2delta) + { + overlap = Object1.x - Object2.width - Object2.x; + if((-overlap > maxOverlap) || !(Object1.allowCollisions & LEFT) || !(Object2.allowCollisions & RIGHT)) + overlap = 0; + else + { + Object1.touching |= LEFT; + Object2.touching |= RIGHT; + } + } + } + } + + //Then adjust their positions and velocities accordingly (if there was any overlap) + if(overlap != 0) + { + var obj1v:Number = Object1.velocity.x; + var obj2v:Number = Object2.velocity.x; + + if(!obj1immovable && !obj2immovable) + { + overlap *= 0.5; + Object1.x = Object1.x - overlap; + Object2.x += overlap; + + var obj1velocity:Number = Math.sqrt((obj2v * obj2v * Object2.mass)/Object1.mass) * ((obj2v > 0)?1:-1); + var obj2velocity:Number = Math.sqrt((obj1v * obj1v * Object1.mass)/Object2.mass) * ((obj1v > 0)?1:-1); + var average:Number = (obj1velocity + obj2velocity)*0.5; + obj1velocity -= average; + obj2velocity -= average; + Object1.velocity.x = average + obj1velocity * Object1.elasticity; + Object2.velocity.x = average + obj2velocity * Object2.elasticity; + } + else if(!obj1immovable) + { + Object1.x = Object1.x - overlap; + Object1.velocity.x = obj2v - obj1v*Object1.elasticity; + } + else if(!obj2immovable) + { + Object2.x += overlap; + Object2.velocity.x = obj1v - obj2v*Object2.elasticity; + } + return true; + } + else + return false; + } + + /** + * The Y-axis component of the object separation process. + * + * @param Object1 Any FlxObject. + * @param Object2 Any other FlxObject. + * + * @return Whether the objects in fact touched and were separated along the Y axis. + */ + static public function separateY(Object1:FlxObject, Object2:FlxObject):Boolean + { + //can't separate two immovable objects + var obj1immovable:Boolean = Object1.immovable; + var obj2immovable:Boolean = Object2.immovable; + if(obj1immovable && obj2immovable) + return false; + + //If one of the objects is a tilemap, just pass it off. + if(Object1 is FlxTilemap) + return (Object1 as FlxTilemap).overlapsWithCallback(Object2,separateY); + if(Object2 is FlxTilemap) + return (Object2 as FlxTilemap).overlapsWithCallback(Object1,separateY,true); + + //First, get the two object deltas + var overlap:Number = 0; + var obj1delta:Number = Object1.y - Object1.last.y; + var obj2delta:Number = Object2.y - Object2.last.y; + if(obj1delta != obj2delta) + { + //Check if the Y hulls actually overlap + var obj1deltaAbs:Number = (obj1delta > 0)?obj1delta:-obj1delta; + var obj2deltaAbs:Number = (obj2delta > 0)?obj2delta:-obj2delta; + var obj1rect:FlxRect = new FlxRect(Object1.x,Object1.y-((obj1delta > 0)?obj1delta:0),Object1.width,Object1.height+obj1deltaAbs); + var obj2rect:FlxRect = new FlxRect(Object2.x,Object2.y-((obj2delta > 0)?obj2delta:0),Object2.width,Object2.height+obj2deltaAbs); + 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)) + { + var maxOverlap:Number = obj1deltaAbs + obj2deltaAbs + OVERLAP_BIAS; + + //If they did overlap (and can), figure out by how much and flip the corresponding flags + if(obj1delta > obj2delta) + { + overlap = Object1.y + Object1.height - Object2.y; + if((overlap > maxOverlap) || !(Object1.allowCollisions & DOWN) || !(Object2.allowCollisions & UP)) + overlap = 0; + else + { + Object1.touching |= DOWN; + Object2.touching |= UP; + } + } + else if(obj1delta < obj2delta) + { + overlap = Object1.y - Object2.height - Object2.y; + if((-overlap > maxOverlap) || !(Object1.allowCollisions & UP) || !(Object2.allowCollisions & DOWN)) + overlap = 0; + else + { + Object1.touching |= UP; + Object2.touching |= DOWN; + } + } + } + } + + //Then adjust their positions and velocities accordingly (if there was any overlap) + if(overlap != 0) + { + var obj1v:Number = Object1.velocity.y; + var obj2v:Number = Object2.velocity.y; + + if(!obj1immovable && !obj2immovable) + { + overlap *= 0.5; + Object1.y = Object1.y - overlap; + Object2.y += overlap; + + var obj1velocity:Number = Math.sqrt((obj2v * obj2v * Object2.mass)/Object1.mass) * ((obj2v > 0)?1:-1); + var obj2velocity:Number = Math.sqrt((obj1v * obj1v * Object1.mass)/Object2.mass) * ((obj1v > 0)?1:-1); + var average:Number = (obj1velocity + obj2velocity)*0.5; + obj1velocity -= average; + obj2velocity -= average; + Object1.velocity.y = average + obj1velocity * Object1.elasticity; + Object2.velocity.y = average + obj2velocity * Object2.elasticity; + } + else if(!obj1immovable) + { + Object1.y = Object1.y - overlap; + Object1.velocity.y = obj2v - obj1v*Object1.elasticity; + //This is special case code that handles cases like horizontal moving platforms you can ride + if(Object2.active && Object2.moves && (obj1delta > obj2delta)) + Object1.x += Object2.x - Object2.last.x; + } + else if(!obj2immovable) + { + Object2.y += overlap; + Object2.velocity.y = obj1v - obj2v*Object2.elasticity; + //This is special case code that handles cases like horizontal moving platforms you can ride + if(Object1.active && Object1.moves && (obj1delta < obj2delta)) + Object2.x += Object1.x - Object1.last.x; + } + return true; + } + else + return false; + } + } +} diff --git a/src/org/flixel/FlxParticle.as b/src/org/flixel/FlxParticle.as new file mode 100644 index 0000000..61d516f --- /dev/null +++ b/src/org/flixel/FlxParticle.as @@ -0,0 +1,93 @@ +package org.flixel +{ + + /** + * This is a simple particle class that extends the default behavior + * of FlxSprite to have slightly more specialized behavior + * common to many game scenarios. You can override and extend this class + * just like you would FlxSprite. While FlxEmitter + * used to work with just any old sprite, it now requires a + * FlxParticle based class. + * + * @author Adam Atomic + */ + public class FlxParticle extends FlxSprite + { + /** + * How long this particle lives before it disappears. + * NOTE: this is a maximum, not a minimum; the object + * could get recycled before its lifespan is up. + */ + public var lifespan:Number; + + /** + * Determines how quickly the particles come to rest on the ground. + * Only used if the particle has gravity-like acceleration applied. + * @default 500 + */ + public var friction:Number; + + /** + * Instantiate a new particle. Like FlxSprite, all meaningful creation + * happens during loadGraphic() or makeGraphic() or whatever. + */ + public function FlxParticle() + { + super(); + lifespan = 0; + friction = 500; + } + + /** + * The particle's main update logic. Basically it checks to see if it should + * be dead yet, and then has some special bounce behavior if there is some gravity on it. + */ + override public function update():void + { + //lifespan behavior + if(lifespan <= 0) + return; + lifespan -= FlxG.elapsed; + if(lifespan <= 0) + kill(); + + //simpler bounce/spin behavior for now + if(touching) + { + if(angularVelocity != 0) + angularVelocity = -angularVelocity; + } + if(acceleration.y > 0) //special behavior for particles with gravity + { + if(touching & FLOOR) + { + drag.x = friction; + + if(!(wasTouching & FLOOR)) + { + if(velocity.y < -elasticity*10) + { + if(angularVelocity != 0) + angularVelocity *= -elasticity; + } + else + { + velocity.y = 0; + angularVelocity = 0; + } + } + } + else + drag.x = 0; + } + } + + /** + * Triggered whenever this object is launched by a FlxEmitter. + * You can override this to add custom behavior like a sound or AI or something. + */ + public function onEmit():void + { + } + } +} diff --git a/src/org/flixel/FlxPath.as b/src/org/flixel/FlxPath.as new file mode 100644 index 0000000..bf16834 --- /dev/null +++ b/src/org/flixel/FlxPath.as @@ -0,0 +1,280 @@ +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 FlxPoints 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. + * @default false + */ + 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; + ignoreDrawDebug = false; + + 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)[0]; + 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)[0]; + } + + /** + * 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; + } + } +} \ No newline at end of file diff --git a/src/org/flixel/FlxPoint.as b/src/org/flixel/FlxPoint.as new file mode 100644 index 0000000..f0938ef --- /dev/null +++ b/src/org/flixel/FlxPoint.as @@ -0,0 +1,102 @@ +package org.flixel +{ + import flash.geom.Point; + + /** + * Stores a 2D floating point coordinate. + * + * @author Adam Atomic + */ + public class FlxPoint + { + /** + * @default 0 + */ + public var x:Number; + /** + * @default 0 + */ + public var y:Number; + + /** + * Instantiate a new point object. + * + * @param X The X-coordinate of the point in space. + * @param Y The Y-coordinate of the point in space. + */ + public function FlxPoint(X:Number=0, Y:Number=0) + { + x = X; + y = Y; + } + + /** + * Instantiate a new point object. + * + * @param X The X-coordinate of the point in space. + * @param Y The Y-coordinate of the point in space. + */ + public function make(X:Number=0, Y:Number=0):FlxPoint + { + x = X; + y = Y; + return this; + } + + /** + * Helper function, just copies the values from the specified point. + * + * @param Point Any FlxPoint. + * + * @return A reference to itself. + */ + public function copyFrom(Point:FlxPoint):FlxPoint + { + x = Point.x; + y = Point.y; + return this; + } + + /** + * Helper function, just copies the values from this point to the specified point. + * + * @param Point Any FlxPoint. + * + * @return A reference to the altered point parameter. + */ + public function copyTo(Point:FlxPoint):FlxPoint + { + Point.x = x; + Point.y = y; + return Point; + } + + /** + * Helper function, just copies the values from the specified Flash point. + * + * @param Point Any Point. + * + * @return A reference to itself. + */ + public function copyFromFlash(FlashPoint:Point):FlxPoint + { + x = FlashPoint.x; + y = FlashPoint.y; + return this; + } + + /** + * Helper function, just copies the values from this point to the specified Flash point. + * + * @param Point Any Point. + * + * @return A reference to the altered point parameter. + */ + public function copyToFlash(FlashPoint:Point):Point + { + FlashPoint.x = x; + FlashPoint.y = y; + return FlashPoint; + } + } +} diff --git a/src/org/flixel/FlxRect.as b/src/org/flixel/FlxRect.as new file mode 100644 index 0000000..b2e24be --- /dev/null +++ b/src/org/flixel/FlxRect.as @@ -0,0 +1,172 @@ +package org.flixel +{ + import flash.geom.Rectangle; + + /** + * Stores a rectangle. + * + * @author Adam Atomic + */ + public class FlxRect + { + /** + * @default 0 + */ + public var x:Number; + /** + * @default 0 + */ + public var y:Number; + /** + * @default 0 + */ + public var width:Number; + /** + * @default 0 + */ + public var height:Number; + + /** + * Instantiate a new rectangle. + * + * @param X The X-coordinate of the point in space. + * @param Y The Y-coordinate of the point in space. + * @param Width Desired width of the rectangle. + * @param Height Desired height of the rectangle. + */ + public function FlxRect(X:Number=0, Y:Number=0, Width:Number=0, Height:Number=0) + { + x = X; + y = Y; + width = Width; + height = Height; + } + + /** + * The X coordinate of the left side of the rectangle. Read-only. + */ + public function get left():Number + { + return x; + } + + /** + * The X coordinate of the right side of the rectangle. Read-only. + */ + public function get right():Number + { + return x + width; + } + + /** + * The Y coordinate of the top of the rectangle. Read-only. + */ + public function get top():Number + { + return y; + } + + /** + * The Y coordinate of the bottom of the rectangle. Read-only. + */ + public function get bottom():Number + { + return y + height; + } + + /** + * Instantiate a new rectangle. + * + * @param X The X-coordinate of the point in space. + * @param Y The Y-coordinate of the point in space. + * @param Width Desired width of the rectangle. + * @param Height Desired height of the rectangle. + * + * @return A reference to itself. + */ + public function make(X:Number=0, Y:Number=0, Width:Number=0, Height:Number=0):FlxRect + { + x = X; + y = Y; + width = Width; + height = Height; + return this; + } + + /** + * Helper function, just copies the values from the specified rectangle. + * + * @param Rect Any FlxRect. + * + * @return A reference to itself. + */ + public function copyFrom(Rect:FlxRect):FlxRect + { + x = Rect.x; + y = Rect.y; + width = Rect.width; + height = Rect.height; + return this; + } + + /** + * Helper function, just copies the values from this rectangle to the specified rectangle. + * + * @param Point Any FlxRect. + * + * @return A reference to the altered rectangle parameter. + */ + public function copyTo(Rect:FlxRect):FlxRect + { + Rect.x = x; + Rect.y = y; + Rect.width = width; + Rect.height = height; + return Rect; + } + + /** + * Helper function, just copies the values from the specified Flash rectangle. + * + * @param FlashRect Any Rectangle. + * + * @return A reference to itself. + */ + public function copyFromFlash(FlashRect:Rectangle):FlxRect + { + x = FlashRect.x; + y = FlashRect.y; + width = FlashRect.width; + height = FlashRect.height; + return this; + } + + /** + * Helper function, just copies the values from this rectangle to the specified Flash rectangle. + * + * @param Point Any Rectangle. + * + * @return A reference to the altered rectangle parameter. + */ + public function copyToFlash(FlashRect:Rectangle):Rectangle + { + FlashRect.x = x; + FlashRect.y = y; + FlashRect.width = width; + FlashRect.height = height; + return FlashRect; + } + + /** + * Checks to see if some FlxRect object overlaps this FlxRect object. + * + * @param Rect The rectangle being tested. + * + * @return Whether or not the two rectangles overlap. + */ + public function overlaps(Rect:FlxRect):Boolean + { + return (Rect.x + Rect.width > x) && (Rect.x < x+width) && (Rect.y + Rect.height > y) && (Rect.y < y+height); + } + } +} diff --git a/src/org/flixel/FlxSave.as b/src/org/flixel/FlxSave.as new file mode 100644 index 0000000..5307823 --- /dev/null +++ b/src/org/flixel/FlxSave.as @@ -0,0 +1,195 @@ +package org.flixel +{ + import flash.events.NetStatusEvent; + import flash.net.SharedObject; + import flash.net.SharedObjectFlushStatus; + + /** + * A class to help automate and simplify save game functionality. + * Basicaly a wrapper for the Flash SharedObject thing, but + * handles some annoying storage request stuff too. + * + * @author Adam Atomic + */ + public class FlxSave extends Object + { + static protected var SUCCESS:uint = 0; + static protected var PENDING:uint = 1; + static protected var ERROR:uint = 2; + /** + * Allows you to directly access the data container in the local shared object. + * @default null + */ + public var data:Object; + /** + * The name of the local shared object. + * @default null + */ + public var name:String; + /** + * The local shared object itself. + * @default null + */ + protected var _sharedObject:SharedObject; + + /** + * Internal tracker for callback function in case save takes too long. + */ + protected var _onComplete:Function; + /** + * Internal tracker for save object close request. + */ + protected var _closeRequested:Boolean; + + /** + * Blanks out the containers. + */ + public function FlxSave() + { + destroy(); + } + + /** + * Clean up memory. + */ + public function destroy():void + { + _sharedObject = null; + name = null; + data = null; + _onComplete = null; + _closeRequested = false; + } + + /** + * Automatically creates or reconnects to locally saved data. + * + * @param Name The name of the object (should be the same each time to access old data). + * + * @return Whether or not you successfully connected to the save data. + */ + public function bind(Name:String):Boolean + { + destroy(); + name = Name; + try + { + _sharedObject = SharedObject.getLocal(name); + } + catch(e:Error) + { + FlxG.log("ERROR: There was a problem binding to\nthe shared object data from FlxSave."); + destroy(); + return false; + } + data = _sharedObject.data; + return true; + } + + /** + * A way to safely call flush() and destroy() on your save file. + * Will correctly handle storage size popups and all that good stuff. + * If you don't want to save your changes first, just call destroy() instead. + * + * @param MinFileSize If you need X amount of space for your save, specify it here. + * @param OnComplete This callback will be triggered when the data is written successfully. + * + * @return The result of result of the flush() call (see below for more details). + */ + public function close(MinFileSize:uint=0,OnComplete:Function=null):Boolean + { + _closeRequested = true; + return flush(MinFileSize,OnComplete); + } + + /** + * Writes the local shared object to disk immediately. Leaves the object open in memory. + * + * @param MinFileSize If you need X amount of space for your save, specify it here. + * @param OnComplete This callback will be triggered when the data is written successfully. + * + * @return Whether or not the data was written immediately. False could be an error OR a storage request popup. + */ + public function flush(MinFileSize:uint=0,OnComplete:Function=null):Boolean + { + if(!checkBinding()) + return false; + _onComplete = OnComplete; + var result:String = null; + try { result = _sharedObject.flush(MinFileSize); } + catch (e:Error) { return onDone(ERROR); } + if(result == SharedObjectFlushStatus.PENDING) + _sharedObject.addEventListener(NetStatusEvent.NET_STATUS,onFlushStatus); + return onDone((result == SharedObjectFlushStatus.FLUSHED)?SUCCESS:PENDING); + } + + /** + * Erases everything stored in the local shared object. + * Data is immediately erased and the object is saved that way, + * so use with caution! + * + * @return Returns false if the save object is not bound yet. + */ + public function erase():Boolean + { + if(!checkBinding()) + return false; + _sharedObject.clear(); + return true; + } + + /** + * Event handler for special case storage requests. + * + * @param E Flash net status event. + */ + protected function onFlushStatus(E:NetStatusEvent):void + { + _sharedObject.removeEventListener(NetStatusEvent.NET_STATUS,onFlushStatus); + onDone((E.info.code == "SharedObject.Flush.Success")?SUCCESS:ERROR); + } + + /** + * Event handler for special case storage requests. + * Handles logging of errors and calling of callback. + * + * @param Result One of the result codes (PENDING, ERROR, or SUCCESS). + * + * @return Whether the operation was a success or not. + */ + protected function onDone(Result:uint):Boolean + { + switch(Result) + { + case PENDING: + FlxG.log("FLIXEL: FlxSave is requesting extra storage space."); + break; + case ERROR: + FlxG.log("ERROR: There was a problem flushing\nthe shared object data from FlxSave."); + break; + default: + break; + } + if(_onComplete != null) + _onComplete(Result == SUCCESS); + if(_closeRequested) + destroy(); + return Result == SUCCESS; + } + + /** + * Handy utility function for checking and warning if the shared object is bound yet or not. + * + * @return Whether the shared object was bound yet. + */ + protected function checkBinding():Boolean + { + if(_sharedObject == null) + { + FlxG.log("FLIXEL: You must call FlxSave.bind()\nbefore you can read or write data."); + return false; + } + return true; + } + } +} diff --git a/src/org/flixel/FlxSound.as b/src/org/flixel/FlxSound.as new file mode 100644 index 0000000..2c9b367 --- /dev/null +++ b/src/org/flixel/FlxSound.as @@ -0,0 +1,542 @@ +package org.flixel +{ + import flash.events.Event; + import flash.media.Sound; + import flash.media.SoundChannel; + import flash.media.SoundTransform; + import flash.net.URLRequest; + + /** + * This is the universal flixel sound object, used for streaming, music, and sound effects. + * + * @author Adam Atomic + */ + public class FlxSound extends FlxBasic + { + /** + * The X position of this sound in world coordinates. + * Only really matters if you are doing proximity/panning stuff. + */ + public var x:Number; + /** + * The Y position of this sound in world coordinates. + * Only really matters if you are doing proximity/panning stuff. + */ + public var y:Number; + /** + * Whether or not this sound should be automatically destroyed when you switch states. + */ + public var survive:Boolean; + /** + * The ID3 song name. Defaults to null. Currently only works for streamed sounds. + */ + public var name:String; + /** + * The ID3 artist name. Defaults to null. Currently only works for streamed sounds. + */ + public var artist:String; + /** + * Stores the average wave amplitude of both stereo channels + */ + public var amplitude:Number; + /** + * Just the amplitude of the left stereo channel + */ + public var amplitudeLeft:Number; + /** + * Just the amplitude of the left stereo channel + */ + public var amplitudeRight:Number; + /** + * Whether to call destroy() when the sound has finished. + */ + public var autoDestroy:Boolean; + + /** + * Internal tracker for a Flash sound object. + */ + protected var _sound:Sound; + /** + * Internal tracker for a Flash sound channel object. + */ + protected var _channel:SoundChannel; + /** + * Internal tracker for a Flash sound transform object. + */ + protected var _transform:SoundTransform; + /** + * Internal tracker for the position in runtime of the music playback. + */ + protected var _position:Number; + /** + * Internal tracker for how loud the sound is. + */ + protected var _volume:Number; + /** + * Internal tracker for total volume adjustment. + */ + protected var _volumeAdjust:Number; + /** + * Internal tracker for whether the sound is looping or not. + */ + protected var _looped:Boolean; + /** + * Internal tracker for the sound's "target" (for proximity and panning). + */ + protected var _target:FlxObject; + /** + * Internal tracker for the maximum effective radius of this sound (for proximity and panning). + */ + protected var _radius:Number; + /** + * Internal tracker for whether to pan the sound left and right. Default is false. + */ + protected var _pan:Boolean; + /** + * Internal timer used to keep track of requests to fade out the sound playback. + */ + protected var _fadeOutTimer:Number; + /** + * Internal helper for fading out sounds. + */ + protected var _fadeOutTotal:Number; + /** + * Internal flag for whether to pause or stop the sound when it's done fading out. + */ + protected var _pauseOnFadeOut:Boolean; + /** + * Internal timer for fading in the sound playback. + */ + protected var _fadeInTimer:Number; + /** + * Internal helper for fading in sounds. + */ + protected var _fadeInTotal:Number; + + /** + * The FlxSound constructor gets all the variables initialized, but NOT ready to play a sound yet. + */ + public function FlxSound() + { + super(); + createSound(); + } + + /** + * An internal function for clearing all the variables used by sounds. + */ + protected function createSound():void + { + destroy(); + x = 0; + y = 0; + if(_transform == null) + _transform = new SoundTransform(); + _transform.pan = 0; + _sound = null; + _position = 0; + _volume = 1.0; + _volumeAdjust = 1.0; + _looped = false; + _target = null; + _radius = 0; + _pan = false; + _fadeOutTimer = 0; + _fadeOutTotal = 0; + _pauseOnFadeOut = false; + _fadeInTimer = 0; + _fadeInTotal = 0; + exists = false; + active = false; + visible = false; + name = null; + artist = null; + amplitude = 0; + amplitudeLeft = 0; + amplitudeRight = 0; + autoDestroy = false; + } + + /** + * Clean up memory. + */ + override public function destroy():void + { + kill(); + + _transform = null; + _sound = null; + _channel = null; + _target = null; + name = null; + artist = null; + + super.destroy(); + } + + /** + * Handles fade out, fade in, panning, proximity, and amplitude operations each frame. + */ + override public function update():void + { + if(_position != 0) + return; + + var radial:Number = 1.0; + var fade:Number = 1.0; + + //Distance-based volume control + if(_target != null) + { + radial = FlxU.getDistance(new FlxPoint(_target.x,_target.y),new FlxPoint(x,y))/_radius; + if(radial < 0) radial = 0; + if(radial > 1) radial = 1; + + if(_pan) + { + var d:Number = (_target.x-x)/_radius; + if(d < -1) d = -1; + else if(d > 1) d = 1; + _transform.pan = d; + } + } + + //Cross-fading volume control + if(_fadeOutTimer > 0) + { + _fadeOutTimer -= FlxG.elapsed; + if(_fadeOutTimer <= 0) + { + if(_pauseOnFadeOut) + pause(); + else + stop(); + } + fade = _fadeOutTimer/_fadeOutTotal; + if(fade < 0) fade = 0; + } + else if(_fadeInTimer > 0) + { + _fadeInTimer -= FlxG.elapsed; + fade = _fadeInTimer/_fadeInTotal; + if(fade < 0) fade = 0; + fade = 1 - fade; + } + + _volumeAdjust = radial*fade; + updateTransform(); + + if((_transform.volume > 0) && (_channel != null)) + { + amplitudeLeft = _channel.leftPeak/_transform.volume; + amplitudeRight = _channel.rightPeak/_transform.volume; + amplitude = (amplitudeLeft+amplitudeRight)*0.5; + } + } + + override public function kill():void + { + super.kill(); + if(_channel != null) + stop(); + } + + /** + * One of two main setup functions for sounds, this function loads a sound from an embedded MP3. + * + * @param EmbeddedSound An embedded Class object representing an MP3 file. + * @param Looped Whether or not this sound should loop endlessly. + * @param AutoDestroy Whether or not this FlxSound instance should be destroyed when the sound finishes playing. Default value is false, but FlxG.play() and FlxG.stream() will set it to true by default. + * + * @return This FlxSound instance (nice for chaining stuff together, if you're into that). + */ + public function loadEmbedded(EmbeddedSound:Class, Looped:Boolean=false, AutoDestroy:Boolean=false):FlxSound + { + stop(); + createSound(); + _sound = new EmbeddedSound(); + //NOTE: can't pull ID3 info from embedded sound currently + _looped = Looped; + updateTransform(); + exists = true; + return this; + } + + /** + * One of two main setup functions for sounds, this function loads a sound from a URL. + * + * @param EmbeddedSound A string representing the URL of the MP3 file you want to play. + * @param Looped Whether or not this sound should loop endlessly. + * @param AutoDestroy Whether or not this FlxSound instance should be destroyed when the sound finishes playing. Default value is false, but FlxG.play() and FlxG.stream() will set it to true by default. + * + * @return This FlxSound instance (nice for chaining stuff together, if you're into that). + */ + public function loadStream(SoundURL:String, Looped:Boolean=false, AutoDestroy:Boolean=false):FlxSound + { + stop(); + createSound(); + _sound = new Sound(); + _sound.addEventListener(Event.ID3, gotID3); + _sound.load(new URLRequest(SoundURL)); + _looped = Looped; + updateTransform(); + exists = true; + return this; + } + + /** + * Call this function if you want this sound's volume to change + * based on distance from a particular FlxCore object. + * + * @param X The X position of the sound. + * @param Y The Y position of the sound. + * @param Object The object you want to track. + * @param Radius The maximum distance this sound can travel. + * @param Pan Whether the sound should pan in addition to the volume changes (default: true). + * + * @return This FlxSound instance (nice for chaining stuff together, if you're into that). + */ + public function proximity(X:Number,Y:Number,Object:FlxObject,Radius:Number,Pan:Boolean=true):FlxSound + { + x = X; + y = Y; + _target = Object; + _radius = Radius; + _pan = Pan; + return this; + } + + /** + * Call this function to play the sound - also works on paused sounds. + * + * @param ForceRestart Whether to start the sound over or not. Default value is false, meaning if the sound is already playing or was paused when you call play(), it will continue playing from its current position, NOT start again from the beginning. + */ + public function play(ForceRestart:Boolean=false):void + { + if(_position < 0) + return; + if(ForceRestart) + { + var oldAutoDestroy:Boolean = autoDestroy; + autoDestroy = false; + stop(); + autoDestroy = oldAutoDestroy; + } + if(_looped) + { + if(_position == 0) + { + if(_channel == null) + _channel = _sound.play(0,9999,_transform); + if(_channel == null) + exists = false; + } + else + { + _channel = _sound.play(_position,0,_transform); + if(_channel == null) + exists = false; + else + _channel.addEventListener(Event.SOUND_COMPLETE, looped); + } + } + else + { + if(_position == 0) + { + if(_channel == null) + { + _channel = _sound.play(0,0,_transform); + if(_channel == null) + exists = false; + else + _channel.addEventListener(Event.SOUND_COMPLETE, stopped); + } + } + else + { + _channel = _sound.play(_position,0,_transform); + if(_channel == null) + exists = false; + } + } + active = (_channel != null); + _position = 0; + } + + /** + * Unpause a sound. Only works on sounds that have been paused. + */ + public function resume():void + { + if(_position <= 0) + return; + if(_looped) + { + _channel = _sound.play(_position,0,_transform); + if(_channel == null) + exists = false; + else + _channel.addEventListener(Event.SOUND_COMPLETE, looped); + } + else + { + _channel = _sound.play(_position,0,_transform); + if(_channel == null) + exists = false; + } + active = (_channel != null); + } + + /** + * Call this function to pause this sound. + */ + public function pause():void + { + if(_channel == null) + { + _position = -1; + return; + } + _position = _channel.position; + _channel.stop(); + if(_looped) + { + while(_position >= _sound.length) + _position -= _sound.length; + } + if(_position <= 0) + _position = 1; + _channel = null; + active = false; + } + + /** + * Call this function to stop this sound. + */ + public function stop():void + { + _position = 0; + if(_channel != null) + { + _channel.stop(); + stopped(); + } + } + + /** + * Call this function to make this sound fade out over a certain time interval. + * + * @param Seconds The amount of time the fade out operation should take. + * @param PauseInstead Tells the sound to pause on fadeout, instead of stopping. + */ + public function fadeOut(Seconds:Number,PauseInstead:Boolean=false):void + { + _pauseOnFadeOut = PauseInstead; + _fadeInTimer = 0; + _fadeOutTimer = Seconds; + _fadeOutTotal = _fadeOutTimer; + } + + /** + * Call this function to make a sound fade in over a certain + * time interval (calls play() automatically). + * + * @param Seconds The amount of time the fade-in operation should take. + */ + public function fadeIn(Seconds:Number):void + { + _fadeOutTimer = 0; + _fadeInTimer = Seconds; + _fadeInTotal = _fadeInTimer; + play(); + } + + /** + * Set volume to a value between 0 and 1 to change how this sound is. + */ + public function get volume():Number + { + return _volume; + } + + /** + * @private + */ + public function set volume(Volume:Number):void + { + _volume = Volume; + if(_volume < 0) + _volume = 0; + else if(_volume > 1) + _volume = 1; + updateTransform(); + } + + /** + * Returns the currently selected "real" volume of the sound (takes fades and proximity into account). + * + * @return The adjusted volume of the sound. + */ + public function getActualVolume():Number + { + return _volume*_volumeAdjust; + } + + /** + * Call after adjusting the volume to update the sound channel's settings. + */ + internal function updateTransform():void + { + _transform.volume = (FlxG.mute?0:1)*FlxG.volume*_volume*_volumeAdjust; + if(_channel != null) + _channel.soundTransform = _transform; + } + + /** + * An internal helper function used to help Flash resume playing a looped sound. + * + * @param event An Event object. + */ + protected function looped(event:Event=null):void + { + if (_channel == null) + return; + _channel.removeEventListener(Event.SOUND_COMPLETE,looped); + _channel = null; + play(); + } + + /** + * An internal helper function used to help Flash clean up and re-use finished sounds. + * + * @param event An Event object. + */ + protected function stopped(event:Event=null):void + { + if(!_looped) + _channel.removeEventListener(Event.SOUND_COMPLETE,stopped); + else + _channel.removeEventListener(Event.SOUND_COMPLETE,looped); + _channel = null; + active = false; + if(autoDestroy) + destroy(); + } + + /** + * Internal event handler for ID3 info (i.e. fetching the song name). + * + * @param event An Event object. + */ + protected function gotID3(event:Event=null):void + { + FlxG.log("got ID3 info!"); + if(_sound.id3.songName.length > 0) + name = _sound.id3.songName; + if(_sound.id3.artist.length > 0) + artist = _sound.id3.artist; + _sound.removeEventListener(Event.ID3, gotID3); + } + } +} diff --git a/src/org/flixel/FlxSprite.as b/src/org/flixel/FlxSprite.as new file mode 100755 index 0000000..191a04c --- /dev/null +++ b/src/org/flixel/FlxSprite.as @@ -0,0 +1,920 @@ +package org.flixel +{ + import flash.display.Bitmap; + import flash.display.BitmapData; + import flash.display.Graphics; + import flash.geom.ColorTransform; + import flash.geom.Matrix; + import flash.geom.Point; + import flash.geom.Rectangle; + + import org.flixel.system.FlxAnim; + + /** + * The main "game object" class, the sprite is a FlxObject + * with a bunch of graphics options and abilities, like animation and stamping. + * + * @author Adam Atomic + */ + public class FlxSprite extends FlxObject + { + [Embed(source="data/default.png")] protected var ImgDefault:Class; + + /** + * WARNING: The origin of the sprite will default to its center. + * If you change this, the visuals and the collisions will likely be + * pretty out-of-sync if you do any rotation. + */ + public var origin:FlxPoint; + /** + * If you changed the size of your sprite object after loading or making the graphic, + * you might need to offset the graphic away from the bound box to center it the way you want. + */ + public var offset:FlxPoint; + + /** + * Change the size of your sprite's graphic. + * NOTE: Scale doesn't currently affect collisions automatically, + * you will need to adjust the width, height and offset manually. + * WARNING: scaling sprites decreases rendering performance for this sprite by a factor of 10x! + */ + public var scale:FlxPoint; + /** + * Blending modes, just like Photoshop or whatever. + * E.g. "multiply", "screen", etc. + * @default null + */ + public var blend:String; + /** + * Controls whether the object is smoothed when rotated, affects performance. + * @default false + */ + public var antialiasing:Boolean; + /** + * Whether the current animation has finished its first (or only) loop. + */ + public var finished:Boolean; + /** + * The width of the actual graphic or image being displayed (not necessarily the game object/bounding box). + * NOTE: Edit at your own risk!! This is intended to be read-only. + */ + public var frameWidth:uint; + /** + * The height of the actual graphic or image being displayed (not necessarily the game object/bounding box). + * NOTE: Edit at your own risk!! This is intended to be read-only. + */ + public var frameHeight:uint; + /** + * The total number of frames in this image. WARNING: assumes each row in the sprite sheet is full! + */ + public var frames:uint; + /** + * The actual Flash BitmapData object representing the current display state of the sprite. + */ + public var framePixels:BitmapData; + /** + * Set this flag to true to force the sprite to update during the draw() call. + * NOTE: Rarely if ever necessary, most sprite operations will flip this flag automatically. + */ + public var dirty:Boolean; + + /** + * Internal, stores all the animations that were added to this sprite. + */ + protected var _animations:Array; + /** + * Internal, keeps track of whether the sprite was loaded with support for automatic reverse/mirroring. + */ + protected var _flipped:uint; + /** + * Internal, keeps track of the current animation being played. + */ + protected var _curAnim:FlxAnim; + /** + * Internal, keeps track of the current frame of animation. + * This is NOT an index into the tile sheet, but the frame number in the animation object. + */ + protected var _curFrame:uint; + /** + * Internal, keeps track of the current index into the tile sheet based on animation or rotation. + */ + protected var _curIndex:uint; + /** + * Internal, used to time each frame of animation. + */ + protected var _frameTimer:Number; + /** + * Internal tracker for the animation callback. Default is null. + * If assigned, will be called each time the current frame changes. + * A function that has 3 parameters: a string name, a uint frame number, and a uint frame index. + */ + protected var _callback:Function; + /** + * Internal tracker for what direction the sprite is currently facing, used with Flash getter/setter. + */ + protected var _facing:uint; + /** + * Internal tracker for opacity, used with Flash getter/setter. + */ + protected var _alpha:Number; + /** + * Internal tracker for color tint, used with Flash getter/setter. + */ + protected var _color:uint; + /** + * Internal tracker for how many frames of "baked" rotation there are (if any). + */ + protected var _bakedRotation:Number; + /** + * Internal, stores the entire source graphic (not the current displayed animation frame), used with Flash getter/setter. + */ + protected var _pixels:BitmapData; + + /** + * Internal, reused frequently during drawing and animating. + */ + protected var _flashPoint:Point; + /** + * Internal, reused frequently during drawing and animating. + */ + protected var _flashRect:Rectangle; + /** + * Internal, reused frequently during drawing and animating. + */ + protected var _flashRect2:Rectangle; + /** + * Internal, reused frequently during drawing and animating. Always contains (0,0). + */ + protected var _flashPointZero:Point; + /** + * Internal, helps with animation, caching and drawing. + */ + protected var _colorTransform:ColorTransform; + /** + * Internal, helps with animation, caching and drawing. + */ + protected var _matrix:Matrix; + + /** + * Creates a white 8x8 square FlxSprite at the specified position. + * Optionally can load a simple, one-frame graphic instead. + * + * @param X The initial X position of the sprite. + * @param Y The initial Y position of the sprite. + * @param SimpleGraphic The graphic you want to display (OPTIONAL - for simple stuff only, do NOT use for animated images!). + */ + public function FlxSprite(X:Number=0,Y:Number=0,SimpleGraphic:Class=null) + { + super(X,Y); + + health = 1; + + _flashPoint = new Point(); + _flashRect = new Rectangle(); + _flashRect2 = new Rectangle(); + _flashPointZero = new Point(); + offset = new FlxPoint(); + origin = new FlxPoint(); + + scale = new FlxPoint(1.0,1.0); + _alpha = 1; + _color = 0x00ffffff; + blend = null; + antialiasing = false; + cameras = null; + + finished = false; + _facing = RIGHT; + _animations = new Array(); + _flipped = 0; + _curAnim = null; + _curFrame = 0; + _curIndex = 0; + _frameTimer = 0; + + _matrix = new Matrix(); + _callback = null; + + if(SimpleGraphic == null) + SimpleGraphic = ImgDefault; + loadGraphic(SimpleGraphic); + } + + /** + * Clean up memory. + */ + override public function destroy():void + { + if(_animations != null) + { + var a:FlxAnim; + var i:uint = 0; + var l:uint = _animations.length; + while(i < l) + { + a = _animations[i++]; + if(a != null) + a.destroy(); + } + _animations = null; + } + + _flashPoint = null; + _flashRect = null; + _flashRect2 = null; + _flashPointZero = null; + offset = null; + origin = null; + scale = null; + _curAnim = null; + _matrix = null; + _callback = null; + framePixels = null; + } + + /** + * Load an image from an embedded graphic file. + * + * @param Graphic The image you want to use. + * @param Animated Whether the Graphic parameter is a single sprite or a row of sprites. + * @param Reverse Whether you need this class to generate horizontally flipped versions of the animation frames. + * @param Width Optional, specify the width of your sprite (helps FlxSprite figure out what to do with non-square sprites or sprite sheets). + * @param Height Optional, specify the height of your sprite (helps FlxSprite figure out what to do with non-square sprites or sprite sheets). + * @param Unique Optional, whether the graphic should be a unique instance in the graphics cache. Default is false. + * + * @return This FlxSprite instance (nice for chaining stuff together, if you're into that). + */ + public function loadGraphic(Graphic:Class,Animated:Boolean=false,Reverse:Boolean=false,Width:uint=0,Height:uint=0,Unique:Boolean=false):FlxSprite + { + _bakedRotation = 0; + _pixels = FlxG.addBitmap(Graphic,Reverse,Unique); + if(Reverse) + _flipped = _pixels.width>>1; + else + _flipped = 0; + if(Width == 0) + { + if(Animated) + Width = _pixels.height; + else if(_flipped > 0) + Width = _pixels.width*0.5; + else + Width = _pixels.width; + } + width = frameWidth = Width; + if(Height == 0) + { + if(Animated) + Height = width; + else + Height = _pixels.height; + } + height = frameHeight = Height; + resetHelpers(); + return this; + } + + /** + * Create a pre-rotated sprite sheet from a simple sprite. + * This can make a huge difference in graphical performance! + * + * @param Graphic The image you want to rotate and stamp. + * @param Rotations The number of rotation frames the final sprite should have. For small sprites this can be quite a large number (360 even) without any problems. + * @param Frame If the Graphic has a single row of square animation frames on it, you can specify which of the frames you want to use here. Default is -1, or "use whole graphic." + * @param AntiAliasing Whether to use high quality rotations when creating the graphic. Default is false. + * @param AutoBuffer Whether to automatically increase the image size to accomodate rotated corners. Default is false. Will create frames that are 150% larger on each axis than the original frame or graphic. + * + * @return This FlxSprite instance (nice for chaining stuff together, if you're into that). + */ + public function loadRotatedGraphic(Graphic:Class, Rotations:uint=16, Frame:int=-1, AntiAliasing:Boolean=false, AutoBuffer:Boolean=false):FlxSprite + { + //Create the brush and canvas + var rows:uint = Math.sqrt(Rotations); + var brush:BitmapData = FlxG.addBitmap(Graphic); + if(Frame >= 0) + { + //Using just a segment of the graphic - find the right bit here + var full:BitmapData = brush; + brush = new BitmapData(full.height,full.height); + var rx:uint = Frame*brush.width; + var ry:uint = 0; + var fw:uint = full.width; + if(rx >= fw) + { + ry = uint(rx/fw)*brush.height; + rx %= fw; + } + _flashRect.x = rx; + _flashRect.y = ry; + _flashRect.width = brush.width; + _flashRect.height = brush.height; + brush.copyPixels(full,_flashRect,_flashPointZero); + } + + var max:uint = brush.width; + if(brush.height > max) + max = brush.height; + if(AutoBuffer) + max *= 1.5; + var columns:uint = FlxU.ceil(Rotations/rows); + width = max*columns; + height = max*rows; + var key:String = String(Graphic) + ":" + Frame + ":" + width + "x" + height; + var skipGen:Boolean = FlxG.checkBitmapCache(key); + _pixels = FlxG.createBitmap(width, height, 0, true, key); + width = frameWidth = _pixels.width; + height = frameHeight = _pixels.height; + _bakedRotation = 360/Rotations; + + //Generate a new sheet if necessary, then fix up the width and height + if(!skipGen) + { + var row:uint = 0; + var column:uint; + var bakedAngle:Number = 0; + var halfBrushWidth:uint = brush.width*0.5; + var halfBrushHeight:uint = brush.height*0.5; + var midpointX:uint = max*0.5; + var midpointY:uint = max*0.5; + while(row < rows) + { + column = 0; + while(column < columns) + { + _matrix.identity(); + _matrix.translate(-halfBrushWidth,-halfBrushHeight); + _matrix.rotate(bakedAngle*0.017453293); + _matrix.translate(max*column+midpointX, midpointY); + bakedAngle += _bakedRotation; + _pixels.draw(brush,_matrix,null,null,null,AntiAliasing); + column++; + } + midpointY += max; + row++; + } + } + frameWidth = frameHeight = width = height = max; + resetHelpers(); + if(AutoBuffer) + { + width = brush.width; + height = brush.height; + centerOffsets(); + } + return this; + } + + /** + * This function creates a flat colored square image dynamically. + * + * @param Width The width of the sprite you want to generate. + * @param Height The height of the sprite you want to generate. + * @param Color Specifies the color of the generated block. + * @param Unique Whether the graphic should be a unique instance in the graphics cache. Default is false. + * @param Key Optional parameter - specify a string key to identify this graphic in the cache. Trumps Unique flag. + * + * @return This FlxSprite instance (nice for chaining stuff together, if you're into that). + */ + public function makeGraphic(Width:uint,Height:uint,Color:uint=0xffffffff,Unique:Boolean=false,Key:String=null):FlxSprite + { + _bakedRotation = 0; + _pixels = FlxG.createBitmap(Width,Height,Color,Unique,Key); + width = frameWidth = _pixels.width; + height = frameHeight = _pixels.height; + resetHelpers(); + return this; + } + + /** + * Resets some important variables for sprite optimization and rendering. + */ + protected function resetHelpers():void + { + _flashRect.x = 0; + _flashRect.y = 0; + _flashRect.width = frameWidth; + _flashRect.height = frameHeight; + _flashRect2.x = 0; + _flashRect2.y = 0; + _flashRect2.width = _pixels.width; + _flashRect2.height = _pixels.height; + if((framePixels == null) || (framePixels.width != width) || (framePixels.height != height)) + framePixels = new BitmapData(width,height); + origin.make(frameWidth*0.5,frameHeight*0.5); + framePixels.copyPixels(_pixels,_flashRect,_flashPointZero); + frames = (_flashRect2.width / _flashRect.width) * (_flashRect2.height / _flashRect.height); + if(_colorTransform != null) framePixels.colorTransform(_flashRect,_colorTransform); + _curIndex = 0; + } + + /** + * Automatically called after update() by the game loop, + * this function just calls updateAnimation(). + */ + override public function postUpdate():void + { + super.postUpdate(); + updateAnimation(); + } + + /** + * Called by game loop, updates then blits or renders current frame of animation to the screen + */ + override public function draw():void + { + if(_flickerTimer != 0) + { + _flicker = !_flicker; + if(_flicker) + return; + } + + if(dirty) //rarely + calcFrame(); + + if(cameras == null) + cameras = FlxG.cameras; + var camera:FlxCamera; + var i:uint = 0; + var l:uint = cameras.length; + while(i < l) + { + camera = cameras[i++]; + if(!onScreen(camera)) + continue; + _point.x = x - int(camera.scroll.x*scrollFactor.x) - offset.x; + _point.y = y - int(camera.scroll.y*scrollFactor.y) - offset.y; + _point.x += (_point.x > 0)?0.0000001:-0.0000001; + _point.y += (_point.y > 0)?0.0000001:-0.0000001; + if(((angle == 0) || (_bakedRotation > 0)) && (scale.x == 1) && (scale.y == 1) && (blend == null)) + { //Simple render + _flashPoint.x = _point.x; + _flashPoint.y = _point.y; + camera.buffer.copyPixels(framePixels,_flashRect,_flashPoint,null,null,true); + } + else + { //Advanced render + _matrix.identity(); + _matrix.translate(-origin.x,-origin.y); + _matrix.scale(scale.x,scale.y); + if((angle != 0) && (_bakedRotation <= 0)) + _matrix.rotate(angle * 0.017453293); + _matrix.translate(_point.x+origin.x,_point.y+origin.y); + camera.buffer.draw(framePixels,_matrix,null,blend,null,antialiasing); + } + _VISIBLECOUNT++; + if(FlxG.visualDebug && !ignoreDrawDebug) + drawDebug(camera); + } + } + + /** + * This function draws or stamps one FlxSprite onto another. + * This function is NOT intended to replace draw()! + * + * @param Brush The image you want to use as a brush or stamp or pen or whatever. + * @param X The X coordinate of the brush's top left corner on this sprite. + * @param Y They Y coordinate of the brush's top left corner on this sprite. + */ + public function stamp(Brush:FlxSprite,X:int=0,Y:int=0):void + { + Brush.drawFrame(); + var bitmapData:BitmapData = Brush.framePixels; + + //Simple draw + if(((Brush.angle == 0) || (Brush._bakedRotation > 0)) && (Brush.scale.x == 1) && (Brush.scale.y == 1) && (Brush.blend == null)) + { + _flashPoint.x = X; + _flashPoint.y = Y; + _flashRect2.width = bitmapData.width; + _flashRect2.height = bitmapData.height; + _pixels.copyPixels(bitmapData,_flashRect2,_flashPoint,null,null,true); + _flashRect2.width = _pixels.width; + _flashRect2.height = _pixels.height; + calcFrame(); + return; + } + + //Advanced draw + _matrix.identity(); + _matrix.translate(-Brush.origin.x,-Brush.origin.y); + _matrix.scale(Brush.scale.x,Brush.scale.y); + if(Brush.angle != 0) + _matrix.rotate(Brush.angle * 0.017453293); + _matrix.translate(X+Brush.origin.x,Y+Brush.origin.y); + _pixels.draw(bitmapData,_matrix,null,Brush.blend,null,Brush.antialiasing); + calcFrame(); + } + + /** + * This function draws a line on this sprite from position X1,Y1 + * to position X2,Y2 with the specified color. + * + * @param StartX X coordinate of the line's start point. + * @param StartY Y coordinate of the line's start point. + * @param EndX X coordinate of the line's end point. + * @param EndY Y coordinate of the line's end point. + * @param Color The line's color. + * @param Thickness How thick the line is in pixels (default value is 1). + */ + public function drawLine(StartX:Number,StartY:Number,EndX:Number,EndY:Number,Color:uint,Thickness:uint=1):void + { + //Draw line + var gfx:Graphics = FlxG.flashGfx; + gfx.clear(); + gfx.moveTo(StartX,StartY); + var alphaComponent:Number = Number((Color >> 24) & 0xFF) / 255; + if(alphaComponent <= 0) + alphaComponent = 1; + gfx.lineStyle(Thickness,Color,alphaComponent); + gfx.lineTo(EndX,EndY); + + //Cache line to bitmap + _pixels.draw(FlxG.flashGfxSprite); + dirty = true; + } + + /** + * Fills this sprite's graphic with a specific color. + * + * @param Color The color with which to fill the graphic, format 0xAARRGGBB. + */ + public function fill(Color:uint):void + { + _pixels.fillRect(_flashRect2,Color); + if(_pixels != framePixels) + dirty = true; + } + + /** + * Internal function for updating the sprite's animation. + * Useful for cases when you need to update this but are buried down in too many supers. + * This function is called automatically by FlxSprite.postUpdate(). + */ + protected function updateAnimation():void + { + if(_bakedRotation > 0) + { + var oldIndex:uint = _curIndex; + var angleHelper:int = angle%360; + if(angleHelper < 0) + angleHelper += 360; + _curIndex = angleHelper/_bakedRotation + 0.5; + if(oldIndex != _curIndex) + dirty = true; + } + else if((_curAnim != null) && (_curAnim.delay > 0) && (_curAnim.looped || !finished)) + { + _frameTimer += FlxG.elapsed; + while(_frameTimer > _curAnim.delay) + { + _frameTimer = _frameTimer - _curAnim.delay; + if(_curFrame == _curAnim.frames.length-1) + { + if(_curAnim.looped) + _curFrame = 0; + finished = true; + } + else + _curFrame++; + _curIndex = _curAnim.frames[_curFrame]; + dirty = true; + } + } + + if(dirty) + calcFrame(); + } + + /** + * Request (or force) that the sprite update the frame before rendering. + * Useful if you are doing procedural generation or other weirdness! + * + * @param Force Force the frame to redraw, even if its not flagged as necessary. + */ + public function drawFrame(Force:Boolean=false):void + { + if(Force || dirty) + calcFrame(); + } + + /** + * Adds a new animation to the sprite. + * + * @param Name What this animation should be called (e.g. "run"). + * @param Frames An array of numbers indicating what frames to play in what order (e.g. 1, 2, 3). + * @param FrameRate The speed in frames per second that the animation should play at (e.g. 40 fps). + * @param Looped Whether or not the animation is looped or just plays once. + */ + public function addAnimation(Name:String, Frames:Array, FrameRate:Number=0, Looped:Boolean=true):void + { + _animations.push(new FlxAnim(Name,Frames,FrameRate,Looped)); + } + + /** + * Pass in a function to be called whenever this sprite's animation changes. + * + * @param AnimationCallback A function that has 3 parameters: a string name, a uint frame number, and a uint frame index. + */ + public function addAnimationCallback(AnimationCallback:Function):void + { + _callback = AnimationCallback; + } + + /** + * Plays an existing animation (e.g. "run"). + * If you call an animation that is already playing it will be ignored. + * + * @param AnimName The string name of the animation you want to play. + * @param Force Whether to force the animation to restart. + */ + public function play(AnimName:String,Force:Boolean=false):void + { + if(!Force && (_curAnim != null) && (AnimName == _curAnim.name) && (_curAnim.looped || !finished)) return; + _curFrame = 0; + _curIndex = 0; + _frameTimer = 0; + var i:uint = 0; + var l:uint = _animations.length; + while(i < l) + { + if(_animations[i].name == AnimName) + { + _curAnim = _animations[i]; + if(_curAnim.delay <= 0) + finished = true; + else + finished = false; + _curIndex = _curAnim.frames[_curFrame]; + dirty = true; + return; + } + i++; + } + FlxG.log("WARNING: No animation called \""+AnimName+"\""); + } + + /** + * Tell the sprite to change to a random frame of animation + * Useful for instantiating particles or other weird things. + */ + public function randomFrame():void + { + _curAnim = null; + _curIndex = int(FlxG.random()*(_pixels.width/frameWidth)); + dirty = true; + } + + /** + * Helper function that just sets origin to (0,0) + */ + public function setOriginToCorner():void + { + origin.x = origin.y = 0; + } + + /** + * Helper function that adjusts the offset automatically to center the bounding box within the graphic. + * + * @param AdjustPosition Adjusts the actual X and Y position just once to match the offset change. Default is false. + */ + public function centerOffsets(AdjustPosition:Boolean=false):void + { + offset.x = (frameWidth-width)*0.5; + offset.y = (frameHeight-height)*0.5; + if(AdjustPosition) + { + x += offset.x; + y += offset.y; + } + } + + public function replaceColor(Color:uint,NewColor:uint,FetchPositions:Boolean=false):Array + { + var positions:Array = null; + if(FetchPositions) + positions = new Array(); + + var row:uint = 0; + var column:uint; + var rows:uint = _pixels.height; + var columns:uint = _pixels.width; + while(row < rows) + { + column = 0; + while(column < columns) + { + if(_pixels.getPixel32(column,row) == Color) + { + _pixels.setPixel32(column,row,NewColor); + if(FetchPositions) + positions.push(new FlxPoint(column,row)); + dirty = true; + } + column++; + } + row++; + } + + return positions; + } + + /** + * Set pixels to any BitmapData object. + * Automatically adjust graphic size and render helpers. + */ + public function get pixels():BitmapData + { + return _pixels; + } + + /** + * @private + */ + public function set pixels(Pixels:BitmapData):void + { + _pixels = Pixels; + width = frameWidth = _pixels.width; + height = frameHeight = _pixels.height; + resetHelpers(); + } + + /** + * Set facing using FlxSprite.LEFT,RIGHT, + * UP, and DOWN to take advantage of + * flipped sprites and/or just track player orientation more easily. + */ + public function get facing():uint + { + return _facing; + } + + /** + * @private + */ + public function set facing(Direction:uint):void + { + if(_facing != Direction) + dirty = true; + _facing = Direction; + } + + /** + * Set alpha to a number between 0 and 1 to change the opacity of the sprite. + */ + public function get alpha():Number + { + return _alpha; + } + + /** + * @private + */ + public function set alpha(Alpha:Number):void + { + if(Alpha > 1) + Alpha = 1; + if(Alpha < 0) + Alpha = 0; + if(Alpha == _alpha) + return; + _alpha = Alpha; + if((_alpha != 1) || (_color != 0x00ffffff)) + _colorTransform = new ColorTransform((_color>>16)*0.00392,(_color>>8&0xff)*0.00392,(_color&0xff)*0.00392,_alpha); + else + _colorTransform = null; + dirty = true; + } + + /** + * Set color to a number in this format: 0xRRGGBB. + * color IGNORES ALPHA. To change the opacity use alpha. + * Tints the whole sprite to be this color (similar to OpenGL vertex colors). + */ + public function get color():uint + { + return _color; + } + + /** + * @private + */ + public function set color(Color:uint):void + { + Color &= 0x00ffffff; + if(_color == Color) + return; + _color = Color; + if((_alpha != 1) || (_color != 0x00ffffff)) + _colorTransform = new ColorTransform((_color>>16)*0.00392,(_color>>8&0xff)*0.00392,(_color&0xff)*0.00392,_alpha); + else + _colorTransform = null; + dirty = true; + } + + /** + * Tell the sprite to change to a specific frame of animation. + * + * @param Frame The frame you want to display. + */ + public function get frame():uint + { + return _curIndex; + } + + /** + * @private + */ + public function set frame(Frame:uint):void + { + _curAnim = null; + _curIndex = Frame; + dirty = true; + } + + /** + * Check and see if this object is currently on screen. + * Differs from FlxObject's implementation + * in that it takes the actual graphic into account, + * not just the hitbox or bounding box or whatever. + * + * @param Camera Specify which game camera you want. If null getScreenXY() will just grab the first global camera. + * + * @return Whether the object is on screen or not. + */ + override public function onScreen(Camera:FlxCamera=null):Boolean + { + if(Camera == null) + Camera = FlxG.camera; + getScreenXY(_point,Camera); + _point.x = _point.x - offset.x; + _point.y = _point.y - offset.y; + + if(((angle == 0) || (_bakedRotation > 0)) && (scale.x == 1) && (scale.y == 1)) + return ((_point.x + frameWidth > 0) && (_point.x < Camera.width) && (_point.y + frameHeight > 0) && (_point.y < Camera.height)); + + var halfWidth:Number = frameWidth/2; + var halfHeight:Number = frameHeight/2; + var absScaleX:Number = (scale.x>0)?scale.x:-scale.x; + var absScaleY:Number = (scale.y>0)?scale.y:-scale.y; + var radius:Number = Math.sqrt(halfWidth*halfWidth+halfHeight*halfHeight)*((absScaleX >= absScaleY)?absScaleX:absScaleY); + _point.x += halfWidth; + _point.y += halfHeight; + return ((_point.x + radius > 0) && (_point.x - radius < Camera.width) && (_point.y + radius > 0) && (_point.y - radius < Camera.height)); + } + + /** + * Checks to see if a point in 2D world space overlaps this FlxSprite object's current displayed pixels. + * This check is ALWAYS made in screen space, and always takes scroll factors into account. + * + * @param Point The point in world space you want to check. + * @param Mask Used in the pixel hit test to determine what counts as solid. + * @param Camera Specify which game camera you want. If null getScreenXY() will just grab the first global camera. + * + * @return Whether or not the point overlaps this object. + */ + public function pixelsOverlapPoint(Point:FlxPoint,Mask:uint=0xFF,Camera:FlxCamera=null):Boolean + { + if(Camera == null) + Camera = FlxG.camera; + getScreenXY(_point,Camera); + _point.x = _point.x - offset.x; + _point.y = _point.y - offset.y; + _flashPoint.x = (Point.x - Camera.scroll.x) - _point.x; + _flashPoint.y = (Point.y - Camera.scroll.y) - _point.y; + return framePixels.hitTest(_flashPointZero,Mask,_flashPoint); + } + + /** + * Internal function to update the current animation frame. + */ + protected function calcFrame():void + { + var indexX:uint = _curIndex*frameWidth; + var indexY:uint = 0; + + //Handle sprite sheets + var widthHelper:uint = _flipped?_flipped:_pixels.width; + if(indexX >= widthHelper) + { + indexY = uint(indexX/widthHelper)*frameHeight; + indexX %= widthHelper; + } + + //handle reversed sprites + if(_flipped && (_facing == LEFT)) + indexX = (_flipped<<1)-indexX-frameWidth; + + //Update display bitmap + _flashRect.x = indexX; + _flashRect.y = indexY; + framePixels.copyPixels(_pixels,_flashRect,_flashPointZero); + _flashRect.x = _flashRect.y = 0; + if(_colorTransform != null) + framePixels.colorTransform(_flashRect,_colorTransform); + if(_callback != null) + _callback(((_curAnim != null)?(_curAnim.name):null),_curFrame,_curIndex); + dirty = false; + } + } +} \ No newline at end of file diff --git a/src/org/flixel/FlxState.as b/src/org/flixel/FlxState.as new file mode 100755 index 0000000..f3c3d88 --- /dev/null +++ b/src/org/flixel/FlxState.as @@ -0,0 +1,25 @@ +package org.flixel +{ + import org.flixel.system.FlxQuadTree; + + /** + * This is the basic game "state" object - e.g. in a simple game + * you might have a menu state and a play state. + * It is for all intents and purpose a fancy FlxGroup. + * And really, it's not even that fancy. + * + * @author Adam Atomic + */ + public class FlxState extends FlxGroup + { + /** + * This function is called after the game engine successfully switches states. + * Override this function, NOT the constructor, to initialize or set up your game state. + * We do NOT recommend overriding the constructor, unless you want some crazy unpredictable things to happen! + */ + public function create():void + { + + } + } +} diff --git a/src/org/flixel/FlxText.as b/src/org/flixel/FlxText.as new file mode 100644 index 0000000..572f60c --- /dev/null +++ b/src/org/flixel/FlxText.as @@ -0,0 +1,300 @@ +package org.flixel +{ + import flash.display.BitmapData; + import flash.text.TextField; + import flash.text.TextFormat; + + /** + * Extends FlxSprite to support rendering text. + * Can tint, fade, rotate and scale just like a sprite. + * Doesn't really animate though, as far as I know. + * Also does nice pixel-perfect centering on pixel fonts + * as long as they are only one liners. + * + * @author Adam Atomic + */ + public class FlxText extends FlxSprite + { + /** + * Internal reference to a Flash TextField object. + */ + protected var _textField:TextField; + /** + * Whether the actual text field needs to be regenerated and stamped again. + * This is NOT the same thing as FlxSprite.dirty. + */ + protected var _regen:Boolean; + /** + * Internal tracker for the text shadow color, default is clear/transparent. + */ + protected var _shadow:uint; + + /** + * Creates a new FlxText object at the specified position. + * + * @param X The X position of the text. + * @param Y The Y position of the text. + * @param Width The width of the text object (height is determined automatically). + * @param Text The actual text you would like to display initially. + * @param EmbeddedFont Whether this text field uses embedded fonts or nto + */ + public function FlxText(X:Number, Y:Number, Width:uint, Text:String=null, EmbeddedFont:Boolean=true) + { + super(X,Y); + makeGraphic(Width,1,0); + + if(Text == null) + Text = ""; + _textField = new TextField(); + _textField.width = Width; + _textField.embedFonts = EmbeddedFont; + _textField.selectable = false; + _textField.sharpness = 100; + _textField.multiline = true; + _textField.wordWrap = true; + _textField.text = Text; + var format:TextFormat = new TextFormat("system",8,0xffffff); + _textField.defaultTextFormat = format; + _textField.setTextFormat(format); + if(Text.length <= 0) + _textField.height = 1; + else + _textField.height = 10; + + _regen = true; + _shadow = 0; + allowCollisions = NONE; + calcFrame(); + } + + /** + * Clean up memory. + */ + override public function destroy():void + { + _textField = null; + super.destroy(); + } + + /** + * You can use this if you have a lot of text parameters + * to set instead of the individual properties. + * + * @param Font The name of the font face for the text display. + * @param Size The size of the font (in pixels essentially). + * @param Color The color of the text in traditional flash 0xRRGGBB format. + * @param Alignment A string representing the desired alignment ("left,"right" or "center"). + * @param ShadowColor A uint representing the desired text shadow color in flash 0xRRGGBB format. + * + * @return This FlxText instance (nice for chaining stuff together, if you're into that). + */ + public function setFormat(Font:String=null,Size:Number=8,Color:uint=0xffffff,Alignment:String=null,ShadowColor:uint=0):FlxText + { + if(Font == null) + Font = ""; + var format:TextFormat = dtfCopy(); + format.font = Font; + format.size = Size; + format.color = Color; + format.align = Alignment; + _textField.defaultTextFormat = format; + _textField.setTextFormat(format); + _shadow = ShadowColor; + _regen = true; + calcFrame(); + return this; + } + + /** + * The text being displayed. + */ + public function get text():String + { + return _textField.text; + } + + /** + * @private + */ + public function set text(Text:String):void + { + var ot:String = _textField.text; + _textField.text = Text; + if(_textField.text != ot) + { + _regen = true; + calcFrame(); + } + } + + /** + * The size of the text being displayed. + */ + public function get size():Number + { + return _textField.defaultTextFormat.size as Number; + } + + /** + * @private + */ + public function set size(Size:Number):void + { + var format:TextFormat = dtfCopy(); + format.size = Size; + _textField.defaultTextFormat = format; + _textField.setTextFormat(format); + _regen = true; + calcFrame(); + } + + /** + * The color of the text being displayed. + */ + override public function get color():uint + { + return _textField.defaultTextFormat.color as uint; + } + + /** + * @private + */ + override public function set color(Color:uint):void + { + var format:TextFormat = dtfCopy(); + format.color = Color; + _textField.defaultTextFormat = format; + _textField.setTextFormat(format); + _regen = true; + calcFrame(); + } + + /** + * The font used for this text. + */ + public function get font():String + { + return _textField.defaultTextFormat.font; + } + + /** + * @private + */ + public function set font(Font:String):void + { + var format:TextFormat = dtfCopy(); + format.font = Font; + _textField.defaultTextFormat = format; + _textField.setTextFormat(format); + _regen = true; + calcFrame(); + } + + /** + * The alignment of the font ("left", "right", or "center"). + */ + public function get alignment():String + { + return _textField.defaultTextFormat.align; + } + + /** + * @private + */ + public function set alignment(Alignment:String):void + { + var format:TextFormat = dtfCopy(); + format.align = Alignment; + _textField.defaultTextFormat = format; + _textField.setTextFormat(format); + calcFrame(); + } + + /** + * The color of the text shadow in 0xAARRGGBB hex format. + */ + public function get shadow():uint + { + return _shadow; + } + + /** + * @private + */ + public function set shadow(Color:uint):void + { + _shadow = Color; + calcFrame(); + } + + /** + * Internal function to update the current animation frame. + */ + override protected function calcFrame():void + { + if(_regen) + { + //Need to generate a new buffer to store the text graphic + var i:uint = 0; + var nl:uint = _textField.numLines; + height = 0; + while(i < nl) + height += _textField.getLineMetrics(i++).height; + height += 4; //account for 2px gutter on top and bottom + _pixels = new BitmapData(width,height,true,0); + frameHeight = height; + _textField.height = height*1.2; + _flashRect.x = 0; + _flashRect.y = 0; + _flashRect.width = width; + _flashRect.height = height; + _regen = false; + } + else //Else just clear the old buffer before redrawing the text + _pixels.fillRect(_flashRect,0); + + if((_textField != null) && (_textField.text != null) && (_textField.text.length > 0)) + { + //Now that we've cleared a buffer, we need to actually render the text to it + var format:TextFormat = _textField.defaultTextFormat; + var formatAdjusted:TextFormat = format; + _matrix.identity(); + //If it's a single, centered line of text, we center it ourselves so it doesn't blur to hell + if((format.align == "center") && (_textField.numLines == 1)) + { + formatAdjusted = new TextFormat(format.font,format.size,format.color,null,null,null,null,null,"left"); + _textField.setTextFormat(formatAdjusted); + _matrix.translate(Math.floor((width - _textField.getLineMetrics(0).width)/2),0); + } + //Render a single pixel shadow beneath the text + if(_shadow > 0) + { + _textField.setTextFormat(new TextFormat(formatAdjusted.font,formatAdjusted.size,_shadow,null,null,null,null,null,formatAdjusted.align)); + _matrix.translate(1,1); + _pixels.draw(_textField,_matrix,_colorTransform); + _matrix.translate(-1,-1); + _textField.setTextFormat(new TextFormat(formatAdjusted.font,formatAdjusted.size,formatAdjusted.color,null,null,null,null,null,formatAdjusted.align)); + } + //Actually draw the text onto the buffer + _pixels.draw(_textField,_matrix,_colorTransform); + _textField.setTextFormat(new TextFormat(format.font,format.size,format.color,null,null,null,null,null,format.align)); + } + + //Finally, update the visible pixels + if((framePixels == null) || (framePixels.width != _pixels.width) || (framePixels.height != _pixels.height)) + framePixels = new BitmapData(_pixels.width,_pixels.height,true,0); + framePixels.copyPixels(_pixels,_flashRect,_flashPointZero); + } + + /** + * A helper function for updating the TextField that we use for rendering. + * + * @return A writable copy of TextField.defaultTextFormat. + */ + protected function dtfCopy():TextFormat + { + var defaultTextFormat:TextFormat = _textField.defaultTextFormat; + return new TextFormat(defaultTextFormat.font,defaultTextFormat.size,defaultTextFormat.color,defaultTextFormat.bold,defaultTextFormat.italic,defaultTextFormat.underline,defaultTextFormat.url,defaultTextFormat.target,defaultTextFormat.align); + } + } +} diff --git a/src/org/flixel/FlxTileblock.as b/src/org/flixel/FlxTileblock.as new file mode 100644 index 0000000..a0f13c1 --- /dev/null +++ b/src/org/flixel/FlxTileblock.as @@ -0,0 +1,95 @@ +package org.flixel +{ + import flash.display.BitmapData; + import flash.geom.Rectangle; + + /** + * This is a basic "environment object" class, used to create simple walls and floors. + * It can be filled with a random selection of tiles to quickly add detail. + * + * @author Adam Atomic + */ + public class FlxTileblock extends FlxSprite + { + /** + * Creates a new FlxBlock object with the specified position and size. + * + * @param X The X position of the block. + * @param Y The Y position of the block. + * @param Width The width of the block. + * @param Height The height of the block. + */ + public function FlxTileblock(X:int,Y:int,Width:uint,Height:uint) + { + super(X,Y); + makeGraphic(Width,Height,0,true); + active = false; + immovable = true; + } + + /** + * Fills the block with a randomly arranged selection of graphics from the image provided. + * + * @param TileGraphic The graphic class that contains the tiles that should fill this block. + * @param TileWidth The width of a single tile in the graphic. + * @param TileHeight The height of a single tile in the graphic. + * @param Empties The number of "empty" tiles to add to the auto-fill algorithm (e.g. 8 tiles + 4 empties = 1/3 of block will be open holes). + */ + public function loadTiles(TileGraphic:Class,TileWidth:uint=0,TileHeight:uint=0,Empties:uint=0):FlxTileblock + { + if(TileGraphic == null) + return this; + + //First create a tile brush + var sprite:FlxSprite = new FlxSprite().loadGraphic(TileGraphic,true,false,TileWidth,TileHeight); + var spriteWidth:uint = sprite.width; + var spriteHeight:uint = sprite.height; + var total:uint = sprite.frames + Empties; + + //Then prep the "canvas" as it were (just doublechecking that the size is on tile boundaries) + var regen:Boolean = false; + if(width % sprite.width != 0) + { + width = uint(width/spriteWidth+1)*spriteWidth; + regen = true; + } + if(height % sprite.height != 0) + { + height = uint(height/spriteHeight+1)*spriteHeight; + regen = true; + } + if(regen) + makeGraphic(width,height,0,true); + else + this.fill(0); + + //Stamp random tiles onto the canvas + var row:uint = 0; + var column:uint; + var destinationX:uint; + var destinationY:uint = 0; + var widthInTiles:uint = width/spriteWidth; + var heightInTiles:uint = height/spriteHeight; + while(row < heightInTiles) + { + destinationX = 0; + column = 0; + while(column < widthInTiles) + { + if(FlxG.random()*total > Empties) + { + sprite.randomFrame(); + sprite.drawFrame(); + stamp(sprite,destinationX,destinationY); + } + destinationX += spriteWidth; + column++; + } + destinationY += spriteHeight; + row++; + } + + return this; + } + } +} diff --git a/src/org/flixel/FlxTilemap.as b/src/org/flixel/FlxTilemap.as new file mode 100644 index 0000000..441fa28 --- /dev/null +++ b/src/org/flixel/FlxTilemap.as @@ -0,0 +1,1488 @@ +package org.flixel +{ + import flash.display.Bitmap; + import flash.display.BitmapData; + import flash.display.Graphics; + import flash.geom.Matrix; + import flash.geom.Point; + import flash.geom.Rectangle; + + import org.flixel.system.FlxTile; + import org.flixel.system.FlxTilemapBuffer; + + /** + * This is a traditional tilemap display and collision class. + * It takes a string of comma-separated numbers and then associates + * those values with tiles from the sheet you pass in. + * It also includes some handy static parsers that can convert + * arrays or images into strings that can be loaded. + * + * @author Adam Atomic + */ + public class FlxTilemap extends FlxObject + { + [Embed(source="data/autotiles.png")] static public var ImgAuto:Class; + [Embed(source="data/autotiles_alt.png")] static public var ImgAutoAlt:Class; + + /** + * No auto-tiling. + */ + static public const OFF:uint = 0; + /** + * Good for levels with thin walls that don'tile need interior corner art. + */ + static public const AUTO:uint = 1; + /** + * Better for levels with thick walls that look better with interior corner art. + */ + static public const ALT:uint = 2; + + /** + * Set this flag to use one of the 16-tile binary auto-tile algorithms (OFF, AUTO, or ALT). + */ + public var auto:uint; + + /** + * Read-only variable, do NOT recommend changing after the map is loaded! + */ + public var widthInTiles:uint; + /** + * Read-only variable, do NOT recommend changing after the map is loaded! + */ + public var heightInTiles:uint; + /** + * Read-only variable, do NOT recommend changing after the map is loaded! + */ + public var totalTiles:uint; + + /** + * Rendering helper, minimize new object instantiation on repetitive methods. + */ + protected var _flashPoint:Point; + /** + * Rendering helper, minimize new object instantiation on repetitive methods. + */ + protected var _flashRect:Rectangle; + + /** + * Internal reference to the bitmap data object that stores the original tile graphics. + */ + protected var _tiles:BitmapData; + /** + * Internal list of buffers, one for each camera, used for drawing the tilemaps. + */ + protected var _buffers:Array; + /** + * Internal representation of the actual tile data, as a large 1D array of integers. + */ + protected var _data:Array; + /** + * Internal representation of rectangles, one for each tile in the entire tilemap, used to speed up drawing. + */ + protected var _rects:Array; + /** + * Internal, the width of a single tile. + */ + protected var _tileWidth:uint; + /** + * Internal, the height of a single tile. + */ + protected var _tileHeight:uint; + /** + * Internal collection of tile objects, one for each type of tile in the map (NOTE one for every single tile in the whole map). + */ + protected var _tileObjects:Array; + + /** + * Internal, used for rendering the debug bounding box display. + */ + protected var _debugTileNotSolid:BitmapData; + /** + * Internal, used for rendering the debug bounding box display. + */ + protected var _debugTilePartial:BitmapData; + /** + * Internal, used for rendering the debug bounding box display. + */ + protected var _debugTileSolid:BitmapData; + /** + * Internal, used for rendering the debug bounding box display. + */ + protected var _debugRect:Rectangle; + /** + * Internal flag for checking to see if we need to refresh + * the tilemap display to show or hide the bounding boxes. + */ + protected var _lastVisualDebug:Boolean; + /** + * Internal, used to sort of insert blank tiles in front of the tiles in the provided graphic. + */ + protected var _startingIndex:uint; + + /** + * The tilemap constructor just initializes some basic variables. + */ + public function FlxTilemap() + { + super(); + auto = OFF; + widthInTiles = 0; + heightInTiles = 0; + totalTiles = 0; + _buffers = new Array(); + _flashPoint = new Point(); + _flashRect = null; + _data = null; + _tileWidth = 0; + _tileHeight = 0; + _rects = null; + _tiles = null; + _tileObjects = null; + immovable = true; + cameras = null; + _debugTileNotSolid = null; + _debugTilePartial = null; + _debugTileSolid = null; + _debugRect = null; + _lastVisualDebug = FlxG.visualDebug; + _startingIndex = 0; + } + + /** + * Clean up memory. + */ + override public function destroy():void + { + _flashPoint = null; + _flashRect = null; + _tiles = null; + var i:uint = 0; + var l:uint = _tileObjects.length; + while(i < l) + (_tileObjects[i++] as FlxTile).destroy(); + _tileObjects = null; + i = 0; + l = _buffers.length; + while(i < l) + (_buffers[i++] as FlxTilemapBuffer).destroy(); + _buffers = null; + _data = null; + _rects = null; + _debugTileNotSolid = null; + _debugTilePartial = null; + _debugTileSolid = null; + _debugRect = null; + + super.destroy(); + } + + /** + * Load the tilemap with string data and a tile graphic. + * + * @param MapData A string of comma and line-return delineated indices indicating what order the tiles should go in. + * @param TileGraphic All the tiles you want to use, arranged in a strip corresponding to the numbers in MapData. + * @param TileWidth The width of your tiles (e.g. 8) - defaults to height of the tile graphic if unspecified. + * @param TileHeight The height of your tiles (e.g. 8) - defaults to width if unspecified. + * @param AutoTile Whether to load the map using an automatic tile placement algorithm. Setting this to either AUTO or ALT will override any values you put for StartingIndex, DrawIndex, or CollideIndex. + * @param StartingIndex Used to sort of insert empty tiles in front of the provided graphic. Default is 0, usually safest ot leave it at that. Ignored if AutoTile is set. + * @param DrawIndex Initializes all tile objects equal to and after this index as visible. Default value is 1. Ignored if AutoTile is set. + * @param CollideIndex Initializes all tile objects equal to and after this index as allowCollisions = ANY. Default value is 1. Ignored if AutoTile is set. Can override and customize per-tile-type collision behavior using setTileProperties(). + * + * @return A pointer this instance of FlxTilemap, for chaining as usual :) + */ + public function loadMap(MapData:String, TileGraphic:Class, TileWidth:uint=0, TileHeight:uint=0, AutoTile:uint=OFF, StartingIndex:uint=0, DrawIndex:uint=1, CollideIndex:uint=1):FlxTilemap + { + auto = AutoTile; + _startingIndex = StartingIndex; + + //Figure out the map dimensions based on the data string + var columns:Array; + var rows:Array = MapData.split("\n"); + heightInTiles = rows.length; + _data = new Array(); + var row:uint = 0; + var column:uint; + while(row < heightInTiles) + { + columns = rows[row++].split(","); + if(columns.length <= 1) + { + heightInTiles = heightInTiles - 1; + continue; + } + if(widthInTiles == 0) + widthInTiles = columns.length; + column = 0; + while(column < widthInTiles) + _data.push(uint(columns[column++])); + } + + //Pre-process the map data if it's auto-tiled + var i:uint; + totalTiles = widthInTiles*heightInTiles; + if(auto > OFF) + { + _startingIndex = 1; + DrawIndex = 1; + CollideIndex = 1; + i = 0; + while(i < totalTiles) + autoTile(i++); + } + + //Figure out the size of the tiles + _tiles = FlxG.addBitmap(TileGraphic); + _tileWidth = TileWidth; + if(_tileWidth == 0) + _tileWidth = _tiles.height; + _tileHeight = TileHeight; + if(_tileHeight == 0) + _tileHeight = _tileWidth; + + //create some tile objects that we'll use for overlap checks (one for each tile) + i = 0; + var l:uint = (_tiles.width/_tileWidth) * (_tiles.height/_tileHeight); + if(auto > OFF) + l++; + _tileObjects = new Array(l); + var ac:uint; + while(i < l) + { + _tileObjects[i] = new FlxTile(this,i,_tileWidth,_tileHeight,(i >= DrawIndex),(i >= CollideIndex)?allowCollisions:NONE); + i++; + } + + //create debug tiles for rendering bounding boxes on demand + _debugTileNotSolid = makeDebugTile(FlxG.BLUE); + _debugTilePartial = makeDebugTile(FlxG.PINK); + _debugTileSolid = makeDebugTile(FlxG.GREEN); + _debugRect = new Rectangle(0,0,_tileWidth,_tileHeight); + + //Then go through and create the actual map + width = widthInTiles*_tileWidth; + height = heightInTiles*_tileHeight; + _rects = new Array(totalTiles); + i = 0; + while(i < totalTiles) + updateTile(i++); + + return this; + } + + /** + * Internal function to clean up the map loading code. + * Just generates a wireframe box the size of a tile with the specified color. + */ + protected function makeDebugTile(Color:uint):BitmapData + { + var debugTile:BitmapData + debugTile = new BitmapData(_tileWidth,_tileHeight,true,0); + + var gfx:Graphics = FlxG.flashGfx; + gfx.clear(); + gfx.moveTo(0,0); + gfx.lineStyle(1,Color,0.5); + gfx.lineTo(_tileWidth-1,0); + gfx.lineTo(_tileWidth-1,_tileHeight-1); + gfx.lineTo(0,_tileHeight-1); + gfx.lineTo(0,0); + + debugTile.draw(FlxG.flashGfxSprite); + return debugTile; + } + + /** + * Main logic loop for tilemap is pretty simple, + * just checks to see if visual debug got turned on. + * If it did, the tilemap is flagged as dirty so it + * will be redrawn with debug info on the next draw call. + */ + override public function update():void + { + if(_lastVisualDebug != FlxG.visualDebug) + { + _lastVisualDebug = FlxG.visualDebug; + setDirty(); + } + } + + /** + * Internal function that actually renders the tilemap to the tilemap buffer. Called by draw(). + * + * @param Buffer The FlxTilemapBuffer you are rendering to. + * @param Camera The related FlxCamera, mainly for scroll values. + */ + protected function drawTilemap(Buffer:FlxTilemapBuffer,Camera:FlxCamera):void + { + Buffer.fill(); + + //Copy tile images into the tile buffer + _point.x = int(Camera.scroll.x*scrollFactor.x) - x; //modified from getScreenXY() + _point.y = int(Camera.scroll.y*scrollFactor.y) - y; + var screenXInTiles:int = (_point.x + ((_point.x > 0)?0.0000001:-0.0000001))/_tileWidth; + var screenYInTiles:int = (_point.y + ((_point.y > 0)?0.0000001:-0.0000001))/_tileHeight; + var screenRows:uint = Buffer.rows; + var screenColumns:uint = Buffer.columns; + + //Bound the upper left corner + if(screenXInTiles < 0) + screenXInTiles = 0; + if(screenXInTiles > widthInTiles-screenColumns) + screenXInTiles = widthInTiles-screenColumns; + if(screenYInTiles < 0) + screenYInTiles = 0; + if(screenYInTiles > heightInTiles-screenRows) + screenYInTiles = heightInTiles-screenRows; + + var rowIndex:int = screenYInTiles*widthInTiles+screenXInTiles; + _flashPoint.y = 0; + var row:uint = 0; + var column:uint; + var columnIndex:uint; + var tile:FlxTile; + var debugTile:BitmapData; + while(row < screenRows) + { + columnIndex = rowIndex; + column = 0; + _flashPoint.x = 0; + while(column < screenColumns) + { + _flashRect = _rects[columnIndex] as Rectangle; + if(_flashRect != null) + { + Buffer.pixels.copyPixels(_tiles,_flashRect,_flashPoint,null,null,true); + if(FlxG.visualDebug && !ignoreDrawDebug) + { + tile = _tileObjects[_data[columnIndex]]; + if(tile != null) + { + if(tile.allowCollisions <= NONE) + debugTile = _debugTileNotSolid; //blue + else if(tile.allowCollisions != ANY) + debugTile = _debugTilePartial; //pink + else + debugTile = _debugTileSolid; //green + Buffer.pixels.copyPixels(debugTile,_debugRect,_flashPoint,null,null,true); + } + } + } + _flashPoint.x += _tileWidth; + column++; + columnIndex++; + } + rowIndex += widthInTiles; + _flashPoint.y += _tileHeight; + row++; + } + Buffer.x = screenXInTiles*_tileWidth; + Buffer.y = screenYInTiles*_tileHeight; + } + + /** + * Draws the tilemap buffers to the cameras and handles flickering. + */ + override public function draw():void + { + if(_flickerTimer != 0) + { + _flicker = !_flicker; + if(_flicker) + return; + } + + if(cameras == null) + cameras = FlxG.cameras; + var camera:FlxCamera; + var buffer:FlxTilemapBuffer; + var i:uint = 0; + var l:uint = cameras.length; + while(i < l) + { + camera = cameras[i]; + if(_buffers[i] == null) + _buffers[i] = new FlxTilemapBuffer(_tileWidth,_tileHeight,widthInTiles,heightInTiles,camera); + buffer = _buffers[i++] as FlxTilemapBuffer; + if(!buffer.dirty) + { + _point.x = x - int(camera.scroll.x*scrollFactor.x) + buffer.x; //copied from getScreenXY() + _point.y = y - int(camera.scroll.y*scrollFactor.y) + buffer.y; + buffer.dirty = (_point.x > 0) || (_point.y > 0) || (_point.x + buffer.width < camera.width) || (_point.y + buffer.height < camera.height); + } + if(buffer.dirty) + { + drawTilemap(buffer,camera); + buffer.dirty = false; + } + _flashPoint.x = x - int(camera.scroll.x*scrollFactor.x) + buffer.x; //copied from getScreenXY() + _flashPoint.y = y - int(camera.scroll.y*scrollFactor.y) + buffer.y; + _flashPoint.x += (_flashPoint.x > 0)?0.0000001:-0.0000001; + _flashPoint.y += (_flashPoint.y > 0)?0.0000001:-0.0000001; + buffer.draw(camera,_flashPoint); + _VISIBLECOUNT++; + } + } + + /** + * Fetches the tilemap data array. + * + * @param Simple If true, returns the data as copy, as a series of 1s and 0s (useful for auto-tiling stuff). Default value is false, meaning it will return the actual data array (NOT a copy). + * + * @return An array the size of the tilemap full of integers indicating tile placement. + */ + public function getData(Simple:Boolean=false):Array + { + if(!Simple) + return _data; + + var i:uint = 0; + var l:uint = _data.length; + var data:Array = new Array(l); + while(i < l) + { + data[i] = ((_tileObjects[_data[i]] as FlxTile).allowCollisions > 0)?1:0; + i++; + } + return data; + } + + /** + * Set the dirty flag on all the tilemap buffers. + * Basically forces a reset of the drawn tilemaps, even if it wasn'tile necessary. + * + * @param Dirty Whether to flag the tilemap buffers as dirty or not. + */ + public function setDirty(Dirty:Boolean=true):void + { + var i:uint = 0; + var l:uint = _buffers.length; + while(i < l) + (_buffers[i++] as FlxTilemapBuffer).dirty = Dirty; + } + + /** + * Find a path through the tilemap. Any tile with any collision flags set is treated as impassable. + * If no path is discovered then a null reference is returned. + * + * @param Start The start point in world coordinates. + * @param End The end point in world coordinates. + * @param Simplify Whether to run a basic simplification algorithm over the path data, removing extra points that are on the same line. Default value is true. + * @param RaySimplify Whether to run an extra raycasting simplification algorithm over the remaining path data. This can result in some close corners being cut, and should be used with care if at all (yet). Default value is false. + * + * @return A FlxPath from the start to the end. If no path could be found, then a null reference is returned. + */ + public function findPath(Start:FlxPoint,End:FlxPoint,Simplify:Boolean=true,RaySimplify:Boolean=false):FlxPath + { + //figure out what tile we are starting and ending on. + var startIndex:uint = int((Start.y-y)/_tileHeight) * widthInTiles + int((Start.x-x)/_tileWidth); + var endIndex:uint = int((End.y-y)/_tileHeight) * widthInTiles + int((End.x-x)/_tileWidth); + + //check that the start and end are clear. + if( ((_tileObjects[_data[startIndex]] as FlxTile).allowCollisions > 0) || + ((_tileObjects[_data[endIndex]] as FlxTile).allowCollisions > 0) ) + return null; + + //figure out how far each of the tiles is from the starting tile + var distances:Array = computePathDistance(startIndex,endIndex); + if(distances == null) + return null; + + //then count backward to find the shortest path. + var points:Array = new Array(); + walkPath(distances,endIndex,points); + + //reset the start and end points to be exact + var node:FlxPoint; + node = points[points.length-1] as FlxPoint; + node.x = Start.x; + node.y = Start.y; + node = points[0] as FlxPoint; + node.x = End.x; + node.y = End.y; + + //some simple path cleanup options + if(Simplify) + simplifyPath(points); + if(RaySimplify) + raySimplifyPath(points); + + //finally load the remaining points into a new path object and return it + var path:FlxPath = new FlxPath(); + var i:int = points.length - 1; + while(i >= 0) + { + node = points[i--] as FlxPoint; + if(node != null) + path.addPoint(node,true); + } + return path; + } + + /** + * Pathfinding helper function, strips out extra points on the same line. + * + * @param Points An array of FlxPoint nodes. + */ + protected function simplifyPath(Points:Array):void + { + var deltaPrevious:Number; + var deltaNext:Number; + var last:FlxPoint = Points[0]; + var node:FlxPoint; + var i:uint = 1; + var l:uint = Points.length-1; + while(i < l) + { + node = Points[i]; + deltaPrevious = (node.x - last.x)/(node.y - last.y); + deltaNext = (node.x - Points[i+1].x)/(node.y - Points[i+1].y); + if((last.x == Points[i+1].x) || (last.y == Points[i+1].y) || (deltaPrevious == deltaNext)) + Points[i] = null; + else + last = node; + i++; + } + } + + /** + * Pathfinding helper function, strips out even more points by raycasting from one point to the next and dropping unnecessary points. + * + * @param Points An array of FlxPoint nodes. + */ + protected function raySimplifyPath(Points:Array):void + { + var source:FlxPoint = Points[0]; + var lastIndex:int = -1; + var node:FlxPoint; + var i:uint = 1; + var l:uint = Points.length; + while(i < l) + { + node = Points[i++]; + if(node == null) + continue; + if(ray(source,node,_point)) + { + if(lastIndex >= 0) + Points[lastIndex] = null; + } + else + source = Points[lastIndex]; + lastIndex = i-1; + } + } + + /** + * Pathfinding helper function, floods a grid with distance information until it finds the end point. + * NOTE: Currently this process does NOT use any kind of fancy heuristic! It's pretty brute. + * + * @param StartIndex The starting tile's map index. + * @param EndIndex The ending tile's map index. + * + * @return A Flash Array of FlxPoint nodes. If the end tile could not be found, then a null Array is returned instead. + */ + protected function computePathDistance(StartIndex:uint, EndIndex:uint):Array + { + //Create a distance-based representation of the tilemap. + //All walls are flagged as -2, all open areas as -1. + var mapSize:uint = widthInTiles*heightInTiles; + var distances:Array = new Array(mapSize); + var i:int = 0; + while(i < mapSize) + { + if((_tileObjects[_data[i]] as FlxTile).allowCollisions) + distances[i] = -2; + else + distances[i] = -1; + i++; + } + distances[StartIndex] = 0; + var distance:uint = 1; + var neighbors:Array = [StartIndex]; + var current:Array; + var currentIndex:uint; + var left:Boolean; + var right:Boolean; + var up:Boolean; + var down:Boolean; + var currentLength:uint; + var foundEnd:Boolean = false; + while(neighbors.length > 0) + { + current = neighbors; + neighbors = new Array(); + + i = 0; + currentLength = current.length; + while(i < currentLength) + { + currentIndex = current[i++]; + if(currentIndex == EndIndex) + { + foundEnd = true; + neighbors.length = 0; + break; + } + + //basic map bounds + left = currentIndex%widthInTiles > 0; + right = currentIndex%widthInTiles < widthInTiles-1; + up = currentIndex/widthInTiles > 0; + down = currentIndex/widthInTiles < heightInTiles-1; + + var index:uint; + if(up) + { + index = currentIndex - widthInTiles; + if(distances[index] == -1) + { + distances[index] = distance; + neighbors.push(index); + } + } + if(right) + { + index = currentIndex + 1; + if(distances[index] == -1) + { + distances[index] = distance; + neighbors.push(index); + } + } + if(down) + { + index = currentIndex + widthInTiles; + if(distances[index] == -1) + { + distances[index] = distance; + neighbors.push(index); + } + } + if(left) + { + index = currentIndex - 1; + if(distances[index] == -1) + { + distances[index] = distance; + neighbors.push(index); + } + } + if(up && right) + { + index = currentIndex - widthInTiles + 1; + if((distances[index] == -1) && (distances[currentIndex-widthInTiles] >= -1) && (distances[currentIndex+1] >= -1)) + { + distances[index] = distance; + neighbors.push(index); + } + } + if(right && down) + { + index = currentIndex + widthInTiles + 1; + if((distances[index] == -1) && (distances[currentIndex+widthInTiles] >= -1) && (distances[currentIndex+1] >= -1)) + { + distances[index] = distance; + neighbors.push(index); + } + } + if(left && down) + { + index = currentIndex + widthInTiles - 1; + if((distances[index] == -1) && (distances[currentIndex+widthInTiles] >= -1) && (distances[currentIndex-1] >= -1)) + { + distances[index] = distance; + neighbors.push(index); + } + } + if(up && left) + { + index = currentIndex - widthInTiles - 1; + if((distances[index] == -1) && (distances[currentIndex-widthInTiles] >= -1) && (distances[currentIndex-1] >= -1)) + { + distances[index] = distance; + neighbors.push(index); + } + } + } + distance++; + } + if(!foundEnd) + distances = null; + return distances; + } + + /** + * Pathfinding helper function, recursively walks the grid and finds a shortest path back to the start. + * + * @param Data A Flash Array of distance information. + * @param Start The tile we're on in our walk backward. + * @param Points A Flash Array of FlxPoint nodes composing the path from the start to the end, compiled in reverse order. + */ + protected function walkPath(Data:Array,Start:uint,Points:Array):void + { + Points.push(new FlxPoint(x + uint(Start%widthInTiles)*_tileWidth + _tileWidth*0.5, y + uint(Start/widthInTiles)*_tileHeight + _tileHeight*0.5)); + if(Data[Start] == 0) + return; + + //basic map bounds + var left:Boolean = Start%widthInTiles > 0; + var right:Boolean = Start%widthInTiles < widthInTiles-1; + var up:Boolean = Start/widthInTiles > 0; + var down:Boolean = Start/widthInTiles < heightInTiles-1; + + var current:uint = Data[Start]; + var i:uint; + if(up) + { + i = Start - widthInTiles; + if((Data[i] >= 0) && (Data[i] < current)) + { + walkPath(Data,i,Points); + return; + } + } + if(right) + { + i = Start + 1; + if((Data[i] >= 0) && (Data[i] < current)) + { + walkPath(Data,i,Points); + return; + } + } + if(down) + { + i = Start + widthInTiles; + if((Data[i] >= 0) && (Data[i] < current)) + { + walkPath(Data,i,Points); + return; + } + } + if(left) + { + i = Start - 1; + if((Data[i] >= 0) && (Data[i] < current)) + { + walkPath(Data,i,Points); + return; + } + } + if(up && right) + { + i = Start - widthInTiles + 1; + if((Data[i] >= 0) && (Data[i] < current)) + { + walkPath(Data,i,Points); + return; + } + } + if(right && down) + { + i = Start + widthInTiles + 1; + if((Data[i] >= 0) && (Data[i] < current)) + { + walkPath(Data,i,Points); + return; + } + } + if(left && down) + { + i = Start + widthInTiles - 1; + if((Data[i] >= 0) && (Data[i] < current)) + { + walkPath(Data,i,Points); + return; + } + } + if(up && left) + { + i = Start - widthInTiles - 1; + if((Data[i] >= 0) && (Data[i] < current)) + { + walkPath(Data,i,Points); + return; + } + } + } + + /** + * Checks to see if some FlxObject overlaps this FlxObject object in world space. + * If the group has a LOT of things in it, it might be faster to use FlxG.overlaps(). + * WARNING: Currently tilemaps do NOT support screen space overlap checks! + * + * @param Object The object being tested. + * @param InScreenSpace Whether to take scroll factors into account when checking for overlap. + * @param Camera Specify which game camera you want. If null getScreenXY() will just grab the first global camera. + * + * @return Whether or not the two objects overlap. + */ + override public function overlaps(ObjectOrGroup:FlxBasic,InScreenSpace:Boolean=false,Camera:FlxCamera=null):Boolean + { + if(ObjectOrGroup is FlxGroup) + { + var results:Boolean = false; + var basic:FlxBasic; + var i:uint = 0; + var members:Array = (ObjectOrGroup as FlxGroup).members; + while(i < length) + { + basic = members[i++] as FlxBasic; + if(basic is FlxObject) + { + if(overlapsWithCallback(basic as FlxObject)) + results = true; + } + else + { + if(overlaps(basic,InScreenSpace,Camera)) + results = true; + } + } + return results; + } + else if(ObjectOrGroup is FlxObject) + return overlapsWithCallback(ObjectOrGroup as FlxObject); + return false; + } + + /** + * Checks to see if this FlxObject were located at the given position, would it overlap the FlxObject or FlxGroup? + * This is distinct from overlapsPoint(), which just checks that point, rather than taking the object's size into account. + * WARNING: Currently tilemaps do NOT support screen space overlap checks! + * + * @param X The X position you want to check. Pretends this object (the caller, not the parameter) is located here. + * @param Y The Y position you want to check. Pretends this object (the caller, not the parameter) is located here. + * @param ObjectOrGroup The object or group being tested. + * @param InScreenSpace Whether to take scroll factors into account when checking for overlap. Default is false, or "only compare in world space." + * @param Camera Specify which game camera you want. If null getScreenXY() will just grab the first global camera. + * + * @return Whether or not the two objects overlap. + */ + override public function overlapsAt(X:Number,Y:Number,ObjectOrGroup:FlxBasic,InScreenSpace:Boolean=false,Camera:FlxCamera=null):Boolean + { + if(ObjectOrGroup is FlxGroup) + { + var results:Boolean = false; + var basic:FlxBasic; + var i:uint = 0; + var members:Array = (ObjectOrGroup as FlxGroup).members; + while(i < length) + { + basic = members[i++] as FlxBasic; + if(basic is FlxObject) + { + _point.x = X; + _point.y = Y; + if(overlapsWithCallback(basic as FlxObject,null,false,_point)) + results = true; + } + else + { + if(overlapsAt(X,Y,basic,InScreenSpace,Camera)) + results = true; + } + } + return results; + } + else if(ObjectOrGroup is FlxObject) + { + _point.x = X; + _point.y = Y; + return overlapsWithCallback(ObjectOrGroup as FlxObject,null,false,_point); + } + return false; + } + + /** + * Checks if the Object overlaps any tiles with any collision flags set, + * and calls the specified callback function (if there is one). + * Also calls the tile's registered callback if the filter matches. + * + * @param Object The FlxObject you are checking for overlaps against. + * @param Callback An optional function that takes the form "myCallback(Object1:FlxObject,Object2:FlxObject)", where Object1 is a FlxTile object, and Object2 is the object passed in in the first parameter of this method. + * @param FlipCallbackParams Used to preserve A-B list ordering from FlxObject.separate() - returns the FlxTile object as the second parameter instead. + * @param Position Optional, specify a custom position for the tilemap (useful for overlapsAt()-type funcitonality). + * + * @return Whether there were overlaps, or if a callback was specified, whatever the return value of the callback was. + */ + public function overlapsWithCallback(Object:FlxObject,Callback:Function=null,FlipCallbackParams:Boolean=false,Position:FlxPoint=null):Boolean + { + var results:Boolean = false; + + var X:Number = x; + var Y:Number = y; + if(Position != null) + { + X = Position.x; + Y = Position.y; + } + + //Figure out what tiles we need to check against + var selectionX:int = FlxU.floor((Object.x - X)/_tileWidth); + var selectionY:int = FlxU.floor((Object.y - Y)/_tileHeight); + var selectionWidth:uint = selectionX + (FlxU.ceil(Object.width/_tileWidth)) + 1; + var selectionHeight:uint = selectionY + FlxU.ceil(Object.height/_tileHeight) + 1; + + //Then bound these coordinates by the map edges + if(selectionX < 0) + selectionX = 0; + if(selectionY < 0) + selectionY = 0; + if(selectionWidth > widthInTiles) + selectionWidth = widthInTiles; + if(selectionHeight > heightInTiles) + selectionHeight = heightInTiles; + + //Then loop through this selection of tiles and call FlxObject.separate() accordingly + var rowStart:uint = selectionY*widthInTiles; + var row:uint = selectionY; + var column:uint; + var tile:FlxTile; + var overlapFound:Boolean; + var deltaX:Number = X - last.x; + var deltaY:Number = Y - last.y; + while(row < selectionHeight) + { + column = selectionX; + while(column < selectionWidth) + { + overlapFound = false; + tile = _tileObjects[_data[rowStart+column]] as FlxTile; + if(tile.allowCollisions) + { + tile.x = X+column*_tileWidth; + tile.y = Y+row*_tileHeight; + tile.last.x = tile.x - deltaX; + tile.last.y = tile.y - deltaY; + if(Callback != null) + { + if(FlipCallbackParams) + overlapFound = Callback(Object,tile); + else + overlapFound = Callback(tile,Object); + } + else + overlapFound = (Object.x + Object.width > tile.x) && (Object.x < tile.x + tile.width) && (Object.y + Object.height > tile.y) && (Object.y < tile.y + tile.height); + if(overlapFound) + { + if((tile.callback != null) && ((tile.filter == null) || (Object is tile.filter))) + { + tile.mapIndex = rowStart+column; + tile.callback(tile,Object); + } + results = true; + } + } + else if((tile.callback != null) && ((tile.filter == null) || (Object is tile.filter))) + { + tile.mapIndex = rowStart+column; + tile.callback(tile,Object); + } + column++; + } + rowStart += widthInTiles; + row++; + } + return results; + } + + /** + * Checks to see if a point in 2D world space overlaps this FlxObject object. + * + * @param Point The point in world space you want to check. + * @param InScreenSpace Whether to take scroll factors into account when checking for overlap. + * @param Camera Specify which game camera you want. If null getScreenXY() will just grab the first global camera. + * + * @return Whether or not the point overlaps this object. + */ + override public function overlapsPoint(Point:FlxPoint,InScreenSpace:Boolean=false,Camera:FlxCamera=null):Boolean + { + if(!InScreenSpace) + return (_tileObjects[_data[uint(uint((Point.y-y)/_tileHeight)*widthInTiles + (Point.x-x)/_tileWidth)]] as FlxTile).allowCollisions > 0; + + if(Camera == null) + Camera = FlxG.camera; + Point.x = Point.x - Camera.scroll.x; + Point.y = Point.y - Camera.scroll.y; + getScreenXY(_point,Camera); + return (_tileObjects[_data[uint(uint((Point.y-_point.y)/_tileHeight)*widthInTiles + (Point.x-_point.x)/_tileWidth)]] as FlxTile).allowCollisions > 0; + } + + /** + * Check the value of a particular tile. + * + * @param X The X coordinate of the tile (in tiles, not pixels). + * @param Y The Y coordinate of the tile (in tiles, not pixels). + * + * @return A uint containing the value of the tile at this spot in the array. + */ + public function getTile(X:uint,Y:uint):uint + { + return _data[Y * widthInTiles + X] as uint; + } + + /** + * Get the value of a tile in the tilemap by index. + * + * @param Index The slot in the data array (Y * widthInTiles + X) where this tile is stored. + * + * @return A uint containing the value of the tile at this spot in the array. + */ + public function getTileByIndex(Index:uint):uint + { + return _data[Index] as uint; + } + + /** + * Returns a new Flash Array full of every map index of the requested tile type. + * + * @param Index The requested tile type. + * + * @return An Array with a list of all map indices of that tile type. + */ + public function getTileInstances(Index:uint):Array + { + var array:Array = null; + var i:uint = 0; + var l:uint = widthInTiles * heightInTiles; + while(i < l) + { + if(_data[i] == Index) + { + if(array == null) + array = new Array(); + array.push(i); + } + i++; + } + + return array; + } + + /** + * Returns a new Flash Array full of every coordinate of the requested tile type. + * + * @param Index The requested tile type. + * @param Midpoint Whether to return the coordinates of the tile midpoint, or upper left corner. Default is true, return midpoint. + * + * @return An Array with a list of all the coordinates of that tile type. + */ + public function getTileCoords(Index:uint,Midpoint:Boolean=true):Array + { + var array:Array = null; + + var point:FlxPoint; + var i:uint = 0; + var l:uint = widthInTiles * heightInTiles; + while(i < l) + { + if(_data[i] == Index) + { + point = new FlxPoint(x + uint(i%widthInTiles)*_tileWidth,y + uint(i/widthInTiles)*_tileHeight); + if(Midpoint) + { + point.x += _tileWidth*0.5; + point.y += _tileHeight*0.5; + } + if(array == null) + array = new Array(); + array.push(point); + } + i++; + } + + return array; + } + + /** + * Change the data and graphic of a tile in the tilemap. + * + * @param X The X coordinate of the tile (in tiles, not pixels). + * @param Y The Y coordinate of the tile (in tiles, not pixels). + * @param Tile The new integer data you wish to inject. + * @param UpdateGraphics Whether the graphical representation of this tile should change. + * + * @return Whether or not the tile was actually changed. + */ + public function setTile(X:uint,Y:uint,Tile:uint,UpdateGraphics:Boolean=true):Boolean + { + if((X >= widthInTiles) || (Y >= heightInTiles)) + return false; + return setTileByIndex(Y * widthInTiles + X,Tile,UpdateGraphics); + } + + /** + * Change the data and graphic of a tile in the tilemap. + * + * @param Index The slot in the data array (Y * widthInTiles + X) where this tile is stored. + * @param Tile The new integer data you wish to inject. + * @param UpdateGraphics Whether the graphical representation of this tile should change. + * + * @return Whether or not the tile was actually changed. + */ + public function setTileByIndex(Index:uint,Tile:uint,UpdateGraphics:Boolean=true):Boolean + { + if(Index >= _data.length) + return false; + + var ok:Boolean = true; + _data[Index] = Tile; + + if(!UpdateGraphics) + return ok; + + setDirty(); + + if(auto == OFF) + { + updateTile(Index); + return ok; + } + + //If this map is autotiled and it changes, locally update the arrangement + var i:uint; + var row:int = int(Index/widthInTiles) - 1; + var rowLength:int = row + 3; + var column:int = Index%widthInTiles - 1; + var columnHeight:int = column + 3; + while(row < rowLength) + { + column = columnHeight - 3; + while(column < columnHeight) + { + if((row >= 0) && (row < heightInTiles) && (column >= 0) && (column < widthInTiles)) + { + i = row*widthInTiles+column; + autoTile(i); + updateTile(i); + } + column++; + } + row++; + } + + return ok; + } + + /** + * Adjust collision settings and/or bind a callback function to a range of tiles. + * This callback function, if present, is triggered by calls to overlap() or overlapsWithCallback(). + * + * @param Tile The tile or tiles you want to adjust. + * @param AllowCollisions Modify the tile or tiles to only allow collisions from certain directions, use FlxObject constants NONE, ANY, LEFT, RIGHT, etc. Default is "ANY". + * @param Callback The function to trigger, e.g. lavaCallback(Tile:FlxTile, Object:FlxObject). + * @param CallbackFilter If you only want the callback to go off for certain classes or objects based on a certain class, set that class here. + * @param Range If you want this callback to work for a bunch of different tiles, input the range here. Default value is 1. + */ + public function setTileProperties(Tile:uint,AllowCollisions:uint=0x1111,Callback:Function=null,CallbackFilter:Class=null,Range:uint=1):void + { + if(Range <= 0) + Range = 1; + var tile:FlxTile; + var i:uint = Tile; + var l:uint = Tile+Range; + while(i < l) + { + tile = _tileObjects[i++] as FlxTile; + tile.allowCollisions = AllowCollisions; + tile.callback = Callback; + tile.filter = CallbackFilter; + } + } + + /** + * Call this function to lock the automatic camera to the map's edges. + * + * @param Camera Specify which game camera you want. If null getScreenXY() will just grab the first global camera. + * @param Border Adjusts the camera follow boundary by whatever number of tiles you specify here. Handy for blocking off deadends that are offscreen, etc. Use a negative number to add padding instead of hiding the edges. + * @param UpdateWorld Whether to update the collision system's world size, default value is true. + */ + public function follow(Camera:FlxCamera=null,Border:int=0,UpdateWorld:Boolean=true):void + { + if(Camera == null) + Camera = FlxG.camera; + Camera.setBounds(x+Border*_tileWidth,y+Border*_tileHeight,width-Border*_tileWidth*2,height-Border*_tileHeight*2,UpdateWorld); + } + + /** + * Get the world coordinates and size of the entire tilemap as a FlxRect. + * + * @param Bounds Optional, pass in a pre-existing FlxRect to prevent instantiation of a new object. + * + * @return A FlxRect containing the world coordinates and size of the entire tilemap. + */ + public function getBounds(Bounds:FlxRect=null):FlxRect + { + if(Bounds == null) + Bounds = new FlxRect(); + return Bounds.make(x,y,width,height); + } + + /** + * Shoots a ray from the start point to the end point. + * If/when it passes through a tile, it stores that point and returns false. + * + * @param Start The world coordinates of the start of the ray. + * @param End The world coordinates of the end of the ray. + * @param Result A Point object containing the first wall impact. + * @param Resolution Defaults to 1, meaning check every tile or so. Higher means more checks! + * @return Returns true if the ray made it from Start to End without hitting anything. Returns false and fills Result if a tile was hit. + */ + public function ray(Start:FlxPoint, End:FlxPoint, Result:FlxPoint=null, Resolution:Number=1):Boolean + { + var step:Number = _tileWidth; + if(_tileHeight < _tileWidth) + step = _tileHeight; + step /= Resolution; + var deltaX:Number = End.x - Start.x; + var deltaY:Number = End.y - Start.y; + var distance:Number = Math.sqrt(deltaX*deltaX + deltaY*deltaY); + var steps:uint = Math.ceil(distance/step); + var stepX:Number = deltaX/steps; + var stepY:Number = deltaY/steps; + var curX:Number = Start.x - stepX - x; + var curY:Number = Start.y - stepY - y; + var tileX:uint; + var tileY:uint; + var i:uint = 0; + while(i < steps) + { + curX += stepX; + curY += stepY; + + if((curX < 0) || (curX > width) || (curY < 0) || (curY > height)) + { + i++; + continue; + } + + tileX = curX/_tileWidth; + tileY = curY/_tileHeight; + if((_tileObjects[_data[tileY*widthInTiles+tileX]] as FlxTile).allowCollisions) + { + //Some basic helper stuff + tileX *= _tileWidth; + tileY *= _tileHeight; + var rx:Number = 0; + var ry:Number = 0; + var q:Number; + var lx:Number = curX-stepX; + var ly:Number = curY-stepY; + + //Figure out if it crosses the X boundary + q = tileX; + if(deltaX < 0) + q += _tileWidth; + rx = q; + ry = ly + stepY*((q-lx)/stepX); + if((ry > tileY) && (ry < tileY + _tileHeight)) + { + if(Result == null) + Result = new FlxPoint(); + Result.x = rx; + Result.y = ry; + return false; + } + + //Else, figure out if it crosses the Y boundary + q = tileY; + if(deltaY < 0) + q += _tileHeight; + rx = lx + stepX*((q-ly)/stepY); + ry = q; + if((rx > tileX) && (rx < tileX + _tileWidth)) + { + if(Result == null) + Result = new FlxPoint(); + Result.x = rx; + Result.y = ry; + return false; + } + return true; + } + i++; + } + return true; + } + + /** + * Converts a one-dimensional array of tile data to a comma-separated string. + * + * @param Data An array full of integer tile references. + * @param Width The number of tiles in each row. + * @param Invert Recommended only for 1-bit arrays - changes 0s to 1s and vice versa. + * + * @return A comma-separated string containing the level data in a FlxTilemap-friendly format. + */ + static public function arrayToCSV(Data:Array,Width:int,Invert:Boolean=false):String + { + var row:uint = 0; + var column:uint; + var csv:String; + var Height:int = Data.length / Width; + var index:int; + while(row < Height) + { + column = 0; + while(column < Width) + { + index = Data[row*Width+column]; + if(Invert) + { + if(index == 0) + index = 1; + else if(index == 1) + index = 0; + } + + if(column == 0) + { + if(row == 0) + csv += index; + else + csv += "\n"+index; + } + else + csv += ", "+index; + column++; + } + row++; + } + return csv; + } + + /** + * Converts a BitmapData object to a comma-separated string. + * Black pixels are flagged as 'solid' by default, + * non-black pixels are set as non-colliding. + * Black pixels must be PURE BLACK. + * + * @param bitmapData A Flash BitmapData object, preferably black and white. + * @param Invert Load white pixels as solid instead. + * @param Scale Default is 1. Scale of 2 means each pixel forms a 2x2 block of tiles, and so on. + * + * @return A comma-separated string containing the level data in a FlxTilemap-friendly format. + */ + static public function bitmapToCSV(bitmapData:BitmapData,Invert:Boolean=false,Scale:uint=1):String + { + //Import and scale image if necessary + if(Scale > 1) + { + var bd:BitmapData = bitmapData; + bitmapData = new BitmapData(bitmapData.width*Scale,bitmapData.height*Scale); + var mtx:Matrix = new Matrix(); + mtx.scale(Scale,Scale); + bitmapData.draw(bd,mtx); + } + + //Walk image and export pixel values + var row:uint = 0; + var column:uint; + var pixel:uint; + var csv:String = ""; + var bitmapWidth:uint = bitmapData.width; + var bitmapHeight:uint = bitmapData.height; + while(row < bitmapHeight) + { + column = 0; + while(column < bitmapWidth) + { + //Decide if this pixel/tile is solid (1) or not (0) + pixel = bitmapData.getPixel(column,row); + if((Invert && (pixel > 0)) || (!Invert && (pixel == 0))) + pixel = 1; + else + pixel = 0; + + //Write the result to the string + if(column == 0) + { + if(row == 0) + csv += pixel; + else + csv += "\n"+pixel; + } + else + csv += ", "+pixel; + column++; + } + row++; + } + return csv; + } + + /** + * Converts a resource image file to a comma-separated string. + * Black pixels are flagged as 'solid' by default, + * non-black pixels are set as non-colliding. + * Black pixels must be PURE BLACK. + * + * @param ImageFile An embedded graphic, preferably black and white. + * @param Invert Load white pixels as solid instead. + * @param Scale Default is 1. Scale of 2 means each pixel forms a 2x2 block of tiles, and so on. + * + * @return A comma-separated string containing the level data in a FlxTilemap-friendly format. + */ + static public function imageToCSV(ImageFile:Class,Invert:Boolean=false,Scale:uint=1):String + { + return bitmapToCSV((new ImageFile).bitmapData,Invert,Scale); + } + + /** + * An internal function used by the binary auto-tilers. + * + * @param Index The index of the tile you want to analyze. + */ + protected function autoTile(Index:uint):void + { + if(_data[Index] == 0) + return; + + _data[Index] = 0; + if((Index-widthInTiles < 0) || (_data[Index-widthInTiles] > 0)) //UP + _data[Index] += 1; + if((Index%widthInTiles >= widthInTiles-1) || (_data[Index+1] > 0)) //RIGHT + _data[Index] += 2; + if((Index+widthInTiles >= totalTiles) || (_data[Index+widthInTiles] > 0)) //DOWN + _data[Index] += 4; + if((Index%widthInTiles <= 0) || (_data[Index-1] > 0)) //LEFT + _data[Index] += 8; + if((auto == ALT) && (_data[Index] == 15)) //The alternate algo checks for interior corners + { + if((Index%widthInTiles > 0) && (Index+widthInTiles < totalTiles) && (_data[Index+widthInTiles-1] <= 0)) + _data[Index] = 1; //BOTTOM LEFT OPEN + if((Index%widthInTiles > 0) && (Index-widthInTiles >= 0) && (_data[Index-widthInTiles-1] <= 0)) + _data[Index] = 2; //TOP LEFT OPEN + if((Index%widthInTiles < widthInTiles-1) && (Index-widthInTiles >= 0) && (_data[Index-widthInTiles+1] <= 0)) + _data[Index] = 4; //TOP RIGHT OPEN + if((Index%widthInTiles < widthInTiles-1) && (Index+widthInTiles < totalTiles) && (_data[Index+widthInTiles+1] <= 0)) + _data[Index] = 8; //BOTTOM RIGHT OPEN + } + _data[Index] += 1; + } + + /** + * Internal function used in setTileByIndex() and the constructor to update the map. + * + * @param Index The index of the tile you want to update. + */ + protected function updateTile(Index:uint):void + { + var tile:FlxTile = _tileObjects[_data[Index]] as FlxTile; + if((tile == null) || !tile.visible) + { + _rects[Index] = null; + return; + } + var rx:uint = (_data[Index]-_startingIndex)*_tileWidth; + var ry:uint = 0; + if(rx >= _tiles.width) + { + ry = uint(rx/_tiles.width)*_tileHeight; + rx %= _tiles.width; + } + _rects[Index] = (new Rectangle(rx,ry,_tileWidth,_tileHeight)); + } + } +} diff --git a/src/org/flixel/FlxTimer.as b/src/org/flixel/FlxTimer.as new file mode 100644 index 0000000..b3150e8 --- /dev/null +++ b/src/org/flixel/FlxTimer.as @@ -0,0 +1,168 @@ +package org.flixel +{ + import org.flixel.plugin.TimerManager; + + /** + * A simple timer class, leveraging the new plugins system. + * Can be used with callbacks or by polling the finished flag. + * Not intended to be added to a game state or group; the timer manager + * is responsible for actually calling update(), not the user. + * + * @author Adam Atomic + */ + public class FlxTimer + { + /** + * How much time the timer was set for. + */ + public var time:Number; + /** + * How many loops the timer was set for. + */ + public var loops:uint; + /** + * Pauses or checks the pause state of the timer. + */ + public var paused:Boolean; + /** + * Check to see if the timer is finished. + */ + public var finished:Boolean; + + /** + * Internal tracker for the time's-up callback function. + * Callback should be formed "onTimer(Timer:FlxTimer);" + */ + protected var _callback:Function; + /** + * Internal tracker for the actual timer counting up. + */ + protected var _timeCounter:Number; + /** + * Internal tracker for the loops counting up. + */ + protected var _loopsCounter:uint; + + /** + * Instantiate the timer. Does not set or start the timer. + */ + public function FlxTimer() + { + time = 0; + loops = 0; + _callback = null; + _timeCounter = 0; + _loopsCounter = 0; + + paused = false; + finished = false; + } + + /** + * Clean up memory. + */ + public function destroy():void + { + stop(); + _callback = null; + } + + /** + * Called by the timer manager plugin to update the timer. + * If time runs out, the loop counter is advanced, the timer reset, and the callback called if it exists. + * If the timer runs out of loops, then the timer calls stop(). + * However, callbacks are called AFTER stop() is called. + */ + public function update():void + { + _timeCounter += FlxG.elapsed; + while((_timeCounter >= time) && !paused && !finished) + { + _timeCounter -= time; + + _loopsCounter++; + if((loops > 0) && (_loopsCounter >= loops)) + stop(); + + if(_callback != null) + _callback(this); + } + } + + /** + * Starts or resumes the timer. If this timer was paused, + * then all the parameters are ignored, and the timer is resumed. + * Adds the timer to the timer manager. + * + * @param Time How many seconds it takes for the timer to go off. + * @param Loops How many times the timer should go off. Default is 1, or "just count down once." + * @param Callback Optional, triggered whenever the time runs out, once for each loop. Callback should be formed "onTimer(Timer:FlxTimer);" + * + * @return A reference to itself (handy for chaining or whatever). + */ + public function start(Time:Number=1,Loops:uint=1,Callback:Function=null):FlxTimer + { + var timerManager:TimerManager = manager; + if(timerManager != null) + timerManager.add(this); + + if(paused) + { + paused = false; + return this; + } + + paused = false; + finished = false; + time = Time; + loops = Loops; + _callback = Callback; + _timeCounter = 0; + _loopsCounter = 0; + return this; + } + + /** + * Stops the timer and removes it from the timer manager. + */ + public function stop():void + { + finished = true; + var timerManager:TimerManager = manager; + if(timerManager != null) + timerManager.remove(this); + } + + /** + * Read-only: check how much time is left on the timer. + */ + public function get timeLeft():Number + { + return time-_timeCounter; + } + + /** + * Read-only: check how many loops are left on the timer. + */ + public function get loopsLeft():int + { + return loops-_loopsCounter; + } + + /** + * Read-only: how far along the timer is, on a scale of 0.0 to 1.0. + */ + public function get progress():Number + { + if(time > 0) + return _timeCounter/time; + else + return 0; + } + + static public function get manager():TimerManager + { + return FlxG.getPlugin(TimerManager) as TimerManager; + } + } +} \ No newline at end of file diff --git a/src/org/flixel/FlxU.as b/src/org/flixel/FlxU.as new file mode 100644 index 0000000..769a44a --- /dev/null +++ b/src/org/flixel/FlxU.as @@ -0,0 +1,622 @@ +package org.flixel +{ + import flash.net.URLRequest; + import flash.net.navigateToURL; + import flash.utils.getDefinitionByName; + import flash.utils.getQualifiedClassName; + import flash.utils.getTimer; + + public class FlxU + { + /** + * Opens a web page in a new tab or window. + * MUST be called from the UI thread or else badness. + * + * @param URL The address of the web page. + */ + static public function openURL(URL:String):void + { + navigateToURL(new URLRequest(URL), "_blank"); + } + + /** + * Calculate the absolute value of a number. + * + * @param Value Any number. + * + * @return The absolute value of that number. + */ + static public function abs(Value:Number):Number + { + return (Value>0)?Value:-Value; + } + + /** + * Round down to the next whole number. E.g. floor(1.7) == 1, and floor(-2.7) == -2. + * + * @param Value Any number. + * + * @return The rounded value of that number. + */ + static public function floor(Value:Number):Number + { + var number:Number = int(Value); + return (Value>0)?(number):((number!=Value)?(number-1):(number)); + } + + /** + * Round up to the next whole number. E.g. ceil(1.3) == 2, and ceil(-2.3) == -3. + * + * @param Value Any number. + * + * @return The rounded value of that number. + */ + static public function ceil(Value:Number):Number + { + var number:Number = int(Value); + return (Value>0)?((number!=Value)?(number+1):(number)):(number); + } + + /** + * Round to the closest whole number. E.g. round(1.7) == 2, and round(-2.3) == -2. + * + * @param Value Any number. + * + * @return The rounded value of that number. + */ + static public function round(Value:Number):Number + { + var number:Number = int(Value+((Value>0)?0.5:-0.5)); + return (Value>0)?(number):((number!=Value)?(number-1):(number)); + } + + /** + * Figure out which number is smaller. + * + * @param Number1 Any number. + * @param Number2 Any number. + * + * @return The smaller of the two numbers. + */ + static public function min(Number1:Number,Number2:Number):Number + { + return (Number1 <= Number2)?Number1:Number2; + } + + /** + * Figure out which number is larger. + * + * @param Number1 Any number. + * @param Number2 Any number. + * + * @return The larger of the two numbers. + */ + static public function max(Number1:Number,Number2:Number):Number + { + return (Number1 >= Number2)?Number1:Number2; + } + + /** + * Bound a number by a minimum and maximum. + * Ensures that this number is no smaller than the minimum, + * and no larger than the maximum. + * + * @param Value Any number. + * @param Min Any number. + * @param Max Any number. + * + * @return The bounded value of the number. + */ + static public function bound(Value:Number,Min:Number,Max:Number):Number + { + var lowerBound:Number = (ValueMax)?Max:lowerBound; + } + + /** + * Generates a random number based on the seed provided. + * + * @param Seed A number between 0 and 1, used to generate a predictable random number (very optional). + * + * @return A Number between 0 and 1. + */ + static public function srand(Seed:Number):Number + { + return ((69621 * int(Seed * 0x7FFFFFFF)) % 0x7FFFFFFF) / 0x7FFFFFFF; + } + + /** + * Shuffles the entries in an array into a new random order. + * FlxG.shuffle() is deterministic and safe for use with replays/recordings. + * HOWEVER, FlxU.shuffle() is NOT deterministic and unsafe for use with replays/recordings. + * + * @param A A Flash Array object containing...stuff. + * @param HowManyTimes How many swaps to perform during the shuffle operation. Good rule of thumb is 2-4 times as many objects are in the list. + * + * @return The same Flash Array object that you passed in in the first place. + */ + static public function shuffle(Objects:Array,HowManyTimes:uint):Array + { + var i:uint = 0; + var index1:uint; + var index2:uint; + var object:Object; + while(i < HowManyTimes) + { + index1 = Math.random()*Objects.length; + index2 = Math.random()*Objects.length; + object = Objects[index2]; + Objects[index2] = Objects[index1]; + Objects[index1] = object; + i++; + } + return Objects; + } + + /** + * Fetch a random entry from the given array. + * Will return null if random selection is missing, or array has no entries. + * FlxG.getRandom() is deterministic and safe for use with replays/recordings. + * HOWEVER, FlxU.getRandom() is NOT deterministic and unsafe for use with replays/recordings. + * + * @param Objects A Flash array of objects. + * @param StartIndex Optional offset off the front of the array. Default value is 0, or the beginning of the array. + * @param Length Optional restriction on the number of values you want to randomly select from. + * + * @return The random object that was selected. + */ + static public function getRandom(Objects:Array,StartIndex:uint=0,Length:uint=0):Object + { + if(Objects != null) + { + var l:uint = Length; + if((l == 0) || (l > Objects.length - StartIndex)) + l = Objects.length - StartIndex; + if(l > 0) + return Objects[StartIndex + uint(Math.random()*l)]; + } + return null; + } + + /** + * Just grabs the current "ticks" or time in milliseconds that has passed since Flash Player started up. + * Useful for finding out how long it takes to execute specific blocks of code. + * + * @return A uint to be passed to FlxU.endProfile(). + */ + static public function getTicks():uint + { + return getTimer(); + } + + /** + * Takes two "ticks" timestamps and formats them into the number of seconds that passed as a String. + * Useful for logging, debugging, the watch window, or whatever else. + * + * @param StartTicks The first timestamp from the system. + * @param EndTicks The second timestamp from the system. + * + * @return A String containing the formatted time elapsed information. + */ + static public function formatTicks(StartTicks:uint,EndTicks:uint):String + { + return ((EndTicks-StartTicks)/1000)+"s" + } + + /** + * Generate a Flash uint color from RGBA components. + * + * @param Red The red component, between 0 and 255. + * @param Green The green component, between 0 and 255. + * @param Blue The blue component, between 0 and 255. + * @param Alpha How opaque the color should be, either between 0 and 1 or 0 and 255. + * + * @return The color as a uint. + */ + static public function makeColor(Red:uint, Green:uint, Blue:uint, Alpha:Number=1.0):uint + { + return (((Alpha>1)?Alpha:(Alpha * 255)) & 0xFF) << 24 | (Red & 0xFF) << 16 | (Green & 0xFF) << 8 | (Blue & 0xFF); + } + + /** + * Generate a Flash uint color from HSB components. + * + * @param Hue A number between 0 and 360, indicating position on a color strip or wheel. + * @param Saturation A number between 0 and 1, indicating how colorful or gray the color should be. 0 is gray, 1 is vibrant. + * @param Brightness A number between 0 and 1, indicating how bright the color should be. 0 is black, 1 is full bright. + * @param Alpha How opaque the color should be, either between 0 and 1 or 0 and 255. + * + * @return The color as a uint. + */ + static public function makeColorFromHSB(Hue:Number,Saturation:Number,Brightness:Number,Alpha:Number=1.0):uint + { + var red:Number; + var green:Number; + var blue:Number; + if(Saturation == 0.0) + { + red = Brightness; + green = Brightness; + blue = Brightness; + } + else + { + if(Hue == 360) + Hue = 0; + var slice:int = Hue/60; + var hf:Number = Hue/60 - slice; + var aa:Number = Brightness*(1 - Saturation); + var bb:Number = Brightness*(1 - Saturation*hf); + var cc:Number = Brightness*(1 - Saturation*(1.0 - hf)); + switch (slice) + { + case 0: red = Brightness; green = cc; blue = aa; break; + case 1: red = bb; green = Brightness; blue = aa; break; + case 2: red = aa; green = Brightness; blue = cc; break; + case 3: red = aa; green = bb; blue = Brightness; break; + case 4: red = cc; green = aa; blue = Brightness; break; + case 5: red = Brightness; green = aa; blue = bb; break; + default: red = 0; green = 0; blue = 0; break; + } + } + + return (((Alpha>1)?Alpha:(Alpha * 255)) & 0xFF) << 24 | uint(red*255) << 16 | uint(green*255) << 8 | uint(blue*255); + } + + /** + * Loads an array with the RGBA values of a Flash uint color. + * RGB values are stored 0-255. Alpha is stored as a floating point number between 0 and 1. + * + * @param Color The color you want to break into components. + * @param Results An optional parameter, allows you to use an array that already exists in memory to store the result. + * + * @return An Array object containing the Red, Green, Blue and Alpha values of the given color. + */ + static public function getRGBA(Color:uint,Results:Array=null):Array + { + if(Results == null) + Results = new Array(); + Results[0] = (Color >> 16) & 0xFF; + Results[1] = (Color >> 8) & 0xFF; + Results[2] = Color & 0xFF; + Results[3] = Number((Color >> 24) & 0xFF) / 255; + return Results; + } + + /** + * Loads an array with the HSB values of a Flash uint color. + * Hue is a value between 0 and 360. Saturation, Brightness and Alpha + * are as floating point numbers between 0 and 1. + * + * @param Color The color you want to break into components. + * @param Results An optional parameter, allows you to use an array that already exists in memory to store the result. + * + * @return An Array object containing the Red, Green, Blue and Alpha values of the given color. + */ + static public function getHSB(Color:uint,Results:Array=null):Array + { + if(Results == null) + Results = new Array(); + + var red:Number = Number((Color >> 16) & 0xFF) / 255; + var green:Number = Number((Color >> 8) & 0xFF) / 255; + var blue:Number = Number((Color) & 0xFF) / 255; + + var m:Number = (red>green)?red:green; + var dmax:Number = (m>blue)?m:blue; + m = (red>green)?green:red; + var dmin:Number = (m>blue)?blue:m; + var range:Number = dmax - dmin; + + Results[2] = dmax; + Results[1] = 0; + Results[0] = 0; + + if(dmax != 0) + Results[1] = range / dmax; + if(Results[1] != 0) + { + if (red == dmax) + Results[0] = (green - blue) / range; + else if (green == dmax) + Results[0] = 2 + (blue - red) / range; + else if (blue == dmax) + Results[0] = 4 + (red - green) / range; + Results[0] *= 60; + if(Results[0] < 0) + Results[0] += 360; + } + + Results[3] = Number((Color >> 24) & 0xFF) / 255; + return Results; + } + + /** + * Format seconds as minutes with a colon, an optionally with milliseconds too. + * + * @param Seconds The number of seconds (for example, time remaining, time spent, etc). + * @param ShowMS Whether to show milliseconds after a "." as well. Default value is false. + * + * @return A nicely formatted String, like "1:03". + */ + static public function formatTime(Seconds:Number,ShowMS:Boolean=false):String + { + var timeString:String = int(Seconds/60) + ":"; + var timeStringHelper:int = int(Seconds)%60; + if(timeStringHelper < 10) + timeString += "0"; + timeString += timeStringHelper; + if(ShowMS) + { + timeString += "."; + timeStringHelper = (Seconds-int(Seconds))*100; + if(timeStringHelper < 10) + timeString += "0"; + timeString += timeStringHelper; + } + return timeString; + } + + /** + * Generate a comma-separated string from an array. + * Especially useful for tracing or other debug output. + * + * @param AnyArray Any Array object. + * + * @return A comma-separated String containing the .toString() output of each element in the array. + */ + static public function formatArray(AnyArray:Array):String + { + if((AnyArray == null) || (AnyArray.length <= 0)) + return ""; + var string:String = AnyArray[0].toString(); + var i:uint = 0; + var l:uint = AnyArray.length; + while(i < l) + string += ", " + AnyArray[i++].toString(); + return string; + } + + /** + * Automatically commas and decimals in the right places for displaying money amounts. + * Does not include a dollar sign or anything, so doesn't really do much + * if you call say var results:String = FlxU.formatMoney(10,false); + * However, very handy for displaying large sums or decimal money values. + * + * @param Amount How much moneys (in dollars, or the equivalent "main" currency - i.e. not cents). + * @param ShowDecimal Whether to show the decimals/cents component. Default value is true. + * @param EnglishStyle Major quantities (thousands, millions, etc) separated by commas, and decimal by a period. Default value is true. + * + * @return A nicely formatted String. Does not include a dollar sign or anything! + */ + static public function formatMoney(Amount:Number,ShowDecimal:Boolean=true,EnglishStyle:Boolean=true):String + { + var helper:int; + var amount:int = Amount; + var string:String = ""; + var comma:String = ""; + var zeroes:String = ""; + while(amount > 0) + { + if((string.length > 0) && comma.length <= 0) + { + if(EnglishStyle) + comma = ","; + else + comma = "."; + } + zeroes = ""; + helper = amount - int(amount/1000)*1000; + amount /= 1000; + if(amount > 0) + { + if(helper < 100) + zeroes += "0"; + if(helper < 10) + zeroes += "0"; + } + string = zeroes + helper + comma + string; + } + if(ShowDecimal) + { + amount = int(Amount*100)-(int(Amount)*100); + string += (EnglishStyle?".":",") + amount; + if(amount < 10) + string += "0"; + } + return string; + } + + /** + * Get the String name of any Object. + * + * @param Obj The Object object in question. + * @param Simple Returns only the class name, not the package or packages. + * + * @return The name of the Class as a String object. + */ + static public function getClassName(Obj:Object,Simple:Boolean=false):String + { + var string:String = getQualifiedClassName(Obj); + string = string.replace("::","."); + if(Simple) + string = string.substr(string.lastIndexOf(".")+1); + return string; + } + + /** + * Check to see if two objects have the same class name. + * + * @param Object1 The first object you want to check. + * @param Object2 The second object you want to check. + * + * @return Whether they have the same class name or not. + */ + static public function compareClassNames(Object1:Object,Object2:Object):Boolean + { + return getQualifiedClassName(Object1) == getQualifiedClassName(Object2); + } + + /** + * Look up a Class object by its string name. + * + * @param Name The String name of the Class you are interested in. + * + * @return A Class object. + */ + static public function getClass(Name:String):Class + { + return getDefinitionByName(Name) as Class; + } + + /** + * A tween-like function that takes a starting velocity + * and some other factors and returns an altered velocity. + * + * @param Velocity Any component of velocity (e.g. 20). + * @param Acceleration Rate at which the velocity is changing. + * @param Drag Really kind of a deceleration, this is how much the velocity changes if Acceleration is not set. + * @param Max An absolute value cap for the velocity. + * + * @return The altered Velocity value. + */ + static public function computeVelocity(Velocity:Number, Acceleration:Number=0, Drag:Number=0, Max:Number=10000):Number + { + if(Acceleration != 0) + Velocity += Acceleration*FlxG.elapsed; + else if(Drag != 0) + { + var drag:Number = Drag*FlxG.elapsed; + if(Velocity - drag > 0) + Velocity = Velocity - drag; + else if(Velocity + drag < 0) + Velocity += drag; + else + Velocity = 0; + } + if((Velocity != 0) && (Max != 10000)) + { + if(Velocity > Max) + Velocity = Max; + else if(Velocity < -Max) + Velocity = -Max; + } + return Velocity; + } + + //*** NOTE: THESE LAST THREE FUNCTIONS REQUIRE FLXPOINT ***// + + /** + * Rotates a point in 2D space around another point by the given angle. + * + * @param X The X coordinate of the point you want to rotate. + * @param Y The Y coordinate of the point you want to rotate. + * @param PivotX The X coordinate of the point you want to rotate around. + * @param PivotY The Y coordinate of the point you want to rotate around. + * @param Angle Rotate the point by this many degrees. + * @param Point Optional FlxPoint to store the results in. + * + * @return A FlxPoint containing the coordinates of the rotated point. + */ + static public function rotatePoint(X:Number, Y:Number, PivotX:Number, PivotY:Number, Angle:Number,Point:FlxPoint=null):FlxPoint + { + var sin:Number = 0; + var cos:Number = 0; + var radians:Number = Angle * -0.017453293; + while (radians < -3.14159265) + radians += 6.28318531; + while (radians > 3.14159265) + radians = radians - 6.28318531; + + if (radians < 0) + { + sin = 1.27323954 * radians + .405284735 * radians * radians; + if (sin < 0) + sin = .225 * (sin *-sin - sin) + sin; + else + sin = .225 * (sin * sin - sin) + sin; + } + else + { + sin = 1.27323954 * radians - 0.405284735 * radians * radians; + if (sin < 0) + sin = .225 * (sin *-sin - sin) + sin; + else + sin = .225 * (sin * sin - sin) + sin; + } + + radians += 1.57079632; + if (radians > 3.14159265) + radians = radians - 6.28318531; + if (radians < 0) + { + cos = 1.27323954 * radians + 0.405284735 * radians * radians; + if (cos < 0) + cos = .225 * (cos *-cos - cos) + cos; + else + cos = .225 * (cos * cos - cos) + cos; + } + else + { + cos = 1.27323954 * radians - 0.405284735 * radians * radians; + if (cos < 0) + cos = .225 * (cos *-cos - cos) + cos; + else + cos = .225 * (cos * cos - cos) + cos; + } + + var dx:Number = X-PivotX; + var dy:Number = PivotY+Y; //Y axis is inverted in flash, normally this would be a subtract operation + if(Point == null) + Point = new FlxPoint(); + Point.x = PivotX + cos*dx - sin*dy; + Point.y = PivotY - sin*dx - cos*dy; + return Point; + }; + + /** + * Calculates the angle between two points. 0 degrees points straight up. + * + * @param Point1 The X coordinate of the point. + * @param Point2 The Y coordinate of the point. + * + * @return The angle in degrees, between -180 and 180. + */ + static public function getAngle(Point1:FlxPoint, Point2:FlxPoint):Number + { + var x:Number = Point2.x - Point1.x; + var y:Number = Point2.y - Point1.y; + if((x == 0) && (y == 0)) + return 0; + var c1:Number = 3.14159265 * 0.25; + var c2:Number = 3 * c1; + var ay:Number = (y < 0)?-y:y; + var angle:Number = 0; + if (x >= 0) + angle = c1 - c1 * ((x - ay) / (x + ay)); + else + angle = c2 - c1 * ((x + ay) / (ay - x)); + angle = ((y < 0)?-angle:angle)*57.2957796; + if(angle > 90) + angle = angle - 270; + else + angle += 90; + return angle; + }; + + /** + * Calculate the distance between two points. + * + * @param Point1 A FlxPoint object referring to the first location. + * @param Point2 A FlxPoint object referring to the second location. + * + * @return The distance between the two points as a floating point Number object. + */ + static public function getDistance(Point1:FlxPoint,Point2:FlxPoint):Number + { + var dx:Number = Point1.x - Point2.x; + var dy:Number = Point1.y - Point2.y; + return Math.sqrt(dx * dx + dy * dy); + } + } +} diff --git a/src/org/flixel/data/autotiles.png b/src/org/flixel/data/autotiles.png new file mode 100644 index 0000000..a7c53ba Binary files /dev/null and b/src/org/flixel/data/autotiles.png differ diff --git a/src/org/flixel/data/autotiles_alt.png b/src/org/flixel/data/autotiles_alt.png new file mode 100644 index 0000000..1a5b05f Binary files /dev/null and b/src/org/flixel/data/autotiles_alt.png differ diff --git a/src/org/flixel/data/beep.mp3 b/src/org/flixel/data/beep.mp3 new file mode 100644 index 0000000..d70119a Binary files /dev/null and b/src/org/flixel/data/beep.mp3 differ diff --git a/src/org/flixel/data/button.png b/src/org/flixel/data/button.png new file mode 100644 index 0000000..0332dfe Binary files /dev/null and b/src/org/flixel/data/button.png differ diff --git a/src/org/flixel/data/cursor.png b/src/org/flixel/data/cursor.png new file mode 100644 index 0000000..558ca01 Binary files /dev/null and b/src/org/flixel/data/cursor.png differ diff --git a/src/org/flixel/data/default.png b/src/org/flixel/data/default.png new file mode 100644 index 0000000..898d57e Binary files /dev/null and b/src/org/flixel/data/default.png differ diff --git a/src/org/flixel/data/handle.png b/src/org/flixel/data/handle.png new file mode 100644 index 0000000..edca195 Binary files /dev/null and b/src/org/flixel/data/handle.png differ diff --git a/src/org/flixel/data/logo.png b/src/org/flixel/data/logo.png new file mode 100644 index 0000000..18ac8eb Binary files /dev/null and b/src/org/flixel/data/logo.png differ diff --git a/src/org/flixel/data/logo_corners.png b/src/org/flixel/data/logo_corners.png new file mode 100644 index 0000000..48cc3eb Binary files /dev/null and b/src/org/flixel/data/logo_corners.png differ diff --git a/src/org/flixel/data/logo_light.png b/src/org/flixel/data/logo_light.png new file mode 100644 index 0000000..8528d1e Binary files /dev/null and b/src/org/flixel/data/logo_light.png differ diff --git a/src/org/flixel/data/nokiafc22.ttf b/src/org/flixel/data/nokiafc22.ttf new file mode 100644 index 0000000..9dd011a Binary files /dev/null and b/src/org/flixel/data/nokiafc22.ttf differ diff --git a/src/org/flixel/data/vcr/flixel.png b/src/org/flixel/data/vcr/flixel.png new file mode 100644 index 0000000..c583789 Binary files /dev/null and b/src/org/flixel/data/vcr/flixel.png differ diff --git a/src/org/flixel/data/vcr/open.png b/src/org/flixel/data/vcr/open.png new file mode 100644 index 0000000..c89887c Binary files /dev/null and b/src/org/flixel/data/vcr/open.png differ diff --git a/src/org/flixel/data/vcr/pause.png b/src/org/flixel/data/vcr/pause.png new file mode 100644 index 0000000..1dd8b4d Binary files /dev/null and b/src/org/flixel/data/vcr/pause.png differ diff --git a/src/org/flixel/data/vcr/play.png b/src/org/flixel/data/vcr/play.png new file mode 100644 index 0000000..d0af7a8 Binary files /dev/null and b/src/org/flixel/data/vcr/play.png differ diff --git a/src/org/flixel/data/vcr/record_off.png b/src/org/flixel/data/vcr/record_off.png new file mode 100644 index 0000000..8198963 Binary files /dev/null and b/src/org/flixel/data/vcr/record_off.png differ diff --git a/src/org/flixel/data/vcr/record_on.png b/src/org/flixel/data/vcr/record_on.png new file mode 100644 index 0000000..91341f6 Binary files /dev/null and b/src/org/flixel/data/vcr/record_on.png differ diff --git a/src/org/flixel/data/vcr/restart.png b/src/org/flixel/data/vcr/restart.png new file mode 100644 index 0000000..d09caf0 Binary files /dev/null and b/src/org/flixel/data/vcr/restart.png differ diff --git a/src/org/flixel/data/vcr/step.png b/src/org/flixel/data/vcr/step.png new file mode 100644 index 0000000..b48f212 Binary files /dev/null and b/src/org/flixel/data/vcr/step.png differ diff --git a/src/org/flixel/data/vcr/stop.png b/src/org/flixel/data/vcr/stop.png new file mode 100644 index 0000000..ed5e8d3 Binary files /dev/null and b/src/org/flixel/data/vcr/stop.png differ diff --git a/src/org/flixel/data/vis/bounds.png b/src/org/flixel/data/vis/bounds.png new file mode 100644 index 0000000..44eb70a Binary files /dev/null and b/src/org/flixel/data/vis/bounds.png differ diff --git a/src/org/flixel/plugin/DebugPathDisplay.as b/src/org/flixel/plugin/DebugPathDisplay.as new file mode 100644 index 0000000..920ee65 --- /dev/null +++ b/src/org/flixel/plugin/DebugPathDisplay.as @@ -0,0 +1,112 @@ +package org.flixel.plugin +{ + import org.flixel.*; + + /** + * A simple manager for tracking and drawing FlxPath debug data to the screen. + * + * @author Adam Atomic + */ + public class DebugPathDisplay extends FlxBasic + { + protected var _paths:Array; + + /** + * Instantiates a new debug path display manager. + */ + public function DebugPathDisplay() + { + _paths = new Array(); + active = false; //don't call update on this plugin + } + + /** + * Clean up memory. + */ + override public function destroy():void + { + super.destroy(); + clear(); + _paths = null; + } + + /** + * Called by FlxG.drawPlugins() after the game state has been drawn. + * Cycles through cameras and calls drawDebug() on each one. + */ + override public function draw():void + { + if(!FlxG.visualDebug || ignoreDrawDebug) + return; + + if(cameras == null) + cameras = FlxG.cameras; + var i:uint = 0; + var l:uint = cameras.length; + while(i < l) + drawDebug(cameras[i++]); + } + + /** + * Similar to FlxObject's drawDebug() functionality, + * this function calls drawDebug() on each FlxPath for the specified camera. + * Very helpful for debugging! + * + * @param Camera Which FlxCamera object to draw the debug data to. + */ + override public function drawDebug(Camera:FlxCamera=null):void + { + if(Camera == null) + Camera = FlxG.camera; + + var i:int = _paths.length-1; + var path:FlxPath; + while(i >= 0) + { + path = _paths[i--] as FlxPath; + if((path != null) && !path.ignoreDrawDebug) + path.drawDebug(Camera); + } + } + + /** + * Add a path to the path debug display manager. + * Usually called automatically by FlxPath's constructor. + * + * @param Path The FlxPath you want to add to the manager. + */ + public function add(Path:FlxPath):void + { + _paths.push(Path); + } + + /** + * Remove a path from the path debug display manager. + * Usually called automatically by FlxPath's destroy() function. + * + * @param Path The FlxPath you want to remove from the manager. + */ + public function remove(Path:FlxPath):void + { + var index:int = _paths.indexOf(Path); + if(index >= 0) + _paths.splice(index,1); + } + + /** + * Removes all the paths from the path debug display manager. + */ + public function clear():void + { + var i:int = _paths.length-1; + var path:FlxPath; + while(i >= 0) + { + path = _paths[i--] as FlxPath; + if(path != null) + path.destroy(); + } + _paths.length = 0; + } + } +} \ No newline at end of file diff --git a/src/org/flixel/plugin/TimerManager.as b/src/org/flixel/plugin/TimerManager.as new file mode 100644 index 0000000..998da94 --- /dev/null +++ b/src/org/flixel/plugin/TimerManager.as @@ -0,0 +1,88 @@ +package org.flixel.plugin +{ + import org.flixel.*; + + /** + * A simple manager for tracking and updating game timer objects. + * + * @author Adam Atomic + */ + public class TimerManager extends FlxBasic + { + protected var _timers:Array; + + /** + * Instantiates a new timer manager. + */ + public function TimerManager() + { + _timers = new Array(); + visible = false; //don't call draw on this plugin + } + + /** + * Clean up memory. + */ + override public function destroy():void + { + clear(); + _timers = null; + } + + /** + * Called by FlxG.updatePlugins() before the game state has been updated. + * Cycles through timers and calls update() on each one. + */ + override public function update():void + { + var i:int = _timers.length-1; + var timer:FlxTimer; + while(i >= 0) + { + timer = _timers[i--] as FlxTimer; + if((timer != null) && !timer.paused && !timer.finished && (timer.time > 0)) + timer.update(); + } + } + + /** + * Add a new timer to the timer manager. + * Usually called automatically by FlxTimer's constructor. + * + * @param Timer The FlxTimer you want to add to the manager. + */ + public function add(Timer:FlxTimer):void + { + _timers.push(Timer); + } + + /** + * Remove a timer from the timer manager. + * Usually called automatically by FlxTimer's stop() function. + * + * @param Timer The FlxTimer you want to remove from the manager. + */ + public function remove(Timer:FlxTimer):void + { + var index:int = _timers.indexOf(Timer); + if(index >= 0) + _timers.splice(index,1); + } + + /** + * Removes all the timers from the timer manager. + */ + public function clear():void + { + var i:int = _timers.length-1; + var timer:FlxTimer; + while(i >= 0) + { + timer = _timers[i--] as FlxTimer; + if(timer != null) + timer.destroy(); + } + _timers.length = 0; + } + } +} \ No newline at end of file diff --git a/src/org/flixel/system/FlxAnim.as b/src/org/flixel/system/FlxAnim.as new file mode 100644 index 0000000..20da476 --- /dev/null +++ b/src/org/flixel/system/FlxAnim.as @@ -0,0 +1,53 @@ +package org.flixel.system +{ + /** + * Just a helper structure for the FlxSprite animation system. + * + * @author Adam Atomic + */ + public class FlxAnim + { + /** + * String name of the animation (e.g. "walk") + */ + public var name:String; + /** + * Seconds between frames (basically the framerate) + */ + public var delay:Number; + /** + * A list of frames stored as uint objects + */ + public var frames:Array; + /** + * Whether or not the animation is looped + */ + public var looped:Boolean; + + /** + * Constructor + * + * @param Name What this animation should be called (e.g. "run") + * @param Frames An array of numbers indicating what frames to play in what order (e.g. 1, 2, 3) + * @param FrameRate The speed in frames per second that the animation should play at (e.g. 40) + * @param Looped Whether or not the animation is looped or just plays once + */ + public function FlxAnim(Name:String, Frames:Array, FrameRate:Number=0, Looped:Boolean=true) + { + name = Name; + delay = 0; + if(FrameRate > 0) + delay = 1.0/FrameRate; + frames = Frames; + looped = Looped; + } + + /** + * Clean up memory. + */ + public function destroy():void + { + frames = null; + } + } +} \ No newline at end of file diff --git a/src/org/flixel/system/FlxDebugger.as b/src/org/flixel/system/FlxDebugger.as new file mode 100644 index 0000000..0bf1ddf --- /dev/null +++ b/src/org/flixel/system/FlxDebugger.as @@ -0,0 +1,234 @@ +package org.flixel.system +{ + import flash.display.Bitmap; + import flash.display.BitmapData; + import flash.display.Sprite; + import flash.events.MouseEvent; + import flash.geom.Point; + import flash.geom.Rectangle; + import flash.text.TextField; + import flash.text.TextFormat; + + import org.flixel.FlxG; + import org.flixel.system.debug.Log; + import org.flixel.system.debug.Perf; + import org.flixel.system.debug.VCR; + import org.flixel.system.debug.Vis; + import org.flixel.system.debug.Watch; + + /** + * Container for the new debugger overlay. + * Most of the functionality is in the debug folder widgets, + * but this class instantiates the widgets and handles their basic formatting and arrangement. + */ + public class FlxDebugger extends Sprite + { + /** + * Container for the performance monitor widget. + */ + public var perf:Perf; + /** + * Container for the trace output widget. + */ + public var log:Log; + /** + * Container for the watch window widget. + */ + public var watch:Watch; + /** + * Container for the record, stop and play buttons. + */ + public var vcr:VCR; + /** + * Container for the visual debug mode toggle. + */ + public var vis:Vis; + /** + * Whether the mouse is currently over one of the debugger windows or not. + */ + public var hasMouse:Boolean; + + /** + * Internal, tracks what debugger window layout user has currently selected. + */ + protected var _layout:uint; + /** + * Internal, stores width and height of the Flash Player window. + */ + protected var _screen:Point; + /** + * Internal, used to space out windows from the edges. + */ + protected var _gutter:uint; + + /** + * Instantiates the debugger overlay. + * + * @param Width The width of the screen. + * @param Height The height of the screen. + */ + public function FlxDebugger(Width:Number,Height:Number) + { + super(); + visible = false; + hasMouse = false; + _screen = new Point(Width,Height); + + addChild(new Bitmap(new BitmapData(Width,15,true,0x7f000000))); + + var txt:TextField = new TextField(); + txt.x = 2; + txt.width = 160; + txt.height = 16; + txt.selectable = false; + txt.multiline = false; + txt.defaultTextFormat = new TextFormat("Courier",12,0xffffff); + var str:String = FlxG.getLibraryName(); + if(FlxG.debug) + str += " [debug]"; + else + str += " [release]"; + txt.text = str; + addChild(txt); + + _gutter = 8; + var screenBounds:Rectangle = new Rectangle(_gutter,15+_gutter/2,_screen.x-_gutter*2,_screen.y-_gutter*1.5-15); + + log = new Log("log",0,0,true,screenBounds); + addChild(log); + + watch = new Watch("watch",0,0,true,screenBounds); + addChild(watch); + + perf = new Perf("stats",0,0,false,screenBounds); + addChild(perf); + + vcr = new VCR(); + vcr.x = (Width - vcr.width/2)/2; + vcr.y = 2; + addChild(vcr); + + vis = new Vis(); + vis.x = Width-vis.width - 4; + vis.y = 2; + addChild(vis); + + setLayout(FlxG.DEBUGGER_STANDARD); + + //Should help with fake mouse focus type behavior + addEventListener(MouseEvent.MOUSE_OVER,onMouseOver); + addEventListener(MouseEvent.MOUSE_OUT,onMouseOut); + } + + /** + * Clean up memory. + */ + public function destroy():void + { + _screen = null; + removeChild(log); + log.destroy(); + log = null; + removeChild(watch); + watch.destroy(); + watch = null; + removeChild(perf); + perf.destroy(); + perf = null; + removeChild(vcr); + vcr.destroy(); + vcr = null; + removeChild(vis); + vis.destroy(); + vis = null; + + removeEventListener(MouseEvent.MOUSE_OVER,onMouseOver); + removeEventListener(MouseEvent.MOUSE_OUT,onMouseOut); + } + + /** + * Mouse handler that helps with fake "mouse focus" type behavior. + * + * @param E Flash mouse event. + */ + protected function onMouseOver(E:MouseEvent=null):void + { + hasMouse = true; + } + + /** + * Mouse handler that helps with fake "mouse focus" type behavior. + * + * @param E Flash mouse event. + */ + protected function onMouseOut(E:MouseEvent=null):void + { + hasMouse = false; + } + + /** + * Rearrange the debugger windows using one of the constants specified in FlxG. + * + * @param Layout The layout style for the debugger windows, e.g. FlxG.DEBUGGER_MICRO. + */ + public function setLayout(Layout:uint):void + { + _layout = Layout; + resetLayout(); + } + + /** + * Forces the debugger windows to reset to the last specified layout. + * The default layout is FlxG.DEBUGGER_STANDARD. + */ + public function resetLayout():void + { + switch(_layout) + { + case FlxG.DEBUGGER_MICRO: + log.resize(_screen.x/4,68); + log.reposition(0,_screen.y); + watch.resize(_screen.x/4,68); + watch.reposition(_screen.x,_screen.y); + perf.reposition(_screen.x,0); + break; + case FlxG.DEBUGGER_BIG: + log.resize((_screen.x-_gutter*3)/2,_screen.y/2); + log.reposition(0,_screen.y); + watch.resize((_screen.x-_gutter*3)/2,_screen.y/2); + watch.reposition(_screen.x,_screen.y); + perf.reposition(_screen.x,0); + break; + case FlxG.DEBUGGER_TOP: + log.resize((_screen.x-_gutter*3)/2,_screen.y/4); + log.reposition(0,0); + watch.resize((_screen.x-_gutter*3)/2,_screen.y/4); + watch.reposition(_screen.x,0); + perf.reposition(_screen.x,_screen.y); + break; + case FlxG.DEBUGGER_LEFT: + log.resize(_screen.x/3,(_screen.y-15-_gutter*2.5)/2); + log.reposition(0,0); + watch.resize(_screen.x/3,(_screen.y-15-_gutter*2.5)/2); + watch.reposition(0,_screen.y); + perf.reposition(_screen.x,0); + break; + case FlxG.DEBUGGER_RIGHT: + log.resize(_screen.x/3,(_screen.y-15-_gutter*2.5)/2); + log.reposition(_screen.x,0); + watch.resize(_screen.x/3,(_screen.y-15-_gutter*2.5)/2); + watch.reposition(_screen.x,_screen.y); + perf.reposition(0,0); + break; + case FlxG.DEBUGGER_STANDARD: + default: + log.resize((_screen.x-_gutter*3)/2,_screen.y/4); + log.reposition(0,_screen.y); + watch.resize((_screen.x-_gutter*3)/2,_screen.y/4); + watch.reposition(_screen.x,_screen.y); + perf.reposition(_screen.x,0); + break; + } + } + } +} \ No newline at end of file diff --git a/src/org/flixel/system/FlxList.as b/src/org/flixel/system/FlxList.as new file mode 100644 index 0000000..ee3f5b9 --- /dev/null +++ b/src/org/flixel/system/FlxList.as @@ -0,0 +1,41 @@ +package org.flixel.system +{ + import org.flixel.FlxObject; + + /** + * A miniature linked list class. + * Useful for optimizing time-critical or highly repetitive tasks! + * See FlxQuadTree for how to use it, IF YOU DARE. + */ + public class FlxList + { + /** + * Stores a reference to a FlxObject. + */ + public var object:FlxObject; + /** + * Stores a reference to the next link in the list. + */ + public var next:FlxList; + + /** + * Creates a new link, and sets object and next to null. + */ + public function FlxList() + { + object = null; + next = null; + } + + /** + * Clean up memory. + */ + public function destroy():void + { + object = null; + if(next != null) + next.destroy(); + next = null; + } + } +} \ No newline at end of file diff --git a/src/org/flixel/system/FlxPreloader.as b/src/org/flixel/system/FlxPreloader.as new file mode 100755 index 0000000..1ccde43 --- /dev/null +++ b/src/org/flixel/system/FlxPreloader.as @@ -0,0 +1,293 @@ +package org.flixel.system +{ + import flash.display.Bitmap; + import flash.display.BitmapData; + import flash.display.DisplayObject; + import flash.display.MovieClip; + import flash.display.Sprite; + import flash.display.StageAlign; + import flash.display.StageScaleMode; + import flash.events.Event; + import flash.events.MouseEvent; + import flash.net.URLRequest; + import flash.net.navigateToURL; + import flash.text.TextField; + import flash.text.TextFormat; + import flash.utils.getDefinitionByName; + import flash.utils.getTimer; + + import org.flixel.FlxG; + + /** + * This class handles the 8-bit style preloader. + */ + public class FlxPreloader extends MovieClip + { + [Embed(source="../data/logo.png")] protected var ImgLogo:Class; + [Embed(source="../data/logo_corners.png")] protected var ImgLogoCorners:Class; + [Embed(source="../data/logo_light.png")] protected var ImgLogoLight:Class; + + /** + * @private + */ + protected var _init:Boolean; + /** + * @private + */ + protected var _buffer:Sprite; + /** + * @private + */ + protected var _bmpBar:Bitmap; + /** + * @private + */ + protected var _text:TextField; + /** + * Useful for storing "real" stage width if you're scaling your preloader graphics. + */ + protected var _width:uint; + /** + * Useful for storing "real" stage height if you're scaling your preloader graphics. + */ + protected var _height:uint; + /** + * @private + */ + protected var _logo:Bitmap; + /** + * @private + */ + protected var _logoGlow:Bitmap; + /** + * @private + */ + protected var _min:uint; + + /** + * This should always be the name of your main project/document class (e.g. GravityHook). + */ + public var className:String; + /** + * Set this to your game's URL to use built-in site-locking. + */ + public var myURL:String; + /** + * Change this if you want the flixel logo to show for more or less time. Default value is 0 seconds. + */ + public var minDisplayTime:Number; + + /** + * Constructor + */ + public function FlxPreloader() + { + minDisplayTime = 0; + + stop(); + stage.scaleMode = StageScaleMode.NO_SCALE; + stage.align = StageAlign.TOP_LEFT; + + //Check if we are on debug or release mode and set _DEBUG accordingly + try + { + throw new Error("Setting global debug flag..."); + } + catch(E:Error) + { + var re:RegExp = /\[.*:[0-9]+\]/; + FlxG.debug = re.test(E.getStackTrace()); + } + + var tmp:Bitmap; + if(!FlxG.debug && (myURL != null) && (root.loaderInfo.url.indexOf(myURL) < 0)) + { + tmp = new Bitmap(new BitmapData(stage.stageWidth,stage.stageHeight,true,0xFFFFFFFF)); + addChild(tmp); + + var format:TextFormat = new TextFormat(); + format.color = 0x000000; + format.size = 16; + format.align = "center"; + format.bold = true; + format.font = "system"; + + var textField:TextField = new TextField(); + textField.width = tmp.width-16; + textField.height = tmp.height-16; + textField.y = 8; + textField.multiline = true; + textField.wordWrap = true; + textField.embedFonts = true; + textField.defaultTextFormat = format; + textField.text = "Hi there! It looks like somebody copied this game without my permission. Just click anywhere, or copy-paste this URL into your browser.\n\n"+myURL+"\n\nto play the game at my site. Thanks, and have fun!"; + addChild(textField); + + textField.addEventListener(MouseEvent.CLICK,goToMyURL); + tmp.addEventListener(MouseEvent.CLICK,goToMyURL); + return; + } + this._init = false; + addEventListener(Event.ENTER_FRAME, onEnterFrame); + } + + private function goToMyURL(event:MouseEvent=null):void + { + navigateToURL(new URLRequest("http://"+myURL)); + } + + private function onEnterFrame(event:Event):void + { + if(!this._init) + { + if((stage.stageWidth <= 0) || (stage.stageHeight <= 0)) + return; + create(); + this._init = true; + } + graphics.clear(); + var time:uint = getTimer(); + if((framesLoaded >= totalFrames) && (time > _min)) + { + removeEventListener(Event.ENTER_FRAME, onEnterFrame); + nextFrame(); + var mainClass:Class = Class(getDefinitionByName(className)); + if(mainClass) + { + var app:Object = new mainClass(); + addChild(app as DisplayObject); + } + destroy(); + } + else + { + var percent:Number = root.loaderInfo.bytesLoaded/root.loaderInfo.bytesTotal; + if((_min > 0) && (percent > time/_min)) + percent = time/_min; + update(percent); + } + } + + /** + * Override this to create your own preloader objects. + * Highly recommended you also override update()! + */ + protected function create():void + { + _min = 0; + if(!FlxG.debug) + _min = minDisplayTime*1000; + _buffer = new Sprite(); + _buffer.scaleX = 2; + _buffer.scaleY = 2; + addChild(_buffer); + _width = stage.stageWidth/_buffer.scaleX; + _height = stage.stageHeight/_buffer.scaleY; + _buffer.addChild(new Bitmap(new BitmapData(_width,_height,false,0x00345e))); + var bitmap:Bitmap = new ImgLogoLight(); + bitmap.smoothing = true; + bitmap.width = bitmap.height = _height; + bitmap.x = (_width-bitmap.width)/2; + _buffer.addChild(bitmap); + _bmpBar = new Bitmap(new BitmapData(1,7,false,0x5f6aff)); + _bmpBar.x = 4; + _bmpBar.y = _height-11; + _buffer.addChild(_bmpBar); + _text = new TextField(); + _text.defaultTextFormat = new TextFormat("system",8,0x5f6aff); + _text.embedFonts = true; + _text.selectable = false; + _text.multiline = false; + _text.x = 2; + _text.y = _bmpBar.y - 11; + _text.width = 80; + _buffer.addChild(_text); + _logo = new ImgLogo(); + _logo.scaleX = _logo.scaleY = _height/8; + _logo.x = (_width-_logo.width)/2; + _logo.y = (_height-_logo.height)/2; + _buffer.addChild(_logo); + _logoGlow = new ImgLogo(); + _logoGlow.smoothing = true; + _logoGlow.blendMode = "screen"; + _logoGlow.scaleX = _logoGlow.scaleY = _height/8; + _logoGlow.x = (_width-_logoGlow.width)/2; + _logoGlow.y = (_height-_logoGlow.height)/2; + _buffer.addChild(_logoGlow); + bitmap = new ImgLogoCorners(); + bitmap.smoothing = true; + bitmap.width = _width; + bitmap.height = _height; + _buffer.addChild(bitmap); + bitmap = new Bitmap(new BitmapData(_width,_height,false,0xffffff)); + var i:uint = 0; + var j:uint = 0; + while(i < _height) + { + j = 0; + while(j < _width) + bitmap.bitmapData.setPixel(j++,i,0); + i+=2; + } + bitmap.blendMode = "overlay"; + bitmap.alpha = 0.25; + _buffer.addChild(bitmap); + } + + protected function destroy():void + { + removeChild(_buffer); + _buffer = null; + _bmpBar = null; + _text = null; + _logo = null; + _logoGlow = null; + } + + /** + * Override this function to manually update the preloader. + * + * @param Percent How much of the program has loaded. + */ + protected function update(Percent:Number):void + { + _bmpBar.scaleX = Percent*(_width-8); + _text.text = "FLX v"+FlxG.LIBRARY_MAJOR_VERSION+"."+FlxG.LIBRARY_MINOR_VERSION+" "+Math.floor(Percent*100)+"%"; + _text.setTextFormat(_text.defaultTextFormat); + if(Percent < 0.1) + { + _logoGlow.alpha = 0; + _logo.alpha = 0; + } + else if(Percent < 0.15) + { + _logoGlow.alpha = Math.random(); + _logo.alpha = 0; + } + else if(Percent < 0.2) + { + _logoGlow.alpha = 0; + _logo.alpha = 0; + } + else if(Percent < 0.25) + { + _logoGlow.alpha = 0; + _logo.alpha = Math.random(); + } + else if(Percent < 0.7) + { + _logoGlow.alpha = (Percent-0.45)/0.45; + _logo.alpha = 1; + } + else if((Percent > 0.8) && (Percent < 0.9)) + { + _logoGlow.alpha = 1-(Percent-0.8)/0.1; + _logo.alpha = 0; + } + else if(Percent > 0.9) + { + _buffer.alpha = 1-(Percent-0.9)/0.1; + } + } + } +} diff --git a/src/org/flixel/system/FlxQuadTree.as b/src/org/flixel/system/FlxQuadTree.as new file mode 100644 index 0000000..b4ec378 --- /dev/null +++ b/src/org/flixel/system/FlxQuadTree.as @@ -0,0 +1,577 @@ +package org.flixel.system +{ + import org.flixel.FlxBasic; + import org.flixel.FlxGroup; + import org.flixel.FlxObject; + import org.flixel.FlxRect; + + /** + * A fairly generic quad tree structure for rapid overlap checks. + * FlxQuadTree is also configured for single or dual list operation. + * You can add items either to its A list or its B list. + * When you do an overlap check, you can compare the A list to itself, + * or the A list against the B list. Handy for different things! + */ + public class FlxQuadTree extends FlxRect + { + /** + * Flag for specifying that you want to add an object to the A list. + */ + static public const A_LIST:uint = 0; + /** + * Flag for specifying that you want to add an object to the B list. + */ + static public const B_LIST:uint = 1; + + /** + * Controls the granularity of the quad tree. Default is 6 (decent performance on large and small worlds). + */ + static public var divisions:uint; + + /** + * Whether this branch of the tree can be subdivided or not. + */ + protected var _canSubdivide:Boolean; + + /** + * Refers to the internal A and B linked lists, + * which are used to store objects in the leaves. + */ + protected var _headA:FlxList; + /** + * Refers to the internal A and B linked lists, + * which are used to store objects in the leaves. + */ + protected var _tailA:FlxList; + /** + * Refers to the internal A and B linked lists, + * which are used to store objects in the leaves. + */ + protected var _headB:FlxList; + /** + * Refers to the internal A and B linked lists, + * which are used to store objects in the leaves. + */ + protected var _tailB:FlxList; + + /** + * Internal, governs and assists with the formation of the tree. + */ + static protected var _min:uint; + /** + * Internal, governs and assists with the formation of the tree. + */ + protected var _northWestTree:FlxQuadTree; + /** + * Internal, governs and assists with the formation of the tree. + */ + protected var _northEastTree:FlxQuadTree; + /** + * Internal, governs and assists with the formation of the tree. + */ + protected var _southEastTree:FlxQuadTree; + /** + * Internal, governs and assists with the formation of the tree. + */ + protected var _southWestTree:FlxQuadTree; + /** + * Internal, governs and assists with the formation of the tree. + */ + protected var _leftEdge:Number; + /** + * Internal, governs and assists with the formation of the tree. + */ + protected var _rightEdge:Number; + /** + * Internal, governs and assists with the formation of the tree. + */ + protected var _topEdge:Number; + /** + * Internal, governs and assists with the formation of the tree. + */ + protected var _bottomEdge:Number; + /** + * Internal, governs and assists with the formation of the tree. + */ + protected var _halfWidth:Number; + /** + * Internal, governs and assists with the formation of the tree. + */ + protected var _halfHeight:Number; + /** + * Internal, governs and assists with the formation of the tree. + */ + protected var _midpointX:Number; + /** + * Internal, governs and assists with the formation of the tree. + */ + protected var _midpointY:Number; + + /** + * Internal, used to reduce recursive method parameters during object placement and tree formation. + */ + static protected var _object:FlxObject; + /** + * Internal, used to reduce recursive method parameters during object placement and tree formation. + */ + static protected var _objectLeftEdge:Number; + /** + * Internal, used to reduce recursive method parameters during object placement and tree formation. + */ + static protected var _objectTopEdge:Number; + /** + * Internal, used to reduce recursive method parameters during object placement and tree formation. + */ + static protected var _objectRightEdge:Number; + /** + * Internal, used to reduce recursive method parameters during object placement and tree formation. + */ + static protected var _objectBottomEdge:Number; + + /** + * Internal, used during tree processing and overlap checks. + */ + static protected var _list:uint; + /** + * Internal, used during tree processing and overlap checks. + */ + static protected var _useBothLists:Boolean; + /** + * Internal, used during tree processing and overlap checks. + */ + static protected var _processingCallback:Function; + /** + * Internal, used during tree processing and overlap checks. + */ + static protected var _notifyCallback:Function; + /** + * Internal, used during tree processing and overlap checks. + */ + static protected var _iterator:FlxList; + + /** + * Internal, helpers for comparing actual object-to-object overlap - see overlapNode(). + */ + static protected var _objectHullX:Number; + /** + * Internal, helpers for comparing actual object-to-object overlap - see overlapNode(). + */ + static protected var _objectHullY:Number; + /** + * Internal, helpers for comparing actual object-to-object overlap - see overlapNode(). + */ + static protected var _objectHullWidth:Number; + /** + * Internal, helpers for comparing actual object-to-object overlap - see overlapNode(). + */ + static protected var _objectHullHeight:Number; + + /** + * Internal, helpers for comparing actual object-to-object overlap - see overlapNode(). + */ + static protected var _checkObjectHullX:Number; + /** + * Internal, helpers for comparing actual object-to-object overlap - see overlapNode(). + */ + static protected var _checkObjectHullY:Number; + /** + * Internal, helpers for comparing actual object-to-object overlap - see overlapNode(). + */ + static protected var _checkObjectHullWidth:Number; + /** + * Internal, helpers for comparing actual object-to-object overlap - see overlapNode(). + */ + static protected var _checkObjectHullHeight:Number; + + /** + * Instantiate a new Quad Tree node. + * + * @param X The X-coordinate of the point in space. + * @param Y The Y-coordinate of the point in space. + * @param Width Desired width of this node. + * @param Height Desired height of this node. + * @param Parent The parent branch or node. Pass null to create a root. + */ + public function FlxQuadTree(X:Number, Y:Number, Width:Number, Height:Number, Parent:FlxQuadTree=null) + { + super(X,Y,Width,Height); + _headA = _tailA = new FlxList(); + _headB = _tailB = new FlxList(); + + //Copy the parent's children (if there are any) + if(Parent != null) + { + var iterator:FlxList; + var ot:FlxList; + if(Parent._headA.object != null) + { + iterator = Parent._headA; + while(iterator != null) + { + if(_tailA.object != null) + { + ot = _tailA; + _tailA = new FlxList(); + ot.next = _tailA; + } + _tailA.object = iterator.object; + iterator = iterator.next; + } + } + if(Parent._headB.object != null) + { + iterator = Parent._headB; + while(iterator != null) + { + if(_tailB.object != null) + { + ot = _tailB; + _tailB = new FlxList(); + ot.next = _tailB; + } + _tailB.object = iterator.object; + iterator = iterator.next; + } + } + } + else + _min = (width + height)/(2*divisions); + _canSubdivide = (width > _min) || (height > _min); + + //Set up comparison/sort helpers + _northWestTree = null; + _northEastTree = null; + _southEastTree = null; + _southWestTree = null; + _leftEdge = x; + _rightEdge = x+width; + _halfWidth = width/2; + _midpointX = _leftEdge+_halfWidth; + _topEdge = y; + _bottomEdge = y+height; + _halfHeight = height/2; + _midpointY = _topEdge+_halfHeight; + } + + /** + * Clean up memory. + */ + public function destroy():void + { + _headA.destroy(); + _headA = null; + _tailA.destroy(); + _tailA = null; + _headB.destroy(); + _headB = null; + _tailB.destroy(); + _tailB = null; + + if(_northWestTree != null) + _northWestTree.destroy(); + _northWestTree = null; + if(_northEastTree != null) + _northEastTree.destroy(); + _northEastTree = null; + if(_southEastTree != null) + _southEastTree.destroy(); + _southEastTree = null; + if(_southWestTree != null) + _southWestTree.destroy(); + _southWestTree = null; + + _object = null; + _processingCallback = null; + _notifyCallback = null; + } + + /** + * Load objects and/or groups into the quad tree, and register notify and processing callbacks. + * + * @param ObjectOrGroup1 Any object that is or extends FlxObject or FlxGroup. + * @param ObjectOrGroup2 Any object that is or extends FlxObject or FlxGroup. If null, the first parameter will be checked against itself. + * @param NotifyCallback A function with the form myFunction(Object1:FlxObject,Object2:FlxObject):void that is called whenever two objects are found to overlap in world space, and either no ProcessCallback is specified, or the ProcessCallback returns true. + * @param ProcessCallback A function with the form myFunction(Object1:FlxObject,Object2:FlxObject):Boolean that is called whenever two objects are found to overlap in world space. The NotifyCallback is only called if this function returns true. See FlxObject.separate(). + */ + public function load(ObjectOrGroup1:FlxBasic, ObjectOrGroup2:FlxBasic=null, NotifyCallback:Function=null, ProcessCallback:Function=null):void + { + add(ObjectOrGroup1, A_LIST); + if(ObjectOrGroup2 != null) + { + add(ObjectOrGroup2, B_LIST); + _useBothLists = true; + } + else + _useBothLists = false; + _notifyCallback = NotifyCallback; + _processingCallback = ProcessCallback; + } + + /** + * Call this function to add an object to the root of the tree. + * This function will recursively add all group members, but + * not the groups themselves. + * + * @param ObjectOrGroup FlxObjects are just added, FlxGroups are recursed and their applicable members added accordingly. + * @param List A uint flag indicating the list to which you want to add the objects. Options are A_LIST and B_LIST. + */ + public function add(ObjectOrGroup:FlxBasic, List:uint):void + { + _list = List; + if(ObjectOrGroup is FlxGroup) + { + var i:uint = 0; + var basic:FlxBasic; + var members:Array = (ObjectOrGroup as FlxGroup).members; + var l:uint = (ObjectOrGroup as FlxGroup).length; + while(i < l) + { + basic = members[i++] as FlxBasic; + if((basic != null) && basic.exists) + { + if(basic is FlxGroup) + add(basic,List); + else if(basic is FlxObject) + { + _object = basic as FlxObject; + if(_object.exists && _object.allowCollisions) + { + _objectLeftEdge = _object.x; + _objectTopEdge = _object.y; + _objectRightEdge = _object.x + _object.width; + _objectBottomEdge = _object.y + _object.height; + addObject(); + } + } + } + } + } + else + { + _object = ObjectOrGroup as FlxObject; + if(_object.exists && _object.allowCollisions) + { + _objectLeftEdge = _object.x; + _objectTopEdge = _object.y; + _objectRightEdge = _object.x + _object.width; + _objectBottomEdge = _object.y + _object.height; + addObject(); + } + } + } + + /** + * Internal function for recursively navigating and creating the tree + * while adding objects to the appropriate nodes. + */ + protected function addObject():void + { + //If this quad (not its children) lies entirely inside this object, add it here + if(!_canSubdivide || ((_leftEdge >= _objectLeftEdge) && (_rightEdge <= _objectRightEdge) && (_topEdge >= _objectTopEdge) && (_bottomEdge <= _objectBottomEdge))) + { + addToList(); + return; + } + + //See if the selected object fits completely inside any of the quadrants + if((_objectLeftEdge > _leftEdge) && (_objectRightEdge < _midpointX)) + { + if((_objectTopEdge > _topEdge) && (_objectBottomEdge < _midpointY)) + { + if(_northWestTree == null) + _northWestTree = new FlxQuadTree(_leftEdge,_topEdge,_halfWidth,_halfHeight,this); + _northWestTree.addObject(); + return; + } + if((_objectTopEdge > _midpointY) && (_objectBottomEdge < _bottomEdge)) + { + if(_southWestTree == null) + _southWestTree = new FlxQuadTree(_leftEdge,_midpointY,_halfWidth,_halfHeight,this); + _southWestTree.addObject(); + return; + } + } + if((_objectLeftEdge > _midpointX) && (_objectRightEdge < _rightEdge)) + { + if((_objectTopEdge > _topEdge) && (_objectBottomEdge < _midpointY)) + { + if(_northEastTree == null) + _northEastTree = new FlxQuadTree(_midpointX,_topEdge,_halfWidth,_halfHeight,this); + _northEastTree.addObject(); + return; + } + if((_objectTopEdge > _midpointY) && (_objectBottomEdge < _bottomEdge)) + { + if(_southEastTree == null) + _southEastTree = new FlxQuadTree(_midpointX,_midpointY,_halfWidth,_halfHeight,this); + _southEastTree.addObject(); + return; + } + } + + //If it wasn't completely contained we have to check out the partial overlaps + if((_objectRightEdge > _leftEdge) && (_objectLeftEdge < _midpointX) && (_objectBottomEdge > _topEdge) && (_objectTopEdge < _midpointY)) + { + if(_northWestTree == null) + _northWestTree = new FlxQuadTree(_leftEdge,_topEdge,_halfWidth,_halfHeight,this); + _northWestTree.addObject(); + } + if((_objectRightEdge > _midpointX) && (_objectLeftEdge < _rightEdge) && (_objectBottomEdge > _topEdge) && (_objectTopEdge < _midpointY)) + { + if(_northEastTree == null) + _northEastTree = new FlxQuadTree(_midpointX,_topEdge,_halfWidth,_halfHeight,this); + _northEastTree.addObject(); + } + if((_objectRightEdge > _midpointX) && (_objectLeftEdge < _rightEdge) && (_objectBottomEdge > _midpointY) && (_objectTopEdge < _bottomEdge)) + { + if(_southEastTree == null) + _southEastTree = new FlxQuadTree(_midpointX,_midpointY,_halfWidth,_halfHeight,this); + _southEastTree.addObject(); + } + if((_objectRightEdge > _leftEdge) && (_objectLeftEdge < _midpointX) && (_objectBottomEdge > _midpointY) && (_objectTopEdge < _bottomEdge)) + { + if(_southWestTree == null) + _southWestTree = new FlxQuadTree(_leftEdge,_midpointY,_halfWidth,_halfHeight,this); + _southWestTree.addObject(); + } + } + + /** + * Internal function for recursively adding objects to leaf lists. + */ + protected function addToList():void + { + var ot:FlxList; + if(_list == A_LIST) + { + if(_tailA.object != null) + { + ot = _tailA; + _tailA = new FlxList(); + ot.next = _tailA; + } + _tailA.object = _object; + } + else + { + if(_tailB.object != null) + { + ot = _tailB; + _tailB = new FlxList(); + ot.next = _tailB; + } + _tailB.object = _object; + } + if(!_canSubdivide) + return; + if(_northWestTree != null) + _northWestTree.addToList(); + if(_northEastTree != null) + _northEastTree.addToList(); + if(_southEastTree != null) + _southEastTree.addToList(); + if(_southWestTree != null) + _southWestTree.addToList(); + } + + /** + * FlxQuadTree's other main function. Call this after adding objects + * using FlxQuadTree.load() to compare the objects that you loaded. + * + * @return Whether or not any overlaps were found. + */ + public function execute():Boolean + { + var overlapProcessed:Boolean = false; + var iterator:FlxList; + + if(_headA.object != null) + { + iterator = _headA; + while(iterator != null) + { + _object = iterator.object; + if(_useBothLists) + _iterator = _headB; + else + _iterator = iterator.next; + if( _object.exists && (_object.allowCollisions > 0) && + (_iterator != null) && (_iterator.object != null) && + _iterator.object.exists &&overlapNode()) + { + overlapProcessed = true; + } + iterator = iterator.next; + } + } + + //Advance through the tree by calling overlap on each child + if((_northWestTree != null) && _northWestTree.execute()) + overlapProcessed = true; + if((_northEastTree != null) && _northEastTree.execute()) + overlapProcessed = true; + if((_southEastTree != null) && _southEastTree.execute()) + overlapProcessed = true; + if((_southWestTree != null) && _southWestTree.execute()) + overlapProcessed = true; + + return overlapProcessed; + } + + /** + * An internal function for comparing an object against the contents of a node. + * + * @return Whether or not any overlaps were found. + */ + protected function overlapNode():Boolean + { + //Walk the list and check for overlaps + var overlapProcessed:Boolean = false; + var checkObject:FlxObject; + while(_iterator != null) + { + if(!_object.exists || (_object.allowCollisions <= 0)) + break; + + checkObject = _iterator.object; + if((_object === checkObject) || !checkObject.exists || (checkObject.allowCollisions <= 0)) + { + _iterator = _iterator.next; + continue; + } + + //calculate bulk hull for _object + _objectHullX = (_object.x < _object.last.x)?_object.x:_object.last.x; + _objectHullY = (_object.y < _object.last.y)?_object.y:_object.last.y; + _objectHullWidth = _object.x - _object.last.x; + _objectHullWidth = _object.width + ((_objectHullWidth>0)?_objectHullWidth:-_objectHullWidth); + _objectHullHeight = _object.y - _object.last.y; + _objectHullHeight = _object.height + ((_objectHullHeight>0)?_objectHullHeight:-_objectHullHeight); + + //calculate bulk hull for checkObject + _checkObjectHullX = (checkObject.x < checkObject.last.x)?checkObject.x:checkObject.last.x; + _checkObjectHullY = (checkObject.y < checkObject.last.y)?checkObject.y:checkObject.last.y; + _checkObjectHullWidth = checkObject.x - checkObject.last.x; + _checkObjectHullWidth = checkObject.width + ((_checkObjectHullWidth>0)?_checkObjectHullWidth:-_checkObjectHullWidth); + _checkObjectHullHeight = checkObject.y - checkObject.last.y; + _checkObjectHullHeight = checkObject.height + ((_checkObjectHullHeight>0)?_checkObjectHullHeight:-_checkObjectHullHeight); + + //check for intersection of the two hulls + if( (_objectHullX + _objectHullWidth > _checkObjectHullX) && + (_objectHullX < _checkObjectHullX + _checkObjectHullWidth) && + (_objectHullY + _objectHullHeight > _checkObjectHullY) && + (_objectHullY < _checkObjectHullY + _checkObjectHullHeight) ) + { + //Execute callback functions if they exist + if((_processingCallback == null) || _processingCallback(_object,checkObject)) + overlapProcessed = true; + if(overlapProcessed && (_notifyCallback != null)) + _notifyCallback(_object,checkObject); + } + _iterator = _iterator.next; + } + + return overlapProcessed; + } + } +} diff --git a/src/org/flixel/system/FlxReplay.as b/src/org/flixel/system/FlxReplay.as new file mode 100644 index 0000000..2f206bb --- /dev/null +++ b/src/org/flixel/system/FlxReplay.as @@ -0,0 +1,202 @@ +package org.flixel.system +{ + import org.flixel.FlxG; + import org.flixel.system.replay.FrameRecord; + import org.flixel.system.replay.MouseRecord; + + /** + * The replay object both records and replays game recordings, + * as well as handle saving and loading replays to and from files. + * Gameplay recordings are essentially a list of keyboard and mouse inputs, + * but since Flixel is fairly deterministic, we can use these to play back + * recordings of gameplay with a decent amount of fidelity. + * + * @author Adam Atomic + */ + public class FlxReplay + { + /** + * The random number generator seed value for this recording. + */ + public var seed:Number; + /** + * The current frame for this recording. + */ + public var frame:int; + /** + * The number of frames in this recording. + */ + public var frameCount:int; + /** + * Whether the replay has finished playing or not. + */ + public var finished:Boolean; + + /** + * Internal container for all the frames in this replay. + */ + protected var _frames:Array; + /** + * Internal tracker for max number of frames we can fit before growing the _frames again. + */ + protected var _capacity:int; + /** + * Internal helper variable for keeping track of where we are in _frames during recording or replay. + */ + protected var _marker:int; + + /** + * Instantiate a new replay object. Doesn't actually do much until you call create() or load(). + */ + public function FlxReplay() + { + seed = 0; + frame = 0; + frameCount = 0; + finished = false; + _frames = null; + _capacity = 0; + _marker = 0; + } + + /** + * Clean up memory. + */ + public function destroy():void + { + if(_frames == null) + return; + var i:int = frameCount-1; + while(i >= 0) + (_frames[i--] as FrameRecord).destroy(); + _frames = null; + } + + /** + * Create a new gameplay recording. Requires the current random number generator seed. + * + * @param Seed The current seed from the random number generator. + */ + public function create(Seed:Number):void + { + destroy(); + init(); + seed = Seed; + rewind(); + } + + /** + * Load replay data from a String object. + * Strings can come from embedded assets or external + * files loaded through the debugger overlay. + * + * @param FileContents A String object containing a gameplay recording. + */ + public function load(FileContents:String):void + { + init(); + + var lines:Array = FileContents.split("\n"); + + seed = Number(lines[0]); + + var line:String; + var i:uint = 1; + var l:uint = lines.length; + while(i < l) + { + line = lines[i++] as String; + if(line.length > 3) + { + _frames[frameCount++] = new FrameRecord().load(line); + if(frameCount >= _capacity) + { + _capacity *= 2; + _frames.length = _capacity; + } + } + } + + rewind(); + } + + /** + * Common initialization terms used by both create() and load() to set up the replay object. + */ + protected function init():void + { + _capacity = 100; + _frames = new Array(_capacity); + frameCount = 0; + } + + /** + * Save the current recording data off to a String object. + * Basically goes through and calls FrameRecord.save() on each frame in the replay. + * + * return The gameplay recording in simple ASCII format. + */ + public function save():String + { + if(frameCount <= 0) + return null; + var output:String = seed+"\n"; + var i:uint = 0; + while(i < frameCount) + output += _frames[i++].save() + "\n"; + return output; + } + + /** + * Get the current input data from the input managers and store it in a new frame record. + */ + public function recordFrame():void + { + var keysRecord:Array = FlxG.keys.record(); + var mouseRecord:MouseRecord = FlxG.mouse.record(); + if((keysRecord == null) && (mouseRecord == null)) + { + frame++; + return; + } + _frames[frameCount++] = new FrameRecord().create(frame++,keysRecord,mouseRecord); + if(frameCount >= _capacity) + { + _capacity *= 2; + _frames.length = _capacity; + } + } + + /** + * Get the current frame record data and load it into the input managers. + */ + public function playNextFrame():void + { + FlxG.resetInput(); + + if(_marker >= frameCount) + { + finished = true; + return; + } + if((_frames[_marker] as FrameRecord).frame != frame++) + return; + + var fr:FrameRecord = _frames[_marker++]; + if(fr.keys != null) + FlxG.keys.playback(fr.keys); + if(fr.mouse != null) + FlxG.mouse.playback(fr.mouse); + } + + /** + * Reset the replay back to the first frame. + */ + public function rewind():void + { + _marker = 0; + frame = 0; + finished = false; + } + } +} diff --git a/src/org/flixel/system/FlxTile.as b/src/org/flixel/system/FlxTile.as new file mode 100644 index 0000000..d873b1a --- /dev/null +++ b/src/org/flixel/system/FlxTile.as @@ -0,0 +1,81 @@ +package org.flixel.system +{ + import org.flixel.FlxObject; + import org.flixel.FlxTilemap; + + /** + * A simple helper object for FlxTilemap that helps expand collision opportunities and control. + * You can use FlxTilemap.setTileProperties() to alter the collision properties and + * callback functions and filters for this object to do things like one-way tiles or whatever. + * + * @author Adam Atomic + */ + public class FlxTile extends FlxObject + { + /** + * This function is called whenever an object hits a tile of this type. + * This function should take the form myFunction(Tile:FlxTile,Object:FlxObject):void. + * Defaults to null, set through FlxTilemap.setTileProperties(). + */ + public var callback:Function; + /** + * Each tile can store its own filter class for their callback functions. + * That is, the callback will only be triggered if an object with a class + * type matching the filter touched it. + * Defaults to null, set through FlxTilemap.setTileProperties(). + */ + public var filter:Class; + /** + * A reference to the tilemap this tile object belongs to. + */ + public var tilemap:FlxTilemap; + /** + * The index of this tile type in the core map data. + * For example, if your map only has 16 kinds of tiles in it, + * this number is usually between 0 and 15. + */ + public var index:uint; + /** + * The current map index of this tile object at this moment. + * You can think of tile objects as moving around the tilemap helping with collisions. + * This value is only reliable and useful if used from the callback function. + */ + public var mapIndex:uint; + + /** + * Instantiate this new tile object. This is usually called from FlxTilemap.loadMap(). + * + * @param Tilemap A reference to the tilemap object creating the tile. + * @param Index The actual core map data index for this tile type. + * @param Width The width of the tile. + * @param Height The height of the tile. + * @param Visible Whether the tile is visible or not. + * @param AllowCollisions The collision flags for the object. By default this value is ANY or NONE depending on the parameters sent to loadMap(). + */ + public function FlxTile(Tilemap:FlxTilemap, Index:uint, Width:Number, Height:Number, Visible:Boolean, AllowCollisions:uint) + { + super(0, 0, Width, Height); + immovable = true; + moves = false; + callback = null; + filter = null; + + tilemap = Tilemap; + index = Index; + visible = Visible; + allowCollisions = AllowCollisions; + + mapIndex = 0; + } + + /** + * Clean up memory. + */ + override public function destroy():void + { + super.destroy(); + callback = null; + tilemap = null; + } + } +} diff --git a/src/org/flixel/system/FlxTilemapBuffer.as b/src/org/flixel/system/FlxTilemapBuffer.as new file mode 100644 index 0000000..dc5cd43 --- /dev/null +++ b/src/org/flixel/system/FlxTilemapBuffer.as @@ -0,0 +1,119 @@ +package org.flixel.system +{ + import flash.display.BitmapData; + import flash.geom.Point; + import flash.geom.Rectangle; + + import org.flixel.FlxCamera; + import org.flixel.FlxG; + import org.flixel.FlxU; + + /** + * A helper object to keep tilemap drawing performance decent across the new multi-camera system. + * Pretty much don't even have to think about this class unless you are doing some crazy hacking. + * + * @author Adam Atomic + */ + public class FlxTilemapBuffer + { + /** + * The current X position of the buffer. + */ + public var x:Number; + /** + * The current Y position of the buffer. + */ + public var y:Number; + /** + * The width of the buffer (usually just a few tiles wider than the camera). + */ + public var width:Number; + /** + * The height of the buffer (usually just a few tiles taller than the camera). + */ + public var height:Number; + /** + * Whether the buffer needs to be redrawn. + */ + public var dirty:Boolean; + /** + * How many rows of tiles fit in this buffer. + */ + public var rows:uint; + /** + * How many columns of tiles fit in this buffer. + */ + public var columns:uint; + + protected var _pixels:BitmapData; + protected var _flashRect:Rectangle; + + /** + * Instantiates a new camera-specific buffer for storing the visual tilemap data. + * + * @param TileWidth The width of the tiles in this tilemap. + * @param TileHeight The height of the tiles in this tilemap. + * @param WidthInTiles How many tiles wide the tilemap is. + * @param HeightInTiles How many tiles tall the tilemap is. + * @param Camera Which camera this buffer relates to. + */ + public function FlxTilemapBuffer(TileWidth:Number,TileHeight:Number,WidthInTiles:uint,HeightInTiles:uint,Camera:FlxCamera=null) + { + if(Camera == null) + Camera = FlxG.camera; + + columns = FlxU.ceil(Camera.width/TileWidth)+1; + if(columns > WidthInTiles) + columns = WidthInTiles; + rows = FlxU.ceil(Camera.height/TileHeight)+1; + if(rows > HeightInTiles) + rows = HeightInTiles; + + _pixels = new BitmapData(columns*TileWidth,rows*TileHeight,true,0); + width = _pixels.width; + height = _pixels.height; + _flashRect = new Rectangle(0,0,width,height); + dirty = true; + } + + /** + * Clean up memory. + */ + public function destroy():void + { + _pixels = null; + } + + /** + * Fill the buffer with the specified color. + * Default value is transparent. + * + * @param Color What color to fill with, in 0xAARRGGBB hex format. + */ + public function fill(Color:uint=0):void + { + _pixels.fillRect(_flashRect,Color); + } + + /** + * Read-only, nab the actual buffer BitmapData object. + * + * @return The buffer bitmap data. + */ + public function get pixels():BitmapData + { + return _pixels; + } + + /** + * Just stamps this buffer onto the specified camera at the specified location. + * + * @param Camera Which camera to draw the buffer onto. + * @param FlashPoint Where to draw the buffer at in camera coordinates. + */ + public function draw(Camera:FlxCamera,FlashPoint:Point):void + { + Camera.buffer.copyPixels(_pixels,_flashRect,FlashPoint,null,null,true); + } + } +} \ No newline at end of file diff --git a/src/org/flixel/system/FlxWindow.as b/src/org/flixel/system/FlxWindow.as new file mode 100644 index 0000000..a593175 --- /dev/null +++ b/src/org/flixel/system/FlxWindow.as @@ -0,0 +1,328 @@ +package org.flixel.system +{ + import flash.display.Bitmap; + import flash.display.BitmapData; + import flash.display.Sprite; + import flash.events.Event; + import flash.events.MouseEvent; + import flash.geom.Point; + import flash.geom.Rectangle; + import flash.text.TextField; + import flash.text.TextFormat; + + import org.flixel.FlxU; + + /** + * A generic, Flash-based window class, created for use in FlxDebugger. + * + * @author Adam Atomic + */ + public class FlxWindow extends Sprite + { + [Embed(source="../data/handle.png")] protected var ImgHandle:Class; + + /** + * Minimum allowed X and Y dimensions for this window. + */ + public var minSize:Point; + /** + * Maximum allowed X and Y dimensions for this window. + */ + public var maxSize:Point; + + /** + * Width of the window. Using Sprite.width is super unreliable for some reason! + */ + protected var _width:uint; + /** + * Height of the window. Using Sprite.height is super unreliable for some reason! + */ + protected var _height:uint; + /** + * Controls where the window is allowed to be positioned. + */ + protected var _bounds:Rectangle; + + /** + * Window display element. + */ + protected var _background:Bitmap; + /** + * Window display element. + */ + protected var _header:Bitmap; + /** + * Window display element. + */ + protected var _shadow:Bitmap; + /** + * Window display element. + */ + protected var _title:TextField; + /** + * Window display element. + */ + protected var _handle:Bitmap; + + /** + * Helper for interaction. + */ + protected var _overHeader:Boolean; + /** + * Helper for interaction. + */ + protected var _overHandle:Boolean; + /** + * Helper for interaction. + */ + protected var _drag:Point; + /** + * Helper for interaction. + */ + protected var _dragging:Boolean; + /** + * Helper for interaction. + */ + protected var _resizing:Boolean; + /** + * Helper for interaction. + */ + protected var _resizable:Boolean; + + /** + * Creates a new window object. This Flash-based class is mainly (only?) used by FlxDebugger. + * + * @param Title The name of the window, displayed in the header bar. + * @param Width The initial width of the window. + * @param Height The initial height of the window. + * @param Resizable Whether you can change the size of the window with a drag handle. + * @param Bounds A rectangle indicating the valid screen area for the window. + * @param BGColor What color the window background should be, default is gray and transparent. + * @param TopColor What color the window header bar should be, default is black and transparent. + */ + public function FlxWindow(Title:String,Width:Number,Height:Number,Resizable:Boolean=true,Bounds:Rectangle=null,BGColor:uint=0x7f7f7f7f, TopColor:uint=0x7f000000) + { + super(); + _width = Width; + _height = Height; + _bounds = Bounds; + minSize = new Point(50,30); + if(_bounds != null) + maxSize = new Point(_bounds.width,_bounds.height); + else + maxSize = new Point(Number.MAX_VALUE,Number.MAX_VALUE); + _drag = new Point(); + _resizable = Resizable; + + _shadow = new Bitmap(new BitmapData(1,2,true,0xff000000)); + addChild(_shadow); + _background = new Bitmap(new BitmapData(1,1,true,BGColor)); + _background.y = 15; + addChild(_background); + _header = new Bitmap(new BitmapData(1,15,true,TopColor)); + addChild(_header); + + _title = new TextField(); + _title.x = 2; + _title.height = 16; + _title.selectable = false; + _title.multiline = false; + _title.defaultTextFormat = new TextFormat("Courier",12,0xffffff); + _title.text = Title; + addChild(_title); + + if(_resizable) + { + _handle = new ImgHandle(); + addChild(_handle); + } + + if((_width != 0) || (_height != 0)) + updateSize(); + bound(); + + addEventListener(Event.ENTER_FRAME,init); + } + + /** + * Clean up memory. + */ + public function destroy():void + { + minSize = null; + maxSize = null; + _bounds = null; + removeChild(_shadow); + _shadow = null; + removeChild(_background); + _background = null; + removeChild(_header); + _header = null; + removeChild(_title); + _title = null; + if(_handle != null) + removeChild(_handle); + _handle = null; + _drag = null; + } + + /** + * Resize the window. Subject to pre-specified minimums, maximums, and bounding rectangles. + * + * @param Width How wide to make the window. + * @param Height How tall to make the window. + */ + public function resize(Width:Number,Height:Number):void + { + _width = Width; + _height = Height; + updateSize(); + } + + /** + * Change the position of the window. Subject to pre-specified bounding rectangles. + * + * @param X Desired X position of top left corner of the window. + * @param Y Desired Y position of top left corner of the window. + */ + public function reposition(X:Number,Y:Number):void + { + x = X; + y = Y; + bound(); + } + + //***EVENT HANDLERS***// + + /** + * Used to set up basic mouse listeners. + * + * @param E Flash event. + */ + protected function init(E:Event=null):void + { + if(root == null) + return; + removeEventListener(Event.ENTER_FRAME,init); + + stage.addEventListener(MouseEvent.MOUSE_MOVE,onMouseMove); + stage.addEventListener(MouseEvent.MOUSE_DOWN,onMouseDown); + stage.addEventListener(MouseEvent.MOUSE_UP,onMouseUp); + } + + /** + * Mouse movement handler. Figures out if mouse is over handle or header bar or what. + * + * @param E Flash mouse event. + */ + protected function onMouseMove(E:MouseEvent=null):void + { + if(_dragging) //user is moving the window around + { + _overHeader = true; + reposition(parent.mouseX - _drag.x, parent.mouseY - _drag.y); + } + else if(_resizing) + { + _overHandle = true; + resize(mouseX - _drag.x, mouseY - _drag.y); + } + else if((mouseX >= 0) && (mouseX <= _width) && (mouseY >= 0) && (mouseY <= _height)) + { //not dragging, mouse is over the window + _overHeader = (mouseX <= _header.width) && (mouseY <= _header.height); + if(_resizable) + _overHandle = (mouseX >= _width - _handle.width) && (mouseY >= _height - _handle.height); + } + else + { //not dragging, mouse is NOT over window + _overHandle = _overHeader = false; + } + + updateGUI(); + } + + /** + * Figure out if window is being repositioned (clicked on header) or resized (clicked on handle). + * + * @param E Flash mouse event. + */ + protected function onMouseDown(E:MouseEvent=null):void + { + if(_overHeader) + { + _dragging = true; + _drag.x = mouseX; + _drag.y = mouseY; + } + else if(_overHandle) + { + _resizing = true; + _drag.x = _width-mouseX; + _drag.y = _height-mouseY; + } + } + + /** + * User let go of header bar or handler (or nothing), so turn off drag and resize behaviors. + * + * @param E Flash mouse event. + */ + protected function onMouseUp(E:MouseEvent=null):void + { + _dragging = false; + _resizing = false; + } + + //***MISC GUI MGMT STUFF***// + + /** + * Keep the window within the pre-specified bounding rectangle. + */ + protected function bound():void + { + if(_bounds != null) + { + x = FlxU.bound(x,_bounds.left,_bounds.right-_width); + y = FlxU.bound(y,_bounds.top,_bounds.bottom-_height); + } + } + + /** + * Update the Flash shapes to match the new size, and reposition the header, shadow, and handle accordingly. + */ + protected function updateSize():void + { + _width = FlxU.bound(_width,minSize.x,maxSize.x); + _height = FlxU.bound(_height,minSize.y,maxSize.y); + + _header.scaleX = _width; + _background.scaleX = _width; + _background.scaleY = _height-15; + _shadow.scaleX = _width; + _shadow.y = _height; + _title.width = _width-4; + if(_resizable) + { + _handle.x = _width-_handle.width; + _handle.y = _height-_handle.height; + } + } + + /** + * Figure out if the header or handle are highlighted. + */ + protected function updateGUI():void + { + if(_overHeader || _overHandle) + { + if(_title.alpha != 1.0) + _title.alpha = 1.0; + } + else + { + if(_title.alpha != 0.65) + _title.alpha = 0.65; + } + } + } +} diff --git a/src/org/flixel/system/debug/Log.as b/src/org/flixel/system/debug/Log.as new file mode 100644 index 0000000..b3a3c61 --- /dev/null +++ b/src/org/flixel/system/debug/Log.as @@ -0,0 +1,92 @@ +package org.flixel.system.debug +{ + import flash.geom.Rectangle; + import flash.text.TextField; + import flash.text.TextFormat; + import org.flixel.system.FlxWindow; + + /** + * A simple trace output window for use in the debugger overlay. + * + * @author Adam Atomic + */ + public class Log extends FlxWindow + { + static protected const MAX_LOG_LINES:uint = 200; + + protected var _text:TextField; + protected var _lines:Array; + + /** + * Creates a new window object. This Flash-based class is mainly (only?) used by FlxDebugger. + * + * @param Title The name of the window, displayed in the header bar. + * @param Width The initial width of the window. + * @param Height The initial height of the window. + * @param Resizable Whether you can change the size of the window with a drag handle. + * @param Bounds A rectangle indicating the valid screen area for the window. + * @param BGColor What color the window background should be, default is gray and transparent. + * @param TopColor What color the window header bar should be, default is black and transparent. + */ + public function Log(Title:String, Width:Number, Height:Number, Resizable:Boolean=true, Bounds:Rectangle=null, BGColor:uint=0x7f7f7f7f, TopColor:uint=0x7f000000) + { + super(Title, Width, Height, Resizable, Bounds, BGColor, TopColor); + + _text = new TextField(); + _text.x = 2; + _text.y = 15; + _text.multiline = true; + _text.wordWrap = true; + _text.selectable = true; + _text.defaultTextFormat = new TextFormat("Courier",12,0xffffff); + addChild(_text); + + _lines = new Array(); + } + + /** + * Clean up memory. + */ + override public function destroy():void + { + removeChild(_text); + _text = null; + _lines = null; + super.destroy(); + } + + /** + * Adds a new line to the log window. + * + * @param Text The line you want to add to the log window. + */ + public function add(Text:String):void + { + if(_lines.length <= 0) + _text.text = ""; + _lines.push(Text); + if(_lines.length > MAX_LOG_LINES) + { + _lines.shift(); + var newText:String = ""; + for(var i:uint = 0; i < _lines.length; i++) + newText += _lines[i]+"\n"; + _text.text = newText; + } + else + _text.appendText(Text+"\n"); + _text.scrollV = _text.height; + } + + /** + * Adjusts the width and height of the text field accordingly. + */ + override protected function updateSize():void + { + super.updateSize(); + + _text.width = _width-10; + _text.height = _height-15; + } + } +} \ No newline at end of file diff --git a/src/org/flixel/system/debug/Perf.as b/src/org/flixel/system/debug/Perf.as new file mode 100644 index 0000000..0f7f8bc --- /dev/null +++ b/src/org/flixel/system/debug/Perf.as @@ -0,0 +1,210 @@ +package org.flixel.system.debug +{ + import flash.geom.Rectangle; + import flash.system.System; + import flash.text.TextField; + import flash.text.TextFormat; + import flash.utils.getTimer; + + import org.flixel.FlxG; + import org.flixel.system.FlxWindow; + + /** + * A simple performance monitor widget, for use in the debugger overlay. + * + * @author Adam Atomic + */ + public class Perf extends FlxWindow + { + protected var _text:TextField; + + protected var _lastTime:int; + protected var _updateTimer:int; + + protected var _flixelUpdate:Array; + protected var _flixelUpdateMarker:uint; + protected var _flixelDraw:Array; + protected var _flixelDrawMarker:uint; + protected var _flash:Array; + protected var _flashMarker:uint; + protected var _activeObject:Array; + protected var _objectMarker:uint; + protected var _visibleObject:Array; + protected var _visibleObjectMarker:uint; + + /** + * Creates flashPlayerFramerate new window object. This Flash-based class is mainly (only?) used by FlxDebugger. + * + * @param Title The name of the window, displayed in the header bar. + * @param Width The initial width of the window. + * @param Height The initial height of the window. + * @param Resizable Whether you can change the size of the window with flashPlayerFramerate drag handle. + * @param Bounds A rectangle indicating the valid screen area for the window. + * @param BGColor What color the window background should be, default is gray and transparent. + * @param TopColor What color the window header bar should be, default is black and transparent. + */ + public function Perf(Title:String, Width:Number, Height:Number, Resizable:Boolean=true, Bounds:Rectangle=null, BGColor:uint=0x7f7f7f7f, TopColor:uint=0x7f000000) + { + super(Title, Width, Height, Resizable, Bounds, BGColor, TopColor); + resize(90,66); + + _lastTime = 0; + _updateTimer = 0; + + _text = new TextField(); + _text.width = _width; + _text.x = 2; + _text.y = 15; + _text.multiline = true; + _text.wordWrap = true; + _text.selectable = true; + _text.defaultTextFormat = new TextFormat("Courier",12,0xffffff); + addChild(_text); + + _flixelUpdate = new Array(32); + _flixelUpdateMarker = 0; + _flixelDraw = new Array(32); + _flixelDrawMarker = 0; + _flash = new Array(32); + _flashMarker = 0; + _activeObject = new Array(32); + _objectMarker = 0; + _visibleObject = new Array(32); + _visibleObjectMarker = 0; + } + + /** + * Clean up memory. + */ + override public function destroy():void + { + removeChild(_text); + _text = null; + _flixelUpdate = null; + _flixelDraw = null; + _flash = null; + _activeObject = null; + _visibleObject = null; + super.destroy(); + } + + /** + * Called each frame, but really only updates once every second or so, to save on performance. + * Takes all the data in the accumulators and parses it into useful performance data. + */ + public function update():void + { + var time:int = getTimer(); + var elapsed:int = time - _lastTime; + var updateEvery:uint = 500; + if(elapsed > updateEvery) + elapsed = updateEvery; + _lastTime = time; + + _updateTimer += elapsed; + if(_updateTimer > updateEvery) + { + var i:uint; + var output:String = ""; + + var flashPlayerFramerate:Number = 0; + i = 0; + while (i < _flashMarker) + flashPlayerFramerate += _flash[i++]; + flashPlayerFramerate /= _flashMarker; + output += uint(1/(flashPlayerFramerate/1000)) + "/" + FlxG.flashFramerate + "fps\n"; + + output += Number( ( System.totalMemory * 0.000000954 ).toFixed(2) ) + "MB\n"; + + var updateTime:uint = 0; + i = 0; + while(i < _flixelUpdateMarker) + updateTime += _flixelUpdate[i++]; + + var activeCount:uint = 0; + var te:uint = 0; + i = 0; + while(i < _objectMarker) + { + activeCount += _activeObject[i]; + visibleCount += _visibleObject[i++]; + } + activeCount /= _objectMarker; + + output += "U:" + activeCount + " " + uint(updateTime/_flixelDrawMarker) + "ms\n"; + + var drawTime:uint = 0; + i = 0; + while(i < _flixelDrawMarker) + drawTime += _flixelDraw[i++]; + + var visibleCount:uint = 0; + i = 0; + while(i < _visibleObjectMarker) + visibleCount += _visibleObject[i++]; + visibleCount /= _visibleObjectMarker; + + output += "D:" + visibleCount + " " + uint(drawTime/_flixelDrawMarker) + "ms"; + + _text.text = output; + + _flixelUpdateMarker = 0; + _flixelDrawMarker = 0; + _flashMarker = 0; + _objectMarker = 0; + _visibleObjectMarker = 0; + _updateTimer -= updateEvery; + } + } + + /** + * Keep track of how long updates take. + * + * @param Time How long this update took. + */ + public function flixelUpdate(Time:int):void + { + _flixelUpdate[_flixelUpdateMarker++] = Time; + } + + /** + * Keep track of how long renders take. + * + * @param Time How long this render took. + */ + public function flixelDraw(Time:int):void + { + _flixelDraw[_flixelDrawMarker++] = Time; + } + + /** + * Keep track of how long the Flash player and browser take. + * + * @param Time How long Flash/browser took. + */ + public function flash(Time:int):void + { + _flash[_flashMarker++] = Time; + } + + /** + * Keep track of how many objects were updated. + * + * @param Count How many objects were updated. + */ + public function activeObjects(Count:int):void + { + _activeObject[_objectMarker++] = Count; + } + + /** + * Keep track of how many objects were updated. + * + * @param Count How many objects were updated. + */ + public function visibleObjects(Count:int):void + { + _visibleObject[_visibleObjectMarker++] = Count; + } + } +} diff --git a/src/org/flixel/system/debug/VCR.as b/src/org/flixel/system/debug/VCR.as new file mode 100644 index 0000000..948675e --- /dev/null +++ b/src/org/flixel/system/debug/VCR.as @@ -0,0 +1,600 @@ +package org.flixel.system.debug +{ + import flash.display.Bitmap; + import flash.display.BitmapData; + import flash.display.Sprite; + import flash.events.Event; + import flash.events.IOErrorEvent; + import flash.events.MouseEvent; + import flash.net.FileFilter; + import flash.net.FileReference; + import flash.text.TextField; + import flash.text.TextFormat; + import flash.utils.ByteArray; + + import org.flixel.FlxG; + import org.flixel.FlxU; + import org.flixel.system.FlxReplay; + import org.flixel.system.replay.FrameRecord; + import org.flixel.system.replay.MouseRecord; + + /** + * This class contains the record, stop, play, and step 1 frame buttons seen on the top edge of the debugger overlay. + * + * @author Adam Atomic + */ + public class VCR extends Sprite + { + [Embed(source="../../data/vcr/open.png")] protected var ImgOpen:Class; + [Embed(source="../../data/vcr/record_off.png")] protected var ImgRecordOff:Class; + [Embed(source="../../data/vcr/record_on.png")] protected var ImgRecordOn:Class; + [Embed(source="../../data/vcr/stop.png")] protected var ImgStop:Class; + [Embed(source="../../data/vcr/flixel.png")] protected var ImgFlixel:Class; + [Embed(source="../../data/vcr/restart.png")] protected var ImgRestart:Class; + [Embed(source="../../data/vcr/pause.png")] protected var ImgPause:Class; + [Embed(source="../../data/vcr/play.png")] protected var ImgPlay:Class; + [Embed(source="../../data/vcr/step.png")] protected var ImgStep:Class; + + static protected const FILE_TYPES:Array = [new FileFilter("Flixel Game Recording", "*.fgr")]; + static protected const DEFAULT_FILE_NAME:String = "replay.fgr"; + + /** + * Whether the debugger has been paused. + */ + public var paused:Boolean; + /** + * Whether a "1 frame step forward" was requested. + */ + public var stepRequested:Boolean; + + protected var _open:Bitmap; + protected var _recordOff:Bitmap; + protected var _recordOn:Bitmap; + protected var _stop:Bitmap; + protected var _flixel:Bitmap; + protected var _restart:Bitmap; + protected var _pause:Bitmap; + protected var _play:Bitmap; + protected var _step:Bitmap; + + protected var _overOpen:Boolean; + protected var _overRecord:Boolean; + protected var _overRestart:Boolean; + protected var _overPause:Boolean; + protected var _overStep:Boolean; + + protected var _pressingOpen:Boolean; + protected var _pressingRecord:Boolean; + protected var _pressingRestart:Boolean; + protected var _pressingPause:Boolean; + protected var _pressingStep:Boolean; + + protected var _file:FileReference; + + protected var _runtimeDisplay:TextField; + protected var _runtime:uint; + + /** + * Creates the "VCR" control panel for debugger pausing, stepping, and recording. + */ + public function VCR() + { + super(); + + var spacing:uint = 7; + + _open = new ImgOpen(); + addChild(_open); + + _recordOff = new ImgRecordOff(); + _recordOff.x = _open.x + _open.width + spacing; + addChild(_recordOff); + + _recordOn = new ImgRecordOn(); + _recordOn.x = _recordOff.x; + _recordOn.visible = false; + addChild(_recordOn); + + _stop = new ImgStop(); + _stop.x = _recordOff.x; + _stop.visible = false; + addChild(_stop); + + _flixel = new ImgFlixel(); + _flixel.x = _recordOff.x + _recordOff.width + spacing; + addChild(_flixel); + + _restart = new ImgRestart(); + _restart.x = _flixel.x + _flixel.width + spacing; + addChild(_restart); + + _pause = new ImgPause(); + _pause.x = _restart.x + _restart.width + spacing; + addChild(_pause); + + _play = new ImgPlay(); + _play.x = _pause.x; + _play.visible = false; + addChild(_play); + + _step = new ImgStep(); + _step.x = _pause.x + _pause.width + spacing; + addChild(_step); + + _runtimeDisplay = new TextField(); + _runtimeDisplay.width = width; + _runtimeDisplay.x = width; + _runtimeDisplay.y = -2; + _runtimeDisplay.multiline = false; + _runtimeDisplay.wordWrap = false; + _runtimeDisplay.selectable = false; + _runtimeDisplay.defaultTextFormat = new TextFormat("Courier",12,0xffffff,null,null,null,null,null,"center"); + _runtimeDisplay.visible = false; + addChild(_runtimeDisplay); + _runtime = 0; + + stepRequested = false; + _file = null; + + unpress(); + checkOver(); + updateGUI(); + + addEventListener(Event.ENTER_FRAME,init); + } + + /** + * Clean up memory. + */ + public function destroy():void + { + _file = null; + + removeChild(_open); + _open = null; + removeChild(_recordOff); + _recordOff = null; + removeChild(_recordOn); + _recordOn = null; + removeChild(_stop); + _stop = null; + removeChild(_flixel); + _flixel = null; + removeChild(_restart); + _restart = null; + removeChild(_pause); + _pause = null; + removeChild(_play); + _play = null; + removeChild(_step); + _step = null; + + parent.removeEventListener(MouseEvent.MOUSE_MOVE,onMouseMove); + parent.removeEventListener(MouseEvent.MOUSE_DOWN,onMouseDown); + parent.removeEventListener(MouseEvent.MOUSE_UP,onMouseUp); + } + + /** + * Usually called by FlxGame when a requested recording has begun. + * Just updates the VCR GUI so the buttons are in the right state. + */ + public function recording():void + { + _stop.visible = false; + _recordOff.visible = false; + _recordOn.visible = true; + } + + /** + * Usually called by FlxGame when a replay has been stopped. + * Just updates the VCR GUI so the buttons are in the right state. + */ + public function stopped():void + { + _stop.visible = false; + _recordOn.visible = false; + _recordOff.visible = true; + } + + /** + * Usually called by FlxGame when a requested replay has begun. + * Just updates the VCR GUI so the buttons are in the right state. + */ + public function playing():void + { + _recordOff.visible = false; + _recordOn.visible = false; + _stop.visible = true; + } + + /** + * Just updates the VCR GUI so the runtime displays roughly the right thing. + */ + public function updateRuntime(Time:uint):void + { + _runtime += Time; + _runtimeDisplay.text = FlxU.formatTime(_runtime/1000,true); + if(!_runtimeDisplay.visible) + _runtimeDisplay.visible = true; + } + + //*** ACTUAL BUTTON BEHAVIORS ***// + + /** + * Called when the "open file" button is pressed. + * Opens the file dialog and registers event handlers for the file dialog. + */ + public function onOpen():void + { + _file = new FileReference(); + _file.addEventListener(Event.SELECT, onOpenSelect); + _file.addEventListener(Event.CANCEL, onOpenCancel); + _file.browse(FILE_TYPES); + } + + /** + * Called when a file is picked from the file dialog. + * Attempts to load the file and registers file loading event handlers. + * + * @param E Flash event. + */ + protected function onOpenSelect(E:Event=null):void + { + _file.removeEventListener(Event.SELECT, onOpenSelect); + _file.removeEventListener(Event.CANCEL, onOpenCancel); + + _file.addEventListener(Event.COMPLETE, onOpenComplete); + _file.addEventListener(IOErrorEvent.IO_ERROR, onOpenError); + _file.load(); + } + + /** + * Called when a file is opened successfully. + * If there's stuff inside, then the contents are loaded into a new replay. + * + * @param E Flash Event. + */ + protected function onOpenComplete(E:Event=null):void + { + _file.removeEventListener(Event.COMPLETE, onOpenComplete); + _file.removeEventListener(IOErrorEvent.IO_ERROR, onOpenError); + + //Turn the file into a giant string + var fileContents:String = null; + var data:ByteArray = _file.data; + if(data != null) + fileContents = data.readUTFBytes(data.bytesAvailable); + _file = null; + if((fileContents == null) || (fileContents.length <= 0)) + { + FlxG.log("ERROR: Empty flixel gameplay record."); + return; + } + + FlxG.loadReplay(fileContents); + } + + /** + * Called if the open file dialog is canceled. + * + * @param E Flash Event. + */ + protected function onOpenCancel(E:Event=null):void + { + _file.removeEventListener(Event.SELECT, onOpenSelect); + _file.removeEventListener(Event.CANCEL, onOpenCancel); + _file = null; + } + + /** + * Called if there is a file open error. + * + * @param E Flash Event. + */ + protected function onOpenError(E:Event=null):void + { + _file.removeEventListener(Event.COMPLETE, onOpenComplete); + _file.removeEventListener(IOErrorEvent.IO_ERROR, onOpenError); + _file = null; + FlxG.log("ERROR: Unable to open flixel gameplay record."); + } + + /** + * Called when the user presses the white record button. + * If Alt is pressed, the current state is reset, and a new recording is requested. + * If Alt is NOT pressed, the game is reset, and a new recording is requested. + * + * @param StandardMode Whether to reset the whole game, or just this FlxState. StandardMode == false is useful for recording demos or attract modes. + */ + public function onRecord(StandardMode:Boolean=false):void + { + if(_play.visible) + onPlay(); + FlxG.recordReplay(StandardMode); + } + + /** + * Called when the user presses the red record button. + * Stops the current recording, opens the save file dialog, and registers event handlers. + */ + public function stopRecording():void + { + var data:String = FlxG.stopRecording(); + if((data != null) && (data.length > 0)) + { + _file = new FileReference(); + _file.addEventListener(Event.COMPLETE, onSaveComplete); + _file.addEventListener(Event.CANCEL,onSaveCancel); + _file.addEventListener(IOErrorEvent.IO_ERROR, onSaveError); + _file.save(data, DEFAULT_FILE_NAME); + } + } + + /** + * Called when the file is saved successfully. + * + * @param E Flash Event. + */ + protected function onSaveComplete(E:Event=null):void + { + _file.removeEventListener(Event.COMPLETE, onSaveComplete); + _file.removeEventListener(Event.CANCEL,onSaveCancel); + _file.removeEventListener(IOErrorEvent.IO_ERROR, onSaveError); + _file = null; + FlxG.log("FLIXEL: successfully saved flixel gameplay record."); + } + + /** + * Called when the save file dialog is cancelled. + * + * @param E Flash Event. + */ + protected function onSaveCancel(E:Event=null):void + { + _file.removeEventListener(Event.COMPLETE, onSaveComplete); + _file.removeEventListener(Event.CANCEL,onSaveCancel); + _file.removeEventListener(IOErrorEvent.IO_ERROR, onSaveError); + _file = null; + } + + /** + * Called if there is an error while saving the gameplay recording. + * + * @param E Flash Event. + */ + protected function onSaveError(E:Event=null):void + { + _file.removeEventListener(Event.COMPLETE, onSaveComplete); + _file.removeEventListener(Event.CANCEL,onSaveCancel); + _file.removeEventListener(IOErrorEvent.IO_ERROR, onSaveError); + _file = null; + FlxG.log("ERROR: problem saving flixel gameplay record."); + } + + /** + * Called when the user presses the stop button. + * Stops the current replay. + */ + public function onStop():void + { + FlxG.stopReplay(); + } + + /** + * Called when the user presses the Rewind-looking button. + * If Alt is pressed, the entire game is reset. + * If Alt is NOT pressed, only the current state is reset. + * The GUI is updated accordingly. + * + * @param StandardMode Whether to reset the current game (== true), or just the current state. Just resetting the current state can be very handy for debugging. + */ + public function onRestart(StandardMode:Boolean=false):void + { + if(FlxG.reloadReplay(StandardMode)) + { + _recordOff.visible = false; + _recordOn.visible = false; + _stop.visible = true; + } + } + + /** + * Called when the user presses the Pause button. + * This is different from user-defined pause behavior, or focus lost behavior. + * Does NOT pause music playback!! + */ + public function onPause():void + { + paused = true; + _pause.visible = false; + _play.visible = true; + } + + /** + * Called when the user presses the Play button. + * This is different from user-defined unpause behavior, or focus gained behavior. + */ + public function onPlay():void + { + paused = false; + _play.visible = false; + _pause.visible = true; + } + + /** + * Called when the user presses the fast-forward-looking button. + * Requests a 1-frame step forward in the game loop. + */ + public function onStep():void + { + if(!paused) + onPause(); + stepRequested = true; + } + + //***EVENT HANDLERS***// + + /** + * Just sets up basic mouse listeners, a la FlxWindow. + * + * @param E Flash event. + */ + protected function init(E:Event=null):void + { + if(root == null) + return; + removeEventListener(Event.ENTER_FRAME,init); + + parent.addEventListener(MouseEvent.MOUSE_MOVE,onMouseMove); + parent.addEventListener(MouseEvent.MOUSE_DOWN,onMouseDown); + parent.addEventListener(MouseEvent.MOUSE_UP,onMouseUp); + } + + /** + * If the mouse moves, check to see if any buttons should be highlighted. + * + * @param E Flash mouse event. + */ + protected function onMouseMove(E:MouseEvent=null):void + { + if(!checkOver()) + unpress(); + updateGUI(); + } + + /** + * If the mouse is pressed down, check to see if the user started pressing down a specific button. + * + * @param E Flash mouse event. + */ + protected function onMouseDown(E:MouseEvent=null):void + { + unpress(); + if(_overOpen) + _pressingOpen = true; + if(_overRecord) + _pressingRecord = true; + if(_overRestart) + _pressingRestart = true; + if(_overPause) + _pressingPause = true; + if(_overStep) + _pressingStep = true; + } + + /** + * If the mouse is released, check to see if it was released over a button that was pressed. + * If it was, take the appropriate action based on button state and visibility. + * + * @param E Flash mouse event. + */ + protected function onMouseUp(E:MouseEvent=null):void + { + if(_overOpen && _pressingOpen) + onOpen(); + else if(_overRecord && _pressingRecord) + { + if(_stop.visible) + onStop(); + else if(_recordOn.visible) + stopRecording(); + else + onRecord(!E.altKey); + } + else if(_overRestart && _pressingRestart) + onRestart(!E.altKey); + else if(_overPause && _pressingPause) + { + if(_play.visible) + onPlay(); + else + onPause(); + } + else if(_overStep && _pressingStep) + onStep(); + + unpress(); + checkOver(); + updateGUI(); + } + + //***MISC GUI MGMT STUFF***// + + /** + * This function checks to see what button the mouse is currently over. + * Has some special behavior based on whether a recording is happening or not. + * + * @return Whether the mouse was over any buttons or not. + */ + protected function checkOver():Boolean + { + _overOpen = _overRecord = _overRestart = _overPause = _overStep = false; + if((mouseX < 0) || (mouseX > width) || (mouseY < 0) || (mouseY > 15)) + return false; + if((mouseX >= _recordOff.x) && (mouseX <= _recordOff.x + _recordOff.width)) + _overRecord = true; + if(!_recordOn.visible && !_overRecord) + { + if((mouseX >= _open.x) && (mouseX <= _open.x + _open.width)) + _overOpen = true; + else if((mouseX >= _restart.x) && (mouseX <= _restart.x + _restart.width)) + _overRestart = true; + else if((mouseX >= _pause.x) && (mouseX <= _pause.x + _pause.width)) + _overPause = true; + else if((mouseX >= _step.x) && (mouseX <= _step.x + _step.width)) + _overStep = true; + } + return true; + } + + /** + * Sets all the pressed state variables for the buttons to false. + */ + protected function unpress():void + { + _pressingOpen = false; + _pressingRecord = false; + _pressingRestart = false; + _pressingPause = false; + _pressingStep = false; + } + + /** + * Figures out what buttons to highlight based on the _overWhatever and _pressingWhatever variables. + */ + protected function updateGUI():void + { + if(_recordOn.visible) + { + _open.alpha = _restart.alpha = _pause.alpha = _step.alpha = 0.35; + _recordOn.alpha = 1.0; + return; + } + + if(_overOpen && (_open.alpha != 1.0)) + _open.alpha = 1.0; + else if(!_overOpen && (_open.alpha != 0.8)) + _open.alpha = 0.8; + + if(_overRecord && (_recordOff.alpha != 1.0)) + _recordOff.alpha = _recordOn.alpha = _stop.alpha = 1.0; + else if(!_overRecord && (_recordOff.alpha != 0.8)) + _recordOff.alpha = _recordOn.alpha = _stop.alpha = 0.8; + + if(_overRestart && (_restart.alpha != 1.0)) + _restart.alpha = 1.0; + else if(!_overRestart && (_restart.alpha != 0.8)) + _restart.alpha = 0.8; + + if(_overPause && (_pause.alpha != 1.0)) + _pause.alpha = _play.alpha = 1.0; + else if(!_overPause && (_pause.alpha != 0.8)) + _pause.alpha = _play.alpha = 0.8; + + if(_overStep && (_step.alpha != 1.0)) + _step.alpha = 1.0; + else if(!_overStep && (_step.alpha != 0.8)) + _step.alpha = 0.8; + } + } +} \ No newline at end of file diff --git a/src/org/flixel/system/debug/Vis.as b/src/org/flixel/system/debug/Vis.as new file mode 100644 index 0000000..608d5ca --- /dev/null +++ b/src/org/flixel/system/debug/Vis.as @@ -0,0 +1,171 @@ +package org.flixel.system.debug +{ + import flash.display.Bitmap; + import flash.display.Sprite; + import flash.events.Event; + import flash.events.MouseEvent; + + import org.flixel.FlxG; + + /** + * This control panel has all the visual debugger toggles in it, in the debugger overlay. + * Currently there is only one toggle that flips on all the visual debug settings. + * This panel is heavily based on the VCR panel. + * + * @author Adam Atomic + */ + public class Vis extends Sprite + { + [Embed(source="../../data/vis/bounds.png")] protected var ImgBounds:Class; + + protected var _bounds:Bitmap; + protected var _overBounds:Boolean; + protected var _pressingBounds:Boolean; + + /** + * Instantiate the visual debugger panel. + */ + public function Vis() + { + super(); + + var spacing:uint = 7; + + _bounds = new ImgBounds(); + addChild(_bounds); + + unpress(); + checkOver(); + updateGUI(); + + addEventListener(Event.ENTER_FRAME,init); + } + + /** + * Clean up memory. + */ + public function destroy():void + { + removeChild(_bounds); + _bounds = null; + + parent.removeEventListener(MouseEvent.MOUSE_MOVE,onMouseMove); + parent.removeEventListener(MouseEvent.MOUSE_DOWN,onMouseDown); + parent.removeEventListener(MouseEvent.MOUSE_UP,onMouseUp); + } + + //***ACTUAL BUTTON BEHAVIORS***// + + /** + * Called when the bounding box toggle is pressed. + */ + public function onBounds():void + { + FlxG.visualDebug = !FlxG.visualDebug; + } + + //***EVENT HANDLERS***// + + /** + * Just sets up basic mouse listeners, a la FlxWindow. + * + * @param E Flash event. + */ + protected function init(E:Event=null):void + { + if(root == null) + return; + removeEventListener(Event.ENTER_FRAME,init); + + parent.addEventListener(MouseEvent.MOUSE_MOVE,onMouseMove); + parent.addEventListener(MouseEvent.MOUSE_DOWN,onMouseDown); + parent.addEventListener(MouseEvent.MOUSE_UP,onMouseUp); + } + + /** + * If the mouse moves, check to see if any buttons should be highlighted. + * + * @param E Flash mouse event. + */ + protected function onMouseMove(E:MouseEvent=null):void + { + if(!checkOver()) + unpress(); + updateGUI(); + } + + /** + * If the mouse is pressed down, check to see if the user started pressing down a specific button. + * + * @param E Flash mouse event. + */ + protected function onMouseDown(E:MouseEvent=null):void + { + unpress(); + if(_overBounds) + _pressingBounds = true; + } + + /** + * If the mouse is released, check to see if it was released over a button that was pressed. + * If it was, take the appropriate action based on button state and visibility. + * + * @param E Flash mouse event. + */ + protected function onMouseUp(E:MouseEvent=null):void + { + if(_overBounds && _pressingBounds) + onBounds(); + unpress(); + checkOver(); + updateGUI(); + } + + //***MISC GUI MGMT STUFF***// + + /** + * This function checks to see what button the mouse is currently over. + * Has some special behavior based on whether a recording is happening or not. + * + * @return Whether the mouse was over any buttons or not. + */ + protected function checkOver():Boolean + { + _overBounds = false; + if((mouseX < 0) || (mouseX > width) || (mouseY < 0) || (mouseY > height)) + return false; + if((mouseX > _bounds.x) || (mouseX < _bounds.x + _bounds.width)) + _overBounds = true; + return true; + } + + /** + * Sets all the pressed state variables for the buttons to false. + */ + protected function unpress():void + { + _pressingBounds = false; + } + + /** + * Figures out what buttons to highlight based on the _overWhatever and _pressingWhatever variables. + */ + protected function updateGUI():void + { + if(FlxG.visualDebug) + { + if(_overBounds && (_bounds.alpha != 1.0)) + _bounds.alpha = 1.0; + else if(!_overBounds && (_bounds.alpha != 0.9)) + _bounds.alpha = 0.9; + } + else + { + if(_overBounds && (_bounds.alpha != 0.6)) + _bounds.alpha = 0.6; + else if(!_overBounds && (_bounds.alpha != 0.5)) + _bounds.alpha = 0.5; + } + } + } +} \ No newline at end of file diff --git a/src/org/flixel/system/debug/Watch.as b/src/org/flixel/system/debug/Watch.as new file mode 100644 index 0000000..f342629 --- /dev/null +++ b/src/org/flixel/system/debug/Watch.as @@ -0,0 +1,214 @@ +package org.flixel.system.debug +{ + import flash.display.Sprite; + import flash.geom.Rectangle; + import flash.text.TextField; + import flash.text.TextFormat; + + import org.flixel.FlxU; + import org.flixel.system.FlxWindow; + + /** + * A Visual Studio-style "watch" window, for use in the debugger overlay. + * Track the values of any public variable in real-time, and/or edit their values on the fly. + * + * @author Adam Atomic + */ + public class Watch extends FlxWindow + { + static protected const MAX_LOG_LINES:uint = 1024; + static protected const LINE_HEIGHT:uint = 15; + + /** + * Whether a watch entry is currently being edited or not. + */ + public var editing:Boolean; + + protected var _names:Sprite; + protected var _values:Sprite; + protected var _watching:Array; + + /** + * Creates a new window object. This Flash-based class is mainly (only?) used by FlxDebugger. + * + * @param Title The name of the window, displayed in the header bar. + * @param Width The initial width of the window. + * @param Height The initial height of the window. + * @param Resizable Whether you can change the size of the window with a drag handle. + * @param Bounds A rectangle indicating the valid screen area for the window. + * @param BGColor What color the window background should be, default is gray and transparent. + * @param TopColor What color the window header bar should be, default is black and transparent. + */ + public function Watch(Title:String, Width:Number, Height:Number, Resizable:Boolean=true, Bounds:Rectangle=null, BGColor:uint=0x7f7f7f7f, TopColor:uint=0x7f000000) + { + super(Title, Width, Height, Resizable, Bounds, BGColor, TopColor); + + _names = new Sprite(); + _names.x = 2; + _names.y = 15; + addChild(_names); + + _values = new Sprite(); + _values.x = 2; + _values.y = 15; + addChild(_values); + + _watching = new Array(); + + editing = false; + + removeAll(); + } + + /** + * Clean up memory. + */ + override public function destroy():void + { + removeChild(_names); + _names = null; + removeChild(_values); + _values = null; + var i:int = 0; + var l:uint = _watching.length; + while(i < l) + (_watching[i++] as WatchEntry).destroy(); + _watching = null; + super.destroy(); + } + + /** + * Add a new variable to the watch window. + * Has some simple code in place to prevent + * accidentally watching the same variable twice. + * + * @param AnyObject The Object containing the variable you want to track, e.g. this or Player.velocity. + * @param VariableName The String name of the variable you want to track, e.g. "width" or "x". + * @param DisplayName Optional String that can be displayed in the watch window instead of the basic class-name information. + */ + public function add(AnyObject:Object,VariableName:String,DisplayName:String=null):void + { + //Don't add repeats + var watchEntry:WatchEntry; + var i:int = 0; + var l:uint = _watching.length; + while(i < l) + { + watchEntry = _watching[i++] as WatchEntry; + if((watchEntry.object == AnyObject) && (watchEntry.field == VariableName)) + return; + } + + //Good, no repeats, add away! + watchEntry = new WatchEntry(_watching.length*LINE_HEIGHT,_width/2,_width/2-10,AnyObject,VariableName,DisplayName); + _names.addChild(watchEntry.nameDisplay); + _values.addChild(watchEntry.valueDisplay); + _watching.push(watchEntry); + } + + /** + * Remove a variable from the watch window. + * + * @param AnyObject The Object containing the variable you want to remove, e.g. this or Player.velocity. + * @param VariableName The String name of the variable you want to remove, e.g. "width" or "x". If left null, this will remove all variables of that object. + */ + public function remove(AnyObject:Object,VariableName:String=null):void + { + //splice out the requested object + var watchEntry:WatchEntry; + var i:int = _watching.length-1; + while(i >= 0) + { + watchEntry = _watching[i]; + if((watchEntry.object == AnyObject) && ((VariableName == null) || (watchEntry.field == VariableName))) + { + _watching.splice(i,1); + _names.removeChild(watchEntry.nameDisplay); + _values.removeChild(watchEntry.valueDisplay); + watchEntry.destroy(); + } + i--; + } + watchEntry = null; + + //reset the display heights of the remaining objects + i = 0; + var l:uint = _watching.length; + while(i < l) + { + (_watching[i] as WatchEntry).setY(i*LINE_HEIGHT); + i++; + } + } + + /** + * Remove everything from the watch window. + */ + public function removeAll():void + { + var watchEntry:WatchEntry; + var i:int = 0; + var l:uint = _watching.length; + while(i < l) + { + watchEntry = _watching.pop(); + _names.removeChild(watchEntry.nameDisplay); + _values.removeChild(watchEntry.valueDisplay); + watchEntry.destroy(); + i++ + } + _watching.length = 0; + } + + /** + * Update all the entries in the watch window. + */ + public function update():void + { + editing = false; + var i:uint = 0; + var l:uint = _watching.length; + while(i < l) + { + if(!(_watching[i++] as WatchEntry).updateValue()) + editing = true; + } + } + + /** + * Force any watch entries currently being edited to submit their changes. + */ + public function submit():void + { + var i:uint = 0; + var l:uint = _watching.length; + var watchEntry:WatchEntry; + while(i < l) + { + watchEntry = _watching[i++] as WatchEntry; + if(watchEntry.editing) + watchEntry.submit(); + } + editing = false; + } + + /** + * Update the Flash shapes to match the new size, and reposition the header, shadow, and handle accordingly. + * Also adjusts the width of the entries and stuff, and makes sure there is room for all the entries. + */ + override protected function updateSize():void + { + if(_height < _watching.length*LINE_HEIGHT + 17) + _height = _watching.length*LINE_HEIGHT + 17; + + super.updateSize(); + + _values.x = _width/2 + 2; + + var i:int = 0; + var l:uint = _watching.length; + while(i < l) + (_watching[i++] as WatchEntry).updateWidth(_width/2,_width/2-10); + } + } +} diff --git a/src/org/flixel/system/debug/WatchEntry.as b/src/org/flixel/system/debug/WatchEntry.as new file mode 100644 index 0000000..a3fdc5a --- /dev/null +++ b/src/org/flixel/system/debug/WatchEntry.as @@ -0,0 +1,208 @@ +package org.flixel.system.debug +{ + import flash.events.KeyboardEvent; + import flash.events.MouseEvent; + import flash.text.TextField; + import flash.text.TextFieldType; + import flash.text.TextFormat; + + import org.flixel.FlxU; + + /** + * Helper class for the debugger overlay's Watch window. + * Handles the display and modification of game variables on the fly. + * + * @author Adam Atomic + */ + public class WatchEntry + { + /** + * The Object being watched. + */ + public var object:Object; + /** + * The member variable of that object. + */ + public var field:String; + /** + * A custom display name for this object, if there is any. + */ + public var custom:String; + /** + * The Flash TextField object used to display this entry's name. + */ + public var nameDisplay:TextField; + /** + * The Flash TextField object used to display and edit this entry's value. + */ + public var valueDisplay:TextField; + /** + * Whether the entry is currently being edited or not. + */ + public var editing:Boolean; + /** + * The value of the field before it was edited. + */ + public var oldValue:Object; + + protected var _whiteText:TextFormat; + protected var _blackText:TextFormat; + + /** + * Creates a new watch entry in the watch window. + * + * @param Y The initial height in the Watch window. + * @param NameWidth The initial width of the name field. + * @param ValueWidth The initial width of the value field. + * @param Obj The Object containing the variable we want to watch. + * @param Field The variable name we want to watch. + * @param Custom A custom display name (optional). + */ + public function WatchEntry(Y:Number,NameWidth:Number,ValueWidth:Number,Obj:Object,Field:String,Custom:String=null) + { + editing = false; + + object = Obj; + field = Field; + custom = Custom; + + _whiteText = new TextFormat("Courier",12,0xffffff); + _blackText = new TextFormat("Courier",12,0); + + nameDisplay = new TextField(); + nameDisplay.y = Y; + nameDisplay.multiline = false; + nameDisplay.selectable = true; + nameDisplay.defaultTextFormat = _whiteText; + + valueDisplay = new TextField(); + valueDisplay.y = Y; + valueDisplay.height = 15; + valueDisplay.multiline = false; + valueDisplay.selectable = true; + valueDisplay.doubleClickEnabled = true; + valueDisplay.addEventListener(KeyboardEvent.KEY_UP,onKeyUp); + valueDisplay.addEventListener(MouseEvent.MOUSE_UP,onMouseUp); + valueDisplay.background = false; + valueDisplay.backgroundColor = 0xffffff; + valueDisplay.defaultTextFormat = _whiteText; + + updateWidth(NameWidth,ValueWidth); + } + + /** + * Clean up memory. + */ + public function destroy():void + { + object = null; + oldValue = null; + nameDisplay = null; + field = null; + custom = null; + valueDisplay.removeEventListener(MouseEvent.MOUSE_UP,onMouseUp); + valueDisplay.removeEventListener(KeyboardEvent.KEY_UP,onKeyUp); + valueDisplay = null; + } + + /** + * Set the watch window Y height of the Flash TextField objects. + */ + public function setY(Y:Number):void + { + nameDisplay.y = Y; + valueDisplay.y = Y; + } + + /** + * Adjust the width of the Flash TextField objects. + */ + public function updateWidth(NameWidth:Number,ValueWidth:Number):void + { + nameDisplay.width = NameWidth; + valueDisplay.width = ValueWidth; + if(custom != null) + nameDisplay.text = custom; + else + { + nameDisplay.text = ""; + if(NameWidth > 120) + nameDisplay.appendText(FlxU.getClassName(object,(NameWidth < 240)) + "."); + nameDisplay.appendText(field); + } + } + + /** + * Update the variable value on display with the current in-game value. + */ + public function updateValue():Boolean + { + if(editing) + return false; + valueDisplay.text = object[field].toString(); + return true; + } + + /** + * A watch entry was clicked, so flip into edit mode for that entry. + * + * @param FlashEvent Flash mouse event. + */ + public function onMouseUp(FlashEvent:MouseEvent):void + { + editing = true; + oldValue = object[field]; + valueDisplay.type = TextFieldType.INPUT; + valueDisplay.setTextFormat(_blackText); + valueDisplay.background = true; + + } + + /** + * Check to see if Enter, Tab or Escape were just released. + * Enter or Tab submit the change, and Escape cancels it. + * + * @param FlashEvent Flash keyboard event. + */ + public function onKeyUp(FlashEvent:KeyboardEvent):void + { + if((FlashEvent.keyCode == 13) || (FlashEvent.keyCode == 9) || (FlashEvent.keyCode == 27)) //enter or tab or escape + { + if(FlashEvent.keyCode == 27) + cancel(); + else + submit(); + } + } + + /** + * Cancel the current edits and stop editing. + */ + public function cancel():void + { + valueDisplay.text = oldValue.toString(); + doneEditing(); + } + + /** + * Submit the current edits and stop editing. + */ + public function submit():void + { + object[field] = valueDisplay.text; + doneEditing(); + } + + /** + * Helper function, switches the text field back to display mode. + */ + protected function doneEditing():void + { + valueDisplay.type = TextFieldType.DYNAMIC; + valueDisplay.setTextFormat(_whiteText); + valueDisplay.defaultTextFormat = _whiteText; + valueDisplay.background = false; + editing = false; + } + } +} \ No newline at end of file diff --git a/src/org/flixel/system/input/Input.as b/src/org/flixel/system/input/Input.as new file mode 100644 index 0000000..78e45f9 --- /dev/null +++ b/src/org/flixel/system/input/Input.as @@ -0,0 +1,187 @@ +package org.flixel.system.input +{ + /** + * Basic input class that manages the fast-access Booleans and detailed key-state tracking. + * Keyboard extends this with actual specific key data. + * + * @author Adam Atomic + */ + public class Input + { + /** + * @private + */ + internal var _lookup:Object; + /** + * @private + */ + internal var _map:Array; + /** + * @private + */ + internal const _total:uint = 256; + + /** + * Constructor + */ + public function Input() + { + _lookup = new Object(); + _map = new Array(_total); + } + + /** + * Updates the key states (for tracking just pressed, just released, etc). + */ + public function update():void + { + var i:uint = 0; + while(i < _total) + { + var o:Object = _map[i++]; + if(o == null) continue; + if((o.last == -1) && (o.current == -1)) o.current = 0; + else if((o.last == 2) && (o.current == 2)) o.current = 1; + o.last = o.current; + } + } + + /** + * Resets all the keys. + */ + public function reset():void + { + var i:uint = 0; + while(i < _total) + { + var o:Object = _map[i++]; + if(o == null) continue; + this[o.name] = false; + o.current = 0; + o.last = 0; + } + } + + /** + * Check to see if this key is pressed. + * + * @param Key One of the key constants listed above (e.g. "LEFT" or "A"). + * + * @return Whether the key is pressed + */ + public function pressed(Key:String):Boolean { return this[Key]; } + + /** + * Check to see if this key was just pressed. + * + * @param Key One of the key constants listed above (e.g. "LEFT" or "A"). + * + * @return Whether the key was just pressed + */ + public function justPressed(Key:String):Boolean { return _map[_lookup[Key]].current == 2; } + + /** + * Check to see if this key is just released. + * + * @param Key One of the key constants listed above (e.g. "LEFT" or "A"). + * + * @return Whether the key is just released. + */ + public function justReleased(Key:String):Boolean { return _map[_lookup[Key]].current == -1; } + + /** + * If any keys are not "released" (0), + * this function will return an array indicating + * which keys are pressed and what state they are in. + * + * @return An array of key state data. Null if there is no data. + */ + public function record():Array + { + var data:Array = null; + var i:uint = 0; + while(i < _total) + { + var o:Object = _map[i++]; + if((o == null) || (o.current == 0)) + continue; + if(data == null) + data = new Array(); + data.push({code:i-1,value:o.current}); + } + return data; + } + + /** + * Part of the keystroke recording system. + * Takes data about key presses and sets it into array. + * + * @param Record Array of data about key states. + */ + public function playback(Record:Array):void + { + var i:uint = 0; + var l:uint = Record.length; + var o:Object; + var o2:Object; + while(i < l) + { + o = Record[i++]; + o2 = _map[o.code]; + o2.current = o.value; + if(o.value > 0) + this[o2.name] = true; + } + } + + /** + * Look up the key code for any given string name of the key or button. + * + * @param KeyName The String name of the key. + * + * @return The key code for that key. + */ + public function getKeyCode(KeyName:String):int + { + return _lookup[KeyName]; + } + + /** + * Check to see if any keys are pressed right now. + * + * @return Whether any keys are currently pressed. + */ + public function any():Boolean + { + var i:uint = 0; + while(i < _total) + { + var o:Object = _map[i++]; + if((o != null) && (o.current > 0)) + return true; + } + return false; + } + + /** + * An internal helper function used to build the key array. + * + * @param KeyName String name of the key (e.g. "LEFT" or "A") + * @param KeyCode The numeric Flash code for this key. + */ + protected function addKey(KeyName:String,KeyCode:uint):void + { + _lookup[KeyName] = KeyCode; + _map[KeyCode] = { name: KeyName, current: 0, last: 0 }; + } + + /** + * Clean up memory. + */ + public function destroy():void + { + _lookup = null; + _map = null; + } + } +} diff --git a/src/org/flixel/system/input/Keyboard.as b/src/org/flixel/system/input/Keyboard.as new file mode 100644 index 0000000..d712f71 --- /dev/null +++ b/src/org/flixel/system/input/Keyboard.as @@ -0,0 +1,206 @@ +package org.flixel.system.input +{ + import flash.events.KeyboardEvent; + + /** + * Keeps track of what keys are pressed and how with handy booleans or strings. + * + * @author Adam Atomic + */ + public class Keyboard extends Input + { + public var ESCAPE:Boolean; + public var F1:Boolean; + public var F2:Boolean; + public var F3:Boolean; + public var F4:Boolean; + public var F5:Boolean; + public var F6:Boolean; + public var F7:Boolean; + public var F8:Boolean; + public var F9:Boolean; + public var F10:Boolean; + public var F11:Boolean; + public var F12:Boolean; + public var ONE:Boolean; + public var TWO:Boolean; + public var THREE:Boolean; + public var FOUR:Boolean; + public var FIVE:Boolean; + public var SIX:Boolean; + public var SEVEN:Boolean; + public var EIGHT:Boolean; + public var NINE:Boolean; + public var ZERO:Boolean; + public var NUMPADONE:Boolean; + public var NUMPADTWO:Boolean; + public var NUMPADTHREE:Boolean; + public var NUMPADFOUR:Boolean; + public var NUMPADFIVE:Boolean; + public var NUMPADSIX:Boolean; + public var NUMPADSEVEN:Boolean; + public var NUMPADEIGHT:Boolean; + public var NUMPADNINE:Boolean; + public var NUMPADZERO:Boolean; + public var PAGEUP:Boolean; + public var PAGEDOWN:Boolean; + public var HOME:Boolean; + public var END:Boolean; + public var INSERT:Boolean; + public var MINUS:Boolean; + public var NUMPADMINUS:Boolean; + public var PLUS:Boolean; + public var NUMPADPLUS:Boolean; + public var DELETE:Boolean; + public var BACKSPACE:Boolean; + public var TAB:Boolean; + public var Q:Boolean; + public var W:Boolean; + public var E:Boolean; + public var R:Boolean; + public var T:Boolean; + public var Y:Boolean; + public var U:Boolean; + public var I:Boolean; + public var O:Boolean; + public var P:Boolean; + public var LBRACKET:Boolean; + public var RBRACKET:Boolean; + public var BACKSLASH:Boolean; + public var CAPSLOCK:Boolean; + public var A:Boolean; + public var S:Boolean; + public var D:Boolean; + public var F:Boolean; + public var G:Boolean; + public var H:Boolean; + public var J:Boolean; + public var K:Boolean; + public var L:Boolean; + public var SEMICOLON:Boolean; + public var QUOTE:Boolean; + public var ENTER:Boolean; + public var SHIFT:Boolean; + public var Z:Boolean; + public var X:Boolean; + public var C:Boolean; + public var V:Boolean; + public var B:Boolean; + public var N:Boolean; + public var M:Boolean; + public var COMMA:Boolean; + public var PERIOD:Boolean; + public var NUMPADPERIOD:Boolean; + public var SLASH:Boolean; + public var NUMPADSLASH:Boolean; + public var CONTROL:Boolean; + public var ALT:Boolean; + public var SPACE:Boolean; + public var UP:Boolean; + public var DOWN:Boolean; + public var LEFT:Boolean; + public var RIGHT:Boolean; + + public function Keyboard() + { + var i:uint; + + //LETTERS + i = 65; + while(i <= 90) + addKey(String.fromCharCode(i),i++); + + //NUMBERS + i = 48; + addKey("ZERO",i++); + addKey("ONE",i++); + addKey("TWO",i++); + addKey("THREE",i++); + addKey("FOUR",i++); + addKey("FIVE",i++); + addKey("SIX",i++); + addKey("SEVEN",i++); + addKey("EIGHT",i++); + addKey("NINE",i++); + i = 96; + addKey("NUMPADZERO",i++); + addKey("NUMPADONE",i++); + addKey("NUMPADTWO",i++); + addKey("NUMPADTHREE",i++); + addKey("NUMPADFOUR",i++); + addKey("NUMPADFIVE",i++); + addKey("NUMPADSIX",i++); + addKey("NUMPADSEVEN",i++); + addKey("NUMPADEIGHT",i++); + addKey("NUMPADNINE",i++); + addKey("PAGEUP", 33); + addKey("PAGEDOWN", 34); + addKey("HOME", 36); + addKey("END", 35); + addKey("INSERT", 45); + + //FUNCTION KEYS + i = 1; + while(i <= 12) + addKey("F"+i,111+(i++)); + + //SPECIAL KEYS + PUNCTUATION + addKey("ESCAPE",27); + addKey("MINUS",189); + addKey("NUMPADMINUS",109); + addKey("PLUS",187); + addKey("NUMPADPLUS",107); + addKey("DELETE",46); + addKey("BACKSPACE",8); + addKey("LBRACKET",219); + addKey("RBRACKET",221); + addKey("BACKSLASH",220); + addKey("CAPSLOCK",20); + addKey("SEMICOLON",186); + addKey("QUOTE",222); + addKey("ENTER",13); + addKey("SHIFT",16); + addKey("COMMA",188); + addKey("PERIOD",190); + addKey("NUMPADPERIOD",110); + addKey("SLASH",191); + addKey("NUMPADSLASH",191); + addKey("CONTROL",17); + addKey("ALT",18); + addKey("SPACE",32); + addKey("UP",38); + addKey("DOWN",40); + addKey("LEFT",37); + addKey("RIGHT",39); + addKey("TAB",9); + } + + /** + * Event handler so FlxGame can toggle keys. + * + * @param FlashEvent A KeyboardEvent object. + */ + public function handleKeyDown(FlashEvent:KeyboardEvent):void + { + var object:Object = _map[FlashEvent.keyCode]; + if(object == null) return; + if(object.current > 0) object.current = 1; + else object.current = 2; + this[object.name] = true; + } + + /** + * Event handler so FlxGame can toggle keys. + * + * @param FlashEvent A KeyboardEvent object. + */ + public function handleKeyUp(FlashEvent:KeyboardEvent):void + { + var object:Object = _map[FlashEvent.keyCode]; + if(object == null) return; + if(object.current > 0) object.current = -1; + else object.current = 0; + this[object.name] = false; + } + } +} diff --git a/src/org/flixel/system/input/Mouse.as b/src/org/flixel/system/input/Mouse.as new file mode 100644 index 0000000..f588e33 --- /dev/null +++ b/src/org/flixel/system/input/Mouse.as @@ -0,0 +1,338 @@ +package org.flixel.system.input +{ + import flash.display.Bitmap; + import flash.display.Sprite; + import flash.events.MouseEvent; + + import org.flixel.FlxCamera; + import org.flixel.FlxG; + import org.flixel.FlxPoint; + import org.flixel.FlxSprite; + import org.flixel.FlxU; + import org.flixel.system.replay.MouseRecord; + + /** + * This class helps contain and track the mouse pointer in your game. + * Automatically accounts for parallax scrolling, etc. + * + * @author Adam Atomic + */ + public class Mouse extends FlxPoint + { + [Embed(source="../../data/cursor.png")] protected var ImgDefaultCursor:Class; + + /** + * Current "delta" value of mouse wheel. If the wheel was just scrolled up, it will have a positive value. If it was just scrolled down, it will have a negative value. If it wasn't just scroll this frame, it will be 0. + */ + public var wheel:int; + /** + * Current X position of the mouse pointer on the screen. + */ + public var screenX:int; + /** + * Current Y position of the mouse pointer on the screen. + */ + public var screenY:int; + + /** + * Helper variable for tracking whether the mouse was just pressed or just released. + */ + protected var _current:int; + /** + * Helper variable for tracking whether the mouse was just pressed or just released. + */ + protected var _last:int; + /** + * A display container for the mouse cursor. + * This container is a child of FlxGame and sits at the right "height". + */ + protected var _cursorContainer:Sprite; + /** + * This is just a reference to the current cursor image, if there is one. + */ + protected var _cursor:Bitmap; + /** + * Helper variables for recording purposes. + */ + protected var _lastX:int; + protected var _lastY:int; + protected var _lastWheel:int; + protected var _point:FlxPoint; + protected var _globalScreenPosition:FlxPoint; + + /** + * Constructor. + */ + public function Mouse(CursorContainer:Sprite) + { + super(); + _cursorContainer = CursorContainer; + _lastX = screenX = 0; + _lastY = screenY = 0; + _lastWheel = wheel = 0; + _current = 0; + _last = 0; + _cursor = null; + _point = new FlxPoint(); + _globalScreenPosition = new FlxPoint(); + } + + /** + * Clean up memory. + */ + public function destroy():void + { + _cursorContainer = null; + _cursor = null; + _point = null; + _globalScreenPosition = null; + } + + /** + * Either show an existing cursor or load a new one. + * + * @param Graphic The image you want to use for the cursor. + * @param Scale Change the size of the cursor. Default = 1, or native size. 2 = 2x as big, 0.5 = half size, etc. + * @param XOffset The number of pixels between the mouse's screen position and the graphic's top left corner. + * @param YOffset The number of pixels between the mouse's screen position and the graphic's top left corner. + */ + public function show(Graphic:Class=null,Scale:Number=1,XOffset:int=0,YOffset:int=0):void + { + _cursorContainer.visible = true; + if(Graphic != null) + load(Graphic,Scale,XOffset,YOffset); + else if(_cursor == null) + load(); + } + + /** + * Hides the mouse cursor + */ + public function hide():void + { + _cursorContainer.visible = false; + } + + /** + * Read only, check visibility of mouse cursor. + */ + public function get visible():Boolean + { + return _cursorContainer.visible; + } + + /** + * Load a new mouse cursor graphic + * + * @param Graphic The image you want to use for the cursor. + * @param Scale Change the size of the cursor. + * @param XOffset The number of pixels between the mouse's screen position and the graphic's top left corner. + * @param YOffset The number of pixels between the mouse's screen position and the graphic's top left corner. + */ + public function load(Graphic:Class=null,Scale:Number=1,XOffset:int=0,YOffset:int=0):void + { + if(_cursor != null) + _cursorContainer.removeChild(_cursor); + + if(Graphic == null) + Graphic = ImgDefaultCursor; + _cursor = new Graphic(); + _cursor.x = XOffset; + _cursor.y = YOffset; + _cursor.scaleX = Scale; + _cursor.scaleY = Scale; + + _cursorContainer.addChild(_cursor); + } + + /** + * Unload the current cursor graphic. If the current cursor is visible, + * then the default system cursor is loaded up to replace the old one. + */ + public function unload():void + { + if(_cursor != null) + { + if(_cursorContainer.visible) + load(); + else + { + _cursorContainer.removeChild(_cursor) + _cursor = null; + } + } + } + + /** + * Called by the internal game loop to update the mouse pointer's position in the game world. + * Also updates the just pressed/just released flags. + * + * @param X The current X position of the mouse in the window. + * @param Y The current Y position of the mouse in the window. + * @param XScroll The amount the game world has scrolled horizontally. + * @param YScroll The amount the game world has scrolled vertically. + */ + public function update(X:int,Y:int):void + { + _globalScreenPosition.x = X; + _globalScreenPosition.y = Y; + updateCursor(); + if((_last == -1) && (_current == -1)) + _current = 0; + else if((_last == 2) && (_current == 2)) + _current = 1; + _last = _current; + } + + /** + * Internal function for helping to update the mouse cursor and world coordinates. + */ + protected function updateCursor():void + { + //actually position the flixel mouse cursor graphic + _cursorContainer.x = _globalScreenPosition.x; + _cursorContainer.y = _globalScreenPosition.y; + + //update the x, y, screenX, and screenY variables based on the default camera. + //This is basically a combination of getWorldPosition() and getScreenPosition() + var camera:FlxCamera = FlxG.camera; + screenX = (_globalScreenPosition.x - camera.x)/camera.zoom; + screenY = (_globalScreenPosition.y - camera.y)/camera.zoom; + x = screenX + camera.scroll.x; + y = screenY + camera.scroll.y; + } + + /** + * Fetch the world position of the mouse on any given camera. + * NOTE: Mouse.x and Mouse.y also store the world position of the mouse cursor on the main camera. + * + * @param Camera If unspecified, first/main global camera is used instead. + * @param Point An existing point object to store the results (if you don't want a new one created). + * + * @return The mouse's location in world space. + */ + public function getWorldPosition(Camera:FlxCamera=null,Point:FlxPoint=null):FlxPoint + { + if(Camera == null) + Camera = FlxG.camera; + if(Point == null) + Point = new FlxPoint(); + getScreenPosition(Camera,_point); + Point.x = _point.x + Camera.scroll.x; + Point.y = _point.y + Camera.scroll.y; + return Point; + } + + /** + * Fetch the screen position of the mouse on any given camera. + * NOTE: Mouse.screenX and Mouse.screenY also store the screen position of the mouse cursor on the main camera. + * + * @param Camera If unspecified, first/main global camera is used instead. + * @param Point An existing point object to store the results (if you don't want a new one created). + * + * @return The mouse's location in screen space. + */ + public function getScreenPosition(Camera:FlxCamera=null,Point:FlxPoint=null):FlxPoint + { + if(Camera == null) + Camera = FlxG.camera; + if(Point == null) + Point = new FlxPoint(); + Point.x = (_globalScreenPosition.x - Camera.x)/Camera.zoom; + Point.y = (_globalScreenPosition.y - Camera.y)/Camera.zoom; + return Point; + } + + /** + * Resets the just pressed/just released flags and sets mouse to not pressed. + */ + public function reset():void + { + _current = 0; + _last = 0; + } + + /** + * Check to see if the mouse is pressed. + * + * @return Whether the mouse is pressed. + */ + public function pressed():Boolean { return _current > 0; } + + /** + * Check to see if the mouse was just pressed. + * + * @return Whether the mouse was just pressed. + */ + public function justPressed():Boolean { return _current == 2; } + + /** + * Check to see if the mouse was just released. + * + * @return Whether the mouse was just released. + */ + public function justReleased():Boolean { return _current == -1; } + + /** + * Event handler so FlxGame can update the mouse. + * + * @param FlashEvent A MouseEvent object. + */ + public function handleMouseDown(FlashEvent:MouseEvent):void + { + if(_current > 0) _current = 1; + else _current = 2; + } + + /** + * Event handler so FlxGame can update the mouse. + * + * @param FlashEvent A MouseEvent object. + */ + public function handleMouseUp(FlashEvent:MouseEvent):void + { + if(_current > 0) _current = -1; + else _current = 0; + } + + /** + * Event handler so FlxGame can update the mouse. + * + * @param FlashEvent A MouseEvent object. + */ + public function handleMouseWheel(FlashEvent:MouseEvent):void + { + wheel = FlashEvent.delta; + } + + /** + * If the mouse changed state or is pressed, return that info now + * + * @return An array of key state data. Null if there is no data. + */ + public function record():MouseRecord + { + if((_lastX == _globalScreenPosition.x) && (_lastY == _globalScreenPosition.y) && (_current == 0) && (_lastWheel == wheel)) + return null; + _lastX = _globalScreenPosition.x; + _lastY = _globalScreenPosition.y; + _lastWheel = wheel; + return new MouseRecord(_lastX,_lastY,_current,_lastWheel); + } + + /** + * Part of the keystroke recording system. + * Takes data about key presses and sets it into array. + * + * @param KeyStates Array of data about key states. + */ + public function playback(Record:MouseRecord):void + { + _current = Record.button; + wheel = Record.wheel; + _globalScreenPosition.x = Record.x; + _globalScreenPosition.y = Record.y; + updateCursor(); + } + } +} \ No newline at end of file diff --git a/src/org/flixel/system/replay/FrameRecord.as b/src/org/flixel/system/replay/FrameRecord.as new file mode 100644 index 0000000..a8f326e --- /dev/null +++ b/src/org/flixel/system/replay/FrameRecord.as @@ -0,0 +1,143 @@ +package org.flixel.system.replay +{ + + /** + * Helper class for the new replay system. Represents all the game inputs for one "frame" or "step" of the game loop. + * + * @author Adam Atomic + */ + public class FrameRecord + { + /** + * Which frame of the game loop this record is from or for. + */ + public var frame:int; + /** + * An array of simple integer pairs referring to what key is pressed, and what state its in. + */ + public var keys:Array; + /** + * A container for the 4 mouse state integers. + */ + public var mouse:MouseRecord; + + /** + * Instantiate array new frame record. + */ + public function FrameRecord() + { + frame = 0; + keys = null; + mouse = null; + } + + /** + * Load this frame record with input data from the input managers. + * + * @param Frame What frame it is. + * @param Keys Keyboard data from the keyboard manager. + * @param Mouse Mouse data from the mouse manager. + * + * @return A reference to this FrameRecord object. + * + */ + public function create(Frame:Number,Keys:Array=null,Mouse:MouseRecord=null):FrameRecord + { + frame = Frame; + keys = Keys; + mouse = Mouse; + return this; + } + + /** + * Clean up memory. + */ + public function destroy():void + { + keys = null; + mouse = null; + } + + /** + * Save the frame record data to array simple ASCII string. + * + * @return A String object containing the relevant frame record data. + */ + public function save():String + { + var output:String = frame+"k"; + + if(keys != null) + { + var object:Object; + var i:uint = 0; + var l:uint = keys.length; + while(i < l) + { + if(i > 0) + output += ","; + object = keys[i++]; + output += object.code+":"+object.value; + } + } + + output += "m"; + if(mouse != null) + output += mouse.x + "," + mouse.y + "," + mouse.button + "," + mouse.wheel; + + return output; + } + + /** + * Load the frame record data from array simple ASCII string. + * + * @param Data A String object containing the relevant frame record data. + */ + public function load(Data:String):FrameRecord + { + var i:uint; + var l:uint; + + //get frame number + var array:Array = Data.split("k"); + frame = int(array[0] as String); + + //split up keyboard and mouse data + array = (array[1] as String).split("m"); + var keyData:String = array[0]; + var mouseData:String = array[1]; + + //parse keyboard data + if(keyData.length > 0) + { + //get keystroke data pairs + array = keyData.split(","); + + //go through each data pair and enter it into this frame's key state + var keyPair:Array; + i = 0; + l = array.length; + while(i < l) + { + keyPair = (array[i++] as String).split(":"); + if(keyPair.length == 2) + { + if(keys == null) + keys = new Array(); + keys.push({code:int(keyPair[0] as String),value:int(keyPair[1] as String)}); + } + } + } + + //mouse data is just 4 integers, easy peezy + if(mouseData.length > 0) + { + array = mouseData.split(","); + if(array.length >= 4) + mouse = new MouseRecord(int(array[0] as String),int(array[1] as String),int(array[2] as String),int(array[3] as String)); + } + + return this; + } + } +} diff --git a/src/org/flixel/system/replay/MouseRecord.as b/src/org/flixel/system/replay/MouseRecord.as new file mode 100644 index 0000000..8594fb5 --- /dev/null +++ b/src/org/flixel/system/replay/MouseRecord.as @@ -0,0 +1,43 @@ +package org.flixel.system.replay +{ + /** + * A helper class for the frame records, part of the replay/demo/recording system. + * + * @author Adam Atomic + */ + public class MouseRecord + { + /** + * The main X value of the mouse in screen space. + */ + public var x:int; + /** + * The main Y value of the mouse in screen space. + */ + public var y:int; + /** + * The state of the left mouse button. + */ + public var button:int; + /** + * The state of the mouse wheel. + */ + public var wheel:int; + + /** + * Instantiate a new mouse input record. + * + * @param X The main X value of the mouse in screen space. + * @param Y The main Y value of the mouse in screen space. + * @param Button The state of the left mouse button. + * @param Wheel The state of the mouse wheel. + */ + public function MouseRecord(X:int,Y:int,Button:int,Wheel:int) + { + x = X; + y = Y; + button = Button; + wheel = Wheel; + } + } +} \ No newline at end of file