initial commit

This commit is contained in:
Jeremy Penner 2011-01-28 17:39:36 -08:00
commit 710ea0e879
84 changed files with 11693 additions and 0 deletions

17
AIR_readme.txt Normal file
View file

@ -0,0 +1,17 @@
Instructions for DISTRIBUTING* your application:
1. Creating a self-signed certificate:
- edit CreateCertificate.bat to change the path to Flex SDK,
- edit CreateCertificate.bat to set your certificate password (and name if you like),
- run CreateCertificate.bat to generate your self-signed certificate,
- wait a minute before packaging.
2. Packaging the application:
- edit PackageApplication.bat and change the path to Flex SDK,
- if you have a signed certificate, edit PackageApplication.bat to change the path to the certificate,
- run PackageApplication.bat, you will be prompted for the certificate password,
(note that you may not see '***' when typing your password - it works anyway)
- the packaged application should appear in your project in a new 'air' directory.
* to test your application from FlashDevelop, just press F5 as usual.

86
KlikPunk.as3proj Normal file
View file

@ -0,0 +1,86 @@
<?xml version="1.0" encoding="utf-8"?>
<project>
<!-- Output SWF options -->
<output>
<movie disabled="False" />
<movie input="" />
<movie path="bin\KlikPunk.swf" />
<movie fps="30" />
<movie width="800" />
<movie height="600" />
<movie version="10" />
<movie background="#FFFFFF" />
</output>
<!-- Other classes to be compiled into your SWF -->
<classpaths>
<class path="src" />
</classpaths>
<!-- Build options -->
<build>
<option accessible="False" />
<option allowSourcePathOverlap="False" />
<option benchmark="False" />
<option es="False" />
<option locale="" />
<option loadConfig="" />
<option optimize="True" />
<option showActionScriptWarnings="True" />
<option showBindingWarnings="True" />
<option showInvalidCSS="True" />
<option showDeprecationWarnings="True" />
<option showUnusedTypeSelectorWarnings="True" />
<option strict="True" />
<option useNetwork="True" />
<option useResourceBundleMetadata="True" />
<option warnings="True" />
<option verboseStackTraces="False" />
<option linkReport="" />
<option loadExterns="" />
<option staticLinkRSL="True" />
<option additional="+configname=air" />
<option compilerConstants="" />
<option customSDK="" />
</build>
<!-- SWC Include Libraries -->
<includeLibraries>
<!-- example: <element path="..." /> -->
</includeLibraries>
<!-- SWC Libraries -->
<libraryPaths>
<!-- example: <element path="..." /> -->
</libraryPaths>
<!-- External Libraries -->
<externalLibraryPaths>
<!-- example: <element path="..." /> -->
</externalLibraryPaths>
<!-- Runtime Shared Libraries -->
<rslPaths>
<!-- example: <element path="..." /> -->
</rslPaths>
<!-- Intrinsic Libraries -->
<intrinsics>
<!-- example: <element path="..." /> -->
</intrinsics>
<!-- Assets to embed into the output SWF -->
<library>
<!-- example: <asset path="..." id="..." update="..." glyphs="..." mode="..." place="..." sharepoint="..." /> -->
</library>
<!-- Class files to compile (other referenced classes will automatically be included) -->
<compileTargets>
<compile path="src\Main.as" />
</compileTargets>
<!-- Paths to exclude from the Project Explorer tree -->
<hiddenPaths>
<!-- example: <hidden path="..." /> -->
</hiddenPaths>
<!-- Executed before build -->
<preBuildCommand>taskkill /f /fi "IMAGENAME eq adl.exe"</preBuildCommand>
<!-- Executed after build -->
<postBuildCommand alwaysRun="False" />
<!-- Other project options -->
<options>
<option showHiddenPaths="False" />
<option testMovie="Custom" />
<option testMovieCommand="$(FlexSDK)\bin\adl.exe;application.xml bin" />
</options>
</project>

48
PackageApplication.bat Normal file
View file

@ -0,0 +1,48 @@
@echo off
:: AIR application packaging
:: More information:
:: http://livedocs.adobe.com/flex/3/html/help.html?content=CommandLineTools_5.html#1035959
:: Path to Flex SDK binaries
set PATH=%PATH%;C:\Dev\flash\flex\bin
:: Signature (see 'CreateCertificate.bat')
set CERTIFICATE="KlikPunk.pfx"
set SIGNING_OPTIONS=-storetype pkcs12 -keystore %CERTIFICATE% -tsa none
if not exist %CERTIFICATE% goto certificate
:: Output
if not exist air md air
set AIR_FILE=air/KlikPunk.air
:: Input
set APP_XML=application.xml
set FILE_OR_DIR=-C bin .
echo Signing AIR setup using certificate %CERTIFICATE%.
call adt -package %SIGNING_OPTIONS% %AIR_FILE% %APP_XML% %FILE_OR_DIR%
if errorlevel 1 goto failed
echo.
echo AIR setup created: %AIR_FILE%
echo.
goto end
:certificate
echo Certificate not found: %CERTIFICATE%
echo.
echo Troubleshotting:
echo A certificate is required, generate one using 'CreateCertificate.bat'
echo.
goto end
:failed
echo AIR setup creation FAILED.
echo.
echo Troubleshotting:
echo did you configure the Flex SDK path in this Batch file?
echo.
:end
pause

27
application.xml Normal file
View file

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8" ?>
<application xmlns="http://ns.adobe.com/air/application/1.5">
<id>KlikPunk</id>
<version>1.0</version>
<filename>KlikPunk</filename>
<name>KlikPunk</name>
<description></description>
<copyright></copyright>
<initialWindow>
<title>KlikPunk</title>
<content>KlikPunk.swf</content>
<systemChrome>standard</systemChrome>
<transparent>false</transparent>
<visible>true</visible>
<minimizable>true</minimizable>
<maximizable>false</maximizable>
<resizable>false</resizable>
</initialWindow>
<!--
More options:
http://livedocs.adobe.com/flex/3/html/File_formats_1.html#1043413
-->
</application>

