git-svn-id: https://aklabs.dyndns.org/svn/aklabs/trunk/games/wilysays@50 eb184899-6090-47d4-a65b-558f62f6ea1c
529 lines
18 KiB
ActionScript
Executable File
529 lines
18 KiB
ActionScript
Executable File
package net.aklabs.demo.simonsays {
|
|
|
|
import net.aklabs.demo.simonsays.LoadingObject;
|
|
import flash.utils.Dictionary;
|
|
import flash.net.URLRequest;
|
|
import flash.display.Loader;
|
|
import flash.display.LoaderInfo;
|
|
import flash.display.Bitmap;
|
|
import flash.display.Sprite;
|
|
import flash.text.TextField;
|
|
import flash.text.TextFormat;
|
|
import flash.text.TextFieldAutoSize;
|
|
import flash.media.Sound;
|
|
import flash.display.MovieClip;
|
|
import flash.events.IOErrorEvent;
|
|
import flash.events.SecurityErrorEvent;
|
|
import flash.events.ProgressEvent;
|
|
import flash.events.Event;
|
|
|
|
/*
|
|
* Class Preloader
|
|
*
|
|
* This class is what loads and manages the cache of media assets for the game
|
|
* This class is a singleton, which was annoyingly difficulty to implement in
|
|
* Actionscript 3 because it doesn't support private constructors.
|
|
*
|
|
* Be careful subclassing this, because of the way the singleton mechanism was implemented,
|
|
* it may break.
|
|
*
|
|
* This class can be added as a child asset, and it will display a pair of progress
|
|
* bars as it loads all the assets. The top bar is the progress on the current file,
|
|
* the bottom bar is the progress on the total set of assets. You don't have to display the
|
|
* preloader, but if you do, make sure to load via the assets array, rather than calling loadImage()
|
|
* and such manually, as the progress bars will act funky.
|
|
*
|
|
* When the preloader has loaded all of the assets, it will dispatch a COMPLETE event that should be handled
|
|
* by whatever higher game class, as a signal that all assets are ready and cached.
|
|
*/
|
|
|
|
public class Preloader extends Sprite {
|
|
protected var objects:Dictionary; // this contains all loaded objects, keyed by their handle
|
|
protected var loading_by_instance:Dictionary; // this contains the [handle, bytes read, bytes total] of all objects that are currently loading, keyed by their instance
|
|
protected var loading_by_handle:Dictionary; // this contains the [instance, bytes read, bytes total] of all objects that are currently loading, keyed by their handle
|
|
protected static var instance:Preloader = null; // the singleton instance
|
|
protected var curAsset:Number; // the index of the asset (in the assets array) currently being loaded
|
|
|
|
protected var assets:Array; // the array of all the assets to be loaded
|
|
/* format of the 'assets' array:
|
|
[ ASSET_ARRAY, ASSET_ARRAY ...]
|
|
|
|
each ASSET_ARRAY is...
|
|
|
|
["images"|"sounds"|"movies", "TEXT HANDLE", "URI", "CLASS NAME"]
|
|
|
|
... The "images"|"sounds"|"movies" serves an obvious purpose; it says what kind of asset we're loading,
|
|
as the mechanism for loading images/sounds/movies are different.
|
|
|
|
The TEXT HANDLE is a text handle that can be used when referencing this asset in the preloader
|
|
|
|
The URI is just that, the URI where this object can be found. Usual security restrictions apply.
|
|
|
|
The CLASS NAME is only important for exported SWF movies, and must equal whatever the class name is
|
|
that you set for that SWF when telling Flash to export it for Actionscript. If this is wrong, or you
|
|
don't export your SWFs for actionscript, you won't be able to instantiate new MovieClip/Sprites of them.
|
|
*/
|
|
|
|
protected var loadingLabel:TextField; // just a text label that says "Please wait; loading"
|
|
protected var progressSpinner:Array = new Array("-", "\\", "|", "/"); // an array of characters that creates a spinner for the loading label
|
|
protected var progSpin:Number = 0; // the current index in the progressSpinner array
|
|
protected var classDefs:Dictionary; // classDefs holds the class definitions for each of the given objects (if supplied), keyed by text handle
|
|
protected var classNames:Dictionary; // classNames holds the name of the classes for each of the given objects (if supplied), keyed by text handle
|
|
|
|
/*
|
|
* getInstance()
|
|
*
|
|
* Returns the instance of the singleton
|
|
*
|
|
* arguments: none
|
|
*
|
|
* Returns: Preloader
|
|
*/
|
|
public static function getInstance():Preloader
|
|
{
|
|
if ( Preloader.instance == null )
|
|
Preloader.instance = new Preloader();
|
|
return Preloader.instance;
|
|
}
|
|
|
|
/*
|
|
* Preloader(assets)
|
|
*
|
|
* Default constructor for Preloader
|
|
*
|
|
* arguments:
|
|
* @assets:Array, the asset array to be loaded (default null)
|
|
*
|
|
* Returns: none
|
|
*/
|
|
public function Preloader(assets:Array = null)
|
|
{
|
|
if ( Preloader.instance != null )
|
|
throw("Don't use (new Preloader()) directly, use Preloader.getInstance() to prevent duplication of this singleton class.");
|
|
this.objects = new Dictionary();
|
|
this.loading_by_instance = new Dictionary();
|
|
this.loading_by_handle = new Dictionary();
|
|
Preloader.instance = this;
|
|
this.curAsset = 0;
|
|
if ( assets != null)
|
|
this.assets = assets;
|
|
var labelFormat = new TextFormat();
|
|
labelFormat.font = "Courier New";
|
|
labelFormat.bold = false;
|
|
labelFormat.color = 0xFFFFFF;
|
|
labelFormat.size = 12;;
|
|
this.loadingLabel = new TextField();
|
|
this.loadingLabel.background = false;
|
|
this.loadingLabel.autoSize = TextFieldAutoSize.LEFT;
|
|
this.loadingLabel.defaultTextFormat = labelFormat;
|
|
this.loadingLabel.text = "Please wait; loading |"
|
|
this.loadingLabel.x = 0;
|
|
this.loadingLabel.y = 0;
|
|
this.addChild(loadingLabel);
|
|
this.classDefs = new Dictionary();
|
|
this.classNames = new Dictionary();
|
|
}
|
|
|
|
/*
|
|
* setAssets(assets)
|
|
*
|
|
* Set the asset array for the preloader
|
|
*
|
|
* arguments:
|
|
* @assets:Array, the array of assets
|
|
*
|
|
* returns: none
|
|
*/
|
|
public function setAssets(assets:Array)
|
|
{
|
|
this.assets = assets;
|
|
}
|
|
|
|
/*
|
|
* loadAssets()
|
|
*
|
|
* Start loading all the assets
|
|
*
|
|
* arguments: none
|
|
*
|
|
* returns: none
|
|
*/
|
|
public function loadAssets()
|
|
{
|
|
this.onLoadComplete(null);
|
|
}
|
|
|
|
/*
|
|
* getObject(handle)
|
|
*
|
|
* Get a generic Object referenced by the given handle
|
|
*
|
|
* argument:
|
|
* @handle:String, the text handle of the object you want
|
|
*
|
|
* returns: object you want, or null
|
|
*/
|
|
public function getObject(handle:String)
|
|
{
|
|
if ( this.objects[handle] )
|
|
return this.objects[handle];
|
|
return null;
|
|
}
|
|
|
|
/*
|
|
* getBitmap(handle:String)
|
|
*
|
|
* This function returns a CLONE of a Bitmap object in the preloader. Use this when
|
|
* you want a bitmap that you can use in more than one place, e.g., more like a sprite.
|
|
* All instances will reference the same bitmap data, however, so any modification to
|
|
* the core bitmap data will show up in all instances.
|
|
*
|
|
* arguments:
|
|
* @handle:String, the text handle reference for the bitmap you want
|
|
*
|
|
* returns Bitmap on success, null on failure
|
|
*/
|
|
|
|
public function getBitmap(handle:String)
|
|
{
|
|
var obj = this.getObject(handle);
|
|
if ( (obj) && (obj is Bitmap) ) {
|
|
var bm = new Bitmap(obj.bitmapData);
|
|
return bm;
|
|
}
|
|
return obj;
|
|
}
|
|
|
|
/*
|
|
* getBitmap(handle:String)
|
|
*
|
|
* This function returns a CLONE of a MovieClip object in the preloader. Use this when
|
|
* you want a MovieClip that you can use in more than one place, e.g., more like a sprite.
|
|
* All instances will reference the same frame data, however, so any modification to
|
|
* the core frame data will show up in all instances.
|
|
*
|
|
* arguments:
|
|
* @handle:String, the text handle reference for the bitmap you want
|
|
*
|
|
* returns MovieClip on success, null on failure
|
|
*/
|
|
public function getMovieClip(handle:String):MovieClip
|
|
{
|
|
var obj = this.getObject(handle);
|
|
try {
|
|
var clip:Class = this.classDefs[handle];
|
|
if ( clip ) {
|
|
return new clip();
|
|
}
|
|
} catch (error:Error) {
|
|
trace(error);
|
|
return null;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/*
|
|
* startObject(obj, handle)
|
|
*
|
|
* Start loading the given object with the given handle. Sets up event handling for notifications on the event while it loads.
|
|
*
|
|
* arguments:
|
|
* @obj:Loader|Sound, either a Loader or a Sound object, referencing the object which has already had its URI set and begun loading
|
|
* @handle:String, the text string by which this object should be referenced
|
|
*
|
|
* returns: none
|
|
*/
|
|
protected function startObject(obj, handle:String)
|
|
{
|
|
var toadd = obj;
|
|
if ( obj is Loader ) {
|
|
toadd = obj.contentLoaderInfo;
|
|
}
|
|
toadd.addEventListener(Event.COMPLETE, this.onLoadComplete);
|
|
toadd.addEventListener(ProgressEvent.PROGRESS, this.onProgressUpdate);
|
|
toadd.addEventListener(IOErrorEvent.IO_ERROR, this.onIOError);
|
|
toadd.addEventListener(SecurityErrorEvent.SECURITY_ERROR, this.onSecurityError);
|
|
this.loading_by_instance[toadd] = new Array(handle, 0, 0);
|
|
this.loading_by_handle[handle] = new Array(obj, 0, 0);
|
|
}
|
|
|
|
/*
|
|
* stopObject(obj)
|
|
*
|
|
* Stops loading on a given object, unhooks all the event listeners, etc
|
|
*
|
|
* arguments:
|
|
* @obj:Loader|Sound, the object on which loading should stop
|
|
*
|
|
* returns: none
|
|
*/
|
|
protected function stopObject(obj)
|
|
{
|
|
var obj_info = this.loading_by_instance[obj];
|
|
obj.removeEventListener(Event.COMPLETE, this.onLoadComplete);
|
|
obj.removeEventListener(ProgressEvent.PROGRESS, this.onProgressUpdate);
|
|
obj.removeEventListener(IOErrorEvent.IO_ERROR, this.onIOError);
|
|
obj.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, this.onSecurityError);
|
|
delete this.loading_by_handle[this.loading_by_instance[obj][0]];
|
|
delete this.loading_by_instance[obj];
|
|
}
|
|
|
|
/*
|
|
* getObjectStatus(handle)
|
|
*
|
|
* Returns a LoadingObject object that describes the current loading status of the object you're inquiring about
|
|
*
|
|
* arguments:
|
|
* @handle:String, the handle of the object you want to know about
|
|
*
|
|
* returns : LoadingObject
|
|
*/
|
|
public function getObjectStatus(handle:String):LoadingObject
|
|
{
|
|
var objStats = new LoadingObject();
|
|
objStats.handle = handle;
|
|
if ( this.objects[handle] ) {
|
|
objStats.state = LoadingObject.STATE_READY;
|
|
} else if ( this.loading_by_handle[handle] ) {
|
|
objStats.state = LoadingObject.STATE_LOADING;
|
|
objStats.bytesRead = this.loading_by_handle[handle][1];
|
|
objStats.bytesTotal = this.loading_by_handle[handle][2];
|
|
}
|
|
return objStats;
|
|
}
|
|
|
|
/*
|
|
* loadImage(uri, handle)
|
|
*
|
|
* Start loading the image at the given URI with the given handle
|
|
*
|
|
* arguments:
|
|
* @uri:String, the URI where the file lives
|
|
* @handle:String, the handle for the object
|
|
*
|
|
* returns: none
|
|
*/
|
|
public function loadImage(uri:String, handle:String)
|
|
{
|
|
var request:URLRequest = new URLRequest(uri);
|
|
var img = new Loader();
|
|
img.load(request);
|
|
this.startObject(img, handle);
|
|
}
|
|
|
|
/*
|
|
* loadMovie(uri, handle, className)
|
|
*
|
|
* Loads an external SWF object from URI and stores it as 'handle' and stores the class definition from it where the class name is className
|
|
*
|
|
* arguments:
|
|
* @uri:String, the URI where the file lives
|
|
* @handle:String, the handle for this object
|
|
* @className:String, the name of the SWF class when exported for Actionscript
|
|
*
|
|
* returns: none
|
|
*/
|
|
public function loadMovie(uri:String, handle:String, className:String)
|
|
{
|
|
var request:URLRequest = new URLRequest(uri);
|
|
var movie = new Loader();
|
|
movie.load(request);
|
|
this.startObject(movie, handle);
|
|
this.classNames[handle] = className;
|
|
trace(this.classNames[handle]);
|
|
}
|
|
|
|
/*
|
|
* loadSound(uri, handle)
|
|
*
|
|
* Loads the given sound
|
|
*
|
|
* arguments:
|
|
* @uri:String, the URI from which to load the sound
|
|
* @handle:String, the handle to store the sound as
|
|
*
|
|
* returns: none
|
|
*/
|
|
public function loadSound(uri:String, handle:String)
|
|
{
|
|
var request:URLRequest = new URLRequest(uri);
|
|
var snd = new Sound();
|
|
snd.load(request);
|
|
this.startObject(snd, handle);
|
|
}
|
|
|
|
/*
|
|
* playSound(handle)
|
|
*
|
|
* Locates the sound associated with the given handle, and plays it
|
|
*
|
|
* arguments:
|
|
* @handle:String, the text handle for the sound you want
|
|
*
|
|
* returns: Boolean (true on success, false on error)
|
|
*/
|
|
public function playSound(handle:String):Boolean
|
|
{
|
|
var snd = this.getObject(handle);
|
|
if ( !snd )
|
|
return false;
|
|
snd.play(); // we don't care about the channel it returns
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* onIOError(evt)
|
|
*
|
|
* This function fires whenever there's an IO Error that prevents the object from finishing loading
|
|
*
|
|
* arguments:
|
|
* @evt:IOErrorEvent, the event firing this function
|
|
*
|
|
* returns: none
|
|
*/
|
|
protected function onIOError(evt:IOErrorEvent)
|
|
{
|
|
var obj = evt.target;
|
|
var obj_info = this.loading_by_instance[obj];
|
|
var handle = obj_info[0];
|
|
var read = obj_info[1];
|
|
var total = obj_info[2];
|
|
this.stopObject(obj);
|
|
}
|
|
|
|
/*
|
|
* onSecurityError(evt)
|
|
*
|
|
* This function fires whenever there's a security error that prevents the object from finishing loading
|
|
*
|
|
* arguments:
|
|
* @evt:SecurityErrorEvent, the event firing this function
|
|
*
|
|
* returns: none
|
|
*/
|
|
protected function onSecurityError(evt:SecurityErrorEvent)
|
|
{
|
|
var obj = evt.target;
|
|
var obj_info = this.loading_by_instance[obj];
|
|
var handle = obj_info[0];
|
|
var read = obj_info[1];
|
|
var total = obj_info[2];
|
|
this.stopObject(obj);
|
|
}
|
|
|
|
/*
|
|
* onProgressUpdate(evt)
|
|
*
|
|
* This function fires whenever there's a progress update on the object currently loading
|
|
*
|
|
* arguments:
|
|
* @evt:ProgressEvent, the event firing this function
|
|
*
|
|
* returns: none
|
|
*/
|
|
protected function onProgressUpdate(evt:ProgressEvent)
|
|
{
|
|
var obj = evt.target;
|
|
var handle = this.loading_by_instance[obj][0];
|
|
this.loading_by_instance[obj][1] = this.loading_by_handle[handle][1] = evt.bytesLoaded;
|
|
this.loading_by_instance[obj][2] = this.loading_by_handle[handle][2] = evt.bytesTotal;
|
|
this.drawProgressMeters(evt);
|
|
this.progSpin += 1;
|
|
if ( this.progSpin >= this.progressSpinner.length ) {
|
|
this.progSpin = 0;
|
|
}
|
|
this.loadingLabel.text = "Please wait; loading ... " + this.progressSpinner[this.progSpin];
|
|
}
|
|
|
|
/*
|
|
* onLoadComplete(evt)
|
|
*
|
|
* This function fires whenever a file finishes loading. You can call this, after setting the assets array, with a null event.
|
|
* In such a case, the loading process will simply be started.
|
|
*
|
|
* arguments:
|
|
* @evt:Event, the event firing this function (default null)
|
|
*
|
|
* returns: none
|
|
*/
|
|
protected function onLoadComplete(evt:Event = null)
|
|
{
|
|
if ( evt != null ) {
|
|
var obj = evt.target;
|
|
var handle = this.loading_by_instance[obj][0];
|
|
if ( obj is LoaderInfo ) {
|
|
this.objects[handle] = obj.content;
|
|
trace(this.classNames[handle]);
|
|
if ( (this.classNames[handle]) && (this.classNames[handle] != "") )
|
|
this.classDefs[handle] = obj.applicationDomain.getDefinition(this.classNames[handle]);
|
|
else
|
|
this.classDefs[handle] = null;
|
|
} else
|
|
this.objects[handle] = obj
|
|
this.stopObject(obj);
|
|
this.curAsset += 1;
|
|
}
|
|
if ( this.curAsset < this.assets.length ) {
|
|
var asset = this.assets[this.curAsset];
|
|
var asset_stat = this.getObjectStatus(asset[1]);
|
|
if ( asset_stat.state == LoadingObject.STATE_NOTFOUND ) {
|
|
if ( asset[0] == "sounds" ){
|
|
this.loadSound(asset[2], asset[1]);
|
|
} else if ( asset[0] == "images" ) {
|
|
this.loadImage(asset[2], asset[1]);
|
|
} else if ( asset[0] == "movies" ) {
|
|
this.loadMovie(asset[2], asset[1], asset[3]);
|
|
}
|
|
}
|
|
} else if ( this.curAsset >= this.assets.length ) {
|
|
var evt = new Event(Event.COMPLETE);
|
|
this.dispatchEvent(evt);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* drawProgressMeters(evt)
|
|
*
|
|
* This function redraws the visible progress meters; it is generally called from inside of onProgressUpdate.
|
|
* It isn't fired by a timer, but it does need to have the event passed in to it so it can access the bytes read, etc.
|
|
*
|
|
* arguments:
|
|
* @evt:ProgressEvent, the event firing this function (default null)
|
|
*
|
|
* returns: none
|
|
*/
|
|
protected function drawProgressMeters(evt:ProgressEvent)
|
|
{
|
|
this.graphics.clear();
|
|
// draw the file progress meter
|
|
// white box
|
|
this.graphics.lineStyle(1,0xFFFFFF);
|
|
this.graphics.beginFill(0xFFFFFF);
|
|
this.graphics.drawRoundRect(60, 30, 200, 15, 5);
|
|
this.graphics.endFill();
|
|
// blue bar
|
|
this.graphics.lineStyle(1,0x6E6BF4);
|
|
this.graphics.beginFill(0x6E6BF4);
|
|
this.graphics.drawRoundRect(60, 30, 200*(evt.bytesLoaded/evt.bytesTotal), 15, 5);
|
|
this.graphics.endFill();
|
|
// red box
|
|
this.graphics.lineStyle(3, 0xAC2626);
|
|
this.graphics.drawRoundRect(60, 30, 200, 15, 5);
|
|
// draw the total progress meter
|
|
// white box
|
|
this.graphics.lineStyle(1,0xFFFFFF);
|
|
this.graphics.beginFill(0xFFFFFF);
|
|
this.graphics.drawRoundRect(60, 60, 200, 15, 5);
|
|
this.graphics.endFill();
|
|
// blue bar
|
|
this.graphics.lineStyle(1,0x6E6BF4);
|
|
this.graphics.beginFill(0x6E6BF4);
|
|
this.graphics.drawRoundRect(60, 60, 200*(this.curAsset/this.assets.length), 15, 5);
|
|
this.graphics.endFill();
|
|
// red box
|
|
this.graphics.lineStyle(3, 0xAC2626);
|
|
this.graphics.drawRoundRect(60, 60, 200, 15, 5);
|
|
}
|
|
}
|
|
|
|
} |