DAN ZEN EXPO - CODE EXHIBIT - POMP
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>ZIM Game</title> 

<!-- for CreateJS and ZIMjs http://zimjs.com - free to modify - Dan Zen 2015 -->
<!-- see http://zimjs.com/templates for more templates and meta tags -->

<script>var zon = true; // true for comments from zim code</script>
<script src="libraries/zim_1.4.3.js"></script>
<script src="libraries/createjs-2015.05.21.min.js"></script> 
<!-- or can go to zimjs.com and createjs.com for individual modules -->

<style>
	body {margin:0px; padding:0px; background-color:#000;}
	#myCanvas {position:absolute; background-color:#333;}
</style>

<script>

// SCALING OPTIONS
// "none"		sets canvas and stage to dimensions and does not scale if window changes
// "fit"		sets canvas and stage to dimensions and scales to fit inside window size
// "outside"		sets canvas and stage to dimensions and scales to fit outside window size
// "full"		sets canvas and stage to window size (canvas is actually set to screen size)

var scaling = "fit"; // fit scales to fit the browser window while keeping the aspect ratio
var width = 960;
var height = 640;
var frame = new zim.Frame(scaling, width, height); 
frame.on("ready", function() {	
	zog("ready from ZIM Frame");
	
	var stage = frame.stage;
	var stageW = frame.width;
	var stageH = frame.height;
	
	// LOAD SOUND
	// handle asset loading and call main app function when complete
	// we only have sounds to load for this game and they are in the game folder
	var contentPath = "game/";
	var manifest = [
		{src:"pomp.png", id:"logo"},
		{src:"backing.mp3", id:"backing"},
		{src:"end.mp3", id:"end"},
		{src:"grow.mp3", id:"grow"},
		{src:"hit.mp3", id:"hit"}
	]; // array of objects
	// add the numbered blip sounds for when pressed smaller
	// could have manually added them like above
	for (var i=1; i<=10; i++) {
		manifest.push({src:"b"+i+".mp3", id:"b"+i});
	}
	var preload = new createjs.LoadQueue(true, contentPath);
	preload.installPlugin(createjs.Sound);
	preload.on("complete", app);	 // call the main code when ready	
	preload.loadManifest(manifest);
	
	// a little animated waiting component
	var waiter = new zim.Waiter(stage);
	waiter.show();

	// when the sounds load we can begin!
	function app() {
		
		// generally, the game is to keep a bunch of circles from hitting one another
		// the circles grow bigger and you press them to make them smaller
		// the longer you keep them apart, the more points you get (seconds playing game)
				
		waiter.hide(); // now that the sound has loaded, hide the waiter component
		createjs.Sound.play("backing",{loop:-1, volume:.5}); // loop forever
		
		// INITIAL SETUP
		var startTime = new Date(); // used to calculate the score based on time played
		var endCheck = false;		// has the game ended?
		
		// game settings
		var spacing = 200; 	// space between circles
		var range = 50; 	// randomly offset the circles by no more than this amount
		var minSize = 20; 	// minimum starting size
		var maxSize = 40; 	// maximum starting size
		var odds = .995; 	// if our random number is greater than this, we grow
		var bigger = 1.3; 	// make circle bigger by this much each time
		var smaller = .9; 	// when we press, make circle smaller by this much
			
		// calculate circle positions
		var horizontal = Math.floor(stageW / spacing); 			// figure out how many circles
		var vertical = Math.floor(stageH / spacing); 			// and vertically
		var hPadding = (stageW - (horizontal-1)*spacing) / 2; 	// center circles
		var vPadding = (stageH - (vertical-1)*spacing) / 2; 	// and vertically
		
		// make a list of colors to cycle through and then randomize the order
		var colors = ["#f58e25", "#acd241", "#e472c4", "#50c4b7", "#d1a170"];
		zim.shuffle(colors);
		
		
		// MAKE CIRCLES
		// create a container to hold all our circles
		var circles = new createjs.Container();
		stage.addChild(circles);
		
		// make the circles by looping in a grid of horizontal and vertical positions		
		var c;				// temporarily stores each circle in loop
		var lastClicked;	// to remember which was last clicked
		var count = 0;		// how many circles have we made
		var radius;			// the radius of the circle we are making
		var color;			// the color of the cicle we are making
		for (var i=0; i<horizontal; i++) { 
			for (var j=0; j<vertical; j++) {
				radius = zim.rand(minSize, maxSize); // get a radius in the range
				color = colors[count%colors.length]; // % gets the remainder (modulus)
				c = new zim.Circle(radius, color);
				count++; // now that we have assigned the color, increase the count
				c.cursor = "pointer"; // indicate that it can be pressed
				c.size = 1; // used to keep track of scale
				c.pressNum = 1; // used to change the blip sound
				circles.addChild(c); // add the circle to the container
				// place the circle based on its iterator in the loops
				// and then randomize its position a little based on the range
				c.x = i * spacing + hPadding + zim.rand(-range, range);
				c.y = j * spacing + vPadding + zim.rand(-range, range);
				// just make sure something is marked as clicked if they do not click at all
				lastClicked = c; 
			}		
		}
		
		// CLICKING CIRCLES
		// here is where we do code for when any of the circles get clicked
		// e.target will tell us which circle gets clicked and we store that in c
		// if c has hit another circle then its end property will be set to true
		circles.on("click", function(e) {
			var c = e.target;
			if (c.end) return; // do not allow clicks on circles that have touched
			var pressSound = "b" + c.pressNum; // get the blip sound based on pressNum			
			createjs.Sound.play(pressSound);
			c.pressNum++; // increase the pressNum and loop back to 1 if higher than 10
			if (c.pressNum > 10) c.pressNum = 1;
			// if the circle is animating then stop the animation
			if (createjs.Tween.hasActiveTweens(c)) {
				createjs.Tween.removeTweens(c);
				c.pause = false;  // mark the circle as not animating
				c.size /= bigger; // set the circle back to size before animation
			} else {
				c.size *= smaller; // was not animating so just make smaller
			}
			if (c.size < .1) { // make sure it is not too small
				c.size = .1;	
			}
			zim.scale(c, c.size); 	// set the scale
			circles.addChild(c);  	// bring clicked circle to top of circles
			lastClicked = c;		// record circle as the last clicked (to receive score if game ends)
			stage.update();	
		});
		
		
		// make logo
		var logo = new createjs.Bitmap(preload.getResult("logo"));
		stage.addChild(logo);
		logo.y = stageH - logo.getBounds().height - 4;
		
		
		// GAME ANIMATION
		// here is the code that handles the circles getting bigger
		// and checking if any circles are hitting and if the game should end
		// this ticker goes at 60 frames per second so quite fast
		var timer = createjs.Ticker.on("tick", function() {		
			var c; 	// a temporary reference to a circle that is being made bigger
			for (var i=0; i<circles.numChildren; i++) { // loop through all the circles
				if (Math.random() > odds) { // have we rolled higher than the odds?
					c = circles.getChildAt(i); // if so, get a reference to the circle
					if (c.end || c.pause) continue; // if it is already hitting or currently animating go to next i
					c.size *= bigger; 	// set the new scale
					c.pause = true; 	// set the circle as animating
					createjs.Sound.play("grow");
					// animate the circle to the new scale
					// call the done function when done and pass to it which circle is animating
					zim.animate(c, {scaleX:c.size, scaleY:c.size}, 1700, "elasticOut", done, c);
									
				}			
			}
			
			stage.update();		
		});	
		createjs.Ticker.setFPS(60);
		
		
		// HIT TESTS
		// here is the function that runs when a circle is finished growing
		// we collect the circle in the parameter c
		function done(c) {
			if (c.pause == false) return; // if it was not animating then leave
			c.pause = false; // set it to be done animating
			var c2; // a temporary variable to hold a second circle
			// loop through all the circles to see if it is hitting any now it is bigger
			for (var i=0; i<circles.numChildren; i++) {
				c2 = circles.getChildAt(i); // get the second circle
				if (c == c2) continue;		// if it is the same as the first, go to the next i
				if (zim.hitTestCircle(c, c2, 8)) { // if the first circle is hitting any of 8 points on the second circle
					createjs.Sound.play("hit");
					c2.end = true;			// mark circles as ended (hitting)
					c.end = true;
					c2.cursor = "default";	// take off the pointer indicator
					c.cursor = "default";
					odds -= .01;			// decrease the odds to make circles more likely to grow
				}								
			}
			
			// find out if there is zero or one circle left
			// we need to count how many circles have the end property set to true
			var ends = 0;
			var last; // this will reference the last circle alive
			for (i=0; i<circles.numChildren; i++) {
				c2 = circles.getChildAt(i);
				if (c2.end) {
					ends++;	 	// increase our counter
				} else {
					last = c2;	// this could be the last circle as it has not ended
				}
			}	
			// when two circles end together there is a possibility of ending twice
			// so only use one of them to end by setting an end check
			// if we have already ended then do not end again!
			if (ends >= circles.numChildren - 1 && !endCheck) {
				endCheck = true; // mark that we have ended
				// if there was one last circle then we want to use last
				// if two circles ended together then we will just use the lastClicked circle
				if (!last) last = lastClicked; 
				circles.addChild(last); // bring the last circle to the top
				last.end = true; 		// make sure the last circle registers as ended (so we can't click on it)
				last.cursor = "default";	
				if (last.size < 3) { 	// make sure it is big enough and if not, then make it bigger
					zim.animate(last, {scaleX:3, scaleY:3}, 1700, "elasticOut", final, last);
				} else {
					final(last);
				}
				createjs.Sound.play("end");
			}
		}
		
		// GAME END
		// this is the function that runs once there is a final circle identified and sized
		// we collect the reference to the last circle in parameter c
		function final(c) {
			createjs.Ticker.off("tick", timer); // stop the ticker from running
			var currentTime = new Date(); // get the current time and subtract from the start time
			var time = Math.round((currentTime.getTime() - startTime.getTime()) / 1000);
			var label = new zim.Label(String(time),60,null,"white",null,"#666");
			zim.centerReg(label);
			stage.addChild(label);
			label.x = c.x; // position the text on the last circle
			label.y = c.y;	
			
			// wait a bit while the ending sound plays and they view their score
			setTimeout(function() {		
				// darken the game		
				var rect = new zim.Rectangle(stageW,stageH);
				rect.alpha = .7;
				stage.addChild(rect);
				// add a restart button
				var button = new zim.Button(300, 100, new zim.Label("RESTART", null, null, "white"), "#666", "#777");
				stage.addChild(button);
				zim.centerReg(button);
				button.x = stageW/2;
				button.y = stageH/2;
				button.on("click", function() {
					zgo("game.html"); // load the page again (poor man's restart)
				});
				
				stage.update();
				
			}, 3000);
			
			stage.update();
		}
		
		
		// SOUND MUTE
		// this code handles the mute button (just a label)	or M key press	
		var mute = new zim.Label("MUTE", 20, null, "white", "violet");
		mute.cursor = "pointer";
		mute.x = stageW - mute.width - 30;
		mute.y = stageH - mute.height - 10;
		stage.addChild(mute);
		mute.on("click", doSound);
		function doSound() {
			createjs.Sound.muted = !createjs.Sound.muted; 
			if (createjs.Sound.muted) {
				mute.text = "SOUND";
			} else {
				mute.text = "MUTE";
			}
		}
		window.addEventListener("keydown", function(e) {
			// zog(e.keyCode);
			if (e.keyCode == 77) { // M key
				doSound();
			}
		});
		
		stage.update();	
	}
	
}); // end of ready
</script>
</head>

<body>
<!-- canvas with id="myCanvas" is made by zim Frame -->
</body>
</html>