BIN
assets/folder.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 537 B

BIN
assets/punk.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

2
assets/readme.txt Normal file
View file

@ -0,0 +1,2 @@
Icons by Mark James, licensed under a Creative Commons Attribution 2.5 license:
http://www.famfamfam.com/lab/icons/silk/

BIN
assets/save.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 620 B

21
src/Button.as Normal file
View file

@ -0,0 +1,21 @@
package
{
/**
* ...
* @author jjp
*/
public class Button extends EntitySidebarImg
{
private var dgOnClick:Function;
public function Button(sidebar:Sidebar, bmp:*, dgOnClick: Function)
{
super(sidebar, bmp);
this.dgOnClick = dgOnClick;
}
override public function OnClick():void
{
this.dgOnClick();
}
}
}

44
src/Drag.as Normal file
View file

@ -0,0 +1,44 @@
package
{
import flash.geom.Point;
import net.flashpunk.utils.Input;
/**
* ...
* @author jjp
*/
public class Drag
{
private var mouseXLast: int;
private var mouseYLast: int;
private static var drag: Drag = null;
public function Drag()
{
Update();
}
public static function Claim() : Drag
{
if (drag === null)
{
drag = new Drag();
return drag;
}
return null;
}
public function Delta(zoom:Number = 1): Point
{
return new Point((Input.mouseX - mouseXLast) / zoom, (Input.mouseY - mouseYLast) / zoom);
}
public function Update(): void
{
mouseXLast = Input.mouseX;
mouseYLast = Input.mouseY;
}
public function Done(): void
{
drag = null;
}
}
}

32
src/EntitySidebar.as Normal file
View file

@ -0,0 +1,32 @@
package
{
import net.flashpunk.Entity;
import net.flashpunk.Graphic;
import net.flashpunk.utils.Input;
/**
* ...
* @author jjp
*/
public class EntitySidebar extends Entity
{
protected var sidebar: Sidebar;
public function EntitySidebar(sidebar: Sidebar, h:int, graphic:Graphic)
{
super(sidebar.x, 0 /*set by sidebar.add*/, graphic);
setHitbox(sidebar.width, h, 0, 0);
this.sidebar = sidebar;
sidebar.Add(this);
}
override public function update():void
{
super.update();
if (Input.mousePressed && collidePoint(x, y, Input.mouseX, Input.mouseY))
OnClick();
}
public function OnClick(): void { }
public function Fade(pct:Number):void { }
}
}

29
src/EntitySidebarImg.as Normal file
View file

@ -0,0 +1,29 @@
package
{
import net.flashpunk.Entity;
import net.flashpunk.Graphic;
import net.flashpunk.graphics.Image;
import net.flashpunk.utils.Input;
/**
* ...
* @author jjp
*/
public class EntitySidebarImg extends EntitySidebar
{
protected var img: Image;
public function EntitySidebarImg(sidebar: Sidebar, bmp:*)
{
img = new Image(bmp);
img.scale = sidebar.width / img.width;
super(sidebar, img.scaledHeight, img);
}
override public function Fade(pct:Number):void
{
super.Fade(pct);
img.alpha = pct;
}
}
}

22
src/EvNewImg.as Normal file
View file

@ -0,0 +1,22 @@
package
{
import flash.display.BitmapData;
import flash.events.Event;
/**
* ...
* @author jjp
*/
public class EvNewImg extends Event
{
public var rel: String;
public var bmp: BitmapData;
public function EvNewImg(type: String, rel: String, bmp: BitmapData)
{
this.rel = rel;
this.bmp = bmp;
super(type);
}
}
}

35
src/Factory.as Normal file
View file

@ -0,0 +1,35 @@
package
{
import flash.display.BitmapData;
import flash.geom.Point;
import net.flashpunk.Entity;
import net.flashpunk.Graphic;
import net.flashpunk.graphics.Image;
import net.flashpunk.Mask;
import net.flashpunk.utils.Input;
/**
* ...
* @author jjp
*/
public class Factory extends EntitySidebarImg
{
private var bmp: BitmapData;
private var relf: String;
public function Factory(sidebar: Sidebar, relf: String, bmp:BitmapData)
{
super(sidebar, bmp);
this.bmp = bmp;
this.relf = relf;
}
override public function OnClick():void
{
var worldStage: WorldStage = WorldStage(world);
var posMouseReal: Point = worldStage.PointRealFromScreen(new Point(Input.mouseX, Input.mouseY));
var relfBmp: String = worldStage.RelFull(relf);
var tok: Token = new Token(bmp, relfBmp, posMouseReal.x - this.bmp.width / 2, posMouseReal.y - this.bmp.height / 2);
world.add(tok);
worldStage.tokSelected = tok;
}
}
}

43
src/Folder.as Normal file
View file

@ -0,0 +1,43 @@
package
{
import net.flashpunk.Entity;
import net.flashpunk.Graphic;
import net.flashpunk.graphics.Graphiclist;
import net.flashpunk.graphics.Image;
import net.flashpunk.graphics.Text;
import net.flashpunk.Mask;
import net.flashpunk.utils.Input;
/**
* ...
* @author jjp
*/
public class Folder extends EntitySidebarImg
{
[Embed(source = '../assets/folder.png')]
private const bmpFolder:Class;
private var reld: String;
private var text: Text;
public function Folder(sidebar: Sidebar, reld: String)
{
super(sidebar, bmpFolder);
text = new Text(reld, 2, (img.scaledHeight / 2) - 6);
text.color = 0x4444FF;
addGraphic(text);
this.reld = reld;
}
override public function OnClick():void
{
WorldStage(world).Chdir(this.reld);
}
override public function Fade(pct:Number):void
{
super.Fade(pct);
text.alpha = pct;
}
}
}

75
src/Imgdir.as Normal file
View file

