package com.actiontad.gameUtils
{
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.display.Shape;
	import flash.utils.Timer;
	import flash.events.TimerEvent;
	import com.actiontad.gameUtils.RPEase;

	/**
	 * com.actiontad.gameUtils.SimpleStaticTweener VERSION 1  A simple and lightweight frame based static Tweener class. 
* Example use:
* SimpleStaticTweener.tweenToXY(myMovieClip, 5, 5, 10, "OutBounce");//obj,x,y,intervals,ease - starts from current x and y

* * SimpleStaticTweener.tweenSpecific(myObj, "someNumericProperty", 15, 1, "Linear");//obj, property, end result, intervals, ease - starts from current value of property

* * SimpleStaticTweener.tweenBatch(mySprite, {x:{start:5, end:10, interval:1, how:"OutBounce"}, y:{start:0, end:10, interval:2, how:"Linear"}}); * *
* Intervals in this class are in milliseconds.
* The rate of any tween can not be faster than the frameRate. (and the real frame rate of the swf, if not using a timer)
* * * * @author (t)ad */ public final class SimpleStaticTweener { public static var speed:int = 1; /** * When useTimer is set to true, this is the delay of the timer. * */ public static var frameRate:Number = 30; /** * Must be set before first tween call if using true. * */ public static var useTimer:Boolean; public static var millisecondRate:Number = 1000; private static var easeOptions:Array = [ "Linear", "InLinear", "OutLinear", "InOutLinear", "InElastic", "OutElastic", "InOutElastic", "InQuad", "OutQuad", "InOutQuad", "InBounce", "InOutBounce", "OutBounce", "InCirc", "InBack", "OutBack", "InOutBack", "InQuint", "OutQuint", "InOutQuint", "OutCirc", "InOutCirc", "In", "Out", "InOut", "InSine", "OutSine", "InOutSine" ]; private static var cache:Array = []; private static var gatherTweeners:Event; private static var tweenerCall:String = "recycle"; private static var loopSource:EventDispatcher; private static var theLoop:Event; private static var recycleMe:Event; private static var globalCommand:String = "recycleTweeners"; private static var individualLoop:Event; private static var iLoopDispatch:String = "imLooping"; private static var capitalLoop:String = "TheLoop"; private static var backs:String = "Back"; private static function nextRunCalc(toThis:Object):Function { //this function is up here because it may well be a setter but just can not be. var tw:Object = toThis; var runCalcsForTw:Function = function (e:Event):void { var tweenObj:Object; for (var i:String in tw.propObject) { tweenObj = tw.propObject[i]; if (tweenObj.finished == 0) { if (tweenObj.index == tweenObj.values.length ) { tweenObj.finished = 1; } else { tw.mover[i] = tweenObj.values[tweenObj.index];//calcs tweenObj.index += 1;// speed; } } } var done:int = 1; for (i in tw.propObject) { if (tw.propObject[i].finished == 0) { done = 0; } } if (done == 1) { e.target.removeEventListener(iLoopDispatch, tw.runCalcs); if (tw.toBeCalled != null) { tw.toBeCalled(); } tw.propObject = { }; tw.runCalcs = null; loopSource.removeEventListener(capitalLoop, tw.callBack); tw.callBack = null; tw.mover = { }; var pickMe:Function = function(e:Event):void { loopSource.removeEventListener(globalCommand, pickMe); tw.evnd.dispatchEvent(recycleMe); }; loopSource.addEventListener(globalCommand, pickMe); } }; return runCalcsForTw; } /** * The constructor is not used. */ public function SimpleStaticTweener() { //Instantiation is not needed. All relevant methods and properties are static. } private static function fillCacheQue():void { if (!loopSource) { cache = []; if(useTimer) { loopSource = new Timer(frameRate, 0); loopSource.addEventListener(TimerEvent.TIMER, dispatchTheLoop); (loopSource as Timer).start(); } else { loopSource = new Shape(); loopSource.addEventListener(Event.ENTER_FRAME, dispatchTheLoop); } gatherTweeners = new Event("recycleTweeners"); recycleMe = new Event("recycle"); theLoop = new Event("TheLoop"); individualLoop = new Event("imLooping"); } loopSource.dispatchEvent(gatherTweeners); if (cache.length == 0) { var count:int = 10; while (count > 0) { cache.push({evnd:new EventDispatcher()}); count--; } } } public static function deactivate(e:Event = null):void { cache = []; loopSource.removeEventListener(TimerEvent.TIMER, dispatchTheLoop); loopSource.removeEventListener(Event.ENTER_FRAME, dispatchTheLoop); if(loopSource as Timer) (loopSource as Timer).stop(); } public static function tweenToXY(obj:Object, x:int = 1, y:int = 1, intervals:Number = 1, how:String = "Linear", thenCall:Function = null):void { if ( cache.length <= 1 ) { fillCacheQue(); } var tw:Object = cache.pop(); //get a tween spec holding object from the cache var duration:Number = intervals;// int( frameOffsetCalc( intervals ) ); tw.mover = obj;//the object to move tw.propObject = { }; if (obj.x is Number && obj.y is Number) { //make sure the object has at least x and y Number properties tw.propObject.x = { start:obj.x, end:x, interval:duration, index:0, finished:0 }; // set the initial specs of the x tween tw.propObject.y = { start:obj.y, end:y, interval:duration, index:0, finished:0 }; // set the initial specs of the y tween, these are 2d objects, or json like objects tw.propObject.x.values = calcProps(tw.propObject.x.start, tw.propObject.x.end, tw.propObject.x.interval, how);// calculate points to cross tw.propObject.y.values = calcProps(tw.propObject.y.start, tw.propObject.y.end, tw.propObject.y.interval, how);// this is the magic, caclProps is the heart of the whole system. } if (thenCall != null) { tw.toBeCalled = thenCall; } //if a after tween function is defined reference it for later var cb:Function = function(e:Event):void { tw.evnd.dispatchEvent(individualLoop); }; tw.callBack = cb; tw.runCalcs = nextRunCalc(tw); loopSource.addEventListener(capitalLoop, tw.callBack); tw.evnd.addEventListener(tweenerCall, pleaseTakeMeBack); //subscribe the tweener to the globaly dispatched recycling event tw.evnd.addEventListener(iLoopDispatch, tw.runCalcs);//setup the tween to run on the LoopEvent return; } public static function tweenSpecific(obj:Object, propertyToTween:String, endAmount:Number, intervals:Number = 1, how:String = "Linear", thenCall:Function = null):void { if ( cache.length <= 1 ) { fillCacheQue(); } var tw:Object = cache.pop(); tw.mover = obj; tw.propObject = { }; var duration:Number = intervals;// int( frameOffsetCalc(intervals) ); tw.propObject[propertyToTween] = { start:obj[propertyToTween], end:endAmount, interval:duration, index:0, finished:0 }; tw.propObject[propertyToTween].values = calcProps(tw.propObject[propertyToTween].start, tw.propObject[propertyToTween].end, tw.propObject[propertyToTween].interval, how); if (thenCall != null) { tw.toBeCalled = thenCall; } var cb:Function = function(e:Event):void { tw.evnd.dispatchEvent(individualLoop); }; tw.callBack = cb; tw.runCalcs = nextRunCalc(tw); loopSource.addEventListener(capitalLoop, tw.callBack); tw.evnd.addEventListener(tweenerCall, pleaseTakeMeBack); //subscribe the tweener to the globaly dispatched recycling event tw.evnd.addEventListener(iLoopDispatch, tw.runCalcs);//setup the tween to run on the LoopEvent return; } public static function tweenBatch(obj:Object, propertyBatch:Object, thenCall:Function = null):void { if ( cache.length <= 1 ) { fillCacheQue(); } var tw:Object = cache.pop(); tw.mover = obj; tw.propObject = { }; var duration:Number; for (var i:* in propertyBatch) { duration = propertyBatch[i].interval; tw.propObject[i] = { start:propertyBatch[i].start, end:propertyBatch[i].end, interval:duration, how:propertyBatch[i].how, index:0, finished:0 }; tw.propObject[i].values = calcProps(tw.propObject[i].start, tw.propObject[i].end, tw.propObject[i].interval, tw.propObject[i].how); } if (thenCall != null) { tw.toBeCalled = thenCall; } var cb:Function = function(e:Event):void { tw.evnd.dispatchEvent(individualLoop); }; tw.callBack = cb; tw.runCalcs = nextRunCalc(tw); loopSource.addEventListener(capitalLoop, tw.callBack); tw.evnd.addEventListener(tweenerCall, pleaseTakeMeBack); tw.evnd.addEventListener(iLoopDispatch, tw.runCalcs); return; } private static function pleaseTakeMeBack(e:Event):void { var t:Object = e.target; t.removeEventListener(tweenerCall, pleaseTakeMeBack); cache.unshift({evnd:new EventDispatcher()}); } public static function calcProps(start:Number, end:Number, interval:Number, how:String = "Linear"):Array { var v:Array = []; var si:int = 0; var realHow:String = "Linear"; for (si = 0; si < easeOptions.length; si++) { if ( easeOptions[si].toLowerCase() == how.toLowerCase() ) { realHow = easeOptions[si]; break; } } var ending:Number = end - start; si = 0; var argsers:Array = [ ((realHow.indexOf(backs) != -1)?1.70158:0) , 0 ]; var secondIterations:int = int( frameOffsetCalc(interval) ); var milliRate:int = int(millisecondRate / frameRate); var millisecondIterations:int = (interval / milliRate)>=1?int(interval/milliRate):1; for (si = 0; si <= millisecondIterations; si++) { v[si] = RPEase[realHow](si, start, ending, millisecondIterations, argsers[0], argsers[1]);//heart } return v; } private static function dispatchTheLoop(e:Event = null):void { if(e as TimerEvent) (e as TimerEvent).updateAfterEvent(); loopSource.dispatchEvent(theLoop); } public static function frameOffsetCalc(secondsWanted:Number = 1):Number { var result:Number = ( (millisecondRate / frameRate) * secondsWanted ); return result; } } }