commit c4ae376c8bd0fccefeb4ded81973fe01f9dd1fbc Author: andrew Date: Thu Jul 29 01:33:30 2010 +0000 Moving stuff around, nothing to see here git-svn-id: https://aklabs.dyndns.org/svn/aklabs/trunk/games/wilysays@50 eb184899-6090-47d4-a65b-558f62f6ea1c diff --git a/AC_RunActiveContent.js b/AC_RunActiveContent.js new file mode 100755 index 0000000..30cddb9 --- /dev/null +++ b/AC_RunActiveContent.js @@ -0,0 +1,292 @@ +//v1.7 +// Flash Player Version Detection +// Detect Client Browser type +// Copyright 2005-2007 Adobe Systems Incorporated. All rights reserved. +var isIE = (navigator.appVersion.indexOf("MSIE") != -1) ? true : false; +var isWin = (navigator.appVersion.toLowerCase().indexOf("win") != -1) ? true : false; +var isOpera = (navigator.userAgent.indexOf("Opera") != -1) ? true : false; + +function ControlVersion() +{ + var version; + var axo; + var e; + + // NOTE : new ActiveXObject(strFoo) throws an exception if strFoo isn't in the registry + + try { + // version will be set for 7.X or greater players + axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7"); + version = axo.GetVariable("$version"); + } catch (e) { + } + + if (!version) + { + try { + // version will be set for 6.X players only + axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6"); + + // installed player is some revision of 6.0 + // GetVariable("$version") crashes for versions 6.0.22 through 6.0.29, + // so we have to be careful. + + // default to the first public version + version = "WIN 6,0,21,0"; + + // throws if AllowScripAccess does not exist (introduced in 6.0r47) + axo.AllowScriptAccess = "always"; + + // safe to call for 6.0r47 or greater + version = axo.GetVariable("$version"); + + } catch (e) { + } + } + + if (!version) + { + try { + // version will be set for 4.X or 5.X player + axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.3"); + version = axo.GetVariable("$version"); + } catch (e) { + } + } + + if (!version) + { + try { + // version will be set for 3.X player + axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.3"); + version = "WIN 3,0,18,0"; + } catch (e) { + } + } + + if (!version) + { + try { + // version will be set for 2.X player + axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash"); + version = "WIN 2,0,0,11"; + } catch (e) { + version = -1; + } + } + + return version; +} + +// JavaScript helper required to detect Flash Player PlugIn version information +function GetSwfVer(){ + // NS/Opera version >= 3 check for Flash plugin in plugin array + var flashVer = -1; + + if (navigator.plugins != null && navigator.plugins.length > 0) { + if (navigator.plugins["Shockwave Flash 2.0"] || navigator.plugins["Shockwave Flash"]) { + var swVer2 = navigator.plugins["Shockwave Flash 2.0"] ? " 2.0" : ""; + var flashDescription = navigator.plugins["Shockwave Flash" + swVer2].description; + var descArray = flashDescription.split(" "); + var tempArrayMajor = descArray[2].split("."); + var versionMajor = tempArrayMajor[0]; + var versionMinor = tempArrayMajor[1]; + var versionRevision = descArray[3]; + if (versionRevision == "") { + versionRevision = descArray[4]; + } + if (versionRevision[0] == "d") { + versionRevision = versionRevision.substring(1); + } else if (versionRevision[0] == "r") { + versionRevision = versionRevision.substring(1); + if (versionRevision.indexOf("d") > 0) { + versionRevision = versionRevision.substring(0, versionRevision.indexOf("d")); + } + } + var flashVer = versionMajor + "." + versionMinor + "." + versionRevision; + } + } + // MSN/WebTV 2.6 supports Flash 4 + else if (navigator.userAgent.toLowerCase().indexOf("webtv/2.6") != -1) flashVer = 4; + // WebTV 2.5 supports Flash 3 + else if (navigator.userAgent.toLowerCase().indexOf("webtv/2.5") != -1) flashVer = 3; + // older WebTV supports Flash 2 + else if (navigator.userAgent.toLowerCase().indexOf("webtv") != -1) flashVer = 2; + else if ( isIE && isWin && !isOpera ) { + flashVer = ControlVersion(); + } + return flashVer; +} + +// When called with reqMajorVer, reqMinorVer, reqRevision returns true if that version or greater is available +function DetectFlashVer(reqMajorVer, reqMinorVer, reqRevision) +{ + versionStr = GetSwfVer(); + if (versionStr == -1 ) { + return false; + } else if (versionStr != 0) { + if(isIE && isWin && !isOpera) { + // Given "WIN 2,0,0,11" + tempArray = versionStr.split(" "); // ["WIN", "2,0,0,11"] + tempString = tempArray[1]; // "2,0,0,11" + versionArray = tempString.split(","); // ['2', '0', '0', '11'] + } else { + versionArray = versionStr.split("."); + } + var versionMajor = versionArray[0]; + var versionMinor = versionArray[1]; + var versionRevision = versionArray[2]; + + // is the major.revision >= requested major.revision AND the minor version >= requested minor + if (versionMajor > parseFloat(reqMajorVer)) { + return true; + } else if (versionMajor == parseFloat(reqMajorVer)) { + if (versionMinor > parseFloat(reqMinorVer)) + return true; + else if (versionMinor == parseFloat(reqMinorVer)) { + if (versionRevision >= parseFloat(reqRevision)) + return true; + } + } + return false; + } +} + +function AC_AddExtension(src, ext) +{ + if (src.indexOf('?') != -1) + return src.replace(/\?/, ext+'?'); + else + return src + ext; +} + +function AC_Generateobj(objAttrs, params, embedAttrs) +{ + var str = ''; + if (isIE && isWin && !isOpera) + { + str += ' '; + } + str += ''; + } + else + { + str += '= Game.KEY_0 && evt.keyCode <= Game.KEY_9) ) { + if ( evt.keyCode == Game.KEY_0 ) + evt.keyCode = Game.KEY_0 + 10; // trust me it makes sense (Key 0 is the player's last inventory slot to the right + // but it comes first in the keyCode sequence before 1, which is the far left, so we add 10 to it + // because we're passing an index from 0 - 9 for the player's inventory) + this.player.usePowerupAt(evt.keyCode - 49); + } + + if ( colorPressed != -1 ) + this.checkColorHit(colorPressed); + } else if ( this.primaryState == Game.STATE_MENU ) { + if ( evt.keyCode == Game.KEY_SPACE ) { + this.addChild(cutscenes["difficultychooser"]); + } + } else if ( this.primaryState == Game.STATE_TUTORIAL ) { + if ( evt.keyCode == Game.KEY_SPACE ) { + this.newGame(); + } + } else if ( this.primaryState == Game.STATE_WINGAME ) { + if ( evt.keyCode == Game.KEY_SPACE ) { + this.primaryState = Game.STATE_HIGHSCORE; + this.secondaryState = Game.STATE_HIGHSCORE; + this.removeChild(this.cutscenes["endscreen"]); + this.addChild(this.cutscenes["highscores"]); + this.cutscenes["highscores"].play(); + } + } + } + + /* + * onPreloaderComplete(evt) + * + * This function is fired whenever the Preloader fires an event signifying that all items + * in the asset list have been successfully loaded. + * + * arguments: + * @evt : Event, the event firing this function + * + * Returns: none + */ + + public function onPreloaderComplete(evt:Event) + { + if ( evt.target == this.preloader ){ + this.removeChild(this.preloader); + // -- setup the cutscenes so they're ready to use + this.cutscenes["intro_menu"] = this.preloader.getObject("movie_intro_menu"); + this.cutscenes["intro_menu"].x = 0; + this.cutscenes["intro_menu"].y = 0; + this.cutscenes["intro_menu"].addEventListener(Event.ENTER_FRAME, this.onNonLoopEnterFrame); + this.cutscenes["tutorial"] = this.preloader.getObject("movie_tutorial"); + this.cutscenes["tutorial"].x = 0; + this.cutscenes["tutorial"].y = 0; + this.cutscenes["tutorial"].addEventListener(Event.ENTER_FRAME, this.onNonLoopEnterFrame); + this.cutscenes["highscores"] = this.preloader.getObject("movie_highscore"); + this.cutscenes["highscores"].x = 0; + this.cutscenes["highscores"].y = 0; + this.cutscenes["highscores"].addEventListener(Event.ENTER_FRAME, this.onNonLoopEnterFrame); + this.cutscenes["highscores"].highScorePostBtn.addEventListener(MouseEvent.CLICK, this.onHighScoreEntry); + this.cutscenes["highscores"].highScoreCancelBtn.addEventListener(MouseEvent.CLICK, this.onHighScoreEntry); + this.cutscenes["highscores"].viewScoreBtn.addEventListener(MouseEvent.CLICK, this.onHighScoreEntry); + this.cutscenes["difficultychooser"] = this.preloader.getObject("movie_difficultychooser"); + this.cutscenes["difficultychooser"].x = 300; + this.cutscenes["difficultychooser"].y = 200; + this.cutscenes["difficultychooser"].easyBtn.addEventListener(MouseEvent.CLICK, this.onDifficultySelected); + this.cutscenes["difficultychooser"].normalBtn.addEventListener(MouseEvent.CLICK, this.onDifficultySelected); + this.cutscenes["difficultychooser"].hardBtn.addEventListener(MouseEvent.CLICK, this.onDifficultySelected); + this.cutscenes["difficultychooser"].wilyBtn.addEventListener(MouseEvent.CLICK, this.onDifficultySelected); + this.cutscenes["endscreen"] = this.preloader.getObject("movie_endscreen"); + this.cutscenes["endscreen"].x = 0; + this.cutscenes["endscreen"].y = 0; + + this.addChild(this.cutscenes["intro_menu"]); + + this.gameTimer.removeEventListener(TimerEvent.TIMER, this.onPreloaderComplete); + this.gameTimer.addEventListener(TimerEvent.TIMER, this.onGameTimer); + + this.simon = new Mastermind(); + this.simon.x = (640-(this.simon.width))/2; + this.simon.y = 20; + this.simon.addEventListener(MastermindEvent.BTN_CLICKED, this.onMastermindClicked); + + this.timerLabel = new TextField(); + this.timerLabel.background = false; + this.timerLabel.autoSize = TextFieldAutoSize.LEFT; + var timerLabelFormat = new TextFormat(); + timerLabelFormat.font = "Courier New"; + timerLabelFormat.bold = false; + timerLabelFormat.color = 0xF12B2B; + timerLabelFormat.size = 36; + this.timerLabel.defaultTextFormat = timerLabelFormat; + this.timerLabel.text = "00000"; + this.timerLabel.x = 504; + this.timerLabel.y = 34; + + this.scoreLabel = new TextField(); + this.scoreLabel.background = false; + this.scoreLabel.autoSize = TextFieldAutoSize.LEFT; + this.scoreLabel.defaultTextFormat = timerLabelFormat; + this.scoreLabel.text = "000000"; + this.scoreLabel.x = 12; + this.scoreLabel.y = 34; + + this.levelLabel = new TextField(); + this.levelLabel.background = false; + this.levelLabel.autoSize = TextFieldAutoSize.LEFT; + this.levelNumberLabel = new TextField(); + this.levelNumberLabel.background = false; + this.levelNumberLabel.autoSize = TextFieldAutoSize.LEFT; + var levelLabelFormat = new TextFormat(); + levelLabelFormat.font = "Helvetica"; + levelLabelFormat.bold = true; + levelLabelFormat.color = 0xF12B2B; + levelLabelFormat.size = 72; + this.levelLabel.defaultTextFormat = levelLabelFormat; + this.levelNumberLabel.defaultTextFormat = levelLabelFormat; + this.levelLabel.antiAliasType = AntiAliasType.ADVANCED; + this.levelNumberLabel.antiAliasType = AntiAliasType.ADVANCED; + this.levelLabel.filters = [new GlowFilter(0x000000, 1.0, 4, 4, 300)]; + this.levelNumberLabel.filters = [new GlowFilter(0x000000, 1.0, 4, 4, 300)]; + this.levelLabel.x = 150; + this.levelLabel.y = 500; + this.levelNumberLabel.x = 400; + this.levelNumberLabel.y = -150; + this.levelLabel.text = "LEVEL"; + + this.player.addEventListener(PowerupEvent.USED_POWERUP, this.onUsedPowerup); + this.playBackgroundMusic(); + this.primaryState = Game.STATE_MENU; + this.secondaryState = Game.STATE_MENU; + return; + } + } + + /* + * onBackgroundMusicFinished(evt) + * + * This function just makes sure that the background music loops forever + * + * arguments: + * @evt: Event, event firing this function + * + * Returns: none + */ + public function onBackgroundMusicFinished(evt:Event) + { + this.playBackgroundMusic(); + } + + + /* + * playBackgroundMusic() + * + * This function starts the background music playing + * + * arguments: none + * + * Returns : none + */ + public function playBackgroundMusic() + { + if ( this.bgMusicChannel ) + this.bgMusicChannel.stop(); + var bgmusic = this.preloader.getObject("music_background"); + if ( !bgmusic ) { + return; + } + this.bgMusicChannel = bgmusic.play(); + this.bgMusicChannel.addEventListener(Event.SOUND_COMPLETE, this.onBackgroundMusicFinished); + } + + /* + * onGetPowerup(evt) + * + * This function is fired whenever the Pattern is signifying that the player has gotten a powerup from the pattern + * + * arguments: + * @evt : Event, event firing this function + * + * Returns: none + */ + public function onGetPowerup(evt:PowerupEvent) + { + if ( evt.pwup != null ) { + var pwup = evt.pwup; + pwup.addChild(this.preloader.getBitmap(pwup.imgHandle)); + this.player.addPowerup(pwup); + } + } + + /* + * onGameTimer(evt) + * + * Fires once every 25 ms to run the core game logic + * + * arguments: + * @evt : Event, the event firing this function + * + * Returns : none + */ + + public function onGameTimer(evt:TimerEvent) + { + var i:Number = 0; + + /* This state machine got just a little bit too complex, and I think alot of it could be probably get + done away with in favor of more events driving the show. However for right now, it works. The IF chains + check the primary state first, then go in and check secondary states and ancillary conditions. */ + + if ( (this.primaryState == Game.STATE_PLAYING) && (this.curPattern.state == Pattern.STATE_STOPPED) ) { + if ( this.secondaryState == Game.STATE_PLAYING_FLASHING && (!this.simon.isLit) ) { + // we've flashed the Mastermind once, now let's play back the currently exposed portions of the pattern + this.curPattern.play(true); + this.primaryState = Game.STATE_PLAYING_PLAYBACK; + this.secondaryState = Game.STATE_PLAYING_PLAYBACK; + } else if ( this.secondaryState == Game.STATE_PLAYING_EXPLODING ) { + // the player has died and we're still in the explosion timeframe, so blow some *!$% up + if ( Math.random() < 0.25 ) { + var explosion = this.preloader.getMovieClip("movie_explosion"); + explosion.addEventListener(Event.ENTER_FRAME, this.onNonLoopEnterFrame); + explosion.x = Math.random()*640; + explosion.y = Math.random()*480; + explosion.play(); + this.preloader.playSound("sfx_explosion"); + this.addChild(explosion); + this.explosions.push(explosion); + } + } else if ( this.secondaryState == Game.STATE_PLAYING_WINLEVEL ) { + // player just beat the pattern; scroll "LEVEL" down from the top, and the level number up from the bottom + this.levelNumberLabel.text = "" + this.curLevel; + if ( this.levelLabel.y == 175 && (!this.labelPauseTimer.running)) { + this.labelPauseTimer.addEventListener(TimerEvent.TIMER, this.onLabelPauseTimer); + this.labelPauseTimer.reset(); + this.labelPauseTimer.start(); + } else if ( this.labelPauseTimer.running ) { + // do nothing if the label pause timer is running + } else if ( this.levelLabel.y <= -120 ) { + this.secondaryState = Game.STATE_PLAYING; + this.levelLabel.y = 500; + this.levelNumberLabel.y = -150; + } else { + this.levelLabel.y -= 5; + this.levelNumberLabel.y += 5; + } + } else if ( this.secondaryState == Game.STATE_PLAYING ){ + // flash the Mastermind once before playing back the portions currently exposed, so we have the player's + // attention + this.simon.flashAll(500); + this.secondaryState = Game.STATE_PLAYING_FLASHING; + } + } else if ( (this.primaryState == Game.STATE_PLAYING_PLAYBACK) ) { + if ( (this.secondaryState == Game.STATE_PLAYING_FLASHING ) && (!this.simon.isLit) ) { + // okay we're done flashing around, let the player give input and start the timers + this.curPattern.play(false); + this.primaryState = Game.STATE_PLAYING_INPUT; + this.secondaryState = Game.STATE_PLAYING; + } else if ( this.secondaryState == Game.STATE_PLAYING_PLAYBACK && this.curPattern.state == Pattern.STATE_STOPPED ) { + // flash the Mastermind a 2nd time to let the player know we're done running the pattern, and they need to start hitting buttons + this.simon.flashAll(500); + this.secondaryState = Game.STATE_PLAYING_FLASHING; + } + } else if ( (this.primaryState == Game.STATE_PLAYING_INPUT) && (this.curPattern.state != Pattern.STATE_RUNNING ) ) { + if ( this.curPattern.state == Pattern.STATE_CORRECT ) { + // player has repeated the pattern correctly + this.player.score += this.curPattern.score; + this.curPattern.complexify(); + if ( this.curPattern.patternLength() > this.player.maxPattern ) + this.player.maxPattern = this.curPattern.patternLength(); + // check the state again after complexifying it. If the player has reached the end of the pattern, we won't know + // until we've ran .complexify() on it. + if ( this.curPattern.state == Pattern.STATE_COMPLETE ) { + this.nextLevel(); + if ( this.curLevel >= this.maxLevel ) { + this.wonGame(); + return; + } + return; + } + } else if ( this.curPattern.state == Pattern.STATE_FAILED ){ + // Player irrevocably missed the pattern; deduct a life and either start a new pattern, or game over. + this.player.die(); + if ( this.player.lives <= 0 ) { + this.curPattern.stop() + this.primaryState = Game.STATE_PLAYING; + this.secondaryState = Game.STATE_PLAYING_EXPLODING; + this.explosionTimer.addEventListener(TimerEvent.TIMER, this.onExplosionTimer); + this.explosionTimer.start(); + this.curPattern.forceState(Pattern.STATE_STOPPED); + return; + } else { + this.newPattern(); + } + } + // if we haven't returned out from a previous condition, it's safe to reset the primary/secondary state + // to STATE_PLAYING so we'll flash and run the pattern normally + this.primaryState = Game.STATE_PLAYING; + this.secondaryState = Game.STATE_PLAYING; + this.timerLabel.text = "00000"; + } else if ( this.primaryState == Game.STATE_PLAYING_INPUT ) { + // Player is running input, just update the timer text + this.timerLabel.text = "" + this.curPattern.clearTime; + } + this.scoreLabel.text = "" + int(player.score); + return; + } + + /* + * onLabelPauseTimer(evt) + * + * This function is called when the label pause timer is up, so the Level labels will start moving again + * + * arguments: + * @evt:TimerEvent, the event firing this function + * + * Returns: none + */ + + public function onLabelPauseTimer(evt:TimerEvent) + { + this.labelPauseTimer.removeEventListener(TimerEvent.TIMER, this.onLabelPauseTimer); + this.labelPauseTimer.stop(); + // this bumps it past the pixel mark so we don't duplicate the onLabelPauseTimer call and it sits there forever + this.levelLabel.y -= 5; + this.levelNumberLabel.y == 5. + } + + /* + * onDifficultySelected(evt) + * + * This function is called whenever the player selects a difficulty level from the main screen starting a new game + * + * arguments: + * @evt : Event, the event firing this function + * + * Returns : none + */ + public function onDifficultySelected(evt:MouseEvent) + { + var mapping:Dictionary = new Dictionary; + // -- this just saves me a long block of if () { ... } , and the use of one-line X ? Y : Z ... + mapping[this.cutscenes["difficultychooser"].easyBtn] = 0; + mapping[this.cutscenes["difficultychooser"].normalBtn] = 1; + mapping[this.cutscenes["difficultychooser"].hardBtn] = 2; + mapping[this.cutscenes["difficultychooser"].wilyBtn] = 3; + this.difficulty = mapping[evt.target]; + + this.removeChild(this.cutscenes["difficultychooser"]); + this.removeChild(this.cutscenes["intro_menu"]); + this.primaryState = Game.STATE_TUTORIAL; + this.secondaryState = Game.STATE_TUTORIAL; + this.addChild(this.cutscenes["tutorial"]); + this.cutscenes["tutorial"].play(); + } + + /* + * onHighScoreEntry(evt) + * + * This function fires whenever the user clicks any of the buttons on the High Score screen + * + * arguments: + * @evt : Event, the event firing this function + * + * Returns: none + */ + public function onHighScoreEntry(evt:MouseEvent) + { + if ( evt.target == this.cutscenes["highscores"].highScorePostBtn ) { + // post up the user's high score and spin off a new browser window to the high score area + var request:URLRequest = new URLRequest("http://atlanta.aklabs.net/~akesterson/wilysays/index.php"); + request.method = URLRequestMethod.POST; + var variables:URLVariables = new URLVariables(); + variables.op = "store"; + variables.name = this.cutscenes["highscores"].highScoreName.text; + variables.score = this.player.score; + variables.maxpattern = this.player.maxPattern; + request.data = variables; + sendToURL(request); + var request:URLRequest = new URLRequest("http://atlanta.aklabs.net/~akesterson/wilysays/index.php"); + navigateToURL(request, "_blank"); + } else if ( evt.target == this.cutscenes["highscores"].viewScoreBtn) { + // just send the user to the high scores and return, don't disable any of the buttons or change state + var request:URLRequest = new URLRequest("http://atlanta.aklabs.net/~akesterson/wilysays/index.php"); + navigateToURL(request, "_blank"); + return; + } + // go back to the main menu + this.removeChild(this.cutscenes["highscores"]); + this.addChild(this.cutscenes["intro_menu"]); + this.primaryState = Game.STATE_MENU; + this.primaryState = Game.STATE_MENU; + this.cutscenes["intro_menu"].gotoAndPlay(0); + } + + /* + * clearExplosions() + * + * This function is ran when the explosion timer is done, and right before the high score screen + * is fixing to come up, to make sure that all the explosions are gone from the screen. + * + * arguments: none + * + * Returns: none + */ + public function clearExplosions() + { + var i:Number; + for ( i = 0; i < this.explosions.length ; i++ ) { + this.removeChild(this.explosions[i]); + this.explosions[i].stop(); + this.explosions.splice(i, 1); + } + } + + /* + * clearGameScreen() + * + * This function clears the game screen of all play elements (inventory, lives, Mastermind, etc) + * + * arguments : none + * + * returns: none + */ + public function clearGameScreen() + { + this.removeChild(this.preloader.getObject("gfx_circuitboard")); + this.removeChild(this.preloader.getObject("gfx_background")); + this.removeChild(this.simon); + this.removeChild(this.timerLabel); + this.removeChild(this.scoreLabel); + this.removeChild(this.levelLabel); + this.removeChild(this.levelNumberLabel); + if ( this.curPattern ) + this.removeChild(this.curPattern); + this.removeChild(this.player); + this.removeChild(this.player.lifeDisplay); + + this.player.clearInventory(); + + this.explosionTimer.reset(); + this.explosionTimer.delay = 5000; + this.explosionTimer.stop(); + + } + + /* + * wonGame() + * + * This function is called when the player has beaten all 10 levels and therefore won the game + * + * arguments: None + * + * Returns: None + * + */ + + public function wonGame() + { + this.clearGameScreen(); + this.primaryState = Game.STATE_WINGAME; + this.secondaryState = Game.STATE_WINGAME; + this.addChild(this.cutscenes["endscreen"]); + this.cutscenes["endscreen"].play(); + } + + /* gameOver() + * + * This function is called whenever the player has lost all of their lives, and achieved Game Over + * + * arguments : None + * + * Returns : none + */ + public function gameOver() + { + this.clearGameScreen(); + this.primaryState = Game.STATE_HIGHSCORE; + this.secondaryState = Game.STATE_HIGHSCORE; + this.addChild(this.cutscenes["highscores"]); + this.cutscenes["highscores"].play(); + } + + /* + * nextLevel() + * + * This function runs to setup the next level above the previous one + * + * arguments : none + * + * Returns : none + */ + public function nextLevel() + { + this.curLevel += 1; + this.levelLabel.y = 500; + this.levelNumberLabel.y = -150; + this.levelNumberLabel.text = "" + this.curLevel; + this.primaryState = Game.STATE_PLAYING; + this.secondaryState = Game.STATE_PLAYING_WINLEVEL; + this.player.score += this.curPattern.score; + this.timerLabel.text = "00000"; + this.scoreLabel.text = "" + int(player.score); + this.newPattern(); + } + + /* + * newGame() + * + * This function sets up a new game when the player starts a game from the main menu + * + * arguments : none + * + * Returns: none + */ + public function newGame() + { + // difficulty just really changes how long the patterns are and how many levels you play + if ( this.difficulty > 1 ) { + this.curLevel = 3*this.difficulty; + } else + this.curLevel = 0; + this.maxLevel = this.curLevel + (6+this.difficulty); + this.player.score = 0; + this.player.lives = 3; + this.cutscenes["tutorial"].stop(); + this.cutscenes["highscores"].stop(); + this.cutscenes["intro_menu"].stop(); + + try { + this.removeChild(this.cutscenes["tutorial"]); + } catch (error:Error) { + // do nothing, it wasn't a child for some reason.. (I ran into this a couple times but not sure why) + } + + this.addChild(this.preloader.getObject("gfx_circuitboard")); + this.addChild(this.preloader.getObject("gfx_background")); + this.addChild(this.simon); + this.addChild(this.timerLabel); + this.addChild(this.scoreLabel); + this.addChild(player); + this.addChild(player.lifeDisplay); + this.addChild(this.levelLabel); + this.addChild(this.levelNumberLabel); + + this.player.x = 0; + this.player.y = 480-64; + player.lifeDisplay.x = 400; + player.lifeDisplay.y = 480-64; + this.player.resetLifePositions(); + + this.primaryState = Game.STATE_PLAYING; + this.secondaryState = Game.STATE_PLAYING; + + this.newPattern(); + this.nextLevel(); + } + + /* + * onExplosionTimer(evt) + * + * This function fires when the explosion timeframe is up + * + * arguments : + * @evt : Event, the event firing this function + * + * Returns : none + */ + public function onExplosionTimer(evt:TimerEvent) + { + this.primaryState = Game.STATE_PLAYING; + this.secondaryState = Game.STATE_PLAYING_WAITING; + for ( var i:Number = 0; i < this.explosions.length; i++ ) { + if ( this.explosions[i].currentFrame != this.explosions[i].totalFrames ) { + // some of the explosions aren't done yet, let them finish + this.explosionTimer.delay = 1000; + this.explosionTimer.start(); + return; + } + } + this.clearExplosions(); + this.gameOver(); + } + + /* + * onMastermindClicked(evt) + * + * This function fires whenever the player clicks the mouse on one of the mastermind buttons + * + * arguments: + * @evt : Event, the event firing this function + * + * Returns : none + */ + public function onMastermindClicked(evt:MastermindEvent) + { + this.checkColorHit(evt.colorClicked); + } + + /* + * autoForgiveness() + * + * This function checkes to see if the player has a Forgiveness powerup in his inventory, + * and if it does, it uses it to stop the pattern from blowing out one of the players' lives + * + * arguments : none + * + * Returns : none + */ + public function autoForgiveness() + { + var pwup:Powerup = null; + for ( var i:Number = 0; i < this.player.inventory.length ; i++ ) { + pwup = this.player.inventory[i]; + if ( pwup.pType == Powerup.PTYPE_FORGIVENESS ) { + this.curPattern.forceState(Pattern.STATE_STOPPED); + this.player.usePowerupAt(i); + break; + } + } + } + + /* + * checkColorHit(colorPressed) + * + * arguments: + * @colorPressed : Number, an integer (e.g. Pattern.COLOR_XXXX) specifying which color the player hit + * + * Returns: none + */ + public function checkColorHit(colorPressed:Number) + { + if ( this.curPattern.colorActive(colorPressed) == true ) { + this.simon.lightButton(colorPressed, 100); + this.preloader.playSound(this.buttonSounds[colorPressed]); + } else { + this.autoForgiveness(); + this.curPattern.stop(); + return; + } + } + + /* + * onUsedPowerup() + * + * Fired whenever the player uses a powerup + * + * arguments: + * @evt : Event, the event firing this function + * + * Returns: none + */ + public function onUsedPowerup(evt:PowerupEvent) + { + // the only time we get a powerupevent for used powerups + // is when it's the player dispatching one; we have to + // then re-dispatch it so that the pattern will see it, + // since it's in the opposite direction for bubbling (things bubble up, + // never down). We never actually process them, all powerup handling is + // handled between the player & pattern. We just dispatch. + this.curPattern.onUsedPowerup(evt); + } + + /* + * newPattern() + * + * Creates a new pattern object to challenge the player. + * + * arguments: none + * + * Returns: none + */ + public function newPattern() + { + try { + this.removeChild(this.curPattern); + } catch (error:Error) { + // do nothing, it wasn't in our child list yet + } + this.curPattern = new Pattern(this.curLevel, this.simon, this.difficulty); + this.curPattern.addEventListener(PowerupEvent.GOT_POWERUP, this.onGetPowerup); + this.curPattern.stop(); + this.curPattern.forceState(Pattern.STATE_STOPPED); + this.curPattern.x = 602; + this.curPattern.y = 138; + this.addChild(this.curPattern); + trace("Finished newPattern()"); + } + } + } \ No newline at end of file diff --git a/game.html b/game.html new file mode 100755 index 0000000..345a953 --- /dev/null +++ b/game.html @@ -0,0 +1,52 @@ + + + +simon says + + + + + + + +