@ -0,0 +1,75 @@
package
{
import flash.display.BitmapData;
import flash.display.Bitmap;
import flash.display.Loader;
import flash.display.LoaderInfo;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.FileListEvent;
import flash.events.IOErrorEvent;
import flash.filesystem.File;
import flash.net.URLRequest;
/**
* ...
* @author jjp
*/
public class Imgdir extends EventDispatcher
{
public static const LOADED: String = "ImgLoaded";
private var dir: File;
private var mprelf_bmp: Object;
private var rgreld: Object;
public function Imgdir(url: String)
{
trace("imgdir: " + url);
this.dir = new File(url);
this.dir.addEventListener(FileListEvent.DIRECTORY_LISTING, OnDirUpdate);
this.mprelf_bmp = { };
this.rgreld = { };
}
public function Update() : void
{
this.dir.getDirectoryListingAsync();
}
private function OnDirUpdate(ev:FileListEvent) : void
{
for each (var file: File in ev.files)
{
var rel: String = file.name;
if (file.isDirectory && !rgreld[rel])
{
rgreld[rel] = true;
dispatchEvent(new EvNewImg(LOADED, rel, null));
}
else if (!file.isDirectory && /\.(png|gif|jpg|jpeg)$/i.test(rel) && !mprelf_bmp[rel])
LoadBmp(file, OnBmpLoaded);
}
}
public static function LoadBmp(file: File, dgOnLoad: Function, dgOnFail: Function = null):void
{
var loader : Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE,
function (evBmp: Event) : void {
try
{
var bmp : BitmapData = Bitmap(LoaderInfo(evBmp.target).content).bitmapData;
dgOnLoad(bmp, file);
}
catch(e:*) {}
} );
if (dgOnFail === null)
dgOnFail = function(): void { }; // ignore errors
loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, dgOnFail);
loader.load(new URLRequest(file.url));
}
private function OnBmpLoaded(bmp: BitmapData, file: File) : void
{
mprelf_bmp[file.name] = bmp;
this.dispatchEvent(new EvNewImg(LOADED, file.name, bmp));
}
}
}

27
src/Main.as Normal file
View file

@ -0,0 +1,27 @@
package
{
import net.flashpunk.Engine;
import net.flashpunk.FP;
import splash.Splash;
import flash.events.FileListEvent
/**
* ...
* @author jjp
*/
public class Main extends Engine
{
public function Main():void
{
super(800, 600, 60, false);
}
override public function init():void
{
FP.world = new WorldMenu();
//var worldSplash: Splash = new Splash;
//var worldStage: WorldStage = new WorldStage;
//FP.world.add(worldSplash);
//worldSplash.start(worldStage);
}
}
}

115
src/Sidebar.as Normal file
View file

@ -0,0 +1,115 @@
package
{
import net.flashpunk.Entity;
import net.flashpunk.Graphic;
import net.flashpunk.Mask;
import net.flashpunk.Tween;
import net.flashpunk.tweens.misc.Alarm;
import net.flashpunk.tweens.misc.NumTween;
import net.flashpunk.tweens.misc.VarTween;
import net.flashpunk.utils.Draw;
import net.flashpunk.utils.Ease;
/**
* ...
* @author jjp
*/
public class Sidebar extends Entity
{
private var yNew:int;
private var tweenX:VarTween;
private var tweenY:VarTween;
private var tweenFade:NumTween;
private var yLast:Number;
private var alarm:Alarm;
private var xShow:int;
private var xHide:int;
private var yShow:int;
private var yHide:int;
public var fScrollable:Boolean;
public function Sidebar(xShow:int, xHide: int, yShow:int, yHide:int, w:int, h:int, layer:int, fStartShown: Boolean, fScrollable: Boolean)
{
super(fStartShown ? xShow : xHide, fStartShown ? yShow : yHide);
this.xShow = xShow;
this.xHide = xHide;
this.yShow = yShow;
this.yHide = yHide;
this.yLast = y;
this.fScrollable = fScrollable;
setHitbox(w, h, 0, 0);
this.layer = layer + 1;
this.yNew = y;
tweenX = VarTween(addTween(new VarTween(MoveSidebar)));
tweenY = VarTween(addTween(new VarTween(MoveSidebar)));
tweenFade = NumTween(addTween(new NumTween(MoveSidebar)));
alarm = null;
if (fStartShown)
tweenFade.value = 1.0;
else
tweenFade.value = 0.0;
}
public function LayerEntities():int { return this.layer - 1; }
public function Add(entity: EntitySidebar):void
{
entity.y = yNew;
yNew = yNew + entity.height;
entity.layer = LayerEntities();
world.add(entity); // eehhhhhh
}
public function Die(): void
{
var rgentity: Vector.<EntitySidebar> = new Vector.<EntitySidebar>();
world.getLayer(LayerEntities(), rgentity);
world.removeList(rgentity);
world.remove(this);
}
override public function render():void
{
Draw.rect(x, y, width, height, 0x7777FF, 0.1);
}
override public function update():void
{
super.update();
if (tweenX.active)
MoveSidebar();
}
public function Toggle(dgOnComplete: Function = null): void
{
if (alarm !== null && alarm.active)
removeTween(alarm);
if (x !== xShow || y !== yShow)
{
tweenX.tween(this, "x", xShow, 0.3, Ease.cubeOut);
tweenY.tween(this, "y", yShow, 0.3, Ease.cubeOut);
tweenFade.tween(tweenFade.value, 1.0, 0.3, Ease.cubeOut);
}
else
{
tweenX.tween(this, "x", xHide, 0.3, Ease.cubeIn);
tweenY.tween(this, "y", yHide, 0.3, Ease.cubeIn);
tweenFade.tween(tweenFade.value, 0, 0.3, Ease.cubeIn);
}
if (dgOnComplete !== null)
alarm = Alarm(addTween(new Alarm(0.3, dgOnComplete, Tween.ONESHOT), true));
}
public function MoveSidebar(dy:int = 0): void
{
if (world !== null)
{
dy = dy + (y - yLast);
var rgentity: Array = [];
world.getLayer(LayerEntities(), rgentity);
for each (var entity: EntitySidebar in rgentity)
{
entity.x = x;
entity.y += dy;
entity.Fade(tweenFade.value);
}
yNew += dy;
yLast = y;
}
}
}
}

