DAN ZEN EXPO - CODE EXHIBIT - GOBSTOP
package {
	
	// game to keep balls on screen and add rings for points
	// clicking balls adds ring and changes direction
	// clicking too fast removes ring
	// once there are 10 rings the ball explodes
	// score increases when ball explodes and a new ball is created
	// new ball is created after there are 4 rings added to a ball
	// only one ball is added by a ball (can't have 4 rings then 3 then 4 to add extra ball)
	// balls go slightly faster each explosion
	// if a ball goes off the scree, player loses that many rings from score
	// if all balls travel off screen then game is over
	// high scores are kept.
	
	import flash.desktop.NativeApplication;
	
	import com.danzen.effects.Rings;
	import com.danzen.frameworks.Easy;
	import com.danzen.interfaces.AnimatedScore;
	import com.danzen.interfaces.TimerBacking;
	import com.danzen.utilities.AirLocalObject;
	import com.danzen.utilities.UniqueRandom;
	import com.greensock.TweenLite;
	import com.greensock.easing.Strong;
	
	import flash.display.MovieClip;
	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.events.TimerEvent;
	import flash.events.KeyboardEvent;
	import flash.ui.Keyboard;
	import flash.filters.DropShadowFilter;

	import flash.media.Sound;
	import flash.media.SoundChannel;
	import flash.media.SoundTransform;
	import flash.net.URLRequest;
	import flash.text.Font;
	import flash.text.TextField;
	import flash.utils.Timer;
	import flash.utils.getTimer;
	import flash.display.StageAspectRatio;
		
	public class Gobstop extends Sprite	{
		
		private var assets:Assets;
		
		private var colors:Array = [0xECC6D9, 0x440011, 0xCC2211, 0xBBDDDD, 0x996611 , 0xEEEECC, 0xDD8811, 0x88AA99, 0x333300, 0x447777];
		private var randomArray:UniqueRandom;
		private var sW:Number;
		private var sH:Number;		
		private var introTimer:Timer;
		private var wallTimer:Timer;
		private var wallDelayTimer:Timer;
		private var congratsTimer:Timer;
		private var blackBallTimer:Timer;
		
		private var myData:MyData;
		
		private var gameTime:Number = 5; // minutes of play
		private var ballRadius:Number = 100;
		private var buffer:Number = 20; // start ball outside screen by this much 
		private var minDirection:Number = .5;
		private var maxDirection:Number = 1;
		private var speed:Number;
		private var startSpeed:Number = 3;
		private var blackBallSpeed:Number = 1.5;
		private var clickInterval:Number = 1; // seconds until you can reclick ball
		private var birthNumber:Number = 4; // how many rings until ball click makes a new ball
		private var speedIncreaseGood:Number = .35; // after filling ball
		private var speedIncreaseBad:Number = .15; // after ball goes off stage
		private var levelChecks:Array = new Array();		
		private var levelPoints:Number = 50; // every 100 points get speed reduction
		private var speedLevelReduce:Number = 2;
		private var twoCount:Number = 0; // check if three balls cleared at same time
		private var blackBallCount:Number = 5; // how many balls exist before showing blackball
		private var endCheck:Boolean; // make sure timer and no ball endings do not double end
		
		private var ballHolder:Sprite;
		private var introBall:Rings;
		private var myAnimatedScore:AnimatedScore;
		
		private var helpSound:Sound;
		private var startSound:Sound;
		private var endSound:Sound;
		private var slurp:Sound;
		private var goodRings:Sound;
		private var badRings:Sound;		
		private var slow:Sound;
		private var wallStart:Sound;
		private var wallBounce:Sound;
		private var wallEnd:Sound;
		private var blackBallSound:Sound;
		private var submitSound:Sound;
		private var backingMusic:Sound;
		private var backingChannel:SoundChannel;
		private var bleepSounds:Array;	
		
		private var myDate:Object = new Date();
			
		private var explodeBadTweens:Array;		
		
		private var myTimerBacking:TimerBacking;		
		private var font3:Font = new Font3();
		
		private var blackBall:MovieClip;
		
		private var myScore:TextField;
		private var border:MovieClip;
		private var intro:MovieClip;
		private var backing:MovieClip;
		private var shading:MovieClip;
		
		private var playSoundCheck:Boolean = true;
		private var playSoundCount:Number = 0;
		private var playSoundMax:Number = 4;
		private var playSoundTimer:Timer;
				
		private var nameTimer:Timer;
		private var nameYellowTimer:Timer;
		
		private var myALO:AirLocalObject;
		
		public function Gobstop() {
			trace ("hi from Gobstop");
			
			stage.scaleMode = StageScaleMode.NO_SCALE;
			stage.align = StageAlign.TOP_LEFT;
			stage.autoOrients = false;			
			addEventListener(Event.ADDED_TO_STAGE, init);	
			
			NativeApplication.nativeApplication.addEventListener(Event.DEACTIVATE, handleDeactivate);
			NativeApplication.nativeApplication.addEventListener(KeyboardEvent.KEY_DOWN, handleKeys);
		}
				
		private function handleDeactivate(event:Event):void    {				
			NativeApplication.nativeApplication.exit(); 			
		}            
		
		private function handleKeys(event:KeyboardEvent):void {
			if(event.keyCode == Keyboard.BACK) {
				NativeApplication.nativeApplication.exit(); 
			}		
		}   		
		
		private function init(e:Event):void {		
			
			// set up stage and bring in assets
			if (stage.fullScreenWidth > 0) {
				sW = stage.fullScreenWidth;
				sH = stage.fullScreenHeight;
			} else {
				sW = stage.stageWidth;
				sH = stage.stageHeight;
			}		
			
			stage.color = 0xffffff;
			stage.frameRate = 30;			
			stage.setAspectRatio( StageAspectRatio.LANDSCAPE );
			
			assets = new Assets();
			addChild(assets);
			
			assets.border.width = sW;
			assets.border.height = sH;			
			//assets.removeChild(assets.border);
			
			assets.backing.width = sW;
			assets.backing.height = sH;
			//assets.removeChild(assets.backing);
			
			assets.shading.width = sW;
			assets.shading.height = sH;
			assets.removeChild(assets.shading);
			
			fit (assets.intro, 1136, 640);		
			fit (assets.scoreHolder, 1136, 640);
			//assets.removeChild(assets.scoreHolder);
			//assets.removeChild(assets.intro);	
			
			myScore = assets.scoreHolder.myScore;
			intro = assets.intro;
			border = assets.border;
			backing = assets.backing;
			shading = assets.shading;
			
			addChild(backing);			
			addChild(border);			
			addChild(intro);			
			
			myData = new MyData();
			myData.addEventListener(Event.COMPLETE, doData);
			
			
			helpSound = new Sound(new URLRequest("sounds/help.mp3"));
			startSound = new Sound(new URLRequest("sounds/start.mp3"));	
			endSound = new Sound(new URLRequest("sounds/end.mp3"));
			slurp = new Sound(new URLRequest("sounds/slurp.mp3"));
			goodRings = new Sound(new URLRequest("sounds/powerup.mp3"));
			badRings = new Sound(new URLRequest("sounds/powerdown.mp3"));
			slow = new Sound(new URLRequest("sounds/slow.mp3"));
			wallStart = new Sound(new URLRequest("sounds/wallstart.mp3"));
			wallBounce = new Sound(new URLRequest("sounds/bounce.mp3"));
			wallEnd = new Sound(new URLRequest("sounds/wallend.mp3"));
			blackBallSound = new Sound(new URLRequest("sounds/blackball.mp3"));
			submitSound = new Sound(new URLRequest("sounds/submit.mp3"));
			backingMusic = new Sound(new URLRequest("sounds/backing.mp3"));
			bleepSounds = [
				new Sound(new URLRequest("sounds/b01.mp3")),
				new Sound(new URLRequest("sounds/b02.mp3")),
				new Sound(new URLRequest("sounds/b03.mp3")),
				new Sound(new URLRequest("sounds/b04.mp3")),
				new Sound(new URLRequest("sounds/b05.mp3")),
				new Sound(new URLRequest("sounds/b06.mp3")),
				new Sound(new URLRequest("sounds/b07.mp3")),
				new Sound(new URLRequest("sounds/b08.mp3")),
				new Sound(new URLRequest("sounds/b09.mp3")),
				new Sound(new URLRequest("sounds/b10.mp3"))
			];
			
			makeBlackBall();			
			
			//myScore.x = (sW - myScore.width) / 2;
			//myScore.y = (sH - myScore.height) / 2;
			myScore.mouseEnabled = false;
			
			removeChild(border);
			
			startSound.play();
			introTimer = new Timer(300, 10);
			introTimer.addEventListener(TimerEvent.TIMER, doIntro);
			introTimer.addEventListener(TimerEvent.TIMER_COMPLETE, doneIntro);
			introTimer.start();
			
			congratsTimer = new Timer(200, 13);
			congratsTimer.addEventListener(TimerEvent.TIMER, congratsFlash);
			congratsTimer.addEventListener(TimerEvent.TIMER_COMPLETE, congratsFlashEnd);
			
			blackBallTimer = new Timer(10*1000, 1);
			blackBallTimer.addEventListener(TimerEvent.TIMER, removeBlackBall);
			
			wallTimer = new Timer(250, 80);
			wallTimer.addEventListener(TimerEvent.TIMER, doWallTimer);
			wallTimer.addEventListener(TimerEvent.TIMER_COMPLETE, doneWallTimer);
			
			wallDelayTimer = new Timer(1100, 1); // to introduce wall after balls disolve
			wallDelayTimer.addEventListener(TimerEvent.TIMER_COMPLETE, makeWalls);
			
			playSoundTimer = new Timer(500);
			playSoundTimer.addEventListener(TimerEvent.TIMER, soundCountDown);
			playSoundTimer.start();
			
			nameTimer = new Timer(20*1000);
			nameTimer.addEventListener(TimerEvent.TIMER, adjustName);
			nameTimer.start();
			
			nameYellowTimer = new Timer(2*1000, 1);
			nameYellowTimer.addEventListener(TimerEvent.TIMER, clearYellowName);
			
			Easy.url(intro.danzen, "http://danzen.com", "danzen");
			Easy.url(intro.help.tilty, "http://tilty.mobi", "tilty");
			Easy.url(intro.scores.tilty, "http://tilty.mobi", "tilty");
			
			intro.removeChild(intro.help);
			intro.help.closeBut.buttonMode = true;
			intro.helpBut.buttonMode = true;
			intro.helpBut.addEventListener(MouseEvent.CLICK, getHelp);			
			intro.help.closeBut.addEventListener(MouseEvent.CLICK, closePanels);
			
			shading.addEventListener(MouseEvent.CLICK, closePanels);
			intro.scores.underBacking.addEventListener(MouseEvent.CLICK, closePanels);
			intro.help.underBacking.addEventListener(MouseEvent.CLICK, closePanels);
			
			intro.removeChild(intro.scores);
			intro.scores.closeBut.buttonMode = true;
			intro.scoresBut.buttonMode = true;
			intro.scoresBut.addEventListener(MouseEvent.CLICK, getScores);
			intro.scores.submitBut.addEventListener(MouseEvent.CLICK, doSubmit);
			intro.scores.submitBut.buttonMode = true;		
			intro.scores.closeBut.addEventListener(MouseEvent.CLICK, closePanels);			
			intro.scores.removeChild(intro.scores.congratulations);
			intro.scores.removeChild(intro.scores.submitBut);
			intro.scores.myScore.text = "0";
			intro.scores.best.bestScore.text = "0";
			intro.scores.nameBacking.gotoAndStop(1);			
			
			myALO = new AirLocalObject("gobstop", ["player", "score"]);
			//myALO.clear();
			var p:String = myALO.getProperty("player");
			var s:String = myALO.getProperty("score");
			if (p != "") {
				intro.scores.myName.text = p;
			}
			if (s != "") {
				intro.scores.best.bestScore.text = s;
			}		
						
			// prepare a random list of colors
			// make a ball with rings using the custom Rings Class
			randomArray = new UniqueRandom(colors); 
			introBall = new Rings(sW/5.5,randomArray.getArray(10));						
			introBall.x = sW/2/intro.scaleX-intro.x;
			introBall.y = sH/2.7/intro.scaleX-intro.y;			
			intro.addChildAt(introBall,1);					
			
			
		}
		
		// mobile had a problem with the focus event setting to capitals
		// iOS would only put one letter in the field - don't know why
		// so instead, am using timer and also submit event to set to uppercase
		private function adjustName(e:TimerEvent):void {
			intro.scores.myName.text = intro.scores.myName.text.toUpperCase();
			//intro.scores.nameBacking.gotoAndStop(1);
			myALO.setProperty("player", intro.scores.myName.text);			
		}
		
		private function clearYellowName(e:TimerEvent):void {			
			intro.scores.nameBacking.gotoAndStop(1);			
		}
		
		
		// mobile is bogging at times if it plays too many sounds		
		private function soundCountDown(e:TimerEvent):void {
			playSoundCount--;
			playSoundCount = Math.max(playSoundCount, 0);
		}
		
		private function checkSound():Boolean {
			if (playSoundCount < playSoundMax) {
				playSoundCount++;
				return true;
			} else {
				return false;
			}
		}
		
		private function fit(clip:MovieClip, originalWidth:Number, originalHeight:Number):void {	
			// fit clip inside stage size				
			
			var minX:Number=0;
			var minY:Number=0;
			var maxWidth:Number=sW;
			var maxHeight:Number=sH;
			
			clip.scaleX = clip.scaleY = 1;	
			
			var clipScale:Number = maxHeight / originalHeight;
			clip.scaleX = clip.scaleY = clipScale;	
			
			if (originalWidth*clipScale > maxWidth) {				
				clip.scaleX = clip.scaleY = 1;
				clipScale = maxWidth / originalWidth;
				clip.scaleX = clip.scaleY = clipScale;	
				clip.x = minX;
				clip.y = (sH - originalHeight*clipScale) / 2;
			} else {		
				clip.x = (sW - originalWidth*clipScale) / 2;
				clip.y = minY;
			}	
		}		
						
		private function makeBlackBall():void {
			blackBall = new MovieClip();
			with(blackBall.graphics) {
				beginFill(0x000000); drawCircle(0,0,59);
				beginFill(0x444444); drawCircle(0,0,44);
				beginFill(0x000000); drawCircle(0,0,36);
				beginFill(0x333333); drawCircle(0,0,32);
				beginFill(0x000000); drawCircle(0,0,26);
				beginFill(0x333333); drawCircle(0,0,16);
				beginFill(0x222222); drawCircle(0,0,6);
			}
			blackBall.filters = [new DropShadowFilter(4,45,0,.7,24,24,1,3)];
		}
		
		private function doData(e:Event):void {
			for (var i:uint=1; i<=3; i++) {
				intro.scores["player"+i].text = myData.topScores["p"+i];
				intro.scores["score"+i].text = myData.topScores["s"+i];
			}
			if (myData.command == MyData.CHECK_SCORES) {
				if (Number(intro.scores.myScore.text) >= Number(myData.topScores.s3)) {
					intro.scores.removeChild(intro.scores.best);
					intro.scores.addChild(intro.scores.submitBut);
				}
			}
			
		}
		
		private function doSubmit(e:MouseEvent):void {
			helpSound.play();
			if (intro.scores.myName.text == "" || intro.scores.myName.text == "NAME?") {
				intro.scores.nameBacking.gotoAndStop(2);
				wallBounce.play();
				nameYellowTimer.reset();
				nameYellowTimer.start();
				return;
			}			
			intro.scores.myName.text = intro.scores.myName.text.toUpperCase();
			intro.scores.nameBacking.gotoAndStop(1);			
			myALO.setProperty("player", intro.scores.myName.text);
			intro.scores.removeChild(intro.scores.submitBut);
			intro.scores.congratulations.alpha = 0;
			intro.scores.addChild(intro.scores.congratulations);				
			TweenLite.to(intro.scores.congratulations, 1.5,{alpha:1});
			congratsTimer.reset();
			congratsTimer.start();
			myData.sendScore(intro.scores.myName.text, intro.scores.myScore.text, myDate.date);
			submitSound.play();
		}
		
		private function congratsFlash(e:TimerEvent):void {
			intro.scores.congratulations.visible = !intro.scores.congratulations.visible;
		}
		private function congratsFlashEnd(e:TimerEvent):void {
			intro.scores.congratulations.visible = true;
		}		
		
		private function getScores(e:MouseEvent):void {
			addChildAt(shading,2);
			intro.addChild(intro.scores);
			intro.gobLogo.alpha = .8;
			intro.scoresBut.alpha = .8;
			intro.helpBut.alpha = .8;
			introBall.alpha = .8;
			helpSound.play();
		}
				
		private function getHelp(e:MouseEvent):void {
			addChildAt(shading,2);
			intro.addChild(intro.help);	
			intro.gobLogo.alpha = .8;
			intro.scoresBut.alpha = .8;
			intro.helpBut.alpha = .8;
			introBall.alpha = .8;
			helpSound.play();
		}
		
		private function closePanels(e:MouseEvent):void {		
			if (intro.contains(intro.help)) {
				intro.removeChild(intro.help);
			} else if (intro.contains(intro.scores)) {
				intro.removeChild(intro.scores);
			}			
			intro.scores.myName.text = intro.scores.myName.text.toUpperCase();
			intro.scores.nameBacking.gotoAndStop(1);
			intro.gobLogo.alpha = 1;
			intro.scoresBut.alpha = 1;
			intro.helpBut.alpha = 1;
			introBall.alpha = 1;
			removeChild(shading);
			helpSound.play();
		}
		
		
		private function doIntro(e:TimerEvent):void {
			introBall.addRing();
		}
		
		private function doneIntro(e:TimerEvent):void {
			introBall.buttonMode = true;
			introBall.addEventListener(MouseEvent.CLICK, fadeInGame);			
		}
		
		private function doWallTimer(e:TimerEvent):void {
			if (e.target.currentCount > e.target.repeatCount * .8) {
				if (e.target.currentCount % 2 == 0) {
					border.alpha = 0;
				} else {
					border.alpha = 1;
				}
			}
		}
		
		private function doneWallTimer(e:TimerEvent):void {
			removeChild(border);
			border.alpha = 1;
			wallEnd.play();
		}
		
		private function fadeInGame(e:MouseEvent):void {
			//introTween = new Tween(intro,"alpha",Strong.easeOut,1,0,1.5,true);
			TweenLite.to(intro, 1.5, {alpha:0, ease:Strong.easeOut, onComplete:init2});
			TweenLite.to(backing, 1.5, {alpha:0, ease:Strong.easeOut});
			
			if (contains(border)) {removeChild(border);}
			myScore.text = "0";
			twoCount = 0;
			endCheck = false;
			explodeBadTweens = [];
			
			if (intro.scores.contains(intro.scores.congratulations)) {
				intro.scores.removeChild(intro.scores.congratulations);
			}
			if (intro.scores.contains(intro.scores.submitBut)) {
				intro.scores.removeChild(intro.scores.submitBut);
			}
			intro.scores.addChild(intro.scores.best);
			for (var i:uint=0; i<100; i++) {levelChecks[i] = false;}	
			// fadeInGame gets called on reset too - so only make holder once
			if (!ballHolder) {  
				ballHolder = new Sprite();
				addChild(ballHolder);
				myDate = {date:"myDate.date"};
				//ballHolder.filters = [new DropShadowFilter(4,45,0,.7,24,24,1,3)]	
			} 
			speed = startSpeed;
			
			backingChannel = backingMusic.play(0,10000);
			backingChannel.soundTransform = new SoundTransform(.4); //turn down volume			
			myTimerBacking = new TimerBacking(stage, gameTime*60*1000, 1000, sW, sH, 0xDDDDDD);
			myTimerBacking.addEventListener(TimerBacking.TIME_COMPLETE, timeDone);			
			addChildAt(myTimerBacking, 0);						
		}
		
		private function removeBlackBall(e:TimerEvent):void {
			//blackBallTween = new Tween(blackBall, "alpha", null, 1, 0, 1.5, true);
			//blackBallTween.addEventListener(TweenEvent.MOTION_FINISH, removeBlackBall2);
			TweenLite.to(blackBall, 1.5, {alpha:0, onComplete:removeBlackBall2});
			blackBallSound.play(0,0,new SoundTransform(.7,0));
		}
		
		private function removeBlackBall2():void {
			//blackBallTween.removeEventListener(TweenEvent.MOTION_FINISH, removeBlackBall2);
			//blackBallTween = null;
			removeChild(blackBall);
		}
		
		private function sparse():Object {
			var o:Object = {};
			var b:Rings;
			var q:Array = [0,0,0,0]; // top left, bottom left, top right, bottom right
			
			
			for (var i:uint=0; i<ballHolder.numChildren; i++) {
				b = Rings(ballHolder.getChildAt(i));
				if (b.x < sW /2) {
					if (b.y < sH / 2) {q[0]++;} else {q[1]++;}
				} else {
					if (b.y < sH / 2) {q[2]++;} else {q[3]++}
				}		
			}
			
			if (q[0] < q[1] && q[0] < q[2] && q[0] < q[3]) {			
				
				o.x = sW/3; o.y = sH/3;
			} else if (q[1] < q[2] && q[1] < q[3]) {			
				o.x = sW/3; o.y = 2*sH/3;
			} else if (q[2] < q[3]) {			
				o.x = 2*sW/3; o.y = sH/3;
			} else {
				o.x = 2*sW/3; o.y = 2*sH/3;
			}
			return o;
		}
		
		private function addBlackBall():void {
			/*
			blackBall.x = (Math.random()>.5) ? sW/3 : sW*2/3;
			blackBall.y = (Math.random()>.5) ? sH/3 : sH*2/3;
			*/
			//add blackball to least poplated quadrant
			blackBall.x = sparse().x
			blackBall.y = sparse().y
			addChild(blackBall);
			blackBall.alpha = 0;
			blackBallSound.play();
			//blackBallTween = new Tween(blackBall, "alpha", null, 0, 1, 1.5, true);
			TweenLite.to(blackBall, 1.5, {alpha:1});
			blackBallTimer.reset();
			blackBallTimer.start();
		}
		
		private function timeDone(e:Event):void {			
			endGame();
		}
		
		private function init2():void {
			
			//introTween.removeEventListener(TweenEvent.MOTION_FINISH, init2);
			introBall.removeEventListener(MouseEvent.CLICK, fadeInGame);
			introBall.buttonMode = false;
			removeChild(intro);						
			
			// create object from custom class to animate the score
			myAnimatedScore = new AnimatedScore(myScore);
			
			// this event handles motion of balls
			addEventListener(Event.ENTER_FRAME, animate);
			
			makeBall();
			
			//introTimer = new Timer(2000, 0);
			//introTimer.addEventListener(TimerEvent.TIMER_COMPLETE, restart);	
			
		}
		
		private function addRing(e:TimerEvent):void {
			makeBall();
		}
		
		private function positionBall(ball:MovieClip):void {
			// position ball above or below and left or right of stage		
			// set ball's direction to opposite side
			var r:Number = ball.width/2 + buffer; // make sure can't see dropshadow
			if (Math.random() > .5) {
				ball.x = -r
				ball.dirX = Easy.random(minDirection, maxDirection);				
			} else {
				ball.x = sW+r;
				ball.dirX = Easy.random(-minDirection, -maxDirection);
			}			
			if (Math.random() > .5) {
				ball.y = -r
				ball.dirY = Easy.random(minDirection, maxDirection);
			} else {
				ball.y = sH+r;
				ball.dirY = Easy.random(-minDirection, -maxDirection);
			}
		}
		
		
		private function makeBall():void {
			var ball:Rings = new Rings(ballRadius,randomArray.getArray(10),1,0x333333);							
			
			positionBall(ball);
			if (checkSound()) {
				slurp.play(0,0,new SoundTransform(1,getPan(ball)));
			}
			ball.birthCheck = false; // this ball has not birthed another ball
			ball.insideCheck = false; // true when ball is completely inside viewing area
			ball.aliveCheck = true; // set to false when it hits a blackBall
			//ball.filters = [new DropShadowFilter(4,45,0,.7,24,24,1,3)]			
			ball.addEventListener(Rings.DISOLVED, removeBall);
			ballHolder.addChild(ball);	
			if (contains(border)) {
				setChildIndex(border,numChildren-1);				
			} 
			ball.buttonMode = true;			
			ball.addEventListener(MouseEvent.CLICK, pressBall);
			
			if (ballHolder.numChildren >= blackBallCount && !contains(blackBall)) {
				addBlackBall();
			}
		}
		
		private function explode(ball:Rings):void {
			myAnimatedScore.addToScore(ball.number);
			checkLevelIncrease();
			ball.removeEventListener(MouseEvent.CLICK, pressBall);
			ball.disolve();		
			if (checkSound()) {
				goodRings.play(0,0,new SoundTransform(1,getPan(ball)));
			}
			if (!contains(border)) {
				twoCount++;
			}
			if (twoCount == 2) {				
				wallDelayTimer.start();
			} 
		}
		
		private function makeWalls(e:TimerEvent):void {
			trace ("make walls");
			addChild(border);
			twoCount = 0;
			wallStart.play();
			wallTimer.reset();
			wallTimer.start();
			// could be ball heading out which bounces badly so set all balls to false
			// the animate will test and set to true if good 
			for (var i:uint=0; i<ballHolder.numChildren; i++) {
				var ball:Rings = Rings(ballHolder.getChildAt(i)); // casting as Rings				
				ball.insideCheck = false;					
			}
		}
		
		private function checkLevelIncrease():void {
			var score:Number = Number(myScore.text) + 10; // amimated score delay fix
			var level:Number = Math.floor(score/levelPoints);
			if (level > 0) {
				if (levelChecks[level-1] == false) {
					speed -= speedLevelReduce;
					levelChecks[level-1] = true;
					trace ("level reduce");
					slow.play();
				}
			}
		}
		
		private function removeBall(e:Event):void {
			var ball:Rings = Rings(e.target); // casting
			ballHolder.removeChild(ball);
			ball = null;
			speed += speedIncreaseGood;
			twoCount--;
			twoCount = Math.max(twoCount, 0);
			makeBall();	
		}
		
		private function explodeBad(ball:Rings):void {
			if (Number(myScore.text) - ball.number < 0) {
				myAnimatedScore.addToScore(-Number(myScore.text));
			} else {
				myAnimatedScore.addToScore(-ball.number);
			}
			if (checkSound()) {
				badRings.play(0,0,new SoundTransform(1,getPan(ball)));
			}
			if (contains(blackBall) && !ball.aliveCheck) {	
				TweenLite.to(ball, .5, {scaleX:0, scaleY:0, onComplete:explodeBad2, onCompleteParams:[ball]});
				//explodeBadTweens.push(new Tween (ball, "scaleX", Strong.easeInOut, 1, 0, .5, true));
				//explodeBadTweens.push(new Tween (ball, "scaleY", Strong.easeInOut, 1, 0, .5, true));
				//explodeBadTweens[explodeBadTweens.length-1].addEventListener(TweenEvent.MOTION_FINISH, explodeBad2);			
			} else {
				ballHolder.removeChild(ball);
				ball = null;
			}
			speed += speedIncreaseBad;
		}
		private function explodeBad2(b:Rings):void {
			//var myTween:Tween = Tween(e.currentTarget);			
			//myTween.removeEventListener(TweenEvent.MOTION_FINISH, explodeBad2);				
			var ball:Rings = Rings(b);
			ballHolder.removeChild(ball);
			ball = null;
		}
		
		private function getPan(clip:Sprite):Number {
			var p:Number = clip.x / sW * 2 - 1;
			return Math.max(Math.min(p,1),-1);
		}
		
		private function pressBall(e:MouseEvent):void {
			var ball:Rings = Rings(e.target);
			if (!ball.aliveCheck) {return;}
			//if (checkSound()) {
			//playSoundCount++;
			bleepSounds[ball.number-1].play(0,0,new SoundTransform(1,getPan(ball)));
			//}
			
			// change direction
			ball.dirX *= -1;
			ball.dirY *= -1;
			
			// check to see if ring number same as number of colors
			// if so then explode ring and give points			
			if (ball.number >= ball.colors.length) {
				ball.aliveCheck = false;
				explode(ball);
				return;
			}
			
			// make a new ball if birthNumber ring (only once per ball)
			if (ball.number == birthNumber && !ball.birthCheck) {
				ball.birthCheck = true;
				makeBall();				
			}
			// check to make sure they do not keep clicking to get rings
			// if they click too fast, remove rings
			if (getTimer() - ball.lastClicked < clickInterval*1000) {
				ball.removeRing();				
			} else {
				ball.addRing();				
			}			
			ball.lastClicked = getTimer();
			
		}
		
		private function endGame():void {
			trace("end game");						
			endCheck == true;
			
			myTimerBacking.removeEventListener(TimerBacking.TIME_COMPLETE, timeDone);
			
			var score:Number = myAnimatedScore.total;
			intro.scores.myScore.text = String(score);
			//Easy.font(intro.scores.myScore, font3, 40, 0xCC0000);			
			
			if (score > Number(intro.scores.best.bestScore.text)) {
				intro.scores.best.bestScore.text = String(score);
				myALO.setProperty("score", myScore.text);
			}
			
			myData.checkScores();			
			
			backingChannel.stop();
			endSound.play();							
			intro.alpha = 0;
			intro.addChild(intro.scores);
			if (contains(border)) {							
				wallTimer.stop();
				border.alpha = 1;
				removeChild(border);	
			}
			
			addChildAt(shading,2);
			shading.alpha = 0;
			intro.gobLogo.alpha = .8;
			intro.scoresBut.alpha = .8;
			intro.helpBut.alpha = .8;
			introBall.alpha = .8;
			
			addChild(intro);
			TweenLite.to(shading, 3.5, {alpha:.2, ease:Strong.easeIn});
			TweenLite.to(intro, 3.5, {alpha:1, ease:Strong.easeIn, onComplete:restart});
			TweenLite.to(backing, 3.5, {alpha:1, ease:Strong.easeIn});
			TweenLite.to(ballHolder, 1.7, {alpha:0, ease:Strong.easeIn});
			
			//introTween = new Tween(intro,"alpha",Strong.easeIn,0,1,3.5,true);
			//ballTween = new Tween(ballHolder,"alpha",Strong.easeIn,1,0,1.7,true);
			//introTween.addEventListener(TweenEvent.MOTION_FINISH, restart);
			
			if (contains(blackBall)) {
				removeBlackBall(null);
				blackBallTimer.stop();
			}
		}
		
		private function restart():void {			
			ballHolder.removeChildren();
			ballHolder.alpha = 1; // set back to 1 for next time
			//introTween.removeEventListener(TweenEvent.MOTION_FINISH, restart);
			introBall.addEventListener(MouseEvent.CLICK, fadeInGame);
			introBall.buttonMode = true;
			myTimerBacking.dispose();
			removeChild(myTimerBacking);
			myTimerBacking = null;			
			if (hasEventListener(Event.ENTER_FRAME)) {
				removeEventListener(Event.ENTER_FRAME, animate);
			}
		}
		
		private function animate(e:Event):void {
			var buff:Number = ballRadius + buffer + 5;
			if (ballHolder.numChildren == 0) {
				removeEventListener(Event.ENTER_FRAME, animate);
				if (!endCheck) {					
					endGame();					
				}
			}			
			
			var inside:Number = 10; // make sure ball is inside border
			var bounceCheck:Boolean;
			for (var i:uint=0; i>ballHolder.numChildren; i++) {
				var ball:Rings = Rings(ballHolder.getChildAt(i)); // casting as Rings
				moveBall();
				function moveBall():void {
					ball.x += ball.dirX * speed;
					ball.y += ball.dirY * speed;
				}			
				
				if (contains(blackBall)) {				
					if (ball.aliveCheck && blackBall.hitTestPoint(ball.x, ball.y, true)) {						
						ball.aliveCheck = false;
						explodeBad(ball);
					}
				}
				
				if (contains(border)) {					
					if (!ball.insideCheck) {
						var d:Number = ballRadius + inside;
						if (ball.x > d && ball.x < sW - d  && ball.y > d && ball.y < sH - d) {
							ball.insideCheck = true;						
						}
					}				
					if (ball.insideCheck) {
						bounceCheck = false;
						if (ball.hitTestObject(border.right)) {
							ball.dirX *= -1;
							bounceCheck = true;							
						}
						if (ball.hitTestObject(border.left)) {
							ball.dirX *= -1;
							bounceCheck = true;
						}
						if (ball.hitTestObject(border.top)) {
							ball.dirY *= -1;
							bounceCheck = true;
						}
						if (ball.hitTestObject(border.bottom)) {
							ball.dirY *= -1;
							bounceCheck = true;
						}				
						if (bounceCheck) {
							moveBall();
							if (checkSound()) {
								wallBounce.play(0,0,new SoundTransform(1,getPan(ball)));
							}
						}
					}
				} 		
				
				if (ball.x > sW + buff || ball.x < -buff ||
					ball.y > sH + buff || ball.y < -buff) {					
					if (!contains(intro)) { // do not explode ball if fading in intro						
						explodeBad(ball);
					}
				}				
				
			}		
		}
		
	}
	
}