CLICK HERE FOR HELP!

+ +

+ + + + + diff --git a/gfx/EXCLAMATION.png b/gfx/EXCLAMATION.png new file mode 100755 index 0000000..fc54bdf Binary files /dev/null and b/gfx/EXCLAMATION.png differ diff --git a/gfx/FORGIVENESS.png b/gfx/FORGIVENESS.png new file mode 100755 index 0000000..ac5376d Binary files /dev/null and b/gfx/FORGIVENESS.png differ diff --git a/gfx/LIFE.png b/gfx/LIFE.png new file mode 100755 index 0000000..40b22f7 Binary files /dev/null and b/gfx/LIFE.png differ diff --git a/gfx/POINTDOUBLER.png b/gfx/POINTDOUBLER.png new file mode 100755 index 0000000..4acc149 Binary files /dev/null and b/gfx/POINTDOUBLER.png differ diff --git a/gfx/SHORTCIRCUIT.png b/gfx/SHORTCIRCUIT.png new file mode 100755 index 0000000..ac38dee Binary files /dev/null and b/gfx/SHORTCIRCUIT.png differ diff --git a/gfx/SLOWDOWN.png b/gfx/SLOWDOWN.png new file mode 100755 index 0000000..0e4e0c0 Binary files /dev/null and b/gfx/SLOWDOWN.png differ diff --git a/gfx/SPEEDUP.png b/gfx/SPEEDUP.png new file mode 100755 index 0000000..1965784 Binary files /dev/null and b/gfx/SPEEDUP.png differ diff --git a/gfx/blue-dark.png b/gfx/blue-dark.png new file mode 100755 index 0000000..448b3c5 Binary files /dev/null and b/gfx/blue-dark.png differ diff --git a/gfx/blue-light.png b/gfx/blue-light.png new file mode 100755 index 0000000..b257804 Binary files /dev/null and b/gfx/blue-light.png differ diff --git a/gfx/brushed-steel-lg.jpg b/gfx/brushed-steel-lg.jpg new file mode 100755 index 0000000..79a2bcf Binary files /dev/null and b/gfx/brushed-steel-lg.jpg differ diff --git a/gfx/circuitboard.jpg b/gfx/circuitboard.jpg new file mode 100755 index 0000000..d8ec4cc Binary files /dev/null and b/gfx/circuitboard.jpg differ diff --git a/gfx/circuitboard.png b/gfx/circuitboard.png new file mode 100755 index 0000000..bd4f65d Binary files /dev/null and b/gfx/circuitboard.png differ diff --git a/gfx/cutscenes/difficultychooser.swf b/gfx/cutscenes/difficultychooser.swf new file mode 100755 index 0000000..6058569 Binary files /dev/null and b/gfx/cutscenes/difficultychooser.swf differ diff --git a/gfx/cutscenes/endscreen.swf b/gfx/cutscenes/endscreen.swf new file mode 100755 index 0000000..273feb8 Binary files /dev/null and b/gfx/cutscenes/endscreen.swf differ diff --git a/gfx/cutscenes/highscore.swf b/gfx/cutscenes/highscore.swf new file mode 100755 index 0000000..7bec803 Binary files /dev/null and b/gfx/cutscenes/highscore.swf differ diff --git a/gfx/cutscenes/intro-menu.swf b/gfx/cutscenes/intro-menu.swf new file mode 100755 index 0000000..ea35406 Binary files /dev/null and b/gfx/cutscenes/intro-menu.swf differ diff --git a/gfx/cutscenes/tutorial.swf b/gfx/cutscenes/tutorial.swf new file mode 100755 index 0000000..f6f0355 Binary files /dev/null and b/gfx/cutscenes/tutorial.swf differ diff --git a/gfx/explosion.fla b/gfx/explosion.fla new file mode 100755 index 0000000..2284b31 Binary files /dev/null and b/gfx/explosion.fla differ diff --git a/gfx/explosion.swf b/gfx/explosion.swf new file mode 100755 index 0000000..1511013 Binary files /dev/null and b/gfx/explosion.swf differ diff --git a/gfx/game_background.png b/gfx/game_background.png new file mode 100755 index 0000000..9d8f6b4 Binary files /dev/null and b/gfx/game_background.png differ diff --git a/gfx/green-dark.png b/gfx/green-dark.png new file mode 100755 index 0000000..10c14ee Binary files /dev/null and b/gfx/green-dark.png differ diff --git a/gfx/green-light.png b/gfx/green-light.png new file mode 100755 index 0000000..d5c6f38 Binary files /dev/null and b/gfx/green-light.png differ diff --git a/gfx/megaman_leaderboard.png b/gfx/megaman_leaderboard.png new file mode 100755 index 0000000..28195ac Binary files /dev/null and b/gfx/megaman_leaderboard.png differ diff --git a/gfx/progress_green.png b/gfx/progress_green.png new file mode 100755 index 0000000..2dca09c Binary files /dev/null and b/gfx/progress_green.png differ diff --git a/gfx/progress_red.png b/gfx/progress_red.png new file mode 100755 index 0000000..6063fe8 Binary files /dev/null and b/gfx/progress_red.png differ diff --git a/gfx/red-dark.png b/gfx/red-dark.png new file mode 100755 index 0000000..9142400 Binary files /dev/null and b/gfx/red-dark.png differ diff --git a/gfx/red-light.png b/gfx/red-light.png new file mode 100755 index 0000000..c57b68b Binary files /dev/null and b/gfx/red-light.png differ diff --git a/gfx/whole.png b/gfx/whole.png new file mode 100755 index 0000000..3b493d9 Binary files /dev/null and b/gfx/whole.png differ diff --git a/gfx/yellow-dark.png b/gfx/yellow-dark.png new file mode 100755 index 0000000..6ebe93d Binary files /dev/null and b/gfx/yellow-dark.png differ diff --git a/gfx/yellow-light.png b/gfx/yellow-light.png new file mode 100755 index 0000000..fca1d1a Binary files /dev/null and b/gfx/yellow-light.png differ diff --git a/help.html b/help.html new file mode 100755 index 0000000..5f4eef9 --- /dev/null +++ b/help.html @@ -0,0 +1,145 @@ + + +

MEGA MAN: Wily Says!

+ +

HELP!

+
+ +This is the help information for Mega Man: Wily Says!, the smash-megahit +game currently sweeping teh intarnets. Here's how to play the game. + +
+ +
+ +
+ +Once the game loads up, click inside the game window with your mouse to +make sure it has focus. Then hit your SPACE BAR. Click on one of the +difficulty setting buttons, to choose the difficulty. This determines +how many levels you play, and how long the patterns you're challenged with, +will be. Once you've done that, watch the intro; it'll give you a little +bit of background story, and a little bit of information on how to play +the game. + +

The interface

+
+
+ +
+ + +Simple, right? + +

Credits

+ +
+
+Pre-formatting this because I'm lazy.
+
+Most of this was assembled ad-hoc from various places around the internet
+for a game demo to accompany a resume, so it was never really intended for
+public consumption. But before the whole thing was over, it just became
+so ridiculous I couldn't possibly keep it to myself.
+
+The theme music is a MIDI version of Depeche Mode's "Master and Servant". That's
+where the name "Mastermind" for the Simon Says came from.
+
+The graphics were mostly stolen from mega man fansites around the internet.
+
+The explosion graphic was lifted from someone. I forget who. It's the same 
+one I used in my "HOLY *@#%! ALIENS!" space shooter demo.
+
+The sound effects, again, were lifted from various free sites around the
+net. I honestly didn't keep track of them. I know they were all straight off
+Google links.
+
+I'm solely responsible for this atrocity; if you want to send me hate mail,
+you're a megaman purist and you want to tear off my head, you think this was
+the most ridiculous idea for a game demo ever, or if you want to send me
+big buckets of money because I'm just so goddamned AWESOME, send mail
+to andrew@aklabs.net, or follow me on twitter (@akesterson). Thanks.
+
+The entire demo was coded in ActionScript 3 on Adobe Flash CS3, graphics
+were chopped up with The GIMP, sounds composed/trimmed in Audacity. I think
+I spent about a total of 48 hours on this project, and that includes learning
+ActionScript 3. I'll release the source in a few days after an employer
+is done looking at it.
+
+
+ + + + + diff --git a/help1.png b/help1.png new file mode 100755 index 0000000..2e689d1 Binary files /dev/null and b/help1.png differ diff --git a/help2.png b/help2.png new file mode 100755 index 0000000..1763681 Binary files /dev/null and b/help2.png differ diff --git a/index.php b/index.php new file mode 100755 index 0000000..261a090 --- /dev/null +++ b/index.php @@ -0,0 +1,134 @@ +\n
Groovy
\n"); + } + die(); +} +?> + + + +
+

MEGA MAN: Wily Says!

+

Leaderboards

+

Click HERE to play the mega-hit game!

+

Click HERE for instructions on HOW to play the mega-hit game!

+
+ + + + \n"); + if ( $curPage > 0 ) + printf(" \n", $rowsPerPage, (($curPage*$rowsPerPage)-$rowsPerPage)); + else + printf(" \n"); + + if ( $totalRows > ($curPage * $rowsPerPage) ) + printf(" \n", $rowsPerPage, (($curPage*$rowsPerPage)+$rowsPerPage)); + else + printf(" \n"); + printf("\n"); + + printf("\t\t\t\t\n", + ($curPage*$rowsPerPage), ($curPage*$rowsPerPage), ($curPage*$rowsPerPage), ($curPage*$rowsPerPage)); + while ( $row = mysql_fetch_array($res) ) { + printf("\t\t\t\t\n"); + printf("\t\t\t\t\t\n"); + printf("\t\t\t\t\t\n"); + printf("\t\t\t\t\t\n"); + printf("\t\t\t\t\t\n"); + printf("\t\t\t\t\n"); + } + printf("\n"); + if ( $curPage > 0 ) + printf(" \n", $rowsPerPage, (($curPage*$rowsPerPage)-$rowsPerPage)); + else + printf(" \n"); + + if ( $totalRows > ($curPage * $rowsPerPage) ) + printf(" \n", $rowsPerPage, (($curPage*$rowsPerPage)+$rowsPerPage)); + else + printf(" \n"); + printf("\n"); + + printf("\t\t\t
+ +\n"); + printf("
<< PREVIOUSNEXT >>
Player
Name
Total
Score
Longest
Pattern
When
Recorded
" . $row["name"] . "" . $row["score"] . "" . $row["maxpattern"] . "" . $row["dtime"] . "
<< PREVIOUSNEXT >>
\n"); +} + +?> + + + + + + +
+