25
src/TextSidebar.as Normal file
View file

@ -0,0 +1,25 @@
package
{
import net.flashpunk.Graphic;
import net.flashpunk.graphics.Text;
/**
* ...
* @author jjp
*/
public class TextSidebar extends EntitySidebar
{
public function TextSidebar(sidebar:Sidebar, st:String, zoom:int = 1)
{
var text:Text = new Text(st);
text.scale = zoom;
text.x = (sidebar.width / 2) - (text.scaledWidth / 2);
super(sidebar, text.scaledHeight, text);
}
override public function Fade(pct:Number):void
{
super.Fade(pct);
Text(graphic).alpha = pct;
}
}
}

137
src/Token.as Normal file
View file

@ -0,0 +1,137 @@
package
{
import flash.display.BitmapData;
import flash.geom.Point;
import flash.geom.Rectangle;
import net.flashpunk.Entity;
import net.flashpunk.graphics.Image;
import net.flashpunk.utils.Draw;
import net.flashpunk.utils.Input;
import net.flashpunk.utils.Key;
/**
* ...
* @author jjp
*/
public class Token extends Entity
{
private var drag: Drag;
public var posReal: Point;
private var relf: String;
private var xml: XML;
public function Token(source:BitmapData, relf: String, x: int, y: int, xml:XML = null)
{
this.posReal = new Point(x, y);
super(x, y, new Image(source));
this.type = "Token";
this.layer = WorldStage.LAYER_TOKENS;
this.drag = null;
this.relf = relf;
if (xml === null)
this.xml = XML("<token/>");
else
this.xml = xml;
}
public function FSelected(): Boolean
{
return WorldStage(this.world).tokSelected === this;
}
override public function removed():void
{
super.removed();
if (drag !== null)
{
drag.Done();
drag = null;
}
}
override public function added():void
{
super.added();
FixupZoom();
}
private function FixupZoom(): Number
{
var worldStage: WorldStage = WorldStage(world);
var zoom: Number = worldStage.zoom;
var img: Image = Image(this.graphic);
Image(this.graphic).scale = zoom;
var posScreen : Point = worldStage.PointScreenFromReal(posReal);
x = int(posScreen.x);
y = int(posScreen.y);
this.setHitbox(img.scaledWidth, img.scaledHeight, -img.x, -img.y);
return zoom;
}
override public function update():void
{
super.update();
var zoom: Number = FixupZoom();
if (Input.mouseUp && drag !== null)
{
trace("drag done");
drag.Done();
drag = null;
}
if (FSelected())
{
var deltaMove: Point = null;
if (Input.pressed(Key.UP))
deltaMove = new Point(0, -1);
else if (Input.pressed(Key.DOWN))
deltaMove = new Point(0, 1);
else if (Input.pressed(Key.LEFT))
deltaMove = new Point(-1, 0);
else if (Input.pressed(Key.RIGHT))
deltaMove = new Point(1, 0);
if (Input.pressed(Key.PAGE_UP))
world.bringForward(this);
else if (Input.pressed(Key.PAGE_DOWN))
world.sendBackward(this);
if (Input.mouseDown)
{
if (drag === null && collidePoint(x, y, Input.mouseX, Input.mouseY))
{
drag = Drag.Claim();
if (drag !== null)
trace("drag claimed for " + relf);
else
trace("drag failed for " + relf);
}
if (drag !== null)
{
deltaMove = drag.Delta(zoom);
drag.Update();
}
}
if (deltaMove !== null)
posReal = posReal.add(deltaMove);
if (Input.pressed(Key.DELETE))
WorldStage(world).KillToken(this);
}
}
override public function render():void
{
super.render();
if (FSelected())
Draw.hitbox(this);
}
public function GenXML(): XML
{
var xml: XML = this.xml.copy();
xml.@x = int(posReal.x);
xml.@y = int(posReal.y);
xml.@path = relf;
return xml;
}
}
}

98
src/WorldMenu.as Normal file
View file

@ -0,0 +1,98 @@
package
{
import flash.events.Event;
import flash.filesystem.File;
import flash.net.FileFilter;
import flash.net.FileReference;
import flash.net.FileReferenceList;
import flash.net.URLRequest;
import flash.utils.Dictionary;
import net.flashpunk.Entity;
import net.flashpunk.FP;
import net.flashpunk.graphics.Image;
import net.flashpunk.graphics.Text;
import net.flashpunk.utils.Input;
import net.flashpunk.World;
/**
* ...
* @author jjp
*/
public class WorldMenu extends World
{
[Embed(source = '../assets/punk.png')]
private const bmpPunk:Class;
private var mpentity_dgclick: Dictionary;
public function WorldMenu()
{
super();
AddText("KlikPunk", FP.height / 8, 5);
AddText("icons by Mark James http://www.famfamfam.com/lab/icons/silk/", FP.height - 32);
var imgPunk: Image = new Image(bmpPunk);
imgPunk.scale = 8;
var entityPunk: Entity = new Entity((FP.width / 2) - (imgPunk.scaledWidth / 2), 10, imgPunk);
entityPunk.layer = 10;
add(entityPunk);
SetMenu(["New Stage", NewStage], ["Open Stage", OpenStage]);
}
private function SetMenu(...rgmenu):void
{
var yMenu:int = (FP.height / 2);
var hMenus:int = FP.height - yMenu - (FP.height / 8);
var dyMenu:int = hMenus / rgmenu.length;
mpentity_dgclick = new Dictionary();
for each(var menu:Array in rgmenu)
{
var entityMenu: Entity = AddText(menu[0], yMenu, 2);
entityMenu.type = "menuitem";
mpentity_dgclick[entityMenu] = menu[1];
yMenu = yMenu + dyMenu;
}
}
private function AddText(stText:String, y:int, scale:Number = 1): Entity
{
var text: Text = new Text(stText);
text.scale = scale;
var entity: Entity = addGraphic(text, 0, (FP.width / 2) - (text.scaledWidth / 2), y - (text.scaledHeight / 2));
entity.setHitbox(text.scaledWidth, text.scaledHeight, 0, 0);
return entity;
}
public function NewStage(): void
{
FileForBrowse().browseForSave("Choose your destiny");
}
public function OpenStage(): void
{
FileForBrowse().browseForOpen("Find your thing", [new FileFilter("Stages", "*.xml")]);
}
private function FileForBrowse(): File
{
var file: File = new File(File.userDirectory.nativePath + File.separator + "NewStage.xml");
file.addEventListener(Event.SELECT, function():void {
FP.world = new WorldStage(file.url);
});
return file;
}
override public function update():void
{
var rgentityMenu: Array = [];
getType("menuitem", rgentityMenu);
for each(var entityMenu: Entity in rgentityMenu)
{
if (entityMenu.collidePoint(entityMenu.x, entityMenu.y, Input.mouseX, Input.mouseY))
{
if (Input.mousePressed)
mpentity_dgclick[entityMenu]();
Text(entityMenu.graphic).color = 0xFFFFFF;
}
else
Text(entityMenu.graphic).color = 0x888888;
}
super.update();
}
}
}

