package com.actiontad.gameUtils
{

	import flash.display.DisplayObject;
	import flash.events.EventDispatcher;
	import flash.events.KeyboardEvent;
	import flash.events.TimerEvent;
	import flash.events.Event;
	import flash.ui.Keyboard;
	import flash.utils.Timer;
	import flash.utils.getTimer;

 /**
 * The ComboKeys class simplifies handling keyboard input.
* And allows you to also capture combinations of keys.
* * * Usage: * * var key:ComboKeys = new ComboKeys(stage); * if (key.pressed(37)) { ... } * * if (key.combo([38,38,40,40])) { ... } * Note: This class does not extend the Proxy Class and does not automatically understand the consts of the Keyboard Class. * To use those constants instead of numbers (in your class housing the ComboKeys class), one must import the Keyboard class and use it for the consts. * */ public class ComboKeys extends Object { public var mySelf:DisplayObject; //when do combos register, on key 'up' or 'down' public var combosWhen:String = "up"; public static var comboDelay:Number = 2400; //in epox time private static var keysInSuccession:Array = []; private static var theStage:EventDispatcher; private static var currentKeysPressed:Object; private static var comboTimer:Number = 0; /** * Each const such as this one for 'down' is also in the com.actiontad.basicGameEvents.GameObjectEvents class in captialized standard form. * They exsist in this class only so that this class does not have to be linked with basicGameObejcts.

* If using basicGameObjects, the consts found in com.actiontad.basicGameEvents.GameObjectEvents (such as GameObjectEvents.DOWN) * are what should be used outside of this class. */ public static const dwn:String = "down"; public static const upstr:String = "up"; public static const keystr:String = "key"; public static const skwu:String = "someKeyWentUp"; /** *

* String based key (up) named events are dispatched on mySelf. * So in mySelf you could do, for example: addEventListener(GameObjectEvents.KEY + 32 + GameObjectEvents.DOWN, respondToSpaceUp); * Also a general key up event is dispatched, when any key goes up, it's called - "someKeyWentUp" or GameObjectEvents.ANY_KEY_UP

* This is a way you could set up a key down event dispatch for when multiple keys are down:
* * var keysToRespondTo:Array = [32,37,38,39,40]; * for (var kr:int = 0; kr < _keysToRespondTo.length; kr++) { * var keyis:int= _keysToRespondTo[kr]; * if (key.pressed(keyis)) { * dispatchEvent(new Event(GameObjectEvents.KEY + keyis + GameObjectEvents.DOWN)); * } * } *
* That would need to happen onEnterFrame or some loop in mySelf. * @param theStageToBe The EventDispatcher that will listen for the KeyboardEvents, normally the stage. * @param thiser The DisplayObject directly effected by key presses, normally the main character, * this is the DisplayObject on which you would like to be able to add 'key32down' or 'key32up' style event listeners. * @param comboD The delay of combos in milliseconds, must press combination faster than this number to register. * @param comboResponseWhen When do combos register, on key "up" or key "down", "up" or "down" are the only valid values. * If "up" the keys must each be released first before they get added as part of a successful combo chain. * If "down" the keys can be pressed in combination, piano style, (note, that means continuously, so use with caution) * The default is "up". */ public function ComboKeys(theStageToBe:EventDispatcher, thiser:DisplayObject = null, comboD:Number = 2400, comboResponseWhen:String = "up") { ComboKeys.comboDelay = comboD; mySelf = thiser; combosWhen = comboResponseWhen; initialize(theStageToBe); } protected function initialize(s:EventDispatcher):void { ComboKeys.theStage = s; currentKeysPressed = new Object(); configureKeyListeners(); } /** * You may want to call this function after you have called killAllKeyEvents, to re-initialize the class. * It is an advanced option. */ public function configureKeyListeners():void { theStage.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler); theStage.addEventListener(KeyboardEvent.KEY_UP, keyUpHandler); } public function pressed(keyCodeValue:uint):Boolean { return Boolean(keyCodeValue in currentKeysPressed); } /** * * @param nullTheStageRef Also nulls out theStage reference if you pass true here. Default is false. */ public function killAllKeyEvents(nullTheStageRef:Boolean = false):void { theStage.removeEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler); theStage.removeEventListener(KeyboardEvent.KEY_UP, keyUpHandler); currentKeysPressed = new Object(); if (nullTheStageRef == true) ComboKeys.theStage = null; } protected function keyDownHandler(e:KeyboardEvent):void { var theKeyDown:int = e.keyCode; currentKeysPressed[theKeyDown] = true; if (combosWhen == dwn) { var moment:uint = getTimer(); if (comboTimer == 0) { comboTimer = moment; } if (moment < comboTimer + comboDelay) { keysInSuccession[keysInSuccession.length] = theKeyDown; } else { comboTimer = moment; keysInSuccession = [theKeyDown]; } } //if (mySelf) mySelf.dispatchEvent(new Event(GameObjectEvents.KEY+theKeyDown+GameObjectEvents.DOWN)); //the problem with that is that it will not happen continually //you have to use a loop to continually check if a key is still down. //you can listen for this event and switch a boolean on key up, //but still you would have to use a loop to determine the boolean state for continual key presses. //A good thing to do is handle such a loop in mySelf as described in the ComboKeys constructor. //IKeyControlledObject implementors in the basicGameObjects package handle the loop, and also the creation of events, //in an effort to reduce 'new' calls for key down handling. //That is why the above is commented out of this class. //You can uncomment it if you like, it will cause a conflict with those classes if you do however. } protected function keyUpHandler(e:KeyboardEvent):void { var theKeyUp:int = e.keyCode; delete currentKeysPressed[theKeyUp]; if (combosWhen == upstr) { var moment:uint = getTimer(); if (comboTimer == 0) { comboTimer = moment; } if (moment < comboTimer + comboDelay) { keysInSuccession[keysInSuccession.length] = theKeyUp; } else { comboTimer = moment; keysInSuccession = [theKeyUp]; } } if (mySelf) { //note: The following event strings are also from the com.actiontad.basicGameEvents.GameObjectEvents class //That class has been left out of this class, so that this class does not have to be used //with basicGameObejcts. mySelf.dispatchEvent(new Event(keystr+theKeyUp+upstr)); mySelf.dispatchEvent(new Event(skwu)); } } public function combo(checkFor:Array):Boolean { var status:Boolean = false; var len:int = checkFor.length-1; var cmb:int = 0; var i:int; var lastKeyPressed:Number; if (keysInSuccession.length >= checkFor.length) { lastKeyPressed = keysInSuccession.length-1; for (i = len; i > -1; i--) { if ( keysInSuccession[lastKeyPressed] == checkFor[len] ) { if (keysInSuccession[lastKeyPressed-i] && keysInSuccession[lastKeyPressed-i] == checkFor[len-i]) cmb += 1; } } if (cmb == checkFor.length) { comboTimer = getTimer(); status = true;ComboKeys.keysInSuccession = []; } } return status; } } }