This repository has been archived on 2026-05-18. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files

394 lines
13 KiB
ActionScript
Raw Permalink Normal View History

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;
}
}
}