308
src/WorldStage.as Normal file
View file

@ -0,0 +1,308 @@
package
{
import flash.display.BitmapData;
import flash.events.Event;
import flash.filesystem.File;
import flash.filesystem.FileMode;
import flash.filesystem.FileStream;
import flash.geom.Point;
import net.flashpunk.debug.Console;
import net.flashpunk.Entity;
import net.flashpunk.FP;
import net.flashpunk.graphics.Image;
import net.flashpunk.Tween;
import net.flashpunk.tweens.misc.Alarm;
import net.flashpunk.tweens.misc.VarTween;
import net.flashpunk.utils.Ease;
import net.flashpunk.utils.Key;
import net.flashpunk.World;
import net.flashpunk.utils.Input;
/**
* ...
* @author jjp
*/
public class WorldStage extends World
{
[Embed(source = '../assets/save.png')]
private const bmpSave:Class;
public static const LAYER_TOKENS: int = 100;
public static const LAYER_SIDEBAR: int = 50;
public static const LAYER_SAVE: int = 60;
public static const LAYER_MSG: int = 70;
private var imgdir: Imgdir;
private var drag: Drag;
private var absd: String;
private var relf: String;
private var rgreld: Array;
private var alarmImgdir: Tween;
private var rgsidebar: Vector.<Sidebar>;
private var sidebarMsg: Sidebar;
private var rgmsg: Vector.<String>;
private var alarmMsg: Alarm;
public var tokSelected: Token;
public var zoom: Number;
public var pointView: Point;
public function WorldStage(absf: String)
{
super();
var match:* = /(.*\/)([^\/]*)$/.exec(absf);
this.absd = match[1];
this.relf = match[2];
this.rgreld = [];
this.rgsidebar = new Vector.<Sidebar>();
var sidebarSave: Sidebar = AddSidebar(new Sidebar(FP.width - 32, FP.width, 0, 0, 32, 32, LAYER_SAVE, false, false));
new Button(sidebarSave, bmpSave, Save);
sidebarMsg = null;
alarmMsg = null;
rgmsg = new Vector.<String>()
zoom = 1;
pointView = new Point(0, 0);
drag = null;
Chdir(null);
ToggleUI();
Load();
}
public function KillToken(tok: Token): void
{
remove(tok);
tok.active = false;
if (tokSelected === tok)
tokSelected = null;
}
public function PointRealFromScreen(pointScreen: Point): Point
{
return new Point((pointScreen.x / zoom) + pointView.x, (pointScreen.y / zoom) + pointView.y);
}
public function PointScreenFromReal(pointReal: Point) : Point
{
return new Point((pointReal.x - pointView.x) * zoom, (pointReal.y - pointView.y) * zoom);
}
private function OnNewImg(ev: EvNewImg) : void
{
var entity: Entity;
if (ev.bmp === null)
entity = new Folder(SidebarFind(LAYER_SIDEBAR), ev.rel);
else
entity = new Factory(SidebarFind(LAYER_SIDEBAR), ev.rel, ev.bmp);
}
public function RelFull(relf:String = null): String
{
var rel:String = rgreld.join(File.separator);
if (relf !== null)
rel = rel + File.separator + relf;
return rel;
}
public function Chdir(reld: String): void
{
if (alarmImgdir !== null)
{
removeTween(alarmImgdir);
var rgui: Array = [];
getLayer(LAYER_SIDEBAR, rgui);
for each (var entity: Entity in rgui)
{
remove(entity);
entity.active = false;
}
}
if (reld === "..")
rgreld = rgreld.slice(0, rgreld.length - 1);
else if (reld !== null)
rgreld.push(reld);
imgdir = new Imgdir(absd + RelFull());
imgdir.addEventListener(Imgdir.LOADED, OnNewImg);
imgdir.Update();
alarmImgdir = this.addTween(new Alarm(3, imgdir.Update, Tween.LOOPING), true);
AddSidebar(new Sidebar(0, -32, 0, 0, 32, FP.height, LAYER_SIDEBAR, reld !== null /*fStartShown*/, true));
if (rgreld.length > 0)
OnNewImg(new EvNewImg(Imgdir.LOADED, "..", null));
}
private function SidebarFind(layer: int):Sidebar
{
for each (var sidebar:Sidebar in rgsidebar)
if (sidebar.LayerEntities() === layer)
return sidebar;
return null;
}
private function RemoveSidebar(layer:int):void
{
rgsidebar = rgsidebar.filter(
function(sidebarOld:Sidebar, isidebarOld:int, rg:Vector.<Sidebar>):Boolean {
if (sidebarOld.LayerEntities() === layer)
{
sidebarOld.Die();
return false;
}
return true;
}, this);
}
private function AddSidebar(sidebar: Sidebar):Sidebar
{
RemoveSidebar(sidebar.LayerEntities());
add(sidebar);
rgsidebar.push(sidebar);
return sidebar;
}
private function ToggleUI(): void
{
for each (var sidebar: Sidebar in rgsidebar)
sidebar.Toggle();
}
override public function update():void
{
var dyFactory:int = 0;
if (Input.mousePressed)
{
var rgtok: Array = [];
getLayer(LAYER_TOKENS, rgtok);
tokSelected = null;
// getLayer returns tokens in draw order, which means furthest back first
for (var itok:int = rgtok.length - 1; itok >= 0; itok --)
{
var tok: Token = rgtok[itok];
if (tok.collidePoint(tok.x, tok.y, Input.mouseX, Input.mouseY))
{
tokSelected = tok;
break;
}
}
trace("clicked on " + tokSelected);
}
if (Input.pressed(Key.TAB))
ToggleUI();
if (Input.mouseWheel)
{
var fSidebarScrolled:Boolean = false;
for each (var sidebar: Sidebar in rgsidebar)
{
if (sidebar.fScrollable && sidebar.collidePoint(sidebar.x, sidebar.y, Input.mouseX, Input.mouseY))
{
sidebar.MoveSidebar(Input.mouseWheelDelta * 5);
fSidebarScrolled = true;
break;
}
}
if (!fSidebarScrolled)
{
var zoomNew: Number = zoom + (Input.mouseWheelDelta / 100);
if (zoomNew <= 0)
zoomNew = 0.01;
// keep the point under the mouse cursor in the same place
var dx: Number = (Input.mouseX / zoom) - (Input.mouseX / zoomNew);
var dy: Number = (Input.mouseY / zoom) - (Input.mouseY / zoomNew);
pointView.x = pointView.x + dx;
pointView.y = pointView.y + dy;
zoom = zoomNew;
}
}
super.update();
}
public function ShowMsg(stMsg: String):void
{
rgmsg.push(stMsg);
if (alarmMsg === null)
ShowNextMsg();
}
private function ShowNextMsg(): void
{
if (sidebarMsg !== null)
{
sidebarMsg.Die();
sidebarMsg = null;
}
if (alarmMsg !== null && alarmMsg.active)
removeTween(alarmMsg);
alarmMsg = null;
if (rgmsg.length > 0)
{
var stMsg:String = rgmsg.shift();
var x:int = FP.width / 8;
sidebarMsg = Sidebar(add(new Sidebar(x, x, FP.height - 45, FP.height, x * 6, 45, LAYER_MSG, false, false)));
new TextSidebar(sidebarMsg, stMsg, 2);
sidebarMsg.Toggle();
alarmMsg = Alarm(addTween(new Alarm(2, function():void { sidebarMsg.Toggle(ShowNextMsg); }, Tween.ONESHOT), true));
}
}
public function Save(): void
{
var stream: FileStream = new FileStream();
stream.open(new File(absd + File.separator + relf), FileMode.WRITE);
stream.writeUTFBytes(GenXML().toXMLString());
stream.close();
ShowMsg("Saved.");
}
public function Load(): void
{
var file: File = new File(absd + File.separator + relf);
if (file.exists)
{
var stream: FileStream = new FileStream();
stream.open(file, FileMode.READ);
var xml: XML = XML(stream.readUTFBytes(file.size));
var itoken:int = 0;
var rgtoken:Object = { ctokenLoaded: 0, ctoken: xml.children().length(), rgtoken: [] };
for each (var xmlToken: XML in xml.children())
{
LoadToken(xmlToken, itoken, rgtoken);
itoken = itoken + 1;
}
ShowMsg("Loaded.");
}
}
private function LoadToken(xml: XML, itoken: int, rgtoken:Object): void
{
trace("loading " + xml.@path);
Imgdir.LoadBmp(new File(absd + File.separator + xml.@path),
function(bmp: BitmapData, file: File):void {
trace("loadtoken: " + xml.@path);
var token: Token = new Token(bmp, xml.@path.toString(), int(xml.@x), int(xml.@y), xml);
FixupTokens(rgtoken, token, itoken);
},
function():void
{
FixupTokens(rgtoken, null, itoken);
});
}
private function FixupTokens(rgtoken:Object, tokenLoaded: Token, itokenLoaded:int):void
{
rgtoken.ctokenLoaded ++;
rgtoken.rgtoken[itokenLoaded] = tokenLoaded;
if (rgtoken.ctokenLoaded === rgtoken.ctoken)
{
for (var itoken:int = 0; itoken < rgtoken.ctoken; itoken ++)
{
var token:Token = rgtoken.rgtoken[itoken];
if (token !== null)
add(token);
}
}
}
public function GenXML():XML
{
var xml: XML = XML("<stage/>");
var rgtoken:Array = [];
this.getLayer(LAYER_TOKENS, rgtoken);
for each(var token: Token in rgtoken)
xml.appendChild(token.GenXML());
return xml;
}
}
}

