package net.aklabs.demo.simonsays { import flash.utils.Timer; import flash.events.Event; import flash.events.TimerEvent; import flash.events.EventDispatcher; import flash.display.Sprite; import flash.display.Bitmap; import net.aklabs.demo.simonsays.Mastermind; import net.aklabs.demo.simonsays.Powerup; import net.aklabs.demo.simonsays.PowerupEvent; import net.aklabs.demo.simonsays.Preloader; /* * Class Pattern * * This class defines a "pattern", which is where most of the game challenge exists. * It just defines the pattern currently being traced out on the Simon Says/Mastermind/etc * * In addition to doing processing, this class also does some graphical representation, * in the way of a series of red/green lights to show how far along the player is inside the * pattern. The image is 16 pixels wide, and (16*maxSize) pixels tall. * */ public class Pattern extends Sprite { public static var COLOR_BLUE:Number = 0; public static var COLOR_GREEN:Number = 1; public static var COLOR_RED:Number = 2; public static var COLOR_YELLOW:Number = 3; protected var patternTimer:Timer; // timer that fires to check pattern logic independently of game timer protected var curIndex:Number; // amount of the pattern currently finished protected var pattern:Array; // the array of colors that make up the actual pattern protected var simon:Mastermind; // A link back up to the parent Mastermind (put here because I didn't want to assume that this.parent would always be correct) protected var delay:Number; // The number of milliseconds that should pass before the logic timer fires protected var playCount:Number; // The number of times this pattern has been played through (via Forgiveness). Not currently used for anything. public var clearTime:Number; // the amount of time the player has (in milliseconds) to finish the pattern public var state:Number; // state of the pattern at current public static var STATE_RUNNING = 0; // STATE : pattern is currently "running" - e.g., waiting for input from the player and checking logic public static var STATE_DEMO = 1; // STATE : pattern is currently "demoing", e.g., flashing the lights up to this.curIndex of the pattern public static var STATE_FAILED = 2; // STATE : pattern is stopped and the player screwed up public static var STATE_CORRECT = 3; // STATE : pattern is stopped and the player got the last iteration correct public static var STATE_STOPPED = 4; // STATE : pattern is just stopped w/ no further info public static var STATE_COMPLETE = 5; // STATE : pattern is stopped because the player has finished all iterations of this pattern public var score:Number; // Score currently built up in this pattern protected var maxSize:Number; // Maximum length of the pattern public var patternPowerups:Array; // array of powerups assigned to any given button on this pattern protected var scoreMultiplier:Number; // Set by powerup, the multiplier for current score (default x1) protected var clearDecrement:Number; // Set by powerup, defines how quickly the pattern's timer runs protected var progressLights:Array; // An array of bitmaps equal in length to the pattern; all the lights up to this.curIndex will be green, the rest are red protected var level:Number; // The level of pattern which this is (mainly used for calculating pattern length, could probly be scrapped) protected var difficulty:Number; // The difficulty of the pattern (used in calculating timer speed) /* * Pattern() * * Default constructor * * arguments: * @levelNumber:Number, the level number for which this pattern is being made * @simon:Mastermind, the Mastermind object to which this pattern is to be applied (default null) * @difficulty:Number, the difficulty level for this pattern (default 1) * * Returns: Pattern */ public function Pattern(levelNumber:Number, simon:Mastermind = null, difficulty:Number = 1) { this.state = 0; this.simon = simon; this.level = levelNumber; this.difficulty = difficulty; this.curIndex = -1; this.maxSize = 2+levelNumber; // smallest pattern will never be less than 3 lights total this.pattern = new Array(); this.patternPowerups = new Array(); this.progressLights = new Array(); this.patternTimer = null; this.delay = 1000; this.playCount = 0; this.clearTime = 0; this.score = 0; this.scoreMultiplier = 1; this.clearDecrement = 0; this.patternTimer = new Timer(0); this.addEventListener(PowerupEvent.USED_POWERUP, this.onUsedPowerup); var preloader:Preloader = Preloader.getInstance(); var newLight:Bitmap; // populate all the red lights for this pattern that will turn green as the player goes on for ( var i:Number = 0; i < this.maxSize ; i++ ) { newLight = preloader.getBitmap("gfx_progress_red"); newLight.x = 0; newLight.y = i*16; if ( i > 15 ) { newLight.x += 20; newLight.y = (i-15)*16; } this.progressLights.push(newLight); this.addChild(newLight); } this.complexify(); } /* * patternLength() * * Just a getter for the length of the pattern * * arguments : none * * Returns : Number */ public function patternLength():Number { return this.pattern.length; } /* * forceState() * * Forces a given state onto the pattern * * arguments: none * * Returns : none */ public function forceState(state:Number) { this.state = state; } /* * complexify() * * This function adds another element to the pattern, so that it becomes a longer pattern, up until the maximum length of the pattern * * arguments: none * * Returns : none */ public function complexify() { // we're complete if we're beyond the maximum size if ( this.pattern.length >= this.maxSize ) { this.stop(); this.state = Pattern.STATE_COMPLETE; return null; } // reset the score multiplier and such 'cause complexify only gets called at the end of a round this.scoreMultiplier = 1; this.state = Pattern.STATE_STOPPED; this.stop(); // -- wtf why did I call stop on myself? ... var newcolor = (Math.round(int(Math.random()*4))); this.pattern.push(newcolor); // create a new powerup ~20% of the time that complexify is ran if ( Math.random() < 0.20 ) { var powerup = new Powerup(); powerup.pType = (Math.round(int(Math.random()*Powerup.PTYPE_MAXVALUE))); powerup.imgHandle = Powerup.POWERUP_IMAGES[powerup.pType]; powerup.sndHandle = Powerup.POWERUP_SOUNDS[powerup.pType]; this.patternPowerups.push(powerup); } else { this.patternPowerups.push(null); } this.clearTime = 2000 * ( this.pattern.length ); } /* * play(lightSimon) * * This function tells the pattern to start playback in one of two modes; demo, or running. Demo mode * just has the pattern playing itself back via the lights on the Mastermind. The running mode doesn't * do any playback, it just checks logic. * * arguments: * @lightSimon:Boolean, set this to True to run in Demo mode (default false) * * Returns : none */ public function play(lightSimon:Boolean = false) { if ( this.patternTimer ) { this.patternTimer.reset() } else { this.patternTimer = new Timer( this.delay ); } if ( lightSimon ){ this.state = Pattern.STATE_DEMO; this.patternTimer.delay = 1000; this.patternTimer.addEventListener(TimerEvent.TIMER, this.onDemoTimer); } else { this.state = Pattern.STATE_RUNNING; this.patternTimer.delay = 25; this.clearDecrement = this.patternTimer.delay + (this.level*(this.difficulty)); this.patternTimer.addEventListener(TimerEvent.TIMER, this.onRunningTimer); } this.patternTimer.start(); this.playCount += 1; this.curIndex = 0; } /* * stop() * * Stop all logic/running status on the pattern * * arguments: none * * Returns : none */ public function stop() { if ( this.patternTimer ) { this.patternTimer.removeEventListener(TimerEvent.TIMER, this.onDemoTimer); this.patternTimer.removeEventListener(TimerEvent.TIMER, this.onRunningTimer); } this.curIndex = -1; } /* * getIndex() * * Gets the current value is curIndex * * arguments: none * * Returns : none */ public function getIndex() { return this.curIndex; } /* * getActive() * * OBSOLETE - gets the currecntly active color. This isn't as useful since the input checking method changed around v0.12. * * arguments: none * Returns : Number, -1 on failure, >= 0 on success */ public function getActive() { if ( (this.curIndex < this.pattern.length) && (this.curIndex >= 0) ) { return this.pattern[this.curIndex]; } return -1; } /* * resetLights() * * This function makes sure the ratio of red:green lights in the progress lights is correct * according to the value of curIndex * * arguments: none * * Returns: none */ public function resetLights() { var preloader:Preloader = Preloader.getInstance(); var newLight:Bitmap; for ( var i:Number = 0; i <= this.curIndex ; i++ ) { // remove any existing red lights and replace them with green lights // if they're at an index < curIndex this.removeChild(this.progressLights[i]); newLight = preloader.getBitmap("gfx_progress_green"); newLight.x = this.progressLights[i].x; newLight.y = this.progressLights[i].y; this.progressLights[i] = newLight; this.addChild(this.progressLights[i]); } } /* * colorActive(color) * * Checks to see if the given color is the one currently active on the pattern. * Also updates the current index, modifies state, etc, depending on the result. * * arguments: * @color:Number, the Pattern.COLOR_XXX color you want checked * * Returns: Boolean (Always returns false on a stopped pattern) */ public function colorActive(color:Number):Boolean { if ( this.curIndex == -1 ) { return false; } if ( (this.curIndex < this.pattern.length) && (this.pattern[this.curIndex] == color) ) { // dispatch a PowerupEvent if there is a powerup in this spot at the pattern if ( this.patternPowerups[this.curIndex] != null ) { this.dispatchEvent(new PowerupEvent(PowerupEvent.GOT_POWERUP, this.patternPowerups[this.curIndex])); this.patternPowerups[this.curIndex] = null; } this.resetLights(); this.curIndex += 1; this.score += 5; if ( this.curIndex >= this.pattern.length ) { this.curIndex = -1; this.state = Pattern.STATE_CORRECT; this.stop(); } return true; } this.state = Pattern.STATE_FAILED; this.stop(); return false; } /* * onRunningTimer(evt) * * Fires along w/ the runningTimer to check pattern logic * * arguments: * @evt:Event, event firing this function * * Returns : none */ public function onRunningTimer(evt:TimerEvent) { if ( (!evt) || (evt.target != this.patternTimer) ) return; this.clearTime -= this.clearDecrement; if ( this.clearTime < 0 ) { this.clearTime = 0; this.state = Pattern.STATE_FAILED; this.stop(); } } /* * onDemoTimer(evt) * * Runs every time the Demo timer fires, lighting the buttons in sequence at the right times * * arguments: * @evt:Event, the event firing this function * * Returns : none */ public function onDemoTimer(evt:TimerEvent) { if ( (!evt) || (evt.target != this.patternTimer) || (!this.simon) ) return; if ( this.curIndex >= this.pattern.length ) { this.state = Pattern.STATE_STOPPED; this.stop(); return; } this.simon.lightButton(this.pattern[curIndex], this.patternTimer.delay/2); this.curIndex += 1; } /* * onUsedPowerup(evt) * * This function fires whenever a PowerupEvent filters down from the Mastermind, which originally * filtered up from the Player. It processes and applies the effects of any powerups used by the player. * * arguments: * @evt:PowerupEvent, the event firing this function * * Returns: none */ public function onUsedPowerup(evt:PowerupEvent) { var pwup:Powerup = evt.pwup; if ( pwup.pType == Powerup.PTYPE_FORGIVENESS ) { // we don't actually have to *do* anything with a forgiveness ... return; } else if ( pwup.pType == Powerup.PTYPE_SLOWDOWN ) { this.clearDecrement = 1; } else if ( pwup.pType == Powerup.PTYPE_SKIP ) { this.state = Pattern.STATE_CORRECT; this.stop(); this.resetLights(); } else if ( pwup.pType == Powerup.PTYPE_DOUBLE ) { this.scoreMultiplier = 2; } return; } } }