package { /* MakeEarth.as * * Applet to teach about what combinations of water, atmospheres and clouds * when added to a planet can make it "livable". * * * Copyright(2010) Tom Whittaker * */ import mx.core.Application; import mx.core.UIComponent; import flash.display.SimpleButton; import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.Sprite; import mx.containers.Canvas; import flash.utils.Timer; import flash.geom.Point; import flash.geom.Rectangle; import mx.controls.Label; import mx.controls.Alert; import flash.events.MouseEvent; import flash.events.TimerEvent; import flash.events.Event; import mx.styles.CSSStyleDeclaration; import mx.styles.StyleManager; import mx.formatters.NumberFormatter; import mx.formatters.NumberBaseRoundType; public class MakeEarth { private var mom:Application; private var waterButt:SimpleButton; private var atmosphereButt:SimpleButton; private var uiBack:UIComponent; private var atmospheres:Array; private var clouds:Array; private var water:Array; private var whichCloud:int; private var whichAtmosphere:int; private var whichWater:int; private var paper:Canvas; private var x:int; private var y:int; private var r:Rectangle; private var p:Point; private var angle:Number; private var bmBlue:Bitmap; private var blue:BitmapData; private var land:BitmapData; private var bmLittleCloud:Bitmap; private var backbmd:BitmapData; private var bmBack:Bitmap; private var lab:Label; private var targetTemp:Number; private var nowTemp:Number; private var stepTemp:Number; private var timer:Timer; private var nf:NumberFormatter; private var gameTimer:Timer; private var years:int; private var bestYears:int; private var gameRunning:Boolean; private var gameLab:Label; private var bestLab:Label; private var cs:CSSStyleDeclaration; // define the location and size of things private const ATMOSMAX:int = 8; private const SPHEREX:int = 60; private const SPHEREY:int = 70; private const SPHEREW:int = 373; private const SPHEREH:int = 360; private const WATERX:int = 630; private const WATERY:int = 50; private const WATERW:int = 140; private const WATERH:int = 83; private const ATMOSX:int = 650; private const ATMOSY:int = 220; private const ATMOSW:int = 120; private const ATMOSH:int = 94; private const CLOUDX:int = 635; private const CLOUDY:int = 405; private const CLOUDW:int = 135; private const CLOUDH:int = 75; private const WATERCUTOFF:int = 3; private const ATMOSCUTOFF:int = 4; // background image is 800x500 // Pick up and embed all images [Embed(source="makear-back.jpg")] public var BgndImage:Class; [Embed(source="atmosphere.gif")] public var AtmosphereImage:Class; [Embed(source="cloud.gif")] public var CloudImage:Class; [Embed(source="water.gif")] public var WaterImage:Class; [Embed(source="sphere-blue.jpg")] public var BlueSphereImage:Class; [Embed(source="littlecloud.gif")] public var LittleCloudImage:Class; public static function main(): void { var ec:MakeEarth = new MakeEarth(); ec.layout(); } private function layout() : void { // Necessary connection mom = Application(Application.application); mom.layout="absolute"; bmBlue = new BlueSphereImage(); blue = bmBlue.bitmapData; bmLittleCloud = new LittleCloudImage(); // Create background; two step needed...only can add Bitmap to Sprite bmBack = new BgndImage(); p = new Point(0,0); r = new Rectangle(SPHEREX,SPHEREY,SPHEREW,SPHEREH); // make a copy of the land_sphere to use during water adding/removing land =new BitmapData(SPHEREW,SPHEREH); land.copyPixels(bmBack.bitmapData,r, p); var bs:Sprite =new Sprite(); bs.addChild(bmBack); uiBack = new UIComponent(); uiBack.addChild(bs) // Make some paper to add and draw stuff onto paper = new Canvas(); paper.x = 0; paper.y = 0; paper.width=bmBack.width; paper.height=bmBack.height; paper.addEventListener(MouseEvent.MOUSE_MOVE, dragStuff); uiBack.addChild(paper); uiBack.move(0,0); mom.addChild(uiBack); // Label for the temperature lab = new Label(); lab.move(10,470); lab.setStyle("color",0xffffff); lab.setStyle("fontSize",14); nf = new NumberFormatter(); nf.rounding = NumberBaseRoundType.NEAREST; nf.useThousandsSeparator = false; targetTemp = 260.1; timer = new Timer(100); timer.addEventListener(TimerEvent.TIMER, dolabel); mom.addChild(lab); // Label for the "game" timer gameTimer = new Timer(90); years = 0; gameRunning = false; gameLab = new Label; gameLab.move(10,10); gameLab.setStyle("color",0x808080); gameLab.setStyle("fontSize",14); gameLab.addEventListener(MouseEvent.MOUSE_UP,reset); gameTimer.addEventListener(TimerEvent.TIMER, dodate); mom.addChild(gameLab); bestLab = new Label; bestLab.move(10,30); bestLab.setStyle("color",0xA0A0A0); bestLab.setStyle("fontSize",14); bestYears = 9999999; mom.addChild(bestLab); // Make the fonts all nice looking for the pop-ups (Alerts) cs = new CSSStyleDeclaration("alerts"); cs.setStyle("fontSize","14"); cs.setStyle("color",0xffffff); cs.setStyle("backgroundColor",0x606060); cs.setStyle("themeColor",0x502020); StyleManager.setStyleDeclaration("Alert",cs,true); Alert.show("Drag & Drop waters, atmospheres, and clouds from the red right panel onto the black left panel in order to make a livable global average temperature of about 15\xB0C (59\xB0F). \x0A\x0AIf you add too much of something, you may drag & drop them back to the right panel."); reset(null); } /* reset(Event) will reset everything to a starting point * */ public function reset(e:Event) : void { if (water != null) { if (water.length > 0) { for (var i:int=0; i 0) { for (i=0; i 0) { for (i=0; i WATERX && event.stageX < WATERX+WATERW && event.stageY > WATERY && event.stageY < WATERY+WATERH) { // Drag some new water var bmWater:Bitmap = new WaterImage(); bmWater.alpha = .4; var bmsb:SimpleButton = new SimpleButton(bmWater, bmWater, bmWater); var waterUI:UIComponent = new UIComponent(); waterUI.x = WATERX; waterUI.y = WATERY; waterUI.width=WATERW; waterUI.height=WATERH; waterUI.addChild(bmsb); waterUI.addEventListener(MouseEvent.MOUSE_MOVE,dragStuff); waterUI.addEventListener(MouseEvent.MOUSE_UP,upStuff); uiBack.addChild(waterUI); water.push(waterUI); whichWater = water.length - 1; whichAtmosphere = -1; whichCloud = -1; if (!gameRunning) gameTimer.start(); } else if (event.stageX > ATMOSX && event.stageX < ATMOSX+ATMOSW && event.stageY > ATMOSY && event.stageY < ATMOSY+ATMOSW && atmospheres.length < ATMOSMAX ) { // Drag some new atmosphere var bmAtmosphere:Bitmap = new AtmosphereImage(); bmAtmosphere.alpha = .4; var bmsbv:SimpleButton = new SimpleButton(bmAtmosphere, bmAtmosphere, bmAtmosphere); var atmosphereUI:UIComponent = new UIComponent(); atmosphereUI.x = ATMOSX; atmosphereUI.y = ATMOSY; atmosphereUI.width=ATMOSW; atmosphereUI.height=ATMOSH; atmosphereUI.addChild(bmsbv); atmosphereUI.addEventListener(MouseEvent.MOUSE_MOVE,dragStuff); atmosphereUI.addEventListener(MouseEvent.MOUSE_UP,upStuff); uiBack.addChild(atmosphereUI); atmospheres.push(atmosphereUI); whichAtmosphere = atmospheres.length - 1; whichWater = -1; whichCloud = -1; if (!gameRunning) gameTimer.start(); } else if (event.stageX > CLOUDX && event.stageX < CLOUDX+CLOUDW && event.stageY > CLOUDY && event.stageY < CLOUDY+CLOUDH) { if (atmospheres.length < ATMOSCUTOFF || water.length < WATERCUTOFF) { Alert.show("You must add enough atmosphere and water before you can add clouds!"); return; } // Drag some new clouds var bmCloud:Bitmap = new CloudImage(); bmCloud.alpha = .4; var bmsbc:SimpleButton = new SimpleButton(bmCloud, bmCloud, bmCloud); var cloudUI:UIComponent = new UIComponent(); cloudUI.x = CLOUDX; cloudUI.y = CLOUDY; cloudUI.width=CLOUDW; cloudUI.height=CLOUDH; cloudUI.addChild(bmsbc); cloudUI.addEventListener(MouseEvent.MOUSE_MOVE,dragStuff); cloudUI.addEventListener(MouseEvent.MOUSE_UP, upStuff); uiBack.addChild(cloudUI); clouds.push(cloudUI); whichCloud = clouds.length - 1; whichAtmosphere = -1; whichWater = -1; if (!gameRunning) gameTimer.start(); } return; } // If nothing above catches, then look if one of these is a target if (whichCloud >= 0) { clouds[whichCloud].x = event.stageX-CLOUDW/2; clouds[whichCloud].y = event.stageY-CLOUDH/2; return; } else if (whichAtmosphere >= 0) { atmospheres[whichAtmosphere].x = event.stageX-ATMOSW/2; atmospheres[whichAtmosphere].y = event.stageY-ATMOSH/2; return; } else if (whichWater >= 0) { water[whichWater].x = event.stageX-WATERW/2; water[whichWater].y = event.stageY-WATERH/2; return; } else { // otherwise, a new "grab" for (var i:int=0; i= 0) { if (clouds[whichCloud].x > 600-CLOUDW/2) { uiBack.removeChild(clouds[whichCloud]); clouds.splice(whichCloud,1); } whichCloud = -1; drawit(1); return; } // atmosphere --- if back in the right panel, remove it if (whichAtmosphere >= 0) { if (atmospheres[whichAtmosphere].x > 600-ATMOSW/2) { uiBack.removeChild(atmospheres[whichAtmosphere]); atmospheres.splice(whichAtmosphere,1); } whichAtmosphere = -1; if (atmospheres.length < ATMOSCUTOFF) { for (var i:int=0; i= 0) { if (water[whichWater].x > 600-WATERW/2) { uiBack.removeChild(water[whichWater]); water.splice(whichWater,1); } whichWater = -1; if (water.length < WATERCUTOFF) { for (i=0; i 0) { var atmos:int = atmospheres.length; if (atmos > ATMOSMAX) atmos = ATMOSMAX; for (var k:int=0; k<5*atmos; k++) { paper.graphics.lineStyle(2,(0x005080- (512*k) - (2*k))); paper.graphics.drawCircle(250,249,165+2*k); } //paper.graphics.lineStyle(10*atmos,0x005080); //paper.graphics.drawCircle(250,249,165+5*atmos); } // if clouds are to be shown, do that next if ( atmospheres.length >= ATMOSCUTOFF && water.length >= WATERCUTOFF && clouds.length > 0) { paper.graphics.lineStyle(0,0xff005080); for (var n:int=0; n<20*clouds.length; n++) { angle = 6.28 * Math.random(); // cloud anchor upper left, multiple of 20,11 x = Math.round( (185*Math.sin(angle) + 240)/20)*20; y = Math.round( (185*Math.cos(angle) + 240)/11)*11; paper.graphics.beginBitmapFill(bmLittleCloud.bitmapData,null); paper.graphics.drawRect(x,y,20,11); paper.graphics.endFill(); } } // this is for the water -- use a percentage of the water vs. land // using random rectangles if (which == 2) { bmBack.bitmapData.lock(); r.x=0; r.y=0; r.width=SPHEREW; r.height=SPHEREH; p.x=SPHEREX; p.y=SPHEREY; bmBack.bitmapData.copyPixels(land,r,p); if (water.length > 0) { //bmBack.bitmapData.copyPixels(blue,rect,new Point(SPHEREX,SPHEREY)); for (var m:int=0; m<300*water.length; m++) { x = int(Math.random()*350); // 20 less than width & height y = int(Math.random()*340); p.x = x+SPHEREX; p.y = y+SPHEREY; r.x = x; r.y = y; r.width = int(Math.random()*20); r.height = int(Math.random()*20); bmBack.bitmapData.copyPixels(blue,r,p); } } bmBack.bitmapData.unlock(); } // now compute the temperature & start the timer var S:Number = 1368.; var albedo:Number = .50*(clouds.length/15.0) + .05*(water.length/6.0) + .15*( 1. + (6. - water.length)/6.0); var emiss:Number = .20*(atmospheres.length/2.0); var te:Number = Math.pow(S*(1.0-albedo)/(4.0 * 5.67e-8), .25); var ts:Number = Math.pow(2.0 * Math.pow(te,4) / (2.-emiss), .25); nowTemp = targetTemp; targetTemp = ts; stepTemp = (targetTemp - nowTemp)/20.; if (clouds.length < 3) { // make sure that we don't stop prematurely if ( (targetTemp-273.16) > 14.4 && (targetTemp-273.16) < 15.6 ) { if (stepTemp > 0) { targetTemp = 273.16 + 15.7; } else { targetTemp = 273.16 + 14.3; } } stepTemp = (targetTemp - nowTemp)/20.; } timer.start(); } /* dolabel(TimerEvent) -- draw the temperature label * */ public function dolabel(e:TimerEvent) : void { //trace("temp = "+nowTemp); nf.precision = 1; nowTemp = nowTemp + stepTemp; lab.setStyle("color",0x808080); lab.text = "Planet surface temperature = "+nf.format(nowTemp-273.16)+"\xB0C or "+ Math.round(9.*(nowTemp-273.16)/5.+ 32.) +"\xB0F"; if ( (stepTemp >= 0 && nowTemp >= targetTemp) || (stepTemp <= 0 && nowTemp <= targetTemp) ) { lab.setStyle("color",0xffffff); timer.stop(); // check to see if the game is done.... if ( (nowTemp-273.16) > 14.4 && (nowTemp-273.16) < 15.6 && clouds.length > 2) { gameTimer.stop(); gameRunning = false; gameLab.setStyle("color",0xffffff); gameLab.htmlText = "Finished in "+years+" million years!"; if (years < bestYears) bestYears = years; bestLab.text="Best score = "+bestYears+" million years"; cs.setStyle("backgroundColor",0x106040); cs.setStyle("themeColor",0x208020); Alert.show("Congratulations! You made a livable planet in "+years+" million years! \x0a\x0aClick OK to reset and try again!","Completed!",0x4,null,reset); } } } /* dodate(TimerEvent) - draw the "elapsed time" label for the 'game' * */ public function dodate(e:TimerEvent) : void { gameLab.text = " "+years+" million years"; years = years + Math.random()*10 + 1; gameRunning = true; } } }