291
src/net/flashpunk/Engine.as Normal file
View file

@ -0,0 +1,291 @@
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.<uint> = new Vector.<uint>;
}
}

764
src/net/flashpunk/Entity.as Normal file
View file

@ -0,0 +1,764 @@
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))
{
var list:Graphiclist = new Graphiclist;
if (graphic) list.add(graphic);
graphic = list;
}
(graphic as Graphiclist).add(g);
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;
}
}

886
src/net/flashpunk/FP.as Normal file
View file

@ -0,0 +1,886 @@
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;
}
}

View file

@ -0,0 +1,85 @@
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;
}
}

78
src/net/flashpunk/Mask.as Normal file
View file

@ -0,0 +1,78 @@
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;
}
}

223
src/net/flashpunk/Screen.as Normal file
View file

@ -0,0 +1,223 @@
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.<Bitmap> = new Vector.<Bitmap>(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;
}
}

144
src/net/flashpunk/Sfx.as Normal file
View file

@ -0,0 +1,144 @@
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;
}
}

128
src/net/flashpunk/Tween.as Normal file
View file

@ -0,0 +1,128 @@
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;
}
}

View file

@ -0,0 +1,119 @@
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;
}
}

1128
src/net/flashpunk/World.as Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,878 @@
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.<String> = new Vector.<String>;
// Entity lists.
/** @private */ private const ENTITY_LIST:Vector.<Entity> = new Vector.<Entity>;
/** @private */ private const SCREEN_LIST:Vector.<Entity> = new Vector.<Entity>;
/** @private */ private const SELECT_LIST:Vector.<Entity> = new Vector.<Entity>;
// Watch information.
/** @private */ private const WATCH_LIST:Vector.<String> = Vector.<String>(["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;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 242 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 258 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 242 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 251 B

Binary file not shown.

View file

@ -0,0 +1,66 @@
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;
}
}