"A fabulous tour de force..." -- Game Misinformer Bragazine +

"Quite possibly the most important thing to happen to +
video games since Shigeru Miyamoto." -- Protendo Trading Cards
+

"I believe we made the most beautiful thing in the world. Nobody +
would criticize a renowned architect's blueprint that the position +
of a gate is wrong. It's the same as that." --Ken Kutaragi
+
+ + diff --git a/leaderboard.php b/leaderboard.php new file mode 100755 index 0000000..6b4be5a --- /dev/null +++ b/leaderboard.php @@ -0,0 +1,92 @@ +\n
Groovy
\n"); + } + die(); +} +?> + + + +
+

MEGA MAN: Wily Says!

+

Leaderboards

+
+ + + + \n"); + while ( $row = mysql_fetch_array($res) ) { + printf("\t\t\t\t\n"); + printf("\t\t\t\t\t\n"); + printf("\t\t\t\t\t\n"); + printf("\t\t\t\t\t\n"); + printf("\t\t\t\t\t\n"); + printf("\t\t\t\t\n"); + } + printf("\t\t\t
+ +\n"); + printf("\t\t\t\t
Player
Name
Total
Score
Longest
Pattern
When
Recorded
" . $row["name"] . "" . $row["score"] . "" . $row["maxpattern"] . "" . $row["dtime"] . "
\n"); +} + +?> + + + + +"; + echo " << PREVIOUS"; + echo " NEXT >>"; + echo ""; +} + +?> + + + diff --git a/megaman.png b/megaman.png new file mode 100755 index 0000000..baa125c Binary files /dev/null and b/megaman.png differ diff --git a/net/aklabs/demo/simonsays/LoadingObject.as b/net/aklabs/demo/simonsays/LoadingObject.as new file mode 100755 index 0000000..415d1db --- /dev/null +++ b/net/aklabs/demo/simonsays/LoadingObject.as @@ -0,0 +1,34 @@ +package net.aklabs.demo.simonsays { + + /* + * Class LoadingObject + * + * This class is used to represent a content asset while it is going through the process of being loaded + * by the Preloader class. + */ + public class LoadingObject { + public static var STATE_READY:Number = 0; // Object has been loaded and is ready + public static var STATE_LOADING:Number = 1; // Object is currently loading, not ready yet + public static var STATE_NOTFOUND:Number = 2; // Object wasn't found in the cache + public var handle:String; // Text handle for this object + public var state:Number; // State value + public var bytesRead:Number; // the amount of bytes currently read in to this object over the net + public var bytesTotal:Number; // the total byte size of this object + + /* + * LoadingObject() + * + * Default constructor + * + * arguments : none + * + * Returns : LoadingObject + */ + public function LoadingObject() { + this.handle = ""; + this.state = LoadingObject.STATE_NOTFOUND; + this.bytesRead = 0; + this.bytesTotal = 0; + } + } +} \ No newline at end of file diff --git a/net/aklabs/demo/simonsays/Mastermind.as b/net/aklabs/demo/simonsays/Mastermind.as new file mode 100755 index 0000000..2bde5f2 --- /dev/null +++ b/net/aklabs/demo/simonsays/Mastermind.as @@ -0,0 +1,176 @@ +package net.aklabs.demo.simonsays +{ + + import flash.utils.Timer; + import flash.events.Event; + import flash.events.MouseEvent; + import flash.events.TimerEvent; + import flash.display.Sprite; + import net.aklabs.demo.simonsays.Preloader; + import net.aklabs.demo.simonsays.SimonButton; + import net.aklabs.demo.simonsays.MastermindEvent; + + /* + * Class: Mastermind + * + * This class defines our "Simon Says", "Wily Lok", "Mastermind", whatever you want to call it. + * (I called it 3 or 4 different things during the course of development.) + * + */ + public class Mastermind extends Sprite + { + protected var lightOutTimer:Timer; // timer that fires when it's time to turn off the lights + protected var lightButtons:Array; // array of custom SimonButtons that exists in Pattern.COLOR_XXX order + public var isLit:Boolean; // is the device currently lit? + + public function Mastermind() + { + var preloader = Preloader.getInstance(); + var lightPositions = new Array(); + // this array holds pairs of (x,y) coordinates at which to place the dark/light colored + // Simon Says buttons, since they're meant to cover the existing buttons on the original graphic. + // These are hard-coded; in a better implementation, they would come from an XML or would be + // individual frames of an SWF which could be fired individually, etc. But this works. + // array idx 0: Blue, 1: Green, 2: Red, 3: Yellow + + lightPositions.push( new Array(183, 187)); // blue + lightPositions.push( new Array(18, 17)); // green + lightPositions.push( new Array(185, 17)); // red + lightPositions.push( new Array(18, 186)); // yellow + + // this array holds the button objects for the visible simon says buttons ... + this.lightButtons = new Array(); + // just a temporary array for which images to fetch for creating the down/up button images + var fetchArray = new Array( new Array("gfx_btn_darkblue", "gfx_btn_lightblue"), + new Array("gfx_btn_darkgreen", "gfx_btn_lightgreen"), + new Array("gfx_btn_darkred", "gfx_btn_lightred"), + new Array("gfx_btn_darkyellow", "gfx_btn_lightyellow") ); + // Simon Says body + var whole = preloader.getObject("gfx_mastermind"); + this.addChild(whole); + + for ( var i = 0; i < fetchArray.length ; i++ ) { + var btn:SimonButton = new SimonButton(preloader.getObject(fetchArray[i][1]), preloader.getObject(fetchArray[i][0])); + btn.x = lightPositions[i][0]; + btn.y = lightPositions[i][1]; + this.lightButtons.push(btn); + this.addChild(btn); + btn.addEventListener(MouseEvent.CLICK, this.onMouseClick); // for some reason, MouseEvent.CLICK never actually does anything? + //btn.addEventListener(MouseEvent.MOUSE_UP, this.onMouseClick); + } + this.lightOutTimer = new Timer(0); + } + + /* + * flashAll(length) + * + * This function lights all the lights on the Mastermind for the given length of time + * + * arguments: + * @length:Number, the time in milliseconds for which the Mastermind should stay lit + * + * Returns : none + */ + public function flashAll(length:Number) + { + this.lightOutTimer.delay = length; + this.lightOutTimer.addEventListener(TimerEvent.TIMER, this.onTimer); + this.lightOutTimer.reset(); + this.lightOutTimer.start(); + for (var i = 0 ; i < 4 ; i++) { + // fake mouse event to light it up + this.lightButtons[i].onMouseDown(null); + } + this.isLit = true; + } + + /* + * lightButton(btn, activeTime) + * + * This function lights up a given button on the Mastermind. Mostly used by the Pattern when it's replaying itself. + * + * arguments: + * @btn:Number, the Pattern.COLOR_XXX button that should light + * @activeTime:Number, the amount of milliseconds for which the button should stay lit + * + * Returns : none + */ + public function lightButton(btn:Number, activeTime:Number = 0 ) + { + if ( btn < 4 ) { + this.lightButtons[btn].onMouseDown(null); + this.isLit = true; + } + if ( activeTime != 0 ) { + this.lightOutTimer.delay = activeTime; + this.lightOutTimer.addEventListener(TimerEvent.TIMER, this.onTimer); + this.lightOutTimer.reset(); + this.lightOutTimer.start(); + } + } + + /* + * blackout() + * + * This function makes sure that all lights on the Mastermind are extinguished + * + * arguments : none + * + * Returns : none + */ + public function blackout() + { + if ( this.lightButtons.length <= 0 ) { + return; + } + for ( var i = 0 ; i < this.lightButtons.length ; i++ ){ + this.lightButtons[i].onMouseUp(null); + } + this.isLit = false; + } + + /* + * onTimer(evt) + * + * This function fires whenever the lightOutTimer fires, telling the lights to shut off + * + * arguments : + * @evt:Event, the event that's firing this function + * + * Returns : none + */ + public function onTimer(evt:TimerEvent) + { + if ( evt.target != this.lightOutTimer ) + return; + this.lightOutTimer.removeEventListener(TimerEvent.TIMER, this.onTimer); + this.lightOutTimer.stop(); + this.lightOutTimer.reset(); + this.blackout(); + } + + /* + * onMouseClick() + * + * This function is fired whenever the user clicks one of the Mastermind's buttons + * + * arguments: + * @evt:Event, the event firing this function + * + * Returns : none + */ + public function onMouseClick(evt:MouseEvent) + { + // we don't do any processing here, as the SimonButton has already checked for + // per-pixel accuracy w/ the click on our abnormally shaped buttons. We just propagate + // out a new MastermindEvent for the game to catch. + for ( var i:Number = 0; i < this.lightButtons.length ; i++ ) { + if ( evt.target == this.lightButtons[i] ) { + var newEvt:MastermindEvent = new MastermindEvent(MastermindEvent.BTN_CLICKED); + newEvt.colorClicked = i; + this.dispatchEvent(newEvt); + } + } + } + } +} diff --git a/net/aklabs/demo/simonsays/MastermindEvent.as b/net/aklabs/demo/simonsays/MastermindEvent.as new file mode 100755 index 0000000..36ccf09 --- /dev/null +++ b/net/aklabs/demo/simonsays/MastermindEvent.as @@ -0,0 +1,22 @@ +package net.aklabs.demo.simonsays +{ + import flash.events.Event; + + /* + * Class : MastermindEvent + * + * This class basically just defines a custom event that will let the mastermind send up + * a clicked event w/ a color. + */ + public class MastermindEvent extends Event + { + public static var BTN_CLICKED:String = "MASTERMIND_BUTTON_CLICKED"; + public var colorClicked:Number; + + public function MastermindEvent(evtType:String) + { + super(evtType); + } + } +} + diff --git a/net/aklabs/demo/simonsays/Pattern.as b/net/aklabs/demo/simonsays/Pattern.as new file mode 100755 index 0000000..885f08a --- /dev/null +++ b/net/aklabs/demo/simonsays/Pattern.as @@ -0,0 +1,393 @@ +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; + } + } +} + diff --git a/net/aklabs/demo/simonsays/Player.as b/net/aklabs/demo/simonsays/Player.as new file mode 100755 index 0000000..bf0ed85 --- /dev/null +++ b/net/aklabs/demo/simonsays/Player.as @@ -0,0 +1,228 @@ +package net.aklabs.demo.simonsays +{ + import net.aklabs.demo.simonsays.PowerupEvent; + import flash.display.Sprite; + import flash.events.Event; + import flash.events.MouseEvent; + import net.aklabs.demo.simonsays.Preloader; + import flash.display.Bitmap; + + /* + * Class Player + * + * This class represents the player in the game. + * + * The class has two displayable objects in it: the Player object itself, + * and Player.lifeDisplay. The Player object displays the inventory (64 pixels high, up to (64*5) pixels wide) + * The Player.lifeDisplay object displays the remaining number of lives (64 pixels high, up to 64*3 pixels wide) + * + */ + public class Player extends Sprite { + public var score:Number; // the player's current score + public var lives:Number; // the number of lives the player currently has + public var maxPattern:Number; // the largest pattern the player has completed + public var inventory:Array; // the player's inventory + public var lifeDisplay:Sprite; /* the parent sprite to which all the player life sprites are attached (number of remaining lives) .. + we have this parenting the rest of the images, so the Game class can just add this single child object, rather than tracking + and removing all the images in the individual lifeImages array */ + protected var lifeImages:Array; // an array of images holding all the images showing up in the player's life array + protected var headExplosion; + + /* + * Player() + * + * Default constructor + * + * arguments : none + * + * Returns : none + */ + public function Player() + { + this.score = 0; + this.lives = 3; + this.maxPattern = 0; + this.inventory = new Array(); + this.lifeDisplay = new Sprite(); + this.lifeImages = new Array(); + } + + /* + * addPowerup(pwup) + * + * Adds a new powerup to the player's inventory + * + * arguments: + * @pwup:Powerup, the powerup to be added + * + * Returns: none + */ + public function addPowerup(pwup:Powerup) + { + if ( this.inventory.length >= 5 ) { + return; + } + this.inventory.push(pwup); + this.resetPowerupPositions(); + this.addChild(pwup); + pwup.addEventListener(MouseEvent.MOUSE_UP, this.onPowerupClicked); + } + + /* + * resetPowerupPositions() + * + * This function goes through the player's inventory and makes sure the positions line up (mostly) with the background graphic for their slot + * + * arguments: none + * + * Returns: none + */ + protected function resetPowerupPositions() + { + for ( var i = 0; i < this.inventory.length ; i++ ) { + var pwup:Powerup = this.inventory[i]; + pwup.x = 2+(70*i); + pwup.y = 0; + } + } + + /* + * die() + * + * This function is called whenever the player should die - lose a life + * + * arguments: none + * + * Returns: none + */ + public function die() + { + // lose a life, and do it in style + var preloader:Preloader = Preloader.getInstance(); + this.headExplosion = preloader.getMovieClip("movie_explosion"); + this.headExplosion.addEventListener(Event.ENTER_FRAME, this.stopExplosion); + this.headExplosion.x = (this.lives * 64)-32; + this.headExplosion.y = 32; + this.lifeDisplay.addChild(this.headExplosion); + this.headExplosion.play(); + preloader.playSound("sfx_explosion"); + this.lives -= 1; + this.resetLifePositions(); + } + + /* + * stopExplosion(evt) + * + * This stops the head explosion animation from looping (much like Game.onNonLoopEnterFrame) + * + * arguments: + * @evt:Event, the event firing this function + * + * Returns: none + */ + public function stopExplosion(evt:Event) + { + if ( evt.target.currentFrame == evt.target.totalFrames ) { + this.headExplosion.stop(); + this.lifeDisplay.removeChild(this.headExplosion); + this.headExplosion.removeEventListener(Event.ENTER_FRAME, this.stopExplosion); + this.headExplosion = null; + } + } + + /* + * resetLifePositions() + * + * This function arranges the images representing the number of remaining lives the player has + * + * arguments: none + * + * returns : none + */ + public function resetLifePositions() + { + var i:Number = 0; + var preloader:Preloader = Preloader.getInstance(); + if ( this.lifeImages.length < this.lives ) { + for ( i = this.lifeImages.length; i < this.lives; i++ ) { + var img:Bitmap = preloader.getBitmap("gfx_pwup_freelife"); + img.x = 64*i; + img.y = 0; + this.lifeImages.push(img); + this.lifeDisplay.addChild(img); + } + } else { + for ( var i = 0; i < this.lifeImages.length ; i++ ) { + if ( i >= this.lives ) { + this.lifeDisplay.removeChild(this.lifeImages[i]); + this.lifeImages.splice(i, 1); + } + } + } + for ( var i = 0; i < this.lifeImages.length ; i++ ) { + this.lifeImages[i].x = 64*i; + this.lifeImages[i].y = 0; + } + } + + /* + * usePowerupAt(index) + * + * See if there is a powerup at index 'index' in the inventory, use it, fire off any sounds associated with it, + * and dispatch a new PowerupEvent for it + * + * arguments: + * @index:Number, the index in inventory from which the powerup should be drawn + * + * returns: none + */ + public function usePowerupAt(index:Number) + { + if ( index < this.inventory.length ) { + this.removeChild(this.inventory[index]); + var preloader:Preloader = Preloader.getInstance(); + preloader.playSound(this.inventory[index].sndHandle); + this.resetPowerupPositions(); + this.dispatchEvent(new PowerupEvent(PowerupEvent.USED_POWERUP, this.inventory[index])); + this.inventory[index].removeEventListener(MouseEvent.MOUSE_UP, this.onPowerupClicked); + this.inventory.splice(index, 1); + } + } + + /* + * onPowerupClicked(evt) + * + * This function is fired whenever the player clicks the mouse on a powerup owned by the player + * + * arguments: + * @evt:MouseEvent, the event firing this function + * + * Returns: none + */ + public function onPowerupClicked(evt:MouseEvent) + { + for ( var i:Number = 0 ; i < this.inventory.length ; i++ ) { + if ( this.inventory[i] == evt.target ) { + this.usePowerupAt(i); + } + } + } + + /* + * clearInventory() + * + * Fairly obvious, this function just clears out the inventory + * + * arguments: none + * + * returns: none + */ + public function clearInventory() + { + for ( var i:Number = 0; i < this.inventory.length ; i++ ) { + this.removeChild(this.inventory[i]); + this.inventory.splice(i, 1); + } + } + } +} diff --git a/net/aklabs/demo/simonsays/Powerup.as b/net/aklabs/demo/simonsays/Powerup.as new file mode 100755 index 0000000..17ef5dd --- /dev/null +++ b/net/aklabs/demo/simonsays/Powerup.as @@ -0,0 +1,29 @@ +package net.aklabs.demo.simonsays { + import flash.display.Sprite; + import flash.events.Event; + import flash.events.MouseEvent; + import net.aklabs.demo.simonsays.PowerupEvent; + + /* + * Class Powerup + * + * This class just represents a powerup in the game. + * This is more a data structure than a class, but I don't think + * AS 3.0 has just bare structures. Silly ECMA language... + */ + public class Powerup extends Sprite + { + public static var PTYPE_SLOWDOWN:Number = 0; /* SLOWDOWN - Slows the pattern timer to a much lower rate */ + public static var PTYPE_FORGIVENESS:Number = 1; /* FORGIVENESS - If you have FORGIVENESSS in your inventory and you miss a pattern, it doesn't stop you */ + public static var PTYPE_SKIP:Number = 2; /* SKIP - Lets you "skip" a given pattern iteration, and you still get all the score for it. */ + public static var PTYPE_DOUBLE:Number = 3; /* DOUBLE - Score double points for the pattern on which you use this powerup. */ + public static var PTYPE_MAXVALUE:Number = 4; + // POWERUP_IMAGES - these are the image handles (stored in an array where idx => PTYPE_XXX) for each of the powerup types + public static var POWERUP_IMAGES = new Array("gfx_pwup_slowdown", "gfx_pwup_forgiveness", "gfx_pwup_exclamation", "gfx_pwup_pointdoubler"); + // POWERUP_SOUNDS - these are the sound handles (stored in an array where idx => PTYPE_XXX) for each of the powerup types + public static var POWERUP_SOUNDS = new Array("sfx_slowdown", "sfx_forgiveness", "sfx_exclamation", "sfx_pointdoubler"); + public var imgHandle:String; // the handle for the image of this specific powerup + public var sndHandle:String; // the handle for the sound of this specific powerup + public var pType:Number; // the PTYPE_XXX of this powerup + } +} diff --git a/net/aklabs/demo/simonsays/PowerupEvent.as b/net/aklabs/demo/simonsays/PowerupEvent.as new file mode 100755 index 0000000..671ba0d --- /dev/null +++ b/net/aklabs/demo/simonsays/PowerupEvent.as @@ -0,0 +1,32 @@ +package net.aklabs.demo.simonsays { + import flash.events.Event; + import net.aklabs.demo.simonsays.Powerup; + + /* + * Class PowerupEvent + * + * An event specifically made for actions taken regarding powerups + */ + public class PowerupEvent extends Event { + public static var GOT_POWERUP:String = "SIMONSAYS_GOT_POWERUP"; // Type for events when a powerup was received + public static var USED_POWERUP:String = "SIMONSAYS_USED_POWERUP"; // Type for events when a powerup was used + public var pwup:Powerup; // the powerup (rather than the target) of this event + + /* + * PowerupEvent() + * + * Default Constructor + * + * arguments: + * @evtType:String, the type of this event (GOT_POWERUP or USED_POWERUP) + * @tgt:Powerup, the Powerup object that's being affected by this event + * + * returns: none + */ + public function PowerupEvent(evtType:String, tgt:Powerup = null) + { + super(evtType); + this.pwup = tgt; + } + } +} diff --git a/net/aklabs/demo/simonsays/Preloader.as b/net/aklabs/demo/simonsays/Preloader.as new file mode 100755 index 0000000..a993413 --- /dev/null +++ b/net/aklabs/demo/simonsays/Preloader.as @@ -0,0 +1,529 @@ +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); + } + } + +} \ No newline at end of file diff --git a/net/aklabs/demo/simonsays/SimonButton.as b/net/aklabs/demo/simonsays/SimonButton.as new file mode 100755 index 0000000..d6ea0cf --- /dev/null +++ b/net/aklabs/demo/simonsays/SimonButton.as @@ -0,0 +1,123 @@ +package net.aklabs.demo.simonsays { + + import flash.events.MouseEvent; + import flash.display.Bitmap; + import flash.display.Sprite; + + /* + * class SimonButton + * + * This is just a custom button class that is pixel-accurate, because the Mastermin/Simon Says + * buttons are an extremely odd shape, so I needed something that could detect mouse hits in/ + * outside of the alpha color areas. + * + * Most of this class is really self explanatory, so I'm not going to bother documenting all of it. Only the parts that don't make immediate sense. + */ + public class SimonButton extends Sprite + { + protected var imgDown:Bitmap; // bitmap for when the button is pressed + protected var imgUp:Bitmap; // bitmap for when the button is released + protected var curImg:Bitmap; // the bitmap currently being displayed on the button + protected var magicColor:Number; // the magic color (e.g. "magic pink"), if any (otherwise alpha is used for hit detection) + + public function SimonButton(imgDown:Bitmap = null, imgUp:Bitmap = null, magicColor = 0x00000000) + { + super(); + this.imgDown = imgDown; + this.imgUp = imgUp; + this.curImg = null; + this.magicColor = magicColor; + this.addEventListener(MouseEvent.MOUSE_DOWN, this.onMouseDown); + this.addEventListener(MouseEvent.MOUSE_UP, this.onMouseUp); + this.addEventListener(MouseEvent.CLICK, this.onMouseClick); + } + + public function setMagicColor(color:Number) + { + this.magicColor = color; + } + + public function setBtnDown(obj:Bitmap) + { + if ( obj ) { + this.imgDown = obj; + } + } + + public function setBtnUp(obj:Bitmap) + { + if ( obj ) { + this.imgUp = obj; + } + } + + public function onMouseDown(obj:MouseEvent) + { + if ( (obj) && (! this.hitTestPoint(obj.localX, obj.localY)) ) { + obj.stopImmediatePropagation(); + return; + } + if ( this.curImg ) { + this.removeChild(this.curImg); + } + if ( this.imgDown ) { + this.addChild(this.imgDown); + this.curImg = this.imgDown; + } + } + + public function onMouseUp(obj:MouseEvent) + { + if ( (obj) && (!this.hitTestPoint(obj.localX, obj.localY)) ) { + obj.stopImmediatePropagation(); + return; + } + if ( this.curImg ) { + this.removeChild(this.curImg); + } + if ( this.imgUp ) { + this.addChild(this.imgUp); + this.curImg = this.imgUp; + } + } + + public function onMouseClick(obj:MouseEvent) + { + if ( ! this.hitTestPoint(obj.localX, obj.localY) ) { + obj.stopImmediatePropagation(); + } + } + + /* + * hitTestPoint(x, y, shapeFlag) + * + * An overriden version of hitTestPoint that is pixel-accurate. If the value at (x, y) is either A: of the value + * in the "magic color", or B: containing an alpha value of zero, then the hit is POSITIVE. Otherwise it is false. + * + * arguments: + * @x:Number, the X location of the hit relative to the origin of the object + * @y:Number, the Y location of the hit relative to the origin of the object + * @shapeFlag:Boolean, not used, just here for compatibility + * + * returns: Boolean (true if the hit is positive, false if it's negative) + */ + public override function hitTestPoint(x:Number, y:Number, shapeFlag:Boolean = false):Boolean + { + var color:uint; + var rgb:uint; + var a:uint; + + if ( this.curImg && this.curImg.bitmapData ) { + color = this.curImg.bitmapData.getPixel32(x, y); + a = ((color >> 24) & 0xFF); + rgb = (color & 0xFFFFFF00); + //trace("Alpha : " + a + " RGB " + rgb + " magic color " + this.magicColor); + if ( this.magicColor == rgb || a == 0 ) { + return false; + } + return true; + } + return false; + } + } +} diff --git a/net/aklabs/demo/simonsays/SimonSays.as b/net/aklabs/demo/simonsays/SimonSays.as new file mode 100755 index 0000000..e4d0566 --- /dev/null +++ b/net/aklabs/demo/simonsays/SimonSays.as @@ -0,0 +1,81 @@ +package net.aklabs.demo.simonsays +{ + + import flash.utils.Timer; + import flash.events.Event; + import flash.events.TimerEvent; + import net.aklabs.demo.simonsays.Preloader; + + public class Mastermind + { + protected var lightOutTimer:Timer; + protected var lightBitmaps:Array; + + public function Mastermind(preloader:Preloader, screenX:Number = 0, screenY:Number = 0) + { + // pass in a preloader so the class knows where to get its resources + var lightPositions = new Array(); + // this array holds pairs of (x,y) coordinates at which to place the dark/light colored + // Simon Says buttons, since they're meant to cover the existing buttons on the original graphic. + // These are hard-coded; in a better implementation, they would come from an XML or would be + // individual frames of an SWF which could be fired individually, etc. But this works. + // array idx 0: Blue, 1: Green, 2: Red, 3: Yellow + lightPositions.push( new Array(screenX + 216, screenY + 220)); // blue + lightPositions.push( new Array(screenX + 22, screenY + 20)); // green + lightPositions.push( new Array(screenX + 218, screenY + 20)); // red + lightPositions.push( new Array(screenX + 22, screenY + 218)); // yellow + // this array holds the light/dark bitmaps for the simon says buttons ... each idx is an array which holds (dark, light) bitmap objects + // the indexes (0-3) correspond to the above for colors + this.lightBitmaps = new Array(); + var fetchArray = new Array( new Array("gfx_btn_darkblue", "gfx_btn_lightblue"), + new Array("gfx_btn_darkgreen", "gfx_btn_lightgreen"), + new Array("gfx_btn_darkred", "gfx_btn_lightred"), + new Array("gfx_btn_darkyellow", "gfx_btn_lightyellow") ); + for ( var i = 0; i < fetchArray.length ; i++ ) { + light = preloader.getObject(fetchArray[i][0]); + dark = preloader.getObject(fetchArray[i][1]); + light.x = lightPositions[i][0]; + light.y = lightPositions[i][1]; + dark.x = lightPositions[i][0]; + dark.y = lightPositions[i][1]; + // we don't check the return values here because the preloader shouldn't even fire + // the application up if resources are missing + this.lightBitmaps.push(new Array(light, dark)); + // the dark buttons are always there, the lighted ones are just temporarily overlain on them for effect + addChild(dark); + } + whole = preloader.getObject("gfx_background"); + whole.x = screenX; + whole.y = screenY; + addChild(whole); + + this.lightOutTimer = Timer(1000); + this.lightOutTimer.addEventListener(TimerEvent.TIMER, this.onTimer); + this.lightOutTimer.start(); + } + + public function flashAll(length:Number) + { + this.lightOutTimer.reset(); + this.lightOutTimer.delay(length); + this.lightOutTimer.start(); + } + + public function lightButton(btn:Number, activeTime:Number) + { + if ( btn < 4 ) { + this.lightOutTimer.reset(); + this.lightOutTimer.delay(activeTime); + addChild(this.lightBitmaps[btn][0]); + this.lightOutTimer.start(); + } + } + + public function onTimer(evt:TimerEvent); + { + for ( i = 0 ; i < this.lightBitmaps.length ; i++ ){ + removeChild(this.lightBitmaps[i][0]); + } + } + } +} diff --git a/sfx/BACKGROUND.mp3 b/sfx/BACKGROUND.mp3 new file mode 100755 index 0000000..0291436 Binary files /dev/null and b/sfx/BACKGROUND.mp3 differ diff --git a/sfx/BLUE.mp3 b/sfx/BLUE.mp3 new file mode 100755 index 0000000..4d0dece Binary files /dev/null and b/sfx/BLUE.mp3 differ diff --git a/sfx/BUZZER.mp3 b/sfx/BUZZER.mp3 new file mode 100755 index 0000000..644df8b Binary files /dev/null and b/sfx/BUZZER.mp3 differ diff --git a/sfx/EXCLAMATION.mp3 b/sfx/EXCLAMATION.mp3 new file mode 100755 index 0000000..8f9ce28 Binary files /dev/null and b/sfx/EXCLAMATION.mp3 differ diff --git a/sfx/EXPLOSION.mp3 b/sfx/EXPLOSION.mp3 new file mode 100755 index 0000000..f59512e Binary files /dev/null and b/sfx/EXPLOSION.mp3 differ diff --git a/sfx/FORGIVENESS.mp3 b/sfx/FORGIVENESS.mp3 new file mode 100755 index 0000000..e1617d8 Binary files /dev/null and b/sfx/FORGIVENESS.mp3 differ diff --git a/sfx/GREEN.mp3 b/sfx/GREEN.mp3 new file mode 100755 index 0000000..3e4594e Binary files /dev/null and b/sfx/GREEN.mp3 differ diff --git a/sfx/POINTDOUBLER.mp3 b/sfx/POINTDOUBLER.mp3 new file mode 100755 index 0000000..e940b0d Binary files /dev/null and b/sfx/POINTDOUBLER.mp3 differ diff --git a/sfx/RED.mp3 b/sfx/RED.mp3 new file mode 100755 index 0000000..9887b1f Binary files /dev/null and b/sfx/RED.mp3 differ diff --git a/sfx/SHORTCIRCUIT.mp3 b/sfx/SHORTCIRCUIT.mp3 new file mode 100755 index 0000000..27d11de Binary files /dev/null and b/sfx/SHORTCIRCUIT.mp3 differ diff --git a/sfx/SIMONFAIL.wav b/sfx/SIMONFAIL.wav new file mode 100755 index 0000000..f45db60 Binary files /dev/null and b/sfx/SIMONFAIL.wav differ diff --git a/sfx/SIMONSTART.wav b/sfx/SIMONSTART.wav new file mode 100755 index 0000000..56940f2 Binary files /dev/null and b/sfx/SIMONSTART.wav differ diff --git a/sfx/SIMONTAUNT_1.wav b/sfx/SIMONTAUNT_1.wav new file mode 100755 index 0000000..e3e68f2 Binary files /dev/null and b/sfx/SIMONTAUNT_1.wav differ diff --git a/sfx/SIMONTAUNT_2.wav b/sfx/SIMONTAUNT_2.wav new file mode 100755 index 0000000..3ec5e91 Binary files /dev/null and b/sfx/SIMONTAUNT_2.wav differ diff --git a/sfx/SIMONTAUNT_3.wav b/sfx/SIMONTAUNT_3.wav new file mode 100755 index 0000000..4cd259e Binary files /dev/null and b/sfx/SIMONTAUNT_3.wav differ diff --git a/sfx/SIMONTAUNT_4.wav b/sfx/SIMONTAUNT_4.wav new file mode 100755 index 0000000..4a402c5 Binary files /dev/null and b/sfx/SIMONTAUNT_4.wav differ diff --git a/sfx/SIMONTAUNT_5.wav b/sfx/SIMONTAUNT_5.wav new file mode 100755 index 0000000..19bcba9 Binary files /dev/null and b/sfx/SIMONTAUNT_5.wav differ diff --git a/sfx/SLOWDOWN.mp3 b/sfx/SLOWDOWN.mp3 new file mode 100755 index 0000000..ce1e236 Binary files /dev/null and b/sfx/SLOWDOWN.mp3 differ diff --git a/sfx/SPEEDUP.mp3 b/sfx/SPEEDUP.mp3 new file mode 100755 index 0000000..dc4bc0f Binary files /dev/null and b/sfx/SPEEDUP.mp3 differ diff --git a/sfx/YELLOW.mp3 b/sfx/YELLOW.mp3 new file mode 100755 index 0000000..cd2f5e3 Binary files /dev/null and b/sfx/YELLOW.mp3 differ diff --git a/simon says.swf b/simon says.swf new file mode 100755 index 0000000..e406df2 Binary files /dev/null and b/simon says.swf differ diff --git a/wily.png b/wily.png new file mode 100755 index 0000000..28a785a Binary files /dev/null and b/wily.png differ diff --git a/wilysays-src-Mar-02-2010.zip b/wilysays-src-Mar-02-2010.zip new file mode 100755 index 0000000..6ee5a8c Binary files /dev/null and b/wilysays-src-Mar-02-2010.zip differ diff --git a/wilysays-src-Mar-02-2010/readme.txt b/wilysays-src-Mar-02-2010/readme.txt new file mode 100755 index 0000000..5cbdee6 --- /dev/null +++ b/wilysays-src-Mar-02-2010/readme.txt @@ -0,0 +1,10 @@ +webapp/ + + this directory contains the PHP files that run the leaderboard. + Really simple stuff. Also contains the help files and such. + +src/ + + Contains the actual source files that the game was made from. + + *SHOULD* load right up and run in Flash CS3. diff --git a/wilysays-src-Mar-02-2010/src/Game.as b/wilysays-src-Mar-02-2010/src/Game.as new file mode 100755 index 0000000..802b4e6 --- /dev/null +++ b/wilysays-src-Mar-02-2010/src/Game.as @@ -0,0 +1,909 @@ +/* + * MEGA MAN : "Wily Says" + * Demo for Zynga Games + * (C) Andrew Kesterson 2010 andrew@aklabs.net + * + */ + +// - TODO : Implement proper layering! Quit this *!@# of adding/removing children in the right order. + +package { + import flash.display.MovieClip; + import flash.display.SimpleButton; + import flash.text.TextField; + import flash.text.TextFormat; + import flash.text.TextFieldAutoSize; + import flash.text.AntiAliasType; + import flash.filters.GlowFilter; + import flash.events.Event; + import flash.events.TimerEvent; + import flash.events.KeyboardEvent; + import flash.events.MouseEvent; + import flash.utils.Timer; + import flash.utils.Dictionary; + import flash.media.SoundChannel; + import flash.net.*; + import net.aklabs.demo.simonsays.Preloader; + import net.aklabs.demo.simonsays.Pattern; + import net.aklabs.demo.simonsays.Mastermind; + import net.aklabs.demo.simonsays.LoadingObject; + import net.aklabs.demo.simonsays.Player; + import net.aklabs.demo.simonsays.Powerup; + import net.aklabs.demo.simonsays.PowerupEvent; + import net.aklabs.demo.simonsays.MastermindEvent; + + /* Class : Game + * + * Main application class. Handles player input, logic, etc. + */ + + public class Game extends MovieClip { + + protected var preloader:Preloader; // preloader content manager + protected var gameTimer:Timer; // timer for the game logic + protected var bgMusicChannel:SoundChannel; // channel on which background music is playing - TODO: Allow bgmusic/sfx turned ON/OFF + protected var curLevel:Number; // the level of the current number, from 1-10 + protected var player:Player; // Object representing the player + protected var curPattern:Pattern; // The currently active "Pattern" object being played out + protected var simon:Mastermind; // The "Simon Says" ("Mastermind") + protected var cutscenes:Dictionary; // A dictionary of instantiated and prepared SWF-cloned cutscenes + protected var difficulty:Number; // An integer from 0-n setting the difficulty of the game + public static var STATE_PLAYING:Number = 0; // STATE : Game is currently PLAYING (e.g., not in menu/cinematic/highscore/credits/etc) + public static var STATE_MENU:Number = 1; // STATE: Game is currently sitting at the main menu waiting for player to start the game + public static var STATE_HELP:Number = 2; // STATE : Game is currently on the HELP screen (currently unused) - TODO : Add "Help" screen beyond tutorial + public static var STATE_PASSWORD:Number = 3; /* STATE : Game is currently on the password input screen to resume a game (currently unused) + TODO : Get rid of this, password system was never implemented and doesn't make sense for this game */ + public static var STATE_GAMEOVER:Number = 4; // STATE : Player has lost the game and is dead + public static var STATE_PLAYING_PLAYBACK:Number = 5; /* STATE : Substate of STATE_PLAYING. Used while the Mastermind is playing back the currently + exposed portions of the currently playing pattern */ + public static var STATE_PLAYING_INPUT:Number = 6; // STATE : Substate of STATE_PLAYING. Used while the Mastermind is accepting input from the player. + public static var STATE_PLAYING_FLASHING:Number = 7; // STATE : Substate of STATE_PLAYING. Used while the Mastermind is flashing all its lights for a second. + public static var STATE_PLAYING_EXPLODING:Number = 8; // STATE : Substate of STATE_PLAYING. Used after the player loses, and while the screen is exploding. + public static var STATE_PLAYING_WAITING:Number = 9; /* STATE : Substate of STATE_PLAYING. When the explosion timer has fired, this substate is used while + waiting for the explosions to finish their animation before going to STATE_HIGHSCORE. */ + public static var STATE_PLAYING_WINLEVEL:Number = 10; /* STATE : Substate of STATE_PLAYING. Used when the player has completed all iterations of the + current pattern. Implies that the level number is scrolling past, etc.*/ + public static var STATE_WINGAME:Number = 11; // STATE : Used when the player has completed all patterns and has won the game. + public static var STATE_TUTORIAL = 12; // STATE : Game is currently running the tutorial/intro + public static var STATE_HIGHSCORE = 13; // STATE : Game is currently awaiting input on the high score screen + protected var primaryState:Number; // Stores the game's current primary state value + protected var secondaryState:Number; // Stores any applicable substate for the game, if any + protected var buttonSounds:Dictionary; // Stores references to the loaded sounds in the preloader for each of the Mastermind buttons + protected var timerLabel:TextField; // Text label for the timer display (upper right) + protected var scoreLabel:TextField; // Text label for the score display (upper left) + protected var levelLabel:TextField; // Text label for the "LEVEL" that scrolls across the screen after beating a pattern + protected var levelNumberLabel:TextField; // Text label for the level number (e.g., the "12" in "LEVEL 12") that scrolls after beating a pattern + protected var explosionTimer:Timer; // Timer that runs long enough to generate a bunch of sparse explosions when the player loses. + protected var explosions:Array; /* An array containing to references to all the currently running explosions on the screen + (used so that they can all be safely cleaned up & removed from the screen after Game Over and before moving to the High Score screen) */ + protected var maxLevel:Number; // maximum level number achievable before winning the game + protected var levelClearTime:Number; // The time in which a given pattern/level was cleared; currently unused. TODO - Implement this and put it in the high scores + protected var labelPauseTimer:Timer; // the timer that lets the "Level X" label pause in the center of the screen briefly + + public static var KEY_NUM1:Number = 97; + public static var KEY_NUM3:Number = 99; + public static var KEY_NUM7:Number = 103; + public static var KEY_NUM9:Number = 105; + public static var KEY_COMMA:Number = 188; + public static var KEY_BACKSLASH:Number = 191; + public static var KEY_K:Number = 75; + public static var KEY_APOSTROPHE = 222; + public static var KEY_0:Number = 48; + public static var KEY_1:Number = 49; + public static var KEY_2:Number = 50; + public static var KEY_3:Number = 51; + public static var KEY_4:Number = 52; + public static var KEY_5:Number = 53; + public static var KEY_6:Number = 54; + public static var KEY_7:Number = 55; + public static var KEY_8:Number = 56; + public static var KEY_9:Number = 57; + public static var KEY_SPACE:Number = 32; + + /* + * Game() + * + * Default constructor for Game class + * + * arguments: none + * + * returns : Game + */ + public function Game() + { + // -- setup the preloader and start it downloading + var assets = new Array( new Array("sounds", "music_background", "sfx/BACKGROUND.mp3"), + new Array("sounds", "sfx_explosion", "sfx/EXPLOSION.mp3"), + new Array("sounds", "sfx_btn_blue", "sfx/BLUE.mp3"), + new Array("sounds", "sfx_btn_green", "sfx/GREEN.mp3"), + new Array("sounds", "sfx_btn_red", "sfx/RED.mp3"), + new Array("sounds", "sfx_btn_yellow", "sfx/YELLOW.mp3"), + new Array("sounds", "sfx_slowdown", "sfx/SLOWDOWN.mp3"), + new Array("sounds", "sfx_shortcircuit", "sfx/SHORTCIRCUIT.mp3"), + new Array("sounds", "sfx_pointdoubler", "sfx/POINTDOUBLER.mp3"), + new Array("sounds", "sfx_exclamation", "sfx/EXCLAMATION.mp3"), + new Array("sounds", "sfx_forgiveness", "sfx/FORGIVENESS.mp3"), + new Array("images", "gfx_btn_darkblue", "gfx/blue-dark.png"), + new Array("images", "gfx_btn_lightblue", "gfx/blue-light.png"), + new Array("images", "gfx_btn_darkgreen", "gfx/green-dark.png"), + new Array("images", "gfx_btn_lightgreen", "gfx/green-light.png"), + new Array("images", "gfx_btn_darkred", "gfx/red-dark.png"), + new Array("images", "gfx_btn_lightred", "gfx/red-light.png"), + new Array("images", "gfx_btn_darkyellow", "gfx/yellow-dark.png"), + new Array("images", "gfx_btn_lightyellow", "gfx/yellow-light.png"), + new Array("images", "gfx_mastermind", "gfx/whole.png"), + new Array("images", "gfx_background", "gfx/game_background.png"), + new Array("images", "gfx_circuitboard", "gfx/circuitboard.png"), + new Array("images", "gfx_pwup_exclamation", "gfx/EXCLAMATION.png"), + new Array("images", "gfx_pwup_forgiveness", "gfx/FORGIVENESS.png"), + new Array("images", "gfx_pwup_pointdoubler", "gfx/POINTDOUBLER.png"), + new Array("images", "gfx_pwup_shortcircuit", "gfx/SHORTCIRCUIT.png"), + new Array("images", "gfx_pwup_slowdown", "gfx/SLOWDOWN.png"), + new Array("images", "gfx_progress_red", "gfx/progress_red.png"), + new Array("images", "gfx_progress_green", "gfx/progress_green.png"), + new Array("images", "gfx_pwup_freelife", "gfx/LIFE.png"), + new Array("movies", "movie_endscreen", "gfx/cutscenes/endscreen.swf", ""), + new Array("movies", "movie_intro_menu", "gfx/cutscenes/intro-menu.swf", ""), + new Array("movies", "movie_tutorial", "gfx/cutscenes/tutorial.swf", ""), + new Array("movies", "movie_highscore", "gfx/cutscenes/highscore.swf", ""), + new Array("movies", "movie_difficultychooser", "gfx/cutscenes/difficultychooser.swf", ""), + new Array("movies", "movie_explosion", "gfx/explosion.swf", "SmallExplosion") ); + this.preloader = new Preloader(assets); + this.preloader.x = 200; + this.preloader.y = 140; + this.addChild(this.preloader); + this.preloader.addEventListener(Event.COMPLETE, this.onPreloaderComplete); + this.preloader.loadAssets(); + + this.buttonSounds = new Dictionary(); + this.buttonSounds[0] = "sfx_btn_blue"; + this.buttonSounds[1] = "sfx_btn_green"; + this.buttonSounds[2] = "sfx_btn_red"; + this.buttonSounds[3] = "sfx_btn_yellow"; + + // -- set up all the timers + this.gameTimer = new Timer(25); + this.gameTimer.start(); + this.explosionTimer = new Timer(5000); + stage.addEventListener(KeyboardEvent.KEY_UP, this.onKeyUp); + this.labelPauseTimer = new Timer(2000); + + // -- miscellanious stuff + this.player = new Player(); + this.cutscenes = new Dictionary(); + this.explosions = new Array(); + this.curLevel = 1; + this.curPattern = null; + this.difficulty = 3; + } + + /* + * onNonLoopEnterFrame(evt) + * + * This function serves as a trigger on sprites which should not loop their animations. There may be + * a simpler way to do this, but being pressed for time I knew this would stop it. + * + * arguments: + * @evt : Event , event that triggered this function + * + * Returns: none + */ + public function onNonLoopEnterFrame(evt:Event) + { + if ( evt.target.currentFrame == evt.target.totalFrames ) { + evt.target.stop(); + } + } + + /* + * onKeyUp(evt) + * + * This function grabs the player's keyboard input and passes it off to the Player, Pattern objects, etc + * + * arguments: + * @evt: Event, event that triggered this function + * + * Returns : none + */ + public function onKeyUp(evt:KeyboardEvent) + { + var pwup:Powerup = null; + + if ( this.primaryState == Game.STATE_PLAYING_INPUT ) { + var colorPressed:Number = -1; + if ( (( evt.keyCode == Game.KEY_NUM1) || (evt.keyCode == Game.KEY_COMMA)) ) { + colorPressed = Pattern.COLOR_YELLOW; + } else if ( (( evt.keyCode == Game.KEY_NUM3) || (evt.keyCode == Game.KEY_BACKSLASH)) ) { + colorPressed = Pattern.COLOR_BLUE; + } else if ( (( evt.keyCode == Game.KEY_NUM7) || (evt.keyCode == Game.KEY_K)) ) { + colorPressed = Pattern.COLOR_GREEN; + } else if ( (( evt.keyCode == Game.KEY_NUM9) || (evt.keyCode == Game.KEY_APOSTROPHE)) ) { + colorPressed = Pattern.COLOR_RED; + } else if ( ( evt.keyCode >= Game.KEY_0 && evt.keyCode <= Game.KEY_9) ) { + if ( evt.keyCode == Game.KEY_0 ) + evt.keyCode = Game.KEY_0 + 10; // trust me it makes sense (Key 0 is the player's last inventory slot to the right + // but it comes first in the keyCode sequence before 1, which is the far left, so we add 10 to it + // because we're passing an index from 0 - 9 for the player's inventory) + this.player.usePowerupAt(evt.keyCode - 49); + } + + if ( colorPressed != -1 ) + this.checkColorHit(colorPressed); + } else if ( this.primaryState == Game.STATE_MENU ) { + if ( evt.keyCode == Game.KEY_SPACE ) { + this.addChild(cutscenes["difficultychooser"]); + } + } else if ( this.primaryState == Game.STATE_TUTORIAL ) { + if ( evt.keyCode == Game.KEY_SPACE ) { + this.newGame(); + } + } else if ( this.primaryState == Game.STATE_WINGAME ) { + if ( evt.keyCode == Game.KEY_SPACE ) { + this.primaryState = Game.STATE_HIGHSCORE; + this.secondaryState = Game.STATE_HIGHSCORE; + this.removeChild(this.cutscenes["endscreen"]); + this.addChild(this.cutscenes["highscores"]); + this.cutscenes["highscores"].play(); + } + } + } + + /* + * onPreloaderComplete(evt) + * + * This function is fired whenever the Preloader fires an event signifying that all items + * in the asset list have been successfully loaded. + * + * arguments: + * @evt : Event, the event firing this function + * + * Returns: none + */ + + public function onPreloaderComplete(evt:Event) + { + if ( evt.target == this.preloader ){ + this.removeChild(this.preloader); + // -- setup the cutscenes so they're ready to use + this.cutscenes["intro_menu"] = this.preloader.getObject("movie_intro_menu"); + this.cutscenes["intro_menu"].x = 0; + this.cutscenes["intro_menu"].y = 0; + this.cutscenes["intro_menu"].addEventListener(Event.ENTER_FRAME, this.onNonLoopEnterFrame); + this.cutscenes["tutorial"] = this.preloader.getObject("movie_tutorial"); + this.cutscenes["tutorial"].x = 0; + this.cutscenes["tutorial"].y = 0; + this.cutscenes["tutorial"].addEventListener(Event.ENTER_FRAME, this.onNonLoopEnterFrame); + this.cutscenes["highscores"] = this.preloader.getObject("movie_highscore"); + this.cutscenes["highscores"].x = 0; + this.cutscenes["highscores"].y = 0; + this.cutscenes["highscores"].addEventListener(Event.ENTER_FRAME, this.onNonLoopEnterFrame); + this.cutscenes["highscores"].highScorePostBtn.addEventListener(MouseEvent.CLICK, this.onHighScoreEntry); + this.cutscenes["highscores"].highScoreCancelBtn.addEventListener(MouseEvent.CLICK, this.onHighScoreEntry); + this.cutscenes["highscores"].viewScoreBtn.addEventListener(MouseEvent.CLICK, this.onHighScoreEntry); + this.cutscenes["difficultychooser"] = this.preloader.getObject("movie_difficultychooser"); + this.cutscenes["difficultychooser"].x = 300; + this.cutscenes["difficultychooser"].y = 200; + this.cutscenes["difficultychooser"].easyBtn.addEventListener(MouseEvent.CLICK, this.onDifficultySelected); + this.cutscenes["difficultychooser"].normalBtn.addEventListener(MouseEvent.CLICK, this.onDifficultySelected); + this.cutscenes["difficultychooser"].hardBtn.addEventListener(MouseEvent.CLICK, this.onDifficultySelected); + this.cutscenes["difficultychooser"].wilyBtn.addEventListener(MouseEvent.CLICK, this.onDifficultySelected); + this.cutscenes["endscreen"] = this.preloader.getObject("movie_endscreen"); + this.cutscenes["endscreen"].x = 0; + this.cutscenes["endscreen"].y = 0; + + this.addChild(this.cutscenes["intro_menu"]); + + this.gameTimer.removeEventListener(TimerEvent.TIMER, this.onPreloaderComplete); + this.gameTimer.addEventListener(TimerEvent.TIMER, this.onGameTimer); + + this.simon = new Mastermind(); + this.simon.x = (640-(this.simon.width))/2; + this.simon.y = 20; + this.simon.addEventListener(MastermindEvent.BTN_CLICKED, this.onMastermindClicked); + + this.timerLabel = new TextField(); + this.timerLabel.background = false; + this.timerLabel.autoSize = TextFieldAutoSize.LEFT; + var timerLabelFormat = new TextFormat(); + timerLabelFormat.font = "Courier New"; + timerLabelFormat.bold = false; + timerLabelFormat.color = 0xF12B2B; + timerLabelFormat.size = 36; + this.timerLabel.defaultTextFormat = timerLabelFormat; + this.timerLabel.text = "00000"; + this.timerLabel.x = 504; + this.timerLabel.y = 34; + + this.scoreLabel = new TextField(); + this.scoreLabel.background = false; + this.scoreLabel.autoSize = TextFieldAutoSize.LEFT; + this.scoreLabel.defaultTextFormat = timerLabelFormat; + this.scoreLabel.text = "000000"; + this.scoreLabel.x = 12; + this.scoreLabel.y = 34; + + this.levelLabel = new TextField(); + this.levelLabel.background = false; + this.levelLabel.autoSize = TextFieldAutoSize.LEFT; + this.levelNumberLabel = new TextField(); + this.levelNumberLabel.background = false; + this.levelNumberLabel.autoSize = TextFieldAutoSize.LEFT; + var levelLabelFormat = new TextFormat(); + levelLabelFormat.font = "Helvetica"; + levelLabelFormat.bold = true; + levelLabelFormat.color = 0xF12B2B; + levelLabelFormat.size = 72; + this.levelLabel.defaultTextFormat = levelLabelFormat; + this.levelNumberLabel.defaultTextFormat = levelLabelFormat; + this.levelLabel.antiAliasType = AntiAliasType.ADVANCED; + this.levelNumberLabel.antiAliasType = AntiAliasType.ADVANCED; + this.levelLabel.filters = [new GlowFilter(0x000000, 1.0, 4, 4, 300)]; + this.levelNumberLabel.filters = [new GlowFilter(0x000000, 1.0, 4, 4, 300)]; + this.levelLabel.x = 150; + this.levelLabel.y = 500; + this.levelNumberLabel.x = 400; + this.levelNumberLabel.y = -150; + this.levelLabel.text = "LEVEL"; + + this.player.addEventListener(PowerupEvent.USED_POWERUP, this.onUsedPowerup); + this.playBackgroundMusic(); + this.primaryState = Game.STATE_MENU; + this.secondaryState = Game.STATE_MENU; + return; + } + } + + /* + * onBackgroundMusicFinished(evt) + * + * This function just makes sure that the background music loops forever + * + * arguments: + * @evt: Event, event firing this function + * + * Returns: none + */ + public function onBackgroundMusicFinished(evt:Event) + { + this.playBackgroundMusic(); + } + + + /* + * playBackgroundMusic() + * + * This function starts the background music playing + * + * arguments: none + * + * Returns : none + */ + public function playBackgroundMusic() + { + if ( this.bgMusicChannel ) + this.bgMusicChannel.stop(); + var bgmusic = this.preloader.getObject("music_background"); + if ( !bgmusic ) { + return; + } + this.bgMusicChannel = bgmusic.play(); + this.bgMusicChannel.addEventListener(Event.SOUND_COMPLETE, this.onBackgroundMusicFinished); + } + + /* + * onGetPowerup(evt) + * + * This function is fired whenever the Pattern is signifying that the player has gotten a powerup from the pattern + * + * arguments: + * @evt : Event, event firing this function + * + * Returns: none + */ + public function onGetPowerup(evt:PowerupEvent) + { + if ( evt.pwup != null ) { + var pwup = evt.pwup; + pwup.addChild(this.preloader.getBitmap(pwup.imgHandle)); + this.player.addPowerup(pwup); + } + } + + /* + * onGameTimer(evt) + * + * Fires once every 25 ms to run the core game logic + * + * arguments: + * @evt : Event, the event firing this function + * + * Returns : none + */ + + public function onGameTimer(evt:TimerEvent) + { + var i:Number = 0; + + /* This state machine got just a little bit too complex, and I think alot of it could be probably get + done away with in favor of more events driving the show. However for right now, it works. The IF chains + check the primary state first, then go in and check secondary states and ancillary conditions. */ + + if ( (this.primaryState == Game.STATE_PLAYING) && (this.curPattern.state == Pattern.STATE_STOPPED) ) { + if ( this.secondaryState == Game.STATE_PLAYING_FLASHING && (!this.simon.isLit) ) { + // we've flashed the Mastermind once, now let's play back the currently exposed portions of the pattern + this.curPattern.play(true); + this.primaryState = Game.STATE_PLAYING_PLAYBACK; + this.secondaryState = Game.STATE_PLAYING_PLAYBACK; + } else if ( this.secondaryState == Game.STATE_PLAYING_EXPLODING ) { + // the player has died and we're still in the explosion timeframe, so blow some *!$% up + if ( Math.random() < 0.25 ) { + var explosion = this.preloader.getMovieClip("movie_explosion"); + explosion.addEventListener(Event.ENTER_FRAME, this.onNonLoopEnterFrame); + explosion.x = Math.random()*640; + explosion.y = Math.random()*480; + explosion.play(); + this.preloader.playSound("sfx_explosion"); + this.addChild(explosion); + this.explosions.push(explosion); + } + } else if ( this.secondaryState == Game.STATE_PLAYING_WINLEVEL ) { + // player just beat the pattern; scroll "LEVEL" down from the top, and the level number up from the bottom + this.levelNumberLabel.text = "" + this.curLevel; + if ( this.levelLabel.y == 175 && (!this.labelPauseTimer.running)) { + this.labelPauseTimer.addEventListener(TimerEvent.TIMER, this.onLabelPauseTimer); + this.labelPauseTimer.reset(); + this.labelPauseTimer.start(); + } else if ( this.labelPauseTimer.running ) { + // do nothing if the label pause timer is running + } else if ( this.levelLabel.y <= -120 ) { + this.secondaryState = Game.STATE_PLAYING; + this.levelLabel.y = 500; + this.levelNumberLabel.y = -150; + } else { + this.levelLabel.y -= 5; + this.levelNumberLabel.y += 5; + } + } else if ( this.secondaryState == Game.STATE_PLAYING ){ + // flash the Mastermind once before playing back the portions currently exposed, so we have the player's + // attention + this.simon.flashAll(500); + this.secondaryState = Game.STATE_PLAYING_FLASHING; + } + } else if ( (this.primaryState == Game.STATE_PLAYING_PLAYBACK) ) { + if ( (this.secondaryState == Game.STATE_PLAYING_FLASHING ) && (!this.simon.isLit) ) { + // okay we're done flashing around, let the player give input and start the timers + this.curPattern.play(false); + this.primaryState = Game.STATE_PLAYING_INPUT; + this.secondaryState = Game.STATE_PLAYING; + } else if ( this.secondaryState == Game.STATE_PLAYING_PLAYBACK && this.curPattern.state == Pattern.STATE_STOPPED ) { + // flash the Mastermind a 2nd time to let the player know we're done running the pattern, and they need to start hitting buttons + this.simon.flashAll(500); + this.secondaryState = Game.STATE_PLAYING_FLASHING; + } + } else if ( (this.primaryState == Game.STATE_PLAYING_INPUT) && (this.curPattern.state != Pattern.STATE_RUNNING ) ) { + if ( this.curPattern.state == Pattern.STATE_CORRECT ) { + // player has repeated the pattern correctly + this.player.score += this.curPattern.score; + this.curPattern.complexify(); + if ( this.curPattern.patternLength() > this.player.maxPattern ) + this.player.maxPattern = this.curPattern.patternLength(); + // check the state again after complexifying it. If the player has reached the end of the pattern, we won't know + // until we've ran .complexify() on it. + if ( this.curPattern.state == Pattern.STATE_COMPLETE ) { + this.nextLevel(); + if ( this.curLevel >= this.maxLevel ) { + this.wonGame(); + return; + } + return; + } + } else if ( this.curPattern.state == Pattern.STATE_FAILED ){ + // Player irrevocably missed the pattern; deduct a life and either start a new pattern, or game over. + this.player.die(); + if ( this.player.lives <= 0 ) { + this.curPattern.stop() + this.primaryState = Game.STATE_PLAYING; + this.secondaryState = Game.STATE_PLAYING_EXPLODING; + this.explosionTimer.addEventListener(TimerEvent.TIMER, this.onExplosionTimer); + this.explosionTimer.start(); + this.curPattern.forceState(Pattern.STATE_STOPPED); + return; + } else { + this.newPattern(); + } + } + // if we haven't returned out from a previous condition, it's safe to reset the primary/secondary state + // to STATE_PLAYING so we'll flash and run the pattern normally + this.primaryState = Game.STATE_PLAYING; + this.secondaryState = Game.STATE_PLAYING; + this.timerLabel.text = "00000"; + } else if ( this.primaryState == Game.STATE_PLAYING_INPUT ) { + // Player is running input, just update the timer text + this.timerLabel.text = "" + this.curPattern.clearTime; + } + this.scoreLabel.text = "" + int(player.score); + return; + } + + /* + * onLabelPauseTimer(evt) + * + * This function is called when the label pause timer is up, so the Level labels will start moving again + * + * arguments: + * @evt:TimerEvent, the event firing this function + * + * Returns: none + */ + + public function onLabelPauseTimer(evt:TimerEvent) + { + this.labelPauseTimer.removeEventListener(TimerEvent.TIMER, this.onLabelPauseTimer); + this.labelPauseTimer.stop(); + // this bumps it past the pixel mark so we don't duplicate the onLabelPauseTimer call and it sits there forever + this.levelLabel.y -= 5; + this.levelNumberLabel.y == 5. + } + + /* + * onDifficultySelected(evt) + * + * This function is called whenever the player selects a difficulty level from the main screen starting a new game + * + * arguments: + * @evt : Event, the event firing this function + * + * Returns : none + */ + public function onDifficultySelected(evt:MouseEvent) + { + var mapping:Dictionary = new Dictionary; + // -- this just saves me a long block of if () { ... } , and the use of one-line X ? Y : Z ... + mapping[this.cutscenes["difficultychooser"].easyBtn] = 0; + mapping[this.cutscenes["difficultychooser"].normalBtn] = 1; + mapping[this.cutscenes["difficultychooser"].hardBtn] = 2; + mapping[this.cutscenes["difficultychooser"].wilyBtn] = 3; + this.difficulty = mapping[evt.target]; + + this.removeChild(this.cutscenes["difficultychooser"]); + this.removeChild(this.cutscenes["intro_menu"]); + this.primaryState = Game.STATE_TUTORIAL; + this.secondaryState = Game.STATE_TUTORIAL; + this.addChild(this.cutscenes["tutorial"]); + this.cutscenes["tutorial"].play(); + } + + /* + * onHighScoreEntry(evt) + * + * This function fires whenever the user clicks any of the buttons on the High Score screen + * + * arguments: + * @evt : Event, the event firing this function + * + * Returns: none + */ + public function onHighScoreEntry(evt:MouseEvent) + { + if ( evt.target == this.cutscenes["highscores"].highScorePostBtn ) { + // post up the user's high score and spin off a new browser window to the high score area + var request:URLRequest = new URLRequest("http://atlanta.aklabs.net/~akesterson/wilysays/index.php"); + request.method = URLRequestMethod.POST; + var variables:URLVariables = new URLVariables(); + variables.op = "store"; + variables.name = this.cutscenes["highscores"].highScoreName.text; + variables.score = this.player.score; + variables.maxpattern = this.player.maxPattern; + request.data = variables; + sendToURL(request); + var request:URLRequest = new URLRequest("http://atlanta.aklabs.net/~akesterson/wilysays/index.php"); + navigateToURL(request, "_blank"); + } else if ( evt.target == this.cutscenes["highscores"].viewScoreBtn) { + // just send the user to the high scores and return, don't disable any of the buttons or change state + var request:URLRequest = new URLRequest("http://atlanta.aklabs.net/~akesterson/wilysays/index.php"); + navigateToURL(request, "_blank"); + return; + } + // go back to the main menu + this.removeChild(this.cutscenes["highscores"]); + this.addChild(this.cutscenes["intro_menu"]); + this.primaryState = Game.STATE_MENU; + this.primaryState = Game.STATE_MENU; + this.cutscenes["intro_menu"].gotoAndPlay(0); + } + + /* + * clearExplosions() + * + * This function is ran when the explosion timer is done, and right before the high score screen + * is fixing to come up, to make sure that all the explosions are gone from the screen. + * + * arguments: none + * + * Returns: none + */ + public function clearExplosions() + { + var i:Number; + for ( i = 0; i < this.explosions.length ; i++ ) { + this.removeChild(this.explosions[i]); + this.explosions[i].stop(); + this.explosions.splice(i, 1); + } + } + + /* + * clearGameScreen() + * + * This function clears the game screen of all play elements (inventory, lives, Mastermind, etc) + * + * arguments : none + * + * returns: none + */ + public function clearGameScreen() + { + this.removeChild(this.preloader.getObject("gfx_circuitboard")); + this.removeChild(this.preloader.getObject("gfx_background")); + this.removeChild(this.simon); + this.removeChild(this.timerLabel); + this.removeChild(this.scoreLabel); + this.removeChild(this.levelLabel); + this.removeChild(this.levelNumberLabel); + if ( this.curPattern ) + this.removeChild(this.curPattern); + this.removeChild(this.player); + this.removeChild(this.player.lifeDisplay); + + this.player.clearInventory(); + + this.explosionTimer.reset(); + this.explosionTimer.delay = 5000; + this.explosionTimer.stop(); + + } + + /* + * wonGame() + * + * This function is called when the player has beaten all 10 levels and therefore won the game + * + * arguments: None + * + * Returns: None + * + */ + + public function wonGame() + { + this.clearGameScreen(); + this.primaryState = Game.STATE_WINGAME; + this.secondaryState = Game.STATE_WINGAME; + this.addChild(this.cutscenes["endscreen"]); + this.cutscenes["endscreen"].play(); + } + + /* gameOver() + * + * This function is called whenever the player has lost all of their lives, and achieved Game Over + * + * arguments : None + * + * Returns : none + */ + public function gameOver() + { + this.clearGameScreen(); + this.primaryState = Game.STATE_HIGHSCORE; + this.secondaryState = Game.STATE_HIGHSCORE; + this.addChild(this.cutscenes["highscores"]); + this.cutscenes["highscores"].play(); + } + + /* + * nextLevel() + * + * This function runs to setup the next level above the previous one + * + * arguments : none + * + * Returns : none + */ + public function nextLevel() + { + this.curLevel += 1; + this.levelLabel.y = 500; + this.levelNumberLabel.y = -150; + this.levelNumberLabel.text = "" + this.curLevel; + this.primaryState = Game.STATE_PLAYING; + this.secondaryState = Game.STATE_PLAYING_WINLEVEL; + this.player.score += this.curPattern.score; + this.timerLabel.text = "00000"; + this.scoreLabel.text = "" + int(player.score); + this.newPattern(); + } + + /* + * newGame() + * + * This function sets up a new game when the player starts a game from the main menu + * + * arguments : none + * + * Returns: none + */ + public function newGame() + { + // difficulty just really changes how long the patterns are and how many levels you play + if ( this.difficulty > 1 ) { + this.curLevel = 3*this.difficulty; + } else + this.curLevel = 0; + this.maxLevel = this.curLevel + (6+this.difficulty); + this.player.score = 0; + this.player.lives = 3; + this.cutscenes["tutorial"].stop(); + this.cutscenes["highscores"].stop(); + this.cutscenes["intro_menu"].stop(); + + try { + this.removeChild(this.cutscenes["tutorial"]); + } catch (error:Error) { + // do nothing, it wasn't a child for some reason.. (I ran into this a couple times but not sure why) + } + + this.addChild(this.preloader.getObject("gfx_circuitboard")); + this.addChild(this.preloader.getObject("gfx_background")); + this.addChild(this.simon); + this.addChild(this.timerLabel); + this.addChild(this.scoreLabel); + this.addChild(player); + this.addChild(player.lifeDisplay); + this.addChild(this.levelLabel); + this.addChild(this.levelNumberLabel); + + this.player.x = 0; + this.player.y = 480-64; + player.lifeDisplay.x = 400; + player.lifeDisplay.y = 480-64; + this.player.resetLifePositions(); + + this.primaryState = Game.STATE_PLAYING; + this.secondaryState = Game.STATE_PLAYING; + + this.newPattern(); + this.nextLevel(); + } + + /* + * onExplosionTimer(evt) + * + * This function fires when the explosion timeframe is up + * + * arguments : + * @evt : Event, the event firing this function + * + * Returns : none + */ + public function onExplosionTimer(evt:TimerEvent) + { + this.primaryState = Game.STATE_PLAYING; + this.secondaryState = Game.STATE_PLAYING_WAITING; + for ( var i:Number = 0; i < this.explosions.length; i++ ) { + if ( this.explosions[i].currentFrame != this.explosions[i].totalFrames ) { + // some of the explosions aren't done yet, let them finish + this.explosionTimer.delay = 1000; + this.explosionTimer.start(); + return; + } + } + this.clearExplosions(); + this.gameOver(); + } + + /* + * onMastermindClicked(evt) + * + * This function fires whenever the player clicks the mouse on one of the mastermind buttons + * + * arguments: + * @evt : Event, the event firing this function + * + * Returns : none + */ + public function onMastermindClicked(evt:MastermindEvent) + { + this.checkColorHit(evt.colorClicked); + } + + /* + * autoForgiveness() + * + * This function checkes to see if the player has a Forgiveness powerup in his inventory, + * and if it does, it uses it to stop the pattern from blowing out one of the players' lives + * + * arguments : none + * + * Returns : none + */ + public function autoForgiveness() + { + var pwup:Powerup = null; + for ( var i:Number = 0; i < this.player.inventory.length ; i++ ) { + pwup = this.player.inventory[i]; + if ( pwup.pType == Powerup.PTYPE_FORGIVENESS ) { + this.curPattern.forceState(Pattern.STATE_STOPPED); + this.player.usePowerupAt(i); + break; + } + } + } + + /* + * checkColorHit(colorPressed) + * + * arguments: + * @colorPressed : Number, an integer (e.g. Pattern.COLOR_XXXX) specifying which color the player hit + * + * Returns: none + */ + public function checkColorHit(colorPressed:Number) + { + if ( this.curPattern.colorActive(colorPressed) == true ) { + this.simon.lightButton(colorPressed, 100); + this.preloader.playSound(this.buttonSounds[colorPressed]); + } else { + this.autoForgiveness(); + this.curPattern.stop(); + return; + } + } + + /* + * onUsedPowerup() + * + * Fired whenever the player uses a powerup + * + * arguments: + * @evt : Event, the event firing this function + * + * Returns: none + */ + public function onUsedPowerup(evt:PowerupEvent) + { + // the only time we get a powerupevent for used powerups + // is when it's the player dispatching one; we have to + // then re-dispatch it so that the pattern will see it, + // since it's in the opposite direction for bubbling (things bubble up, + // never down). We never actually process them, all powerup handling is + // handled between the player & pattern. We just dispatch. + this.curPattern.onUsedPowerup(evt); + } + + /* + * newPattern() + * + * Creates a new pattern object to challenge the player. + * + * arguments: none + * + * Returns: none + */ + public function newPattern() + { + try { + this.removeChild(this.curPattern); + } catch (error:Error) { + // do nothing, it wasn't in our child list yet + } + this.curPattern = new Pattern(this.curLevel, this.simon, this.difficulty); + this.curPattern.addEventListener(PowerupEvent.GOT_POWERUP, this.onGetPowerup); + this.curPattern.stop(); + this.curPattern.forceState(Pattern.STATE_STOPPED); + this.curPattern.x = 602; + this.curPattern.y = 138; + this.addChild(this.curPattern); + trace("Finished newPattern()"); + } + } + } \ No newline at end of file diff --git a/wilysays-src-Mar-02-2010/src/gfx/EXCLAMATION.png b/wilysays-src-Mar-02-2010/src/gfx/EXCLAMATION.png new file mode 100755 index 0000000..fc54bdf Binary files /dev/null and b/wilysays-src-Mar-02-2010/src/gfx/EXCLAMATION.png differ diff --git a/wilysays-src-Mar-02-2010/src/gfx/FORGIVENESS.png b/wilysays-src-Mar-02-2010/src/gfx/FORGIVENESS.png new file mode 100755 index 0000000..ac5376d Binary files /dev/null and b/wilysays-src-Mar-02-2010/src/gfx/FORGIVENESS.png differ diff --git a/wilysays-src-Mar-02-2010/src/gfx/LIFE.png b/wilysays-src-Mar-02-2010/src/gfx/LIFE.png new file mode 100755 index 0000000..40b22f7 Binary files /dev/null and b/wilysays-src-Mar-02-2010/src/gfx/LIFE.png differ diff --git a/wilysays-src-Mar-02-2010/src/gfx/POINTDOUBLER.png b/wilysays-src-Mar-02-2010/src/gfx/POINTDOUBLER.png new file mode 100755 index 0000000..4acc149 Binary files /dev/null and b/wilysays-src-Mar-02-2010/src/gfx/POINTDOUBLER.png differ diff --git a/wilysays-src-Mar-02-2010/src/gfx/SHORTCIRCUIT.png b/wilysays-src-Mar-02-2010/src/gfx/SHORTCIRCUIT.png new file mode 100755 index 0000000..ac38dee Binary files /dev/null and b/wilysays-src-Mar-02-2010/src/gfx/SHORTCIRCUIT.png differ diff --git a/wilysays-src-Mar-02-2010/src/gfx/SLOWDOWN.png b/wilysays-src-Mar-02-2010/src/gfx/SLOWDOWN.png new file mode 100755 index 0000000..0e4e0c0 Binary files /dev/null and b/wilysays-src-Mar-02-2010/src/gfx/SLOWDOWN.png differ diff --git a/wilysays-src-Mar-02-2010/src/gfx/blue-dark.png b/wilysays-src-Mar-02-2010/src/gfx/blue-dark.png new file mode 100755 index 0000000..448b3c5 Binary files /dev/null and b/wilysays-src-Mar-02-2010/src/gfx/blue-dark.png differ diff --git a/wilysays-src-Mar-02-2010/src/gfx/blue-light.png b/wilysays-src-Mar-02-2010/src/gfx/blue-light.png new file mode 100755 index 0000000..b257804 Binary files /dev/null and b/wilysays-src-Mar-02-2010/src/gfx/blue-light.png differ diff --git a/wilysays-src-Mar-02-2010/src/gfx/circuitboard.png b/wilysays-src-Mar-02-2010/src/gfx/circuitboard.png new file mode 100755 index 0000000..bd4f65d Binary files /dev/null and b/wilysays-src-Mar-02-2010/src/gfx/circuitboard.png differ diff --git a/wilysays-src-Mar-02-2010/src/gfx/cutscenes/difficultychooser.fla b/wilysays-src-Mar-02-2010/src/gfx/cutscenes/difficultychooser.fla new file mode 100755 index 0000000..548af70 Binary files /dev/null and b/wilysays-src-Mar-02-2010/src/gfx/cutscenes/difficultychooser.fla differ diff --git a/wilysays-src-Mar-02-2010/src/gfx/cutscenes/difficultychooser.swf b/wilysays-src-Mar-02-2010/src/gfx/cutscenes/difficultychooser.swf new file mode 100755 index 0000000..6058569 Binary files /dev/null and b/wilysays-src-Mar-02-2010/src/gfx/cutscenes/difficultychooser.swf differ diff --git a/wilysays-src-Mar-02-2010/src/gfx/cutscenes/endscreen.fla b/wilysays-src-Mar-02-2010/src/gfx/cutscenes/endscreen.fla new file mode 100755 index 0000000..e748561 Binary files /dev/null and b/wilysays-src-Mar-02-2010/src/gfx/cutscenes/endscreen.fla differ diff --git a/wilysays-src-Mar-02-2010/src/gfx/cutscenes/endscreen.swf b/wilysays-src-Mar-02-2010/src/gfx/cutscenes/endscreen.swf new file mode 100755 index 0000000..273feb8 Binary files /dev/null and b/wilysays-src-Mar-02-2010/src/gfx/cutscenes/endscreen.swf differ diff --git a/wilysays-src-Mar-02-2010/src/gfx/cutscenes/highscore.fla b/wilysays-src-Mar-02-2010/src/gfx/cutscenes/highscore.fla new file mode 100755 index 0000000..9619259 Binary files /dev/null and b/wilysays-src-Mar-02-2010/src/gfx/cutscenes/highscore.fla differ diff --git a/wilysays-src-Mar-02-2010/src/gfx/cutscenes/highscore.swf b/wilysays-src-Mar-02-2010/src/gfx/cutscenes/highscore.swf new file mode 100755 index 0000000..7bec803 Binary files /dev/null and b/wilysays-src-Mar-02-2010/src/gfx/cutscenes/highscore.swf differ diff --git a/wilysays-src-Mar-02-2010/src/gfx/cutscenes/intro-menu.fla b/wilysays-src-Mar-02-2010/src/gfx/cutscenes/intro-menu.fla new file mode 100755 index 0000000..0903387 Binary files /dev/null and b/wilysays-src-Mar-02-2010/src/gfx/cutscenes/intro-menu.fla differ diff --git a/wilysays-src-Mar-02-2010/src/gfx/cutscenes/intro-menu.swf b/wilysays-src-Mar-02-2010/src/gfx/cutscenes/intro-menu.swf new file mode 100755 index 0000000..ea35406 Binary files /dev/null and b/wilysays-src-Mar-02-2010/src/gfx/cutscenes/intro-menu.swf differ diff --git a/wilysays-src-Mar-02-2010/src/gfx/cutscenes/tutorial.fla b/wilysays-src-Mar-02-2010/src/gfx/cutscenes/tutorial.fla new file mode 100755 index 0000000..22e4230 Binary files /dev/null and b/wilysays-src-Mar-02-2010/src/gfx/cutscenes/tutorial.fla differ diff --git a/wilysays-src-Mar-02-2010/src/gfx/cutscenes/tutorial.swf b/wilysays-src-Mar-02-2010/src/gfx/cutscenes/tutorial.swf new file mode 100755 index 0000000..f6f0355 Binary files /dev/null and b/wilysays-src-Mar-02-2010/src/gfx/cutscenes/tutorial.swf differ diff --git a/wilysays-src-Mar-02-2010/src/gfx/explosion.fla b/wilysays-src-Mar-02-2010/src/gfx/explosion.fla new file mode 100755 index 0000000..2284b31 Binary files /dev/null and b/wilysays-src-Mar-02-2010/src/gfx/explosion.fla differ diff --git a/wilysays-src-Mar-02-2010/src/gfx/explosion.swf b/wilysays-src-Mar-02-2010/src/gfx/explosion.swf new file mode 100755 index 0000000..1511013 Binary files /dev/null and b/wilysays-src-Mar-02-2010/src/gfx/explosion.swf differ diff --git a/wilysays-src-Mar-02-2010/src/gfx/game_background.png b/wilysays-src-Mar-02-2010/src/gfx/game_background.png new file mode 100755 index 0000000..9d8f6b4 Binary files /dev/null and b/wilysays-src-Mar-02-2010/src/gfx/game_background.png differ diff --git a/wilysays-src-Mar-02-2010/src/gfx/green-dark.png b/wilysays-src-Mar-02-2010/src/gfx/green-dark.png new file mode 100755 index 0000000..10c14ee Binary files /dev/null and b/wilysays-src-Mar-02-2010/src/gfx/green-dark.png differ diff --git a/wilysays-src-Mar-02-2010/src/gfx/green-light.png b/wilysays-src-Mar-02-2010/src/gfx/green-light.png new file mode 100755 index 0000000..d5c6f38 Binary files /dev/null and b/wilysays-src-Mar-02-2010/src/gfx/green-light.png differ diff --git a/wilysays-src-Mar-02-2010/src/gfx/progress_green.png b/wilysays-src-Mar-02-2010/src/gfx/progress_green.png new file mode 100755 index 0000000..2dca09c Binary files /dev/null and b/wilysays-src-Mar-02-2010/src/gfx/progress_green.png differ diff --git a/wilysays-src-Mar-02-2010/src/gfx/progress_red.png b/wilysays-src-Mar-02-2010/src/gfx/progress_red.png new file mode 100755 index 0000000..6063fe8 Binary files /dev/null and b/wilysays-src-Mar-02-2010/src/gfx/progress_red.png differ diff --git a/wilysays-src-Mar-02-2010/src/gfx/red-dark.png b/wilysays-src-Mar-02-2010/src/gfx/red-dark.png new file mode 100755 index 0000000..9142400 Binary files /dev/null and b/wilysays-src-Mar-02-2010/src/gfx/red-dark.png differ diff --git a/wilysays-src-Mar-02-2010/src/gfx/red-light.png b/wilysays-src-Mar-02-2010/src/gfx/red-light.png new file mode 100755 index 0000000..c57b68b Binary files /dev/null and b/wilysays-src-Mar-02-2010/src/gfx/red-light.png differ diff --git a/wilysays-src-Mar-02-2010/src/gfx/whole.png b/wilysays-src-Mar-02-2010/src/gfx/whole.png new file mode 100755 index 0000000..3b493d9 Binary files /dev/null and b/wilysays-src-Mar-02-2010/src/gfx/whole.png differ diff --git a/wilysays-src-Mar-02-2010/src/gfx/yellow-dark.png b/wilysays-src-Mar-02-2010/src/gfx/yellow-dark.png new file mode 100755 index 0000000..6ebe93d Binary files /dev/null and b/wilysays-src-Mar-02-2010/src/gfx/yellow-dark.png differ diff --git a/wilysays-src-Mar-02-2010/src/gfx/yellow-light.png b/wilysays-src-Mar-02-2010/src/gfx/yellow-light.png new file mode 100755 index 0000000..fca1d1a Binary files /dev/null and b/wilysays-src-Mar-02-2010/src/gfx/yellow-light.png differ diff --git a/wilysays-src-Mar-02-2010/src/net/aklabs/demo/simonsays/LoadingObject.as b/wilysays-src-Mar-02-2010/src/net/aklabs/demo/simonsays/LoadingObject.as new file mode 100755 index 0000000..415d1db --- /dev/null +++ b/wilysays-src-Mar-02-2010/src/net/aklabs/demo/simonsays/LoadingObject.as @@ -0,0 +1,34 @@ +package net.aklabs.demo.simonsays { + + /* + * Class LoadingObject + * + * This class is used to represent a content asset while it is going through the process of being loaded + * by the Preloader class. + */ + public class LoadingObject { + public static var STATE_READY:Number = 0; // Object has been loaded and is ready + public static var STATE_LOADING:Number = 1; // Object is currently loading, not ready yet + public static var STATE_NOTFOUND:Number = 2; // Object wasn't found in the cache + public var handle:String; // Text handle for this object + public var state:Number; // State value + public var bytesRead:Number; // the amount of bytes currently read in to this object over the net + public var bytesTotal:Number; // the total byte size of this object + + /* + * LoadingObject() + * + * Default constructor + * + * arguments : none + * + * Returns : LoadingObject + */ + public function LoadingObject() { + this.handle = ""; + this.state = LoadingObject.STATE_NOTFOUND; + this.bytesRead = 0; + this.bytesTotal = 0; + } + } +} \ No newline at end of file diff --git a/wilysays-src-Mar-02-2010/src/net/aklabs/demo/simonsays/Mastermind.as b/wilysays-src-Mar-02-2010/src/net/aklabs/demo/simonsays/Mastermind.as new file mode 100755 index 0000000..2bde5f2 --- /dev/null +++ b/wilysays-src-Mar-02-2010/src/net/aklabs/demo/simonsays/Mastermind.as @@ -0,0 +1,176 @@ +package net.aklabs.demo.simonsays +{ + + import flash.utils.Timer; + import flash.events.Event; + import flash.events.MouseEvent; + import flash.events.TimerEvent; + import flash.display.Sprite; + import net.aklabs.demo.simonsays.Preloader; + import net.aklabs.demo.simonsays.SimonButton; + import net.aklabs.demo.simonsays.MastermindEvent; + + /* + * Class: Mastermind + * + * This class defines our "Simon Says", "Wily Lok", "Mastermind", whatever you want to call it. + * (I called it 3 or 4 different things during the course of development.) + * + */ + public class Mastermind extends Sprite + { + protected var lightOutTimer:Timer; // timer that fires when it's time to turn off the lights + protected var lightButtons:Array; // array of custom SimonButtons that exists in Pattern.COLOR_XXX order + public var isLit:Boolean; // is the device currently lit? + + public function Mastermind() + { + var preloader = Preloader.getInstance(); + var lightPositions = new Array(); + // this array holds pairs of (x,y) coordinates at which to place the dark/light colored + // Simon Says buttons, since they're meant to cover the existing buttons on the original graphic. + // These are hard-coded; in a better implementation, they would come from an XML or would be + // individual frames of an SWF which could be fired individually, etc. But this works. + // array idx 0: Blue, 1: Green, 2: Red, 3: Yellow + + lightPositions.push( new Array(183, 187)); // blue + lightPositions.push( new Array(18, 17)); // green + lightPositions.push( new Array(185, 17)); // red + lightPositions.push( new Array(18, 186)); // yellow + + // this array holds the button objects for the visible simon says buttons ... + this.lightButtons = new Array(); + // just a temporary array for which images to fetch for creating the down/up button images + var fetchArray = new Array( new Array("gfx_btn_darkblue", "gfx_btn_lightblue"), + new Array("gfx_btn_darkgreen", "gfx_btn_lightgreen"), + new Array("gfx_btn_darkred", "gfx_btn_lightred"), + new Array("gfx_btn_darkyellow", "gfx_btn_lightyellow") ); + // Simon Says body + var whole = preloader.getObject("gfx_mastermind"); + this.addChild(whole); + + for ( var i = 0; i < fetchArray.length ; i++ ) { + var btn:SimonButton = new SimonButton(preloader.getObject(fetchArray[i][1]), preloader.getObject(fetchArray[i][0])); + btn.x = lightPositions[i][0]; + btn.y = lightPositions[i][1]; + this.lightButtons.push(btn); + this.addChild(btn); + btn.addEventListener(MouseEvent.CLICK, this.onMouseClick); // for some reason, MouseEvent.CLICK never actually does anything? + //btn.addEventListener(MouseEvent.MOUSE_UP, this.onMouseClick); + } + this.lightOutTimer = new Timer(0); + } + + /* + * flashAll(length) + * + * This function lights all the lights on the Mastermind for the given length of time + * + * arguments: + * @length:Number, the time in milliseconds for which the Mastermind should stay lit + * + * Returns : none + */ + public function flashAll(length:Number) + { + this.lightOutTimer.delay = length; + this.lightOutTimer.addEventListener(TimerEvent.TIMER, this.onTimer); + this.lightOutTimer.reset(); + this.lightOutTimer.start(); + for (var i = 0 ; i < 4 ; i++) { + // fake mouse event to light it up + this.lightButtons[i].onMouseDown(null); + } + this.isLit = true; + } + + /* + * lightButton(btn, activeTime) + * + * This function lights up a given button on the Mastermind. Mostly used by the Pattern when it's replaying itself. + * + * arguments: + * @btn:Number, the Pattern.COLOR_XXX button that should light + * @activeTime:Number, the amount of milliseconds for which the button should stay lit + * + * Returns : none + */ + public function lightButton(btn:Number, activeTime:Number = 0 ) + { + if ( btn < 4 ) { + this.lightButtons[btn].onMouseDown(null); + this.isLit = true; + } + if ( activeTime != 0 ) { + this.lightOutTimer.delay = activeTime; + this.lightOutTimer.addEventListener(TimerEvent.TIMER, this.onTimer); + this.lightOutTimer.reset(); + this.lightOutTimer.start(); + } + } + + /* + * blackout() + * + * This function makes sure that all lights on the Mastermind are extinguished + * + * arguments : none + * + * Returns : none + */ + public function blackout() + { + if ( this.lightButtons.length <= 0 ) { + return; + } + for ( var i = 0 ; i < this.lightButtons.length ; i++ ){ + this.lightButtons[i].onMouseUp(null); + } + this.isLit = false; + } + + /* + * onTimer(evt) + * + * This function fires whenever the lightOutTimer fires, telling the lights to shut off + * + * arguments : + * @evt:Event, the event that's firing this function + * + * Returns : none + */ + public function onTimer(evt:TimerEvent) + { + if ( evt.target != this.lightOutTimer ) + return; + this.lightOutTimer.removeEventListener(TimerEvent.TIMER, this.onTimer); + this.lightOutTimer.stop(); + this.lightOutTimer.reset(); + this.blackout(); + } + + /* + * onMouseClick() + * + * This function is fired whenever the user clicks one of the Mastermind's buttons + * + * arguments: + * @evt:Event, the event firing this function + * + * Returns : none + */ + public function onMouseClick(evt:MouseEvent) + { + // we don't do any processing here, as the SimonButton has already checked for + // per-pixel accuracy w/ the click on our abnormally shaped buttons. We just propagate + // out a new MastermindEvent for the game to catch. + for ( var i:Number = 0; i < this.lightButtons.length ; i++ ) { + if ( evt.target == this.lightButtons[i] ) { + var newEvt:MastermindEvent = new MastermindEvent(MastermindEvent.BTN_CLICKED); + newEvt.colorClicked = i; + this.dispatchEvent(newEvt); + } + } + } + } +} diff --git a/wilysays-src-Mar-02-2010/src/net/aklabs/demo/simonsays/MastermindEvent.as b/wilysays-src-Mar-02-2010/src/net/aklabs/demo/simonsays/MastermindEvent.as new file mode 100755 index 0000000..36ccf09 --- /dev/null +++ b/wilysays-src-Mar-02-2010/src/net/aklabs/demo/simonsays/MastermindEvent.as @@ -0,0 +1,22 @@ +package net.aklabs.demo.simonsays +{ + import flash.events.Event; + + /* + * Class : MastermindEvent + * + * This class basically just defines a custom event that will let the mastermind send up + * a clicked event w/ a color. + */ + public class MastermindEvent extends Event + { + public static var BTN_CLICKED:String = "MASTERMIND_BUTTON_CLICKED"; + public var colorClicked:Number; + + public function MastermindEvent(evtType:String) + { + super(evtType); + } + } +} + diff --git a/wilysays-src-Mar-02-2010/src/net/aklabs/demo/simonsays/Pattern.as b/wilysays-src-Mar-02-2010/src/net/aklabs/demo/simonsays/Pattern.as new file mode 100755 index 0000000..885f08a --- /dev/null +++ b/wilysays-src-Mar-02-2010/src/net/aklabs/demo/simonsays/Pattern.as @@ -0,0 +1,393 @@ +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; + } + } +} + diff --git a/wilysays-src-Mar-02-2010/src/net/aklabs/demo/simonsays/Player.as b/wilysays-src-Mar-02-2010/src/net/aklabs/demo/simonsays/Player.as new file mode 100755 index 0000000..bf0ed85 --- /dev/null +++ b/wilysays-src-Mar-02-2010/src/net/aklabs/demo/simonsays/Player.as @@ -0,0 +1,228 @@ +package net.aklabs.demo.simonsays +{ + import net.aklabs.demo.simonsays.PowerupEvent; + import flash.display.Sprite; + import flash.events.Event; + import flash.events.MouseEvent; + import net.aklabs.demo.simonsays.Preloader; + import flash.display.Bitmap; + + /* + * Class Player + * + * This class represents the player in the game. + * + * The class has two displayable objects in it: the Player object itself, + * and Player.lifeDisplay. The Player object displays the inventory (64 pixels high, up to (64*5) pixels wide) + * The Player.lifeDisplay object displays the remaining number of lives (64 pixels high, up to 64*3 pixels wide) + * + */ + public class Player extends Sprite { + public var score:Number; // the player's current score + public var lives:Number; // the number of lives the player currently has + public var maxPattern:Number; // the largest pattern the player has completed + public var inventory:Array; // the player's inventory + public var lifeDisplay:Sprite; /* the parent sprite to which all the player life sprites are attached (number of remaining lives) .. + we have this parenting the rest of the images, so the Game class can just add this single child object, rather than tracking + and removing all the images in the individual lifeImages array */ + protected var lifeImages:Array; // an array of images holding all the images showing up in the player's life array + protected var headExplosion; + + /* + * Player() + * + * Default constructor + * + * arguments : none + * + * Returns : none + */ + public function Player() + { + this.score = 0; + this.lives = 3; + this.maxPattern = 0; + this.inventory = new Array(); + this.lifeDisplay = new Sprite(); + this.lifeImages = new Array(); + } + + /* + * addPowerup(pwup) + * + * Adds a new powerup to the player's inventory + * + * arguments: + * @pwup:Powerup, the powerup to be added + * + * Returns: none + */ + public function addPowerup(pwup:Powerup) + { + if ( this.inventory.length >= 5 ) { + return; + } + this.inventory.push(pwup); + this.resetPowerupPositions(); + this.addChild(pwup); + pwup.addEventListener(MouseEvent.MOUSE_UP, this.onPowerupClicked); + } + + /* + * resetPowerupPositions() + * + * This function goes through the player's inventory and makes sure the positions line up (mostly) with the background graphic for their slot + * + * arguments: none + * + * Returns: none + */ + protected function resetPowerupPositions() + { + for ( var i = 0; i < this.inventory.length ; i++ ) { + var pwup:Powerup = this.inventory[i]; + pwup.x = 2+(70*i); + pwup.y = 0; + } + } + + /* + * die() + * + * This function is called whenever the player should die - lose a life + * + * arguments: none + * + * Returns: none + */ + public function die() + { + // lose a life, and do it in style + var preloader:Preloader = Preloader.getInstance(); + this.headExplosion = preloader.getMovieClip("movie_explosion"); + this.headExplosion.addEventListener(Event.ENTER_FRAME, this.stopExplosion); + this.headExplosion.x = (this.lives * 64)-32; + this.headExplosion.y = 32; + this.lifeDisplay.addChild(this.headExplosion); + this.headExplosion.play(); + preloader.playSound("sfx_explosion"); + this.lives -= 1; + this.resetLifePositions(); + } + + /* + * stopExplosion(evt) + * + * This stops the head explosion animation from looping (much like Game.onNonLoopEnterFrame) + * + * arguments: + * @evt:Event, the event firing this function + * + * Returns: none + */ + public function stopExplosion(evt:Event) + { + if ( evt.target.currentFrame == evt.target.totalFrames ) { + this.headExplosion.stop(); + this.lifeDisplay.removeChild(this.headExplosion); + this.headExplosion.removeEventListener(Event.ENTER_FRAME, this.stopExplosion); + this.headExplosion = null; + } + } + + /* + * resetLifePositions() + * + * This function arranges the images representing the number of remaining lives the player has + * + * arguments: none + * + * returns : none + */ + public function resetLifePositions() + { + var i:Number = 0; + var preloader:Preloader = Preloader.getInstance(); + if ( this.lifeImages.length < this.lives ) { + for ( i = this.lifeImages.length; i < this.lives; i++ ) { + var img:Bitmap = preloader.getBitmap("gfx_pwup_freelife"); + img.x = 64*i; + img.y = 0; + this.lifeImages.push(img); + this.lifeDisplay.addChild(img); + } + } else { + for ( var i = 0; i < this.lifeImages.length ; i++ ) { + if ( i >= this.lives ) { + this.lifeDisplay.removeChild(this.lifeImages[i]); + this.lifeImages.splice(i, 1); + } + } + } + for ( var i = 0; i < this.lifeImages.length ; i++ ) { + this.lifeImages[i].x = 64*i; + this.lifeImages[i].y = 0; + } + } + + /* + * usePowerupAt(index) + * + * See if there is a powerup at index 'index' in the inventory, use it, fire off any sounds associated with it, + * and dispatch a new PowerupEvent for it + * + * arguments: + * @index:Number, the index in inventory from which the powerup should be drawn + * + * returns: none + */ + public function usePowerupAt(index:Number) + { + if ( index < this.inventory.length ) { + this.removeChild(this.inventory[index]); + var preloader:Preloader = Preloader.getInstance(); + preloader.playSound(this.inventory[index].sndHandle); + this.resetPowerupPositions(); + this.dispatchEvent(new PowerupEvent(PowerupEvent.USED_POWERUP, this.inventory[index])); + this.inventory[index].removeEventListener(MouseEvent.MOUSE_UP, this.onPowerupClicked); + this.inventory.splice(index, 1); + } + } + + /* + * onPowerupClicked(evt) + * + * This function is fired whenever the player clicks the mouse on a powerup owned by the player + * + * arguments: + * @evt:MouseEvent, the event firing this function + * + * Returns: none + */ + public function onPowerupClicked(evt:MouseEvent) + { + for ( var i:Number = 0 ; i < this.inventory.length ; i++ ) { + if ( this.inventory[i] == evt.target ) { + this.usePowerupAt(i); + } + } + } + + /* + * clearInventory() + * + * Fairly obvious, this function just clears out the inventory + * + * arguments: none + * + * returns: none + */ + public function clearInventory() + { + for ( var i:Number = 0; i < this.inventory.length ; i++ ) { + this.removeChild(this.inventory[i]); + this.inventory.splice(i, 1); + } + } + } +} diff --git a/wilysays-src-Mar-02-2010/src/net/aklabs/demo/simonsays/Powerup.as b/wilysays-src-Mar-02-2010/src/net/aklabs/demo/simonsays/Powerup.as new file mode 100755 index 0000000..17ef5dd --- /dev/null +++ b/wilysays-src-Mar-02-2010/src/net/aklabs/demo/simonsays/Powerup.as @@ -0,0 +1,29 @@ +package net.aklabs.demo.simonsays { + import flash.display.Sprite; + import flash.events.Event; + import flash.events.MouseEvent; + import net.aklabs.demo.simonsays.PowerupEvent; + + /* + * Class Powerup + * + * This class just represents a powerup in the game. + * This is more a data structure than a class, but I don't think + * AS 3.0 has just bare structures. Silly ECMA language... + */ + public class Powerup extends Sprite + { + public static var PTYPE_SLOWDOWN:Number = 0; /* SLOWDOWN - Slows the pattern timer to a much lower rate */ + public static var PTYPE_FORGIVENESS:Number = 1; /* FORGIVENESS - If you have FORGIVENESSS in your inventory and you miss a pattern, it doesn't stop you */ + public static var PTYPE_SKIP:Number = 2; /* SKIP - Lets you "skip" a given pattern iteration, and you still get all the score for it. */ + public static var PTYPE_DOUBLE:Number = 3; /* DOUBLE - Score double points for the pattern on which you use this powerup. */ + public static var PTYPE_MAXVALUE:Number = 4; + // POWERUP_IMAGES - these are the image handles (stored in an array where idx => PTYPE_XXX) for each of the powerup types + public static var POWERUP_IMAGES = new Array("gfx_pwup_slowdown", "gfx_pwup_forgiveness", "gfx_pwup_exclamation", "gfx_pwup_pointdoubler"); + // POWERUP_SOUNDS - these are the sound handles (stored in an array where idx => PTYPE_XXX) for each of the powerup types + public static var POWERUP_SOUNDS = new Array("sfx_slowdown", "sfx_forgiveness", "sfx_exclamation", "sfx_pointdoubler"); + public var imgHandle:String; // the handle for the image of this specific powerup + public var sndHandle:String; // the handle for the sound of this specific powerup + public var pType:Number; // the PTYPE_XXX of this powerup + } +} diff --git a/wilysays-src-Mar-02-2010/src/net/aklabs/demo/simonsays/PowerupEvent.as b/wilysays-src-Mar-02-2010/src/net/aklabs/demo/simonsays/PowerupEvent.as new file mode 100755 index 0000000..671ba0d --- /dev/null +++ b/wilysays-src-Mar-02-2010/src/net/aklabs/demo/simonsays/PowerupEvent.as @@ -0,0 +1,32 @@ +package net.aklabs.demo.simonsays { + import flash.events.Event; + import net.aklabs.demo.simonsays.Powerup; + + /* + * Class PowerupEvent + * + * An event specifically made for actions taken regarding powerups + */ + public class PowerupEvent extends Event { + public static var GOT_POWERUP:String = "SIMONSAYS_GOT_POWERUP"; // Type for events when a powerup was received + public static var USED_POWERUP:String = "SIMONSAYS_USED_POWERUP"; // Type for events when a powerup was used + public var pwup:Powerup; // the powerup (rather than the target) of this event + + /* + * PowerupEvent() + * + * Default Constructor + * + * arguments: + * @evtType:String, the type of this event (GOT_POWERUP or USED_POWERUP) + * @tgt:Powerup, the Powerup object that's being affected by this event + * + * returns: none + */ + public function PowerupEvent(evtType:String, tgt:Powerup = null) + { + super(evtType); + this.pwup = tgt; + } + } +} diff --git a/wilysays-src-Mar-02-2010/src/net/aklabs/demo/simonsays/Preloader.as b/wilysays-src-Mar-02-2010/src/net/aklabs/demo/simonsays/Preloader.as new file mode 100755 index 0000000..a993413 --- /dev/null +++ b/wilysays-src-Mar-02-2010/src/net/aklabs/demo/simonsays/Preloader.as @@ -0,0 +1,529 @@ +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); + } + } + +} \ No newline at end of file diff --git a/wilysays-src-Mar-02-2010/src/net/aklabs/demo/simonsays/SimonButton.as b/wilysays-src-Mar-02-2010/src/net/aklabs/demo/simonsays/SimonButton.as new file mode 100755 index 0000000..d6ea0cf --- /dev/null +++ b/wilysays-src-Mar-02-2010/src/net/aklabs/demo/simonsays/SimonButton.as @@ -0,0 +1,123 @@ +package net.aklabs.demo.simonsays { + + import flash.events.MouseEvent; + import flash.display.Bitmap; + import flash.display.Sprite; + + /* + * class SimonButton + * + * This is just a custom button class that is pixel-accurate, because the Mastermin/Simon Says + * buttons are an extremely odd shape, so I needed something that could detect mouse hits in/ + * outside of the alpha color areas. + * + * Most of this class is really self explanatory, so I'm not going to bother documenting all of it. Only the parts that don't make immediate sense. + */ + public class SimonButton extends Sprite + { + protected var imgDown:Bitmap; // bitmap for when the button is pressed + protected var imgUp:Bitmap; // bitmap for when the button is released + protected var curImg:Bitmap; // the bitmap currently being displayed on the button + protected var magicColor:Number; // the magic color (e.g. "magic pink"), if any (otherwise alpha is used for hit detection) + + public function SimonButton(imgDown:Bitmap = null, imgUp:Bitmap = null, magicColor = 0x00000000) + { + super(); + this.imgDown = imgDown; + this.imgUp = imgUp; + this.curImg = null; + this.magicColor = magicColor; + this.addEventListener(MouseEvent.MOUSE_DOWN, this.onMouseDown); + this.addEventListener(MouseEvent.MOUSE_UP, this.onMouseUp); + this.addEventListener(MouseEvent.CLICK, this.onMouseClick); + } + + public function setMagicColor(color:Number) + { + this.magicColor = color; + } + + public function setBtnDown(obj:Bitmap) + { + if ( obj ) { + this.imgDown = obj; + } + } + + public function setBtnUp(obj:Bitmap) + { + if ( obj ) { + this.imgUp = obj; + } + } + + public function onMouseDown(obj:MouseEvent) + { + if ( (obj) && (! this.hitTestPoint(obj.localX, obj.localY)) ) { + obj.stopImmediatePropagation(); + return; + } + if ( this.curImg ) { + this.removeChild(this.curImg); + } + if ( this.imgDown ) { + this.addChild(this.imgDown); + this.curImg = this.imgDown; + } + } + + public function onMouseUp(obj:MouseEvent) + { + if ( (obj) && (!this.hitTestPoint(obj.localX, obj.localY)) ) { + obj.stopImmediatePropagation(); + return; + } + if ( this.curImg ) { + this.removeChild(this.curImg); + } + if ( this.imgUp ) { + this.addChild(this.imgUp); + this.curImg = this.imgUp; + } + } + + public function onMouseClick(obj:MouseEvent) + { + if ( ! this.hitTestPoint(obj.localX, obj.localY) ) { + obj.stopImmediatePropagation(); + } + } + + /* + * hitTestPoint(x, y, shapeFlag) + * + * An overriden version of hitTestPoint that is pixel-accurate. If the value at (x, y) is either A: of the value + * in the "magic color", or B: containing an alpha value of zero, then the hit is POSITIVE. Otherwise it is false. + * + * arguments: + * @x:Number, the X location of the hit relative to the origin of the object + * @y:Number, the Y location of the hit relative to the origin of the object + * @shapeFlag:Boolean, not used, just here for compatibility + * + * returns: Boolean (true if the hit is positive, false if it's negative) + */ + public override function hitTestPoint(x:Number, y:Number, shapeFlag:Boolean = false):Boolean + { + var color:uint; + var rgb:uint; + var a:uint; + + if ( this.curImg && this.curImg.bitmapData ) { + color = this.curImg.bitmapData.getPixel32(x, y); + a = ((color >> 24) & 0xFF); + rgb = (color & 0xFFFFFF00); + //trace("Alpha : " + a + " RGB " + rgb + " magic color " + this.magicColor); + if ( this.magicColor == rgb || a == 0 ) { + return false; + } + return true; + } + return false; + } + } +} diff --git a/wilysays-src-Mar-02-2010/src/sfx/BACKGROUND.mp3 b/wilysays-src-Mar-02-2010/src/sfx/BACKGROUND.mp3 new file mode 100755 index 0000000..0291436 Binary files /dev/null and b/wilysays-src-Mar-02-2010/src/sfx/BACKGROUND.mp3 differ diff --git a/wilysays-src-Mar-02-2010/src/sfx/BLUE.mp3 b/wilysays-src-Mar-02-2010/src/sfx/BLUE.mp3 new file mode 100755 index 0000000..4d0dece Binary files /dev/null and b/wilysays-src-Mar-02-2010/src/sfx/BLUE.mp3 differ diff --git a/wilysays-src-Mar-02-2010/src/sfx/BUZZER.mp3 b/wilysays-src-Mar-02-2010/src/sfx/BUZZER.mp3 new file mode 100755 index 0000000..644df8b Binary files /dev/null and b/wilysays-src-Mar-02-2010/src/sfx/BUZZER.mp3 differ diff --git a/wilysays-src-Mar-02-2010/src/sfx/EXCLAMATION.mp3 b/wilysays-src-Mar-02-2010/src/sfx/EXCLAMATION.mp3 new file mode 100755 index 0000000..8f9ce28 Binary files /dev/null and b/wilysays-src-Mar-02-2010/src/sfx/EXCLAMATION.mp3 differ diff --git a/wilysays-src-Mar-02-2010/src/sfx/EXPLOSION.mp3 b/wilysays-src-Mar-02-2010/src/sfx/EXPLOSION.mp3 new file mode 100755 index 0000000..f59512e Binary files /dev/null and b/wilysays-src-Mar-02-2010/src/sfx/EXPLOSION.mp3 differ diff --git a/wilysays-src-Mar-02-2010/src/sfx/FORGIVENESS.mp3 b/wilysays-src-Mar-02-2010/src/sfx/FORGIVENESS.mp3 new file mode 100755 index 0000000..e1617d8 Binary files /dev/null and b/wilysays-src-Mar-02-2010/src/sfx/FORGIVENESS.mp3 differ diff --git a/wilysays-src-Mar-02-2010/src/sfx/GREEN.mp3 b/wilysays-src-Mar-02-2010/src/sfx/GREEN.mp3 new file mode 100755 index 0000000..3e4594e Binary files /dev/null and b/wilysays-src-Mar-02-2010/src/sfx/GREEN.mp3 differ diff --git a/wilysays-src-Mar-02-2010/src/sfx/POINTDOUBLER.mp3 b/wilysays-src-Mar-02-2010/src/sfx/POINTDOUBLER.mp3 new file mode 100755 index 0000000..e940b0d Binary files /dev/null and b/wilysays-src-Mar-02-2010/src/sfx/POINTDOUBLER.mp3 differ diff --git a/wilysays-src-Mar-02-2010/src/sfx/RED.mp3 b/wilysays-src-Mar-02-2010/src/sfx/RED.mp3 new file mode 100755 index 0000000..9887b1f Binary files /dev/null and b/wilysays-src-Mar-02-2010/src/sfx/RED.mp3 differ diff --git a/wilysays-src-Mar-02-2010/src/sfx/SHORTCIRCUIT.mp3 b/wilysays-src-Mar-02-2010/src/sfx/SHORTCIRCUIT.mp3 new file mode 100755 index 0000000..27d11de Binary files /dev/null and b/wilysays-src-Mar-02-2010/src/sfx/SHORTCIRCUIT.mp3 differ diff --git a/wilysays-src-Mar-02-2010/src/sfx/SIMONFAIL.wav b/wilysays-src-Mar-02-2010/src/sfx/SIMONFAIL.wav new file mode 100755 index 0000000..f45db60 Binary files /dev/null and b/wilysays-src-Mar-02-2010/src/sfx/SIMONFAIL.wav differ diff --git a/wilysays-src-Mar-02-2010/src/sfx/SIMONSTART.wav b/wilysays-src-Mar-02-2010/src/sfx/SIMONSTART.wav new file mode 100755 index 0000000..56940f2 Binary files /dev/null and b/wilysays-src-Mar-02-2010/src/sfx/SIMONSTART.wav differ diff --git a/wilysays-src-Mar-02-2010/src/sfx/SLOWDOWN.mp3 b/wilysays-src-Mar-02-2010/src/sfx/SLOWDOWN.mp3 new file mode 100755 index 0000000..ce1e236 Binary files /dev/null and b/wilysays-src-Mar-02-2010/src/sfx/SLOWDOWN.mp3 differ diff --git a/wilysays-src-Mar-02-2010/src/sfx/SPEEDUP.mp3 b/wilysays-src-Mar-02-2010/src/sfx/SPEEDUP.mp3 new file mode 100755 index 0000000..dc4bc0f Binary files /dev/null and b/wilysays-src-Mar-02-2010/src/sfx/SPEEDUP.mp3 differ diff --git a/wilysays-src-Mar-02-2010/src/sfx/YELLOW.mp3 b/wilysays-src-Mar-02-2010/src/sfx/YELLOW.mp3 new file mode 100755 index 0000000..cd2f5e3 Binary files /dev/null and b/wilysays-src-Mar-02-2010/src/sfx/YELLOW.mp3 differ diff --git a/wilysays-src-Mar-02-2010/src/simon says.fla b/wilysays-src-Mar-02-2010/src/simon says.fla new file mode 100755 index 0000000..b8e85de Binary files /dev/null and b/wilysays-src-Mar-02-2010/src/simon says.fla differ diff --git a/wilysays-src-Mar-02-2010/webapp/AC_RunActiveContent.js b/wilysays-src-Mar-02-2010/webapp/AC_RunActiveContent.js new file mode 100755 index 0000000..30cddb9 --- /dev/null +++ b/wilysays-src-Mar-02-2010/webapp/AC_RunActiveContent.js @@ -0,0 +1,292 @@ +//v1.7 +// Flash Player Version Detection +// Detect Client Browser type +// Copyright 2005-2007 Adobe Systems Incorporated. All rights reserved. +var isIE = (navigator.appVersion.indexOf("MSIE") != -1) ? true : false; +var isWin = (navigator.appVersion.toLowerCase().indexOf("win") != -1) ? true : false; +var isOpera = (navigator.userAgent.indexOf("Opera") != -1) ? true : false; + +function ControlVersion() +{ + var version; + var axo; + var e; + + // NOTE : new ActiveXObject(strFoo) throws an exception if strFoo isn't in the registry + + try { + // version will be set for 7.X or greater players + axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.7"); + version = axo.GetVariable("$version"); + } catch (e) { + } + + if (!version) + { + try { + // version will be set for 6.X players only + axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.6"); + + // installed player is some revision of 6.0 + // GetVariable("$version") crashes for versions 6.0.22 through 6.0.29, + // so we have to be careful. + + // default to the first public version + version = "WIN 6,0,21,0"; + + // throws if AllowScripAccess does not exist (introduced in 6.0r47) + axo.AllowScriptAccess = "always"; + + // safe to call for 6.0r47 or greater + version = axo.GetVariable("$version"); + + } catch (e) { + } + } + + if (!version) + { + try { + // version will be set for 4.X or 5.X player + axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.3"); + version = axo.GetVariable("$version"); + } catch (e) { + } + } + + if (!version) + { + try { + // version will be set for 3.X player + axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash.3"); + version = "WIN 3,0,18,0"; + } catch (e) { + } + } + + if (!version) + { + try { + // version will be set for 2.X player + axo = new ActiveXObject("ShockwaveFlash.ShockwaveFlash"); + version = "WIN 2,0,0,11"; + } catch (e) { + version = -1; + } + } + + return version; +} + +// JavaScript helper required to detect Flash Player PlugIn version information +function GetSwfVer(){ + // NS/Opera version >= 3 check for Flash plugin in plugin array + var flashVer = -1; + + if (navigator.plugins != null && navigator.plugins.length > 0) { + if (navigator.plugins["Shockwave Flash 2.0"] || navigator.plugins["Shockwave Flash"]) { + var swVer2 = navigator.plugins["Shockwave Flash 2.0"] ? " 2.0" : ""; + var flashDescription = navigator.plugins["Shockwave Flash" + swVer2].description; + var descArray = flashDescription.split(" "); + var tempArrayMajor = descArray[2].split("."); + var versionMajor = tempArrayMajor[0]; + var versionMinor = tempArrayMajor[1]; + var versionRevision = descArray[3]; + if (versionRevision == "") { + versionRevision = descArray[4]; + } + if (versionRevision[0] == "d") { + versionRevision = versionRevision.substring(1); + } else if (versionRevision[0] == "r") { + versionRevision = versionRevision.substring(1); + if (versionRevision.indexOf("d") > 0) { + versionRevision = versionRevision.substring(0, versionRevision.indexOf("d")); + } + } + var flashVer = versionMajor + "." + versionMinor + "." + versionRevision; + } + } + // MSN/WebTV 2.6 supports Flash 4 + else if (navigator.userAgent.toLowerCase().indexOf("webtv/2.6") != -1) flashVer = 4; + // WebTV 2.5 supports Flash 3 + else if (navigator.userAgent.toLowerCase().indexOf("webtv/2.5") != -1) flashVer = 3; + // older WebTV supports Flash 2 + else if (navigator.userAgent.toLowerCase().indexOf("webtv") != -1) flashVer = 2; + else if ( isIE && isWin && !isOpera ) { + flashVer = ControlVersion(); + } + return flashVer; +} + +// When called with reqMajorVer, reqMinorVer, reqRevision returns true if that version or greater is available +function DetectFlashVer(reqMajorVer, reqMinorVer, reqRevision) +{ + versionStr = GetSwfVer(); + if (versionStr == -1 ) { + return false; + } else if (versionStr != 0) { + if(isIE && isWin && !isOpera) { + // Given "WIN 2,0,0,11" + tempArray = versionStr.split(" "); // ["WIN", "2,0,0,11"] + tempString = tempArray[1]; // "2,0,0,11" + versionArray = tempString.split(","); // ['2', '0', '0', '11'] + } else { + versionArray = versionStr.split("."); + } + var versionMajor = versionArray[0]; + var versionMinor = versionArray[1]; + var versionRevision = versionArray[2]; + + // is the major.revision >= requested major.revision AND the minor version >= requested minor + if (versionMajor > parseFloat(reqMajorVer)) { + return true; + } else if (versionMajor == parseFloat(reqMajorVer)) { + if (versionMinor > parseFloat(reqMinorVer)) + return true; + else if (versionMinor == parseFloat(reqMinorVer)) { + if (versionRevision >= parseFloat(reqRevision)) + return true; + } + } + return false; + } +} + +function AC_AddExtension(src, ext) +{ + if (src.indexOf('?') != -1) + return src.replace(/\?/, ext+'?'); + else + return src + ext; +} + +function AC_Generateobj(objAttrs, params, embedAttrs) +{ + var str = ''; + if (isIE && isWin && !isOpera) + { + str += ' '; + } + str += ''; + } + else + { + str += ' + + +simon says + + + + + + + +

CLICK HERE FOR HELP!

+ +

+ + + + + diff --git a/wilysays-src-Mar-02-2010/webapp/help.html b/wilysays-src-Mar-02-2010/webapp/help.html new file mode 100755 index 0000000..5f4eef9 --- /dev/null +++ b/wilysays-src-Mar-02-2010/webapp/help.html @@ -0,0 +1,145 @@ + + +

MEGA MAN: Wily Says!

+ +

HELP!

+
+ +This is the help information for Mega Man: Wily Says!, the smash-megahit +game currently sweeping teh intarnets. Here's how to play the game. + +
+ +
+ +
+ +Once the game loads up, click inside the game window with your mouse to +make sure it has focus. Then hit your SPACE BAR. Click on one of the +difficulty setting buttons, to choose the difficulty. This determines +how many levels you play, and how long the patterns you're challenged with, +will be. Once you've done that, watch the intro; it'll give you a little +bit of background story, and a little bit of information on how to play +the game. + +

The interface

+
+
+ +
+ + +Simple, right? + +

Credits

+ +
+
+Pre-formatting this because I'm lazy.
+
+Most of this was assembled ad-hoc from various places around the internet
+for a game demo to accompany a resume, so it was never really intended for
+public consumption. But before the whole thing was over, it just became
+so ridiculous I couldn't possibly keep it to myself.
+
+The theme music is a MIDI version of Depeche Mode's "Master and Servant". That's
+where the name "Mastermind" for the Simon Says came from.
+
+The graphics were mostly stolen from mega man fansites around the internet.
+
+The explosion graphic was lifted from someone. I forget who. It's the same 
+one I used in my "HOLY *@#%! ALIENS!" space shooter demo.
+
+The sound effects, again, were lifted from various free sites around the
+net. I honestly didn't keep track of them. I know they were all straight off
+Google links.
+
+I'm solely responsible for this atrocity; if you want to send me hate mail,
+you're a megaman purist and you want to tear off my head, you think this was
+the most ridiculous idea for a game demo ever, or if you want to send me
+big buckets of money because I'm just so goddamned AWESOME, send mail
+to andrew@aklabs.net, or follow me on twitter (@akesterson). Thanks.
+
+The entire demo was coded in ActionScript 3 on Adobe Flash CS3, graphics
+were chopped up with The GIMP, sounds composed/trimmed in Audacity. I think
+I spent about a total of 48 hours on this project, and that includes learning
+ActionScript 3. I'll release the source in a few days after an employer
+is done looking at it.
+
+
+ + + + + diff --git a/wilysays-src-Mar-02-2010/webapp/help1.png b/wilysays-src-Mar-02-2010/webapp/help1.png new file mode 100755 index 0000000..2e689d1 Binary files /dev/null and b/wilysays-src-Mar-02-2010/webapp/help1.png differ diff --git a/wilysays-src-Mar-02-2010/webapp/help2.png b/wilysays-src-Mar-02-2010/webapp/help2.png new file mode 100755 index 0000000..1763681 Binary files /dev/null and b/wilysays-src-Mar-02-2010/webapp/help2.png differ diff --git a/wilysays-src-Mar-02-2010/webapp/index.php b/wilysays-src-Mar-02-2010/webapp/index.php new file mode 100755 index 0000000..261a090 --- /dev/null +++ b/wilysays-src-Mar-02-2010/webapp/index.php @@ -0,0 +1,134 @@ +\n
Groovy
\n"); + } + die(); +} +?> + + + +
+

MEGA MAN: Wily Says!

+

Leaderboards

+

Click HERE to play the mega-hit game!

+

Click HERE for instructions on HOW to play the mega-hit game!

+
+ + + + \n"); + if ( $curPage > 0 ) + printf(" \n", $rowsPerPage, (($curPage*$rowsPerPage)-$rowsPerPage)); + else + printf(" \n"); + + if ( $totalRows > ($curPage * $rowsPerPage) ) + printf(" \n", $rowsPerPage, (($curPage*$rowsPerPage)+$rowsPerPage)); + else + printf(" \n"); + printf("\n"); + + printf("\t\t\t\t\n", + ($curPage*$rowsPerPage), ($curPage*$rowsPerPage), ($curPage*$rowsPerPage), ($curPage*$rowsPerPage)); + while ( $row = mysql_fetch_array($res) ) { + printf("\t\t\t\t\n"); + printf("\t\t\t\t\t\n"); + printf("\t\t\t\t\t\n"); + printf("\t\t\t\t\t\n"); + printf("\t\t\t\t\t\n"); + printf("\t\t\t\t\n"); + } + printf("\n"); + if ( $curPage > 0 ) + printf(" \n", $rowsPerPage, (($curPage*$rowsPerPage)-$rowsPerPage)); + else + printf(" \n"); + + if ( $totalRows > ($curPage * $rowsPerPage) ) + printf(" \n", $rowsPerPage, (($curPage*$rowsPerPage)+$rowsPerPage)); + else + printf(" \n"); + printf("\n"); + + printf("\t\t\t
+ +\n"); + printf("
<< PREVIOUSNEXT >>
Player
Name
Total
Score
Longest
Pattern
When
Recorded
" . $row["name"] . "" . $row["score"] . "" . $row["maxpattern"] . "" . $row["dtime"] . "
<< PREVIOUSNEXT >>
\n"); +} + +?> + + + + + + +
+

"A fabulous tour de force..." -- Game Misinformer Bragazine +

"Quite possibly the most important thing to happen to +
video games since Shigeru Miyamoto." -- Protendo Trading Cards
+

"I believe we made the most beautiful thing in the world. Nobody +
would criticize a renowned architect's blueprint that the position +
of a gate is wrong. It's the same as that." --Ken Kutaragi
+
+ +