View file

@ -0,0 +1,69 @@
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;
}
}

View file

@ -0,0 +1,313 @@
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.<BitmapData> = new Vector.<BitmapData>;
/** @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;
}
}

View file

@ -0,0 +1,260 @@
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;
}
}

View file

@ -0,0 +1,142 @@
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.<Graphic> = _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.<Graphic> { 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.<Graphic> = new Vector.<Graphic>;
/** @private */ private var _temp:Vector.<Graphic> = new Vector.<Graphic>;
/** @private */ private var _count:uint;
/** @private */ private var _camera:Point = new Point;
}
}

View file

@ -0,0 +1,312 @@
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 width * scaleX * scale; }
/**
* The scaled height of the image.
*/
public function get scaledHeight():uint { return 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 = { };
}
}

View file

@ -0,0 +1,35 @@
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;
}
}

View file

@ -0,0 +1,157 @@
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;
}
}

View file

@ -0,0 +1,106 @@
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;
}
}

View file

@ -0,0 +1,252 @@
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;
}
}

View file

@ -0,0 +1,56 @@
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;
}
}

View file

@ -0,0 +1,126 @@
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;
}
}

View file

@ -0,0 +1,102 @@
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;
}
}

View file

@ -0,0 +1,109 @@
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;
}
}

View file

@ -0,0 +1,360 @@
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;
}
}

View file

@ -0,0 +1,268 @@
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;
}
}

View file

@ -0,0 +1,114 @@
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;
}
}

View file

@ -0,0 +1,159 @@
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.<Mask> = _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.<Mask> = _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.<Mask> = new Vector.<Mask>;
/** @private */ private var _temp:Vector.<Mask> = new Vector.<Mask>;
/** @private */ private var _count:uint;
}
}

View file

@ -0,0 +1,97 @@
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;
}
}

View file

@ -0,0 +1,46 @@
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; }
}
}

View file

@ -0,0 +1,58 @@
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;
}
}

View file

@ -0,0 +1,100 @@
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;
}
}

View file

@ -0,0 +1,61 @@
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.<String> = new Vector.<String>;
/** @private */ private var _start:Vector.<Number> = new Vector.<Number>;
/** @private */ private var _range:Vector.<Number> = new Vector.<Number>;
}
}

View file

@ -0,0 +1,52 @@
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;
}
}

View file

@ -0,0 +1,55 @@
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;
}
}

View file

@ -0,0 +1,94 @@
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;
}
}

View file

@ -0,0 +1,69 @@
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;
}
}

View file

@ -0,0 +1,86 @@
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;
}
}

View file

@ -0,0 +1,133 @@
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.<Point> = new Vector.<Point>;
/** @private */ private var _pointD:Vector.<Number> = new Vector.<Number>;
/** @private */ private var _pointT:Vector.<Number> = new Vector.<Number>;
/** @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;
}
}

View file

@ -0,0 +1,32 @@
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);
}
}
}

View file

@ -0,0 +1,112 @@
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;
}
}

View file

@ -0,0 +1,192 @@
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.<Point> = new Vector.<Point>;
/** @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.<Point> = new Vector.<Point>;
/** @private */ private var _curveT:Vector.<Number> = new Vector.<Number>;
/** @private */ private var _curveD:Vector.<Number> = new Vector.<Number>;
// Curve points.
/** @private */ private var _a:Point;
/** @private */ private var _b:Point;
/** @private */ private var _c:Point;
}
}

View file

@ -0,0 +1,48 @@
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;
}
}

View file

@ -0,0 +1,94 @@
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;
}
}

View file

@ -0,0 +1,146 @@
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;
}
}

View file

@ -0,0 +1,375 @@
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;
}
}

View file

@ -0,0 +1,214 @@
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;
*/
}
}

View file

@ -0,0 +1,296 @@
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.<int>(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.<int> = _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.<int> = _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.<int> = _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.<int>
{
return _control[name] as Vector.<int>;
}
/** @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.<Boolean> = new Vector.<Boolean>(256);
/** @private */ private static var _keyNum:int = 0;
/** @private */ private static var _press:Vector.<int> = new Vector.<int>(256);
/** @private */ private static var _release:Vector.<int> = new Vector.<int>(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;
}
}

View file

@ -0,0 +1,193 @@
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);
}
}
}

219
src/splash/Splash.as Normal file
View file

@ -0,0 +1,219 @@
package splash
{
import flash.display.BitmapData;
import flash.display.BlendMode;
import flash.display.GradientType;
import flash.display.Graphics;
import net.flashpunk.Entity;
import net.flashpunk.FP;
import net.flashpunk.Graphic;
import net.flashpunk.graphics.Graphiclist;
import net.flashpunk.graphics.Image;
import net.flashpunk.tweens.misc.NumTween;
import net.flashpunk.utils.Ease;
import net.flashpunk.World;
/**
* This object displays the FlashPunk splash screen.
*/
public class Splash extends Entity
{
/**
* Embedded graphics.
*/
[Embed(source = 'splash_lines.png')] private const SPLASH_LINES:Class;
[Embed(source = 'splash_cog.png')] private const SPLASH_COG:Class;
[Embed(source = 'splash_left.png')] private const SPLASH_LEFT:Class;
[Embed(source = 'splash_right.png')] private const SPLASH_RIGHT:Class;
/**
* Image objects.
*/
public var list:Graphiclist;
public var lines:Image;
public var cog:Image = new Image(SPLASH_COG);
public var leftText:Image = new Image(SPLASH_LEFT);
public var rightText:Image = new Image(SPLASH_RIGHT);
public var fade:Image = Image.createRect(FP.width, FP.height, 0);
/**
* Tween information.
*/
public var tween:NumTween = new NumTween(tweenEnd);
public var fader:NumTween = new NumTween(faderEnd);
public var leftX:int;
public var rightX:int;
/**
* Constructor, start the splash animation.
*/
public function Splash(color:uint = 0xFF3366, bgColor:uint = 0x202020, fadeTime:Number = .5, spinTime:Number = 2, spinPause:Number = .5, spins:Number = 720)
{
// Create the lines image.
var data:BitmapData = new BitmapData(FP.width, FP.height, false, 0x353535),
g:Graphics = FP.sprite.graphics;
g.clear();
g.beginGradientFill(GradientType.RADIAL, [0, 0], [1, 0], [0, 255]);
g.drawCircle(0, 0, 100);
FP.matrix.identity();
FP.matrix.scale(FP.width / 200, FP.height / 200);
FP.matrix.translate(FP.width / 2, FP.height / 2);
data.draw(FP.sprite, FP.matrix);
g.clear();
g.beginBitmapFill((new SPLASH_LINES).bitmapData);
g.drawRect(0, 0, FP.width, FP.height);
data.draw(FP.sprite);
lines = new Image(data);
// Set the entity information.
x = FP.width / 2;
y = FP.height / 2;
graphic = new Graphiclist(leftText, rightText, cog, lines, fade);
// Set the screen information.
FP.screen.color = bgColor;
// Set the lines properties.
lines.blend = BlendMode.SUBTRACT;
lines.smooth = true;
lines.x -= x;
lines.y -= y;
// Set the big cog properties.
cog.visible = true;
cog.color = color;
cog.smooth = true;
cog.originX = cog.width / 2;
cog.originY = cog.height / 2;
cog.x -= cog.originX;
cog.y -= cog.originY;
// Set the left text properties.
leftText.color = color;
leftText.smooth = true;
leftText.originX = leftText.width;
leftText.originY = leftText.height / 2;
leftText.x -= leftText.originX + cog.width / 4 + 4;
leftText.y -= leftText.originY;
leftX = leftText.x;
// Set the right text properties.
rightText.color = color;
rightText.smooth = true;
rightText.originY = rightText.height / 2;
rightText.x += cog.width / 4;
rightText.y -= rightText.originY;
rightX = rightText.x;
// Set the fade cover properties.
fade.x -= x;
fade.y -= y;
// Set the timing properties.
_fadeTime = fadeTime;
_spinTime = spinTime;
_spinPause = spinPause;
_spins = spins;
// Add the tweens.
addTween(tween);
addTween(fader);
// Make invisible until you start it.
visible = false;
}
/**
* Start the splash screen.
*/
public function start(onComplete:* = null):void
{
_onComplete = onComplete;
visible = true;
fadeIn();
}
/**
* Update the splash screen.
*/
override public function update():void
{
// Text scaling/positioning.
var t:Number = 1 - tween.scale;
leftText.x = leftX - t * FP.width / 2;
rightText.x = rightX + t * FP.width / 2;
leftText.scaleY = rightText.scaleY = tween.scale;
leftText.alpha = rightText.alpha = Ease.cubeIn(tween.scale);
// Cog rotation/positioning.
cog.angle = tween.scale <= 1 ? tween.value : tween.value * 2;
cog.scale = 2.5 - tween.scale * 2;
cog.alpha = tween.scale;
// Fade in/out alpha control.
fade.alpha = fader.value;
// Pause before fade out.
if (_spinWait > 0)
{
_spinWait -= FP.fixed ? 1 : FP.elapsed;
if (_spinWait <= 0) fadeOut();
}
}
/**
* When the fade tween completes.
*/
private function faderEnd():void
{
if (fader.value == 0) tween.tween(_spins, 0, _spinTime, Ease.backOut);
else splashEnd();
}
/**
* When the tween completes.
*/
private function tweenEnd():void
{
if (_spinPause >= 0) _spinWait = _spinPause;
else fadeOut();
}
/**
* When the splash screen has completed.
*/
private function splashEnd():void
{
if (_onComplete == null) return;
else if (_onComplete is Function) _onComplete();
else if (_onComplete is World) FP.world = _onComplete;
else throw new Error("The onComplete parameter must be a Function callback or World object.");
}
/**
* Fades the splash screen in.
*/
private function fadeIn():void
{
fader.tween(1, 0, _fadeTime, Ease.cubeOut);
}
/**
* Fades the splash screen out.
*/
private function fadeOut():void
{
fader.tween(0, 1, _fadeTime, Ease.cubeIn);
}
/**
* Fade in/out time and logo spinning time.
*/
private var _fadeTime:Number;
private var _spinTime:Number;
private var _spins:Number;
private var _spinPause:Number;
private var _spinWait:Number = 0;
private var _onComplete:*;
}
}

BIN
src/splash/splash_cog.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

BIN
src/splash/splash_left.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

BIN
src/splash/splash_lines.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 227 B

BIN
src/splash/splash_right.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB