Repository: noio/kingdom Branch: master Commit: 56d4e622461b Files: 118 Total size: 708.4 KB Directory structure: gitextract_jv9k2wr7/ ├── .gitignore ├── Arrow.as ├── Attention.as ├── Buildable.as ├── Bunny.as ├── CameraTarget.as ├── Campfire.as ├── Castle.as ├── Citizen.as ├── Coin.as ├── CoinFloat.as ├── Coinsack.as ├── Default.css ├── Dust.as ├── ExplodingText.as ├── Farmland.as ├── Firefly.as ├── FlxBackdrop.as ├── FlxBaker.as ├── FlxBumpmap.as ├── FlxMatrixblock.as ├── Fog.as ├── GameOverState.as ├── Haze.as ├── LICENSE.txt ├── Light.as ├── Makefile ├── MenuState.as ├── Minimap.as ├── PlayState.as ├── Player.as ├── Preloader.as ├── Reed.as ├── Scaffold.as ├── Shop.as ├── Sky.as ├── Sparkle.as ├── Splash.as ├── SunMoon.as ├── Torch.as ├── Treeline.as ├── Troll.as ├── Utils.as ├── Wall.as ├── Water.as ├── Weather.as ├── WeatherPresets.as ├── Workable.as ├── assets/ │ ├── levels/ │ │ ├── compiled/ │ │ │ ├── fields.oel │ │ │ ├── fields_alt.oel │ │ │ ├── fields_loose.oel │ │ │ └── fields_old.oel │ │ ├── fields.oel │ │ ├── fields_alt.oel │ │ ├── fields_loose.oel │ │ ├── fields_old.oel │ │ └── ogmoconfig.oep │ └── sound/ │ ├── build.bfxrsound │ ├── cicada.aiff │ ├── hit.bfxrsound │ ├── hitbig.bfxrsound │ ├── hitcitizen.bfxrsound │ ├── hitwall.bfxrsound │ ├── pickup.bfxrsound │ ├── powerup.bfxrsound │ ├── stolen.bfxrsound │ └── throw.bfxrsound ├── com/ │ └── quasimondo/ │ └── geom/ │ └── ColorMatrix.as ├── convert_sounds.sh ├── convert_tiles.py ├── convert_weather.py ├── king.as ├── org/ │ └── flixel/ │ ├── FlxBasic.as │ ├── FlxButton.as │ ├── FlxCamera.as │ ├── FlxEmitter.as │ ├── FlxG.as │ ├── FlxGame.as │ ├── FlxGroup.as │ ├── FlxInputText.as │ ├── FlxObject.as │ ├── FlxParticle.as │ ├── FlxPath.as │ ├── FlxPoint.as │ ├── FlxRect.as │ ├── FlxSave.as │ ├── FlxSound.as │ ├── FlxSprite.as │ ├── FlxState.as │ ├── FlxText.as │ ├── FlxTileblock.as │ ├── FlxTilemap.as │ ├── FlxTimer.as │ ├── FlxU.as │ ├── plugin/ │ │ ├── DebugPathDisplay.as │ │ └── TimerManager.as │ └── system/ │ ├── FlxAnim.as │ ├── FlxDebugger.as │ ├── FlxList.as │ ├── FlxPreloader.as │ ├── FlxQuadTree.as │ ├── FlxReplay.as │ ├── FlxTile.as │ ├── FlxTilemapBuffer.as │ ├── FlxWindow.as │ ├── debug/ │ │ ├── Log.as │ │ ├── Perf.as │ │ ├── VCR.as │ │ ├── Vis.as │ │ ├── Watch.as │ │ └── WatchEntry.as │ ├── input/ │ │ ├── Input.as │ │ ├── Keyboard.as │ │ └── Mouse.as │ └── replay/ │ ├── FrameRecord.as │ └── MouseRecord.as ├── todo.txt └── weathers.json ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ *.swf .DS_Store MochiWrapper.as mochi/ ================================================ FILE: Arrow.as ================================================ package { import flash.geom.Point; import org.flixel.FlxParticle; import org.flixel.FlxG; import org.flixel.FlxObject; import org.flixel.FlxPoint; import org.flixel.FlxSprite; public class Arrow extends FlxParticle{ [Embed(source='/assets/gfx/arrow.png')] private var Img:Class; public var shooter:Citizen = null; public function Arrow(){ super(); loadRotatedGraphic(Img); maxVelocity.x = 200; maxVelocity.y = 275; acceleration.y = 500; offset.x = 3; offset.y = 3; height = 2; width = 2; elasticity = 0.5; } public function shotFrom(from:Citizen, at:FlxObject):void{ x = from.x + from.width/2 + (from.facing == RIGHT ? 6 : -6); y = from.y + 10; revive(); velocity.x = (from.facing == RIGHT ? maxVelocity.x : -maxVelocity.x); velocity.y = - Math.abs(at.x - from.x) + FlxG.random()*40; lifespan = 10; shooter = from; } override public function update():void{ if (y > (FlxG.state as PlayState).water.y){ kill(); var s:Splash = (FlxG.state as PlayState).fx.recycle(Splash) as Splash; s.reset(x,y); } angle = (180/Math.PI) * Math.atan2(velocity.y, velocity.x); } } } ================================================ FILE: Attention.as ================================================ package { import flash.geom.Point; import org.flixel.FlxParticle; import org.flixel.FlxG; import org.flixel.FlxObject; import org.flixel.FlxPoint; import org.flixel.FlxSprite; public class Attention extends FlxParticle{ [Embed(source='/assets/gfx/attention.png')] private var Img:Class; public var citizen:Citizen; public function Attention(){ super(); loadGraphic(Img); maxVelocity.x = 0; maxVelocity.y = 0; height = 8; width = 8; alpha = 0.5; } public function appearAt(at:Citizen):void{ citizen = at; y = at.y - 4; revive(); lifespan = 1; } override public function update():void{ x = citizen.x + citizen.width/2 - 4; super.update() } } } ================================================ FILE: Buildable.as ================================================ package { public interface Buildable { function canBuild():Boolean; function build():void; } } ================================================ FILE: Bunny.as ================================================ package { import flash.geom.Point; import org.flixel.FlxSprite; import org.flixel.FlxG; import org.flixel.FlxObject; import org.flixel.FlxPoint; public class Bunny extends FlxSprite{ [Embed(source='/assets/gfx/bunny.png')] private var Img:Class; public var DECAY_TIME:Number = 5; public var TURN_TIME:Number = 1; public var t:Number = 0; public function Bunny(X:int,Y:int){ super(X,Y); loadGraphic(Img,true,true,16,16); offset.y = 4; height = 12; maxVelocity.y = 100; maxVelocity.x = 30; drag.x = 1000; addAnimation('walk',[0,1,2,3,4,5,6,7,8,9,10],(10+FlxG.random()*5),true); addAnimation('stand',[0],10,true); addAnimation('dead',[11],10,true); y = (FlxG.state as PlayState).groundHeight - 12; } public function getShot(arrow:Arrow):void{ play('dead'); alive = false; velocity.x = 0; t = 0; ((FlxG.state as PlayState).coins.recycle(Coin) as Coin).drop(this, arrow.shooter); } override public function update():void { // Check for movement input acceleration.x = 0; t += FlxG.elapsed ; if (x+width > PlayState.GAME_WIDTH || x < 0){ kill(); } if (alive){ if (t > TURN_TIME){ t = 0; if (FlxG.random() < 0.4) facing = (facing == LEFT) ? RIGHT : LEFT } if(facing == LEFT){ acceleration.x = -maxVelocity.x*4; play('walk'); } else { acceleration.x = maxVelocity.x*4; play('walk'); } super.update(); } else { alpha = 1-(t/DECAY_TIME); if (t > DECAY_TIME){ kill(); } } } } } ================================================ FILE: CameraTarget.as ================================================ package{ import org.flixel.FlxObject; import org.flixel.FlxPoint; import org.flixel.FlxSprite; /** * Class that provides a dummy target for FlxCamera.follow, * and performs tweening and following with an offset. */ public class CameraTarget extends FlxObject{ public var lead:Number = 48; public var speed:FlxPoint = new FlxPoint(0.1, 0.1); public var maxSpeed:FlxPoint = new FlxPoint(20, 20); public var offset:FlxPoint = new FlxPoint(0,0); private var _target:FlxSprite; private var _targetX:Number; private var _targetY:Number; public function CameraTarget(){ super(0,0,1,1); } public function set target(object:FlxSprite):void{ _target = object; } public function get target():FlxSprite{ return _target; } /** * Snap location to target object immediatly, i.e. no tweening */ public function snap():void{ x = _target.x + _target.width/2 + offset.x; y = _target.y + _target.height/2 + offset.y; } override public function update():void{ if (_target == null) return; // Basic target position _targetX = _target.x + _target.width/2 + offset.x; _targetY = _target.y + _target.height/2 + offset.y; // Incorporate the lead if (_target.facing == RIGHT){ _targetX += lead; } else if (_target.facing == LEFT) { _targetX -= lead; } else if (_target.facing == UP) { _targetY -= lead; } else if (_target.facing == DOWN){ _targetY += lead; } // Compute relative movement _targetX = (_targetX - x) * speed.x; _targetY = (_targetY - y) * speed.y; // Cap the speeds if (_targetX >= 1.0) { x += Math.min(maxSpeed.x, _targetX); } else if (_targetX <= -1.0){ x += Math.max(-maxSpeed.x, _targetX); } if (_targetY >= 1.0) { y += Math.min(maxSpeed.y, _targetY); } else if (_targetY <= 1.0) { y += Math.max(-maxSpeed.y, _targetY); } } override public function draw():void{ } } } ================================================ FILE: Campfire.as ================================================ package{ import org.flixel.FlxSprite; import org.flixel.FlxG; public class Campfire extends Light{ [Embed(source='/assets/gfx/campfire.png')] private var CampfireImg:Class; [Embed(source='/assets/gfx/light_large.png')] private var LightLargeImg:Class; [Embed(source='/assets/gfx/light_reflect_wide.png')] private var LightReflectWideImg:Class; public function Campfire(X:Number, Y:Number){ Y -= 12; super(X,Y); offset.x = 16; offset.y = 52; loadGraphic(CampfireImg, true, false, 32, 64); beam.loadGraphic(LightLargeImg); reflected.loadGraphic(LightReflectWideImg); reflected.color = 0xFFfc8f53; addAnimation('on', [0,1,2,3,4,5,6,7], 10, true); addAnimationCallback(this.dim); play('on'); setLight() } } } ================================================ FILE: Castle.as ================================================ package { import flash.geom.Point; import org.flixel.FlxSprite; import org.flixel.FlxGroup; import org.flixel.FlxG; import org.flixel.FlxObject; import org.flixel.FlxPoint; public class Castle extends FlxSprite implements Buildable{ [Embed(source='/assets/gfx/castle.png')] public var CastleImg:Class; public static const POST:int = 0; public static const PLATFORM:int = 1; public static const WATCHTOWER:int = 2; public static const STONETOWER:int = 3; public static const CASTLE:int = 4; public static const BUILD_COOLDOWN:Number = PlayState.CHEATS ? 1 : 15; public static const ARCHER_POSITIONS:Array = [[new FlxPoint(58-32,104-32-12)], [new FlxPoint(48-32,91-32-12),new FlxPoint(136-32,91-32-12)], [new FlxPoint(48-32,91-32-12),new FlxPoint(136-32,91-32-12),new FlxPoint(60,46-32-12)], [new FlxPoint(48-32,91-32-12),new FlxPoint(136-32,91-32-12),new FlxPoint(45,46-32-12),new FlxPoint(75,46-32-12)], [new FlxPoint(48-32,91-32-12),new FlxPoint(136-32,91-32-12), new FlxPoint(38,46-32-12),new FlxPoint(83,46-32-12), new FlxPoint(27,64-32-12),new FlxPoint(93,64-32-12)]]; public static const ARCHER_POSITION_INDEX:Array = [0,0,1,1,1,2,2,2,2,3,3,3,3,3,4] private var playstate:PlayState; private var light:Light; private var lights:FlxGroup; public var t:Number = 0; public var stage:int; public var archers:FlxGroup; public var archer_positions:Array = []; public var capacity:int = 0; public var baseY:Number; public function Castle(X:Number, Y:Number){ super(X,Y); baseY = Y+1; moves = false; playstate = (FlxG.state as PlayState) lights = playstate.lights; archers = playstate.archers; playstate.castle = this; loadGraphic(CastleImg, true, true, 128, 96); addAnimation("stages",[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14],1); morph(POST); light = new Campfire(x+width/2,y+height); lights.add(light); } public function morph(stage:int):void{ archer_positions = ARCHER_POSITIONS[ARCHER_POSITION_INDEX[stage]].slice(); for each (var archer:Citizen in archers.members){ if (archer != null) { archer.leaveGuard(); } } frame = stage; // switch(stage){ // case POST: // loadGraphic(Castle1Img); // break; // case PLATFORM: // loadGraphic(Castle2Img); // break; // case WATCHTOWER: // loadGraphic(Castle3Img); // break; // case STONETOWER: // loadGraphic(Castle4Img); // break; // case CASTLE: // loadGraphic(Castle5Img); // break; // } this.stage = stage; height = 84; y = baseY + 96 - height; offset.y = 96 - height; } public function build():void{ if (stage < ARCHER_POSITION_INDEX.length){ t = 0; Utils.explode(this, playstate.gibs, 0.4); morph(stage + 1); // flicker(); } } public function canBuild():Boolean{ return (t > BUILD_COOLDOWN && stage < 14); } override public function update():void{ t += FlxG.elapsed; playstate.kingdomRight = Math.max(playstate.kingdomRight, x+width) playstate.kingdomLeft = Math.min(playstate.kingdomLeft, x) } } } ================================================ FILE: Citizen.as ================================================ package { import flash.geom.Point; import org.flixel.FlxSprite; import org.flixel.FlxG; import org.flixel.FlxObject; import org.flixel.FlxPoint; import org.flixel.FlxGroup; import org.flixel.FlxSound; public class Citizen extends FlxSprite{ [Embed(source='/assets/gfx/beggar.png')] public static const BeggarImg:Class; [Embed(source='/assets/gfx/citizen.png')] public static const PoorImg:Class; [Embed(source='/assets/gfx/hunter.png')] public static const HunterImg:Class; [Embed(source='/assets/gfx/farmer.png')] public static const FarmerImg:Class; [Embed(source="/assets/sound/shoot.mp3")] public static const ShootSound:Class; [Embed(source="/assets/sound/powerup.mp3")] public static const PowerupSound:Class; [Embed(source="/assets/sound/hitcitizen.mp3")] public static const HitSound:Class; public static var shootSound:FlxSound = FlxG.loadSound(ShootSound); public static const BASE_COLOR:uint = 0xFF567271; public static const BASE_SHADE:uint = 0xFF394b4a; public static const BASE_SKIN:uint = 0xFFedbebf; public static const BASE_DARK:uint = 0xFFbd9898; public static const BASE_EYES:uint = 0xFFa18383; public static const BEGGAR:int = 0; public static const POOR:int = 1; public static const FARMER:int = 2; public static const HUNTER:int = 3; // Behaviors public static const IDLE:int = 0; public static const SHOOT:int = 1; public static const JUST_SHOT:int = 2; public static const SHOVEL:int = 3; public static const GIVE:int = 4; public static const JUST_HACKED:int = 5; public static const COWER:int = 6; // Behavior times public static const SHOOT_COOLDOWN_GUARD:Number = 1.4; public static const SHOOT_COOLDOWN:Number = 2.0; public static const HACK_COOLDOWN:Number = 4.0; public static const SHOVEL_PERIOD:Number = 4.0; public static const SHOVEL_TIME:Number = 1.0; public static const SHOVEL_GOAL_DIST:Number = 600; public static const GIVE_COOLDOWN:Number = 10.0; public static const COWER_COOLDOWN:Number = 5.0; // Other consts public static const HUNTER_BORDER_RANGE:Number = 256; public static const MAX_HUNGRY:Number = 5; // Variables public var occupation:int = BEGGAR; public var action:int = IDLE; public var guarding:Boolean = false; public var t:Number = 0; public var goal:Number; public var myColor:uint; public var skin:uint; public var coins:int = 0; public var giveCooldown:Number = 0; public var shovelCooldown:Number = 0; public var target:FlxObject; public var guardLeftBorder:Boolean; public var hungry:int = 0; public var playstate:PlayState; public var castle:Castle; public function Citizen(X:int,Y:int){ super(X,Y); goal = FlxG.worldBounds.width/2; drag.x = 500; guardLeftBorder = (FlxG.random() > 0.5); myColor = Utils.HSVtoRGB(FlxG.random()*360, 0.1+FlxG.random()*0.2, 0.6); var d:Number = Math.random() * 20; skin = Utils.HSVtoRGB(d, 0.19 + (d / 100), 0.97 - (d / 33)); playstate = FlxG.state as PlayState; castle = playstate.castle; addAnimationCallback(this.animationFrame); morph(BEGGAR); } public function morph(occ:int):Citizen{ action = IDLE; _animations = new Array(); if (occ != BEGGAR && coins <= 0){ coins ++; } switch(occ){ case BEGGAR: if (occupation != BEGGAR) playstate.beggars.add(playstate.characters.remove(this,true)); loadGraphic(BeggarImg,true,true,32,32,true); addAnimation('walk',[0,1,2,3,4,5],5,true); addAnimation('idle',[7,8,7,8,7,6],2,true); addAnimation('cower',[9,10],2,false); maxVelocity.x = 15; hungry = 0; break; case POOR: if (occupation == BEGGAR) playstate.characters.add(playstate.beggars.remove(this,true)); loadGraphic(PoorImg,true,true,32,32,true); Utils.replaceColor(pixels, BASE_COLOR, myColor); Utils.replaceColor(pixels, BASE_SHADE, Utils.interpolateColor(myColor,0xFF000000,0.2)); maxVelocity.x = 17; addAnimation('walk',[0,1,2,3,4,5],10,true); addAnimation('idle',[0,6,0,6,0,7],2,true); break; case HUNTER: if (guardLeftBorder){ myColor = Utils.HSVtoRGB(220 + FlxG.random() * 20, 0.2+FlxG.random()*0.3, 0.7); } else { myColor = Utils.HSVtoRGB(0 + FlxG.random() * 20, 0.2+FlxG.random()*0.3, 0.7); } loadGraphic(HunterImg,true,true,32,32,true); Utils.replaceColor(pixels, BASE_COLOR, myColor); Utils.replaceColor(pixels, BASE_SHADE, Utils.interpolateColor(myColor,0xFF000000,0.2)); maxVelocity.x = 18; addAnimation('walk',[0,1,2,3,4,5],10,true); addAnimation('idle',[6,7,6,7,6,8],2,true); addAnimation('shoot',[9,10,0],6,false); addAnimation('give',[11,12,13],15,false); break; case FARMER: loadGraphic(FarmerImg,true,true,32,32,true); Utils.replaceColor(pixels, BASE_COLOR, myColor); Utils.replaceColor(pixels, BASE_SHADE, Utils.interpolateColor(myColor,0xFF000000,0.2)); maxVelocity.x = 21 + Math.random() * 3; addAnimation('walk',[0,1,2,3,4,5],12,true); addAnimation('idle',[6,7,6,7,6,8],2,true); addAnimation('shovel',[8,9,10,9],6,true) addAnimation('give',[11,12,13],15,false); addAnimation('hack',[14],15,false); break; } Utils.replaceColor(pixels, BASE_SKIN, skin); Utils.replaceColor(pixels, BASE_DARK, Utils.interpolateColor(skin,0xFF000000,0.2)); Utils.replaceColor(pixels, BASE_EYES, Utils.interpolateColor(skin,0xFF000000,0.5)); drawFrame(true); occupation = occ; offset.x = 12; offset.y = 8; width = 8; height = 24; pickNewGoal(); return this; } public function pickup(coin:FlxObject):void{ if (!coin.alive) return; var c:Coin = coin as Coin; // Return if the coin doesn't belong to me. if (c.owner != null && c.owner != this){ return; } c.kill(); // flicker(); var s:Sparkle = (FlxG.state as PlayState).fx.recycle(Sparkle) as Sparkle; s.reset(x-4, y+8); if (occupation == BEGGAR) { playstate.recruitedCitizen = true; morph(POOR); FlxG.play(PowerupSound).proximity(x, y, playstate.player, FlxG.width * 0.75) } coins ++; } public function giveTaxes(p:Player):void{ if (occupation == HUNTER || occupation == FARMER){ if (action == IDLE && coins > 3 && giveCooldown <= 0){ action = GIVE; coins -= 2; play('give'); p.changeCoins(1); giveCooldown = GIVE_COOLDOWN; } } } public function hitByTroll(troll:Troll):void{ // Farmers can defend. if (occupation == FARMER && action != JUST_HACKED){ action = JUST_HACKED; play("hack"); t = 0; troll.getShot(); } else if (coins > 0 && !troll.hasCoin){ (playstate.coins.recycle(Coin) as Coin).drop(this, playstate.player); FlxG.play(HitSound).proximity(x, y, playstate.player, FlxG.width); coins = (coins > 1) ? 1 : 0; Utils.explode(this, playstate.gibs); if (coins == 0){ morph(BEGGAR); } else if (coins == 1){ morph(POOR); } } if (occupation == BEGGAR && action == IDLE){ action = COWER; play('cower', true); } } public function checkShootable(group:FlxGroup):void{ var c:FlxObject; for (var i:int = 0; i < group.length; i++){ c = group.members[i]; if (c != null && c.alive && c.exists && Math.abs(c.x - x) < 96){ // FlxG.log("Shooting "+c+" at "+c.x+','+c.y); play('shoot', true); shootSound.play(false); shootSound.proximity(x, y, playstate.player, FlxG.width); // walk 1 pixel towards goal, just to get // the facing right goal = (c.x > x) ? x + 1 : x - 1; facing = (goal > x) ? RIGHT : LEFT; target = c; action = SHOOT; t = 0; break; } } } public function checkWork(group:FlxGroup):void{ var c:FlxObject; for (var i:int = 0; i < group.length; i ++){ c = group.members[i]; if (c != null){ if (x > c.x && x+width < c.x+c.width){ if ((c as Workable).needsWork()){ (c as Workable).work(this); play('shovel',true); action = SHOVEL; shovelCooldown = SHOVEL_PERIOD; t = 0; } } } } } public function checkGuard():void{ if (action == IDLE && castle.archer_positions.length > 0){ if (Math.abs(castle.x-x) < 192) { for (var i:int = 0; i < castle.archer_positions.length; i++){ var pos:FlxPoint = castle.archer_positions[i]; if(Math.abs(castle.x+pos.x-x) < 4){ x = castle.x + pos.x; y = castle.y + pos.y; guarding = true; playstate.archers.add(playstate.characters.remove(this,true)); castle.archer_positions.splice(i,1); break; } } } } } public function leaveGuard():void{ castle.archer_positions.push(new FlxPoint(x,y)); playstate.characters.add(playstate.archers.remove(this)); action == IDLE; guarding = false; } public function pickNewGoal(preset:Number = NaN):void{ //TODO !!! Hunters don't target well at night var a:Attention = playstate.fx.recycle(Attention) as Attention; a.appearAt(this); if (!isNaN(preset)){ goal = preset; return } if (occupation == POOR){ var shop:Shop = (playstate.shops.getRandom() as Shop); goal = shop.x + shop.width/2; return; } if (coins > 4){ goal = playstate.player.x; return; } if (occupation == FARMER) { // Otherwise check for a wall to work on var needWork:Array = new Array(); var dist:Number = Number.MAX_VALUE; var wall:Wall, closestWall:Wall = null; for (var i:int = 0; i < playstate.walls.length; i++){ wall = playstate.walls.members[i] as Wall; if (wall != null && wall.needsWork() && (Math.abs(wall.x - x) < dist)){ closestWall = wall; dist = Math.abs(wall.x - x); } } if (closestWall != null){ goal = closestWall.x + closestWall.width / 2; return; } } var l:int, r:int; if (occupation == HUNTER) { // Hunters gather around borders at night if (playstate.weather.timeOfDay >= 0.65 || playstate.weather.timeOfDay < 0.20){ if (guardLeftBorder){ l = playstate.kingdomLeft; r = playstate.kingdomLeft + 32; } else { l = playstate.kingdomRight - 32; r = playstate.kingdomRight; } } else { l = playstate.kingdomLeft - HUNTER_BORDER_RANGE; r = playstate.kingdomRight + HUNTER_BORDER_RANGE; } } else if (occupation == BEGGAR){ // Beggars gather outside borders if (playstate.beggars.countLiving() > playstate.minBeggars){ hungry ++; if (hungry > MAX_HUNGRY){ Utils.explode(this, playstate.gibs, 1.0); kill(); } } if (x < PlayState.GAME_WIDTH/2){ l = playstate.kingdomLeft - 256; r = playstate.kingdomLeft; } else { l = playstate.kingdomRight; r = playstate.kingdomRight + 256; } } else { // Move anywhere within the kingdom l = playstate.kingdomLeft; r = playstate.kingdomRight; } goal = int(FlxG.random()*(r-l) + l); /*FlxG.log("Citizen (" + occupation + ") picked goal " + goal)*/ } public function animationFrame(animName:String, frameNum:uint, frameIndex:uint):void{ if (animName == 'give' && frameNum == 2){ action = IDLE; play('idle'); } if (animName == 'shovel'){ var d:Dust = playstate.fx.recycle(Dust) as Dust; d.reset(x + ((facing == RIGHT) ? 14 : -6), y + 19); } } override public function update():void { acceleration.x = 0; t += FlxG.elapsed; shovelCooldown -= FlxG.elapsed; giveCooldown -= FlxG.elapsed; // IDLE MOVING AROUND if(guarding && occupation == HUNTER){ play('idle'); facing = (goal > x) ? RIGHT : LEFT; } else if (action == IDLE){ facing = (goal > x) ? RIGHT : LEFT; // Near Goal if (Math.abs(goal - x) < 2){ if (t > 2.0 && FlxG.random() < 0.3) { t = 0; pickNewGoal(); } else { play('idle'); } // Far away from goal } else { play('walk'); acceleration.x = (facing == RIGHT) ? maxVelocity.x*10 : -maxVelocity.x*10; y = playstate.groundHeight - height; } } // Specific Behavior if (occupation == HUNTER){ // Shooting cycle if (action == SHOOT && t > 0.16){ (playstate.arrows.recycle(Arrow) as Arrow).shotFrom(this, target); t = 0; action = JUST_SHOT; } else if (action == JUST_SHOT && t > (guarding ? SHOOT_COOLDOWN_GUARD : SHOOT_COOLDOWN)){ t = 0; action = IDLE; } else if (action == IDLE){ checkShootable(playstate.trolls); checkShootable(playstate.trollsNoCollide); // Check for idle again since we could be shooting a Troll. if (action == IDLE){ checkShootable(playstate.bunnies); } } // Check if we need to take up a guard post. checkGuard(); } else if (occupation == FARMER){ if (action == JUST_HACKED && t > HACK_COOLDOWN ){ t = 0; action = IDLE; } if (shovelCooldown <= 0 && action == IDLE) { checkWork(playstate.walls); checkWork(playstate.farmlands); } else if (action == SHOVEL && t > SHOVEL_TIME){ t = 0; action = IDLE; } } else if (occupation == BEGGAR){ if (action == COWER && t > COWER_COOLDOWN){ t = 0; action = IDLE; } } super.update(); } } } ================================================ FILE: Coin.as ================================================ package { import flash.geom.Point; import org.flixel.FlxParticle; import org.flixel.FlxG; import org.flixel.FlxObject; import org.flixel.FlxPoint; import org.flixel.FlxSprite; public class Coin extends FlxParticle{ [Embed(source='/assets/gfx/coin.png')] private var Img:Class; public static const TOTAL_LIFESPAN:Number = 25; public static const OWNER_LIFESPAN:Number = 4; public var owner:FlxObject = null; public var justThrown:Boolean = false; public var called:Boolean = false; public function Coin(){ super(); loadGraphic(Img,true,false,10,10); maxVelocity.x = 20; maxVelocity.y = 275; acceleration.y = 900; addAnimation('spin',[0,1,2,3,4,5,6,7],10,true); play('spin'); elasticity = 0.5; } public function drop(from:FlxSprite, owner:FlxObject=null, far:Boolean=false):Coin{ reset(from.x + from.width/2 - 5, Math.max(40, from.y - 10)); lifespan = TOTAL_LIFESPAN; if (far){ velocity.x = FlxG.random()*140 - 70; velocity.y = -180; } else { velocity.x = FlxG.random()*60 - 30; velocity.y = -180; } called = false; this.owner = owner; if (owner != null && owner is Citizen){ (owner as Citizen).pickNewGoal(this.x + this.width/2 + this.velocity.x) } return this; } override public function update():void{ if (!called && lifespan <= TOTAL_LIFESPAN - OWNER_LIFESPAN / 2) { justThrown = false; var cit:Citizen = owner as Citizen; if (cit){ called = true; justThrown = false; flicker(); cit.pickNewGoal(x+width/2); } } if (owner != null && lifespan <= TOTAL_LIFESPAN - OWNER_LIFESPAN){ flicker(); owner = null; } super.update() } } } ================================================ FILE: CoinFloat.as ================================================ package { import flash.geom.Point; import org.flixel.FlxG; import org.flixel.FlxObject; import org.flixel.FlxPoint; import org.flixel.FlxSprite; public class CoinFloat extends FlxSprite{ [Embed(source='/assets/gfx/coin_drop.png')] private var Img:Class; public function CoinFloat(){ super(); loadGraphic(Img,true,false,10,24); addAnimation('spin',[0,1,2,3,4,5,6,7],10,true); play('spin'); } public function float(above:FlxSprite):void{ acceleration.y = 0; x = above.x + above.width/2 - width/2; y = Math.max(above.y - height - 4, 40); above.color = 0xCCCC00; // above.flicker(); } } } ================================================ FILE: Coinsack.as ================================================ package { import org.flixel.FlxSprite; import org.flixel.FlxG; public class Coinsack extends FlxSprite{ [Embed(source='/assets/gfx/sack.png')] private var Img:Class; public static const FADE_TIME:Number = 20; public static var t:Number = 0; public function Coinsack(X:Number=0, Y:Number=0){ super(X, Y); loadGraphic(Img,true,false,16,16); scrollFactor.x = scrollFactor.y = 0; addAnimation('blink',[8,0,8,0,8,0,8,0,8,0],5,false); } public function show(c:int):void{ if (c == 0) { play('blink', true); } else if (c == 1) { frame = 1; } else if (c >= 2 && c <= 3){ frame = 2; } else if (c >= 4 && c <= 5){ frame = 3; } else if (c >= 6 && c <= 8){ frame = 4; } else if (c >= 9 && c <= 11){ frame = 5; } else if (c >= 12 && c <= 15){ frame = 6; } else if (c >= 15){ frame = 7 } t = 0; } override public function update():void{ t += FlxG.elapsed; alpha = FADE_TIME - t; } } } ================================================ FILE: Default.css ================================================ Add this: "-defaults-css-url Default.css" to the project's additonal compiler arguments. ================================================ FILE: Dust.as ================================================ package { import flash.geom.Point; import org.flixel.FlxParticle; import org.flixel.FlxG; import org.flixel.FlxObject; import org.flixel.FlxPoint; import org.flixel.FlxSprite; public class Dust extends FlxParticle{ [Embed(source='/assets/gfx/dust.png')] private var Img:Class; public function Dust(){ super(); loadGraphic(Img,true); addAnimation('fade',[0,1,2,3], 5, false); drag.x = drag.y = 20; } override public function reset(X:Number, Y:Number):void{ super.reset(X,Y); x += (Math.random() < 0.5) ? 4 : -4; velocity.x = Math.random() * 40 - 20; velocity.y = - Math.random() * 20 - 4; lifespan = 1.0; play('fade', true); } } } ================================================ FILE: ExplodingText.as ================================================ package { import org.flixel.FlxSprite; import org.flixel.FlxGroup; import org.flixel.FlxG; import org.flixel.FlxPoint; public class ExplodingText extends FlxGroup{ public function ExplodingText(){ super(0,0); } } } ================================================ FILE: Farmland.as ================================================ package { import flash.geom.Point; import org.flixel.FlxSprite; import org.flixel.FlxG; import org.flixel.FlxObject; import org.flixel.FlxPoint; public class Farmland extends FlxSprite implements Workable{ [Embed(source='/assets/gfx/farmland.png')] private var FarmlandImg:Class; public static const WORK_COOLDOWN:Number = 8; private var t:Number = 0; public function Farmland(X:int, Y:int){ super(X,Y); loadGraphic(FarmlandImg, true, false, 64, 32); addAnimation("grow",[0,1,2,3,4,5,6,7],10); } public function needsWork():Boolean { return t > WORK_COOLDOWN; } public function work(by:Citizen=null):void{ t = 0; if (frame == 7){ ((FlxG.state as PlayState).coins.recycle(Coin) as Coin).drop(this, by); ((FlxG.state as PlayState).coins.recycle(Coin) as Coin).drop(this, by); ((FlxG.state as PlayState).coins.recycle(Coin) as Coin).drop(this, by); ((FlxG.state as PlayState).coins.recycle(Coin) as Coin).drop(this, by); by.pickNewGoal(x+width/2); } frame = (frame + 1) % 8; } override public function update():void{ t += FlxG.elapsed; } } } ================================================ FILE: Firefly.as ================================================ package{ import org.flixel.FlxSprite; import org.flixel.FlxPoint; import org.flixel.FlxG; public class Firefly extends Light{ [Embed(source='/assets/gfx/light_small.png')] private var LightSmallImg:Class; [Embed(source='/assets/gfx/light_reflect_small.png')] private var LightReflectSmallImg:Class; public static const COLOR:uint = 0xFF7affa0; public static const MAX_DIST:Number = 100; private var weatherChanged:Number = 0; private var startPos:FlxPoint = new FlxPoint(); public function Firefly(X:Number, Y:Number){ super(X,Y); startPos.x = X; startPos.y = Y; offset.x = 0; offset.y = 0; makeGraphic(1,1,COLOR,false); beam.loadGraphic(LightSmallImg); reflected.loadGraphic(LightReflectSmallImg); reflected.color = COLOR; addAnimation('on', [0], 6, true); addAnimationCallback(this.dim); play('on'); setLight(); } override public function update():void{ // Move around a bit velocity.x += (FlxG.random() - 0.5) * 0.5; velocity.y += (FlxG.random() - 0.5) * 0.2; if (Math.abs(startPos.x - x) > MAX_DIST || Math.abs(startPos.y - y) > MAX_DIST){ x = startPos.x; y = startPos.y; velocity.x = 0; velocity.y = 0; } if (weather.changed > weatherChanged) { beam.alpha = 1.5 * (weather.darkness) * (1 - weather.wind); beam.drawFrame(true); alpha = beam.alpha; if (alpha < 0.1 && visible) { if (FlxG.random() < 0.05) visible = false; } if (alpha >= 0.1 && !visible){ if (FlxG.random() < 0.05) visible = true } weatherChanged = weather.t; } super.update() } } } ================================================ FILE: FlxBackdrop.as ================================================ package{ import flash.geom.Point; import flash.display.BitmapData; import org.flixel.FlxSprite; import org.flixel.FlxGroup; import org.flixel.FlxG; public class FlxBackdrop extends FlxSprite{ /** * A class that renders a single "backdrop" image repeatedly when drawn. * Depending on the scrollfactor, the backdrop will move along when the * camera moves. Multiple backdrop layers can be used to easily create * a parralax effect. */ private var w:int; public function FlxBackdrop(graphicClass:Class, XScroll:Number=0.333, YScroll:Number=0.2, Color:uint=0xFF474a3d):void{ var graphic:BitmapData = FlxG.addBitmap(graphicClass); w = graphic.width; makeGraphic(w + FlxG.width, graphic.height, 0x00000000, true); this.y = (FlxG.height - graphic.height)/2 // Copy the graphic's pixels to this sprite pixels, adding an extra "fold" var p:Point = new Point(0,0); pixels.copyPixels(graphic, graphic.rect, p); p.x = w; pixels.copyPixels(graphic, graphic.rect, p); dirty = true; scrollFactor.x = XScroll; scrollFactor.y = YScroll; this.color = Color; } override public function update():void{ getScreenXY(_point); if (_point.x < -w){ x += w; } else if (_point.x > 0){ x -= w; } super.update(); } } } ================================================ FILE: FlxBaker.as ================================================ package{ import flash.geom.Rectangle; import flash.geom.Point; import flash.display.BitmapData; import flash.utils.getQualifiedClassName import org.flixel.FlxSprite; import org.flixel.FlxG; public class FlxBaker{ private static const zeroPoint:Point = new Point(0,0); private static var _baked:Object = new Object(); public static function bake(sprite:FlxSprite, key:String='base'){ var id:String = getQualifiedClassName(sprite)+"@("+int(sprite.x)+','+int(sprite.y)+')'+key; var bmp:BitmapData = new BitmapData(sprite.pixels.width,sprite.pixels.height); bmp.copyPixels(sprite.pixels, new Rectangle(0,0,sprite.frameWidth,sprite.frameHeight),zeroPoint,null,null,true); _baked[id] = bmp; } } } ================================================ FILE: FlxBumpmap.as ================================================ package{ import flash.display.BitmapData; import flash.filters.ConvolutionFilter; import flash.geom.Rectangle; import flash.geom.Point; import org.flixel.FlxSprite; public class FlxBumpmap extends FlxSprite { private static var filter:ConvolutionFilter = new ConvolutionFilter(3,3,null,1,0,true,false,0xFF808080); private static var rect:Rectangle; private static var zeroPoint:Point = new Point(0,0); public function FlxBumpmap(){ super(); } public function light(target:FlxSprite, color:uint=0x55FFFFCC, blend:String="normal"):FlxBumpmap{ process(target, [0,0,-1,0,2,-1,0,0,0], color, blend); return this } public function shade(target:FlxSprite, color:uint=0x66333355, blend:String="normal"):FlxBumpmap{ var m:Array = [0,0, 0,0,1, 0,0, 0,1,0, 0,0,-2,0,0, 0,0, 0,0,0, 0,0, 0,0,0] process(target, m, color, blend); return this; } protected function process(target:FlxSprite, matrix:Array, color:uint, blend:String="normal"):void{ rect = new Rectangle(0,0,pixels.width,pixels.height); filter.matrixX = filter.matrixY = Math.sqrt(matrix.length); filter.matrix = matrix; var regions:BitmapData = new BitmapData(pixels.width,pixels.height); regions.applyFilter(pixels,rect,zeroPoint,filter); regions.threshold(regions,rect,zeroPoint,">",0x000000,color,0x00FFFFFF); regions.threshold(regions,rect,zeroPoint,"==",0x000000,0x000000,0x00FFFFFF); target.pixels.draw(regions,null,null,blend); target.dirty = true; regions.dispose(); } public static function lightFlatSprite(target:FlxSprite, color:uint=0x55FFFFCC, blend:String="normal"):void{ processFlatSprite(target, [0,0,-1,0,2,-1,0,0,0], color, blend); } public static function processFlatSprite(target:FlxSprite, matrix:Array, color:uint, blend:String="normal"):void{ rect = new Rectangle(0,0,target.pixels.width,target.pixels.height); filter.matrixX = filter.matrixY = Math.sqrt(matrix.length); filter.matrix = matrix; var regions:BitmapData = target.pixels.clone(); regions.threshold(regions,rect,zeroPoint,">",0x00FFFFFF,0xFFFFFFFF,0xFFFFFFFF); regions.applyFilter(regions,rect,zeroPoint,filter); regions.threshold(regions,rect,zeroPoint,">",0x000000,color,0x00FFFFFF); regions.threshold(regions,rect,zeroPoint,"==",0x000000,0x000000,0x00FFFFFF); target.pixels.draw(regions,null,null,blend); target.dirty = true; regions.dispose(); } } } ================================================ FILE: FlxMatrixblock.as ================================================ package { import org.flixel.FlxSprite; import org.flixel.FlxG; import flash.display.BitmapData; import flash.geom.Rectangle; import flash.geom.Point; /** * This is an extension to flixel's standard Tileblock class. It autofills the block * with a given tilematrix () * It can be filled with a random selection of tiles to quickly add detail. */ public class FlxMatrixblock extends FlxSprite { static public const T:uint = 1; static public const R:uint = 2; static public const B:uint = 4; static public const L:uint = 8; public var tileWidth:int; public var tileHeight:int; public var widthInTiles:int; public var heightInTiles:int; public var tiles:Vector.> = new Vector.>(16) public var mapping:Array; public var refresh:Boolean = true; // Set to true to re-render. protected var _tileBitmap:BitmapData; protected var _bumpmap:BitmapData; public function FlxMatrixblock(X:int, Y:int, Width:uint, Height:uint){ super(X,Y); makeGraphic(Width,Height,0xFF000000,true); moves = false; } public function loadTilematrix(MatrixGraphic:Class,TileWidth:uint,TileHeight:uint,TileSides:Array=null,BumpmapGraphic:Class=null):FlxMatrixblock{ _tileBitmap = FlxG.addBitmap(MatrixGraphic); var mWidth:int = _tileBitmap.width / TileWidth; var mHeight:int = _tileBitmap.height / TileHeight; tileWidth = TileWidth; tileHeight = TileHeight; widthInTiles = Math.floor(this.width / TileWidth); heightInTiles = Math.floor(this.height / TileHeight) //Some default presets for tile mappings var i:int, j:int if (TileSides == null){ // Automap, assume closed/seamless image var tileType:uint; this.mapping = [] for (j = 0; j < mHeight; j++){ for (i = 0; i < mWidth; i++){ tileType = 0; if (i > 0) tileType += L; if (i < mWidth-1) tileType += R; if (j > 0) tileType += T; if (j < mHeight-1) tileType += B; this.mapping.push(tileType); } } } else {this.mapping = TileSides;} // Create rectangles for each tile in the set for (i = 0; i < mWidth; i++){ for (j = 0; j < mHeight; j++){ var m:uint = mapping[j*mWidth+i]; if (tiles[m] == null){ tiles[m] = new Vector.(); } tiles[m].push(new Rectangle(i*tileWidth,j*tileHeight,tileWidth,tileHeight)); } } this.renderTiles(); return this; } public function renderTiles():void{ fill(0); // Fill with transparent color; var tileType:uint = 0; var srcRect:Rectangle; var tgtPoint:Point = new Point(0,0); var tileOpts:Vector. for (var i:int = 0; i < widthInTiles; i ++){ for (var j:int = 0; j < heightInTiles; j ++){ tileType = 0; tgtPoint.x = i*tileWidth; tgtPoint.y = j*tileHeight; if (i > 0) {tileType += L;} if (i < widthInTiles-1) {tileType += R}; if (j > 0) {tileType += T}; if (j < heightInTiles-1) {tileType += B}; tileOpts = tiles[tileType]; if (tileOpts != null){ srcRect = tileOpts[Math.floor(FlxG.random()*tileOpts.length)]; _pixels.copyPixels(_tileBitmap,srcRect,tgtPoint,null,null,true); } } } pixels = pixels; } } } ================================================ FILE: Fog.as ================================================ package { import org.flixel.FlxSprite; import org.flixel.FlxGroup; import org.flixel.FlxObject; import org.flixel.FlxG; import org.flixel.FlxPoint; public class Fog extends FlxGroup{ [Embed(source='/assets/gfx/fog.png')] private const FogImg:Class; public static const MAXFOG:int = 5; private var weather:Weather; private var weatherChanged:Number = -1; private var _fg:FlxSprite; private var _point:FlxPoint = new FlxPoint(); public function Fog(weather:Weather){ super(MAXFOG) this.weather = weather; for (var i:int = 0; i < MAXFOG; i++){ _fg = new FlxSprite(0,0).loadGraphic(FogImg,true,true,256,96); _fg.scrollFactor.y = 1.2; _fg.scrollFactor.x = (FlxG.random() < 0.5) ? 1.5 : 2.5; _fg.facing = (FlxG.random() < 0.5) ? FlxObject.LEFT : FlxObject.RIGHT; _fg.frame = int(FlxG.random()*4); _fg.kill(); add(_fg); } } override public function update():void{ for (var i:int = 0; i < members.length; i++){ _fg = members[i] if (_fg.exists){ _fg.getScreenXY(_point) if (_point.x + _fg.width < -100 || _point.x > FlxG.width + 100) { _fg.kill(); } else { _fg.velocity.x = -weather.wind*20; } } } if (weather.changed > weatherChanged){ // TODO: Does this run every frame? if (countLiving() < MAXFOG * weather.fog) { _fg = getFirstAvailable() as FlxSprite; _fg.reset(0,0); if (FlxG.random() < 0.5){ _fg.x = FlxG.camera.scroll.x*_fg.scrollFactor.x - _fg.width; } else { _fg.x = (FlxG.camera.scroll.x)*_fg.scrollFactor.x + FlxG.width } _fg.y = 112 + 50*FlxG.random(); var comp:uint = (1 - weather.darkness)*255; var color:uint = comp << 16 | comp << 8 | comp; _fg.color = color; _fg.alpha = weather.fog/6 + 0.3; } weatherChanged = weather.t; } super.update(); } } } ================================================ FILE: GameOverState.as ================================================ package { import org.flixel.*; import flash.ui.Mouse; import mochi.as3.MochiScores; public class GameOverState extends FlxState { private var nights:int = 0; public function GameOverState(nightsSurvived:int){ this.nights = nightsSurvived; } override public function create():void { var t:FlxText; t = new FlxText(0,10,FlxG.width,"Game Over"); t.size = 16; t.alignment = "center"; add(t); t = new FlxText(0,FlxG.height-20,FlxG.width,"click to retry"); t.alignment = "center"; add(t); t = new FlxText(0,32,FlxG.width,"'Kingdom' by noio"); t.alignment = "center"; add(t); FlxG.stage.displayState = 'normal'; var o:Object = { n: MochiWrapper.SCOREBOARD_ID, f: function (i:Number,s:String):String { if (s.length == 16) return s; return this.f(i+1,s + this.n[i].toString(16));}}; var boardID:String = o.f(0,""); MochiScores.showLeaderboard({boardID: boardID, score: nights, onDisplay: onLeaderboardDisplay, onClose: onLeaderboardClose}); } private function onLeaderboardDisplay():void{ FlxG.mouse.hide(); Mouse.show(); } private function onLeaderboardClose():void{ FlxG.mouse.show(); Mouse.hide(); } override public function update():void { super.update(); if(FlxG.mouse.justPressed()) { FlxG.mouse.hide(); FlxG.switchState(new MenuState()); MochiScores.closeLeaderboard(); } } } } ================================================ FILE: Haze.as ================================================ package{ import org.flixel.FlxSprite; import org.flixel.FlxG; public class Haze extends FlxSprite{ private var weather:Weather; private var weatherChanged:Number = -1; public function Haze(X:int, Y:int, weather:Weather){ super(X,Y); this.weather = weather; makeGraphic(FlxG.width,FlxG.height,0x00000000); scrollFactor.x = 0; } override public function draw():void{ if (weather.changed > weatherChanged){ fill(0); Utils.gradientOverlay(pixels,[weather.haze&0xFFFFFF,weather.haze], 90,1); weatherChanged = weather.t; dirty = true; } super.draw(); } } } ================================================ FILE: LICENSE.txt ================================================ Copyright (c) 2013 Thomas "noio" van den Berg Permission is granted to any person obtaining a copy of this code and the accompanying assets, and the software resulting from compilation (together the "Software") to copy, modify, distribute and publish the Software under the following conditions: * The Software shall not be used for commercial purposes, including sale of the Software, publication of the Software with advertisements, or publication of the Software with the possibility of in-game purchases. * The Software may not be published for mobile devices, including Apple iPhone, iPad and iPod, Windows Phones, or Android phones. Publication on mobile platforms in a modified form shall only be done with permission from the copyright holder. * For any reuse or distribution, this licence shall be included in all copies or substantial portions of the Software. Note from the author: The reason I included this license is not to limit anyone in their option to learn from, modify, and show off this code, but only to prevent people making a quick buck by compiling the game for mobile devices and selling it. I'd also hate to see clones of the Flash version with ads plastered all over them. If you make an entirely new game out of the assets here, I'm fine with that too, and you can go ahead and make as much money off of that as you want. So please, go ahead and download the code, mess with it, and show the results to all your friends (and me! :). ================================================ FILE: Light.as ================================================ package{ import org.flixel.FlxSprite; import org.flixel.FlxG; public class Light extends FlxSprite{ [Embed(source='/assets/gfx/campfire.png')] private var CampfireImg:Class; [Embed(source='/assets/gfx/torch.png')] private var TorchImg:Class; [Embed(source='/assets/gfx/light_mid.png')] private var LightMidImg:Class; [Embed(source='/assets/gfx/light_large.png')] private var LightLargeImg:Class; [Embed(source='/assets/gfx/light_reflect_small.png')] private var LightReflectSmallImg:Class; [Embed(source='/assets/gfx/light_reflect_wide.png')] private var LightReflectWideImg:Class; public var beam:FlxSprite = new FlxSprite(); public var reflected:FlxSprite = new FlxSprite(); public var darkness:FlxSprite; public var burning:Boolean; public var playstate:PlayState; public var weather:Weather; public function Light(X:Number, Y:Number){ super(X,Y); this.playstate = FlxG.state as PlayState this.darkness = this.playstate.darkness; this.weather = this.playstate.weather; } /* Performs some additional settings that can only be done * after the extending class' constructor is done. */ public function setLight():void{ beam.blend = 'screen'; } override public function update():void{ getScreenXY(_point) burning = (-128 < _point.x && _point.x < FlxG.width + 128); } override public function draw():void{ if(burning){ getScreenXY(_point); darkness.stamp(beam, Math.floor(_point.x - beam.width/2), Math.floor(_point.y - beam.height/2)); } super.draw(); } public function dim(animName:String,frameNumber:uint,frameIndex:uint):void{ if (burning){ beam.alpha += FlxG.random()*0.15 - 0.075; if (beam.alpha < 0.3){ beam.alpha += 0.01; } beam.drawFrame(true); } } } } ================================================ FILE: Makefile ================================================ # term makefile # # files and directories # BINDIR = . SOURCE = ./king.as TARGET = $(BINDIR)/main.swf # # compiler and debugger setup # COMPILER = /Developer/SDKs/flex_sdk_4.6.0/bin/mxmlc DEBUGGER = /Developer/SDKs/flex_sdk_4.6.0/bin/fdb ARGS_COMMON = -source-path . -file-specs $(SOURCE) -o $(TARGET) -static-link-runtime-shared-libraries -strict -headless-server=true ARGS_DEBUG = -debug=true -define=CONFIG::debugging,true ARGS_RELEASE = -debug=false -define=CONFIG::debugging,false # if verbose=1 is supplied on the command line, then we will display the # command lines executed ifeq ($(verbose),1) export EC = else export EC = @ endif # # targets # all: echo Switch the two preloaders in king.as to compile without MochiAds support. # ./convert_sounds.sh python convert_tiles.py python convert_weather.py $(EC)mkdir -p $(BINDIR) $(EC)rm -rf $(TARGET) $(EC)$(COMPILER) $(ARGS_COMMON) $(ARGS_DEBUG) # cp $(TARGET) "${HOME}/Google Drive/Art" open $(TARGET) install: $(EC)mkdir -p $(BINDIR) $(EC)rm -rf $(TARGET) $(EC)$(COMPILER) $(ARGS_COMMON) $(ARGS_RELEASE) run: open $(TARGET) debug: $(DEBUGGER) $(TARGET) clean: rm -rf $(TARGET) ================================================ FILE: MenuState.as ================================================ package { import org.flixel.*; public class MenuState extends FlxState { [Embed(source='/assets/gfx/title.png')] private var TitleImg:Class; [Embed(source='/assets/gfx/outline_noio.png')] private var NoioImg:Class; [Embed(source='/assets/gfx/outline_pez.png')] private var PezImg:Class; public var noioHighlight:FlxSprite; public var pezHighlight:FlxSprite; override public function create():void { add(new FlxSprite(0,0, TitleImg)); add(noioHighlight = new FlxSprite(228,123, NoioImg)); add(pezHighlight = new FlxSprite(258,123, PezImg)); noioHighlight.width = 30; noioHighlight.visible = false; pezHighlight.visible = false; var t:FlxText = new FlxText(0,0,100,king.VERSION); t.alignment = "left"; t.alpha = 0.24 add(t); FlxG.mouse.show(); } override public function update():void { super.update(); if (FlxG.mouse.x > noioHighlight.x && FlxG.mouse.x < noioHighlight.x + noioHighlight.width && FlxG.mouse.y > noioHighlight.y && FlxG.mouse.y < noioHighlight.y + noioHighlight.height){ noioHighlight.visible = true; if(FlxG.mouse.justPressed()){ FlxU.openURL("http://www.noio.nl"); } } else { noioHighlight.visible = false } if (FlxG.mouse.x > pezHighlight.x && FlxG.mouse.x < pezHighlight.x + pezHighlight.width && FlxG.mouse.y > pezHighlight.y && FlxG.mouse.y < pezHighlight.y + pezHighlight.height){ pezHighlight.visible = true; if(FlxG.mouse.justPressed()){ FlxU.openURL("http://soundcloud.com/pez_pez"); } } else { pezHighlight.visible = false; } if(!noioHighlight.visible && !pezHighlight.visible && FlxG.mouse.justPressed()) { FlxG.mouse.hide(); FlxG.switchState(new PlayState()); } } } } ================================================ FILE: Minimap.as ================================================ package { import flash.geom.Point; import org.flixel.FlxSprite; import org.flixel.FlxG; import org.flixel.FlxBasic; import org.flixel.FlxGroup; public class Minimap extends FlxSprite{ public var members:Array = []; public var colors:Array = []; public function Minimap(X:Number=0, Y:Number=0, w:int=100, h:int=10){ super(X, Y); scrollFactor.x = scrollFactor.y = 0; makeGraphic(w,h,0,true); } public function add(member:FlxBasic, color:uint=0xFFFF0000):void{ members.push(member); colors.push(color); } override public function draw():void{ fill(0x55000000); for (var i:int = 0; i < members.length; i++){ drawDot(members[i], colors[i]); } dirty = true; super.draw(); } public function drawDot(m:FlxBasic, color:uint):void{ if (m is FlxGroup){ var group:FlxGroup = m as FlxGroup; for (var i:int = 0; i < group.length; i++){ drawDot(group.members[i], color); } } else if (m is FlxSprite){ if (!m.alive || !m.visible){ return; } if (m is Wall && (m as Wall).stage == 0){ return; } var sprite:FlxSprite = m as FlxSprite; var ex:int = (sprite.x / FlxG.worldBounds.width) * this.width; var ey:int = (sprite.y / FlxG.worldBounds.height) * this.height; this.pixels.setPixel32(ex, ey, color); } } } } ================================================ FILE: PlayState.as ================================================ package { import org.flixel.*; import flash.geom.*; import flash.events.Event; import flash.utils.getDefinitionByName; import flash.utils.getQualifiedClassName; import flash.filters.BlurFilter; import mochi.as3.MochiDigits; public class PlayState extends FlxState { // Forcing flash to do some imports (weird) Reed;Castle;Treeline;Farmland;Wall;Torch;Shop;Firefly; // [Embed(source="assets/aurora.ttf",fontName="Aurora",embedAsCFF="false")] protected var font:String; [Embed(source="assets/04b03.ttf",fontName="04b03",embedAsCFF="false")] protected var font:String; [Embed(source='/assets/levels/compiled/fields.oel', mimeType="application/octet-stream")] private const LevelCity:Class; // Graphics [Embed(source='/assets/gfx/tiles.png')] private const TilesImg:Class; [Embed(source='/assets/gfx/skyline_hills.png')] private const SkylineHillsImg:Class; [Embed(source='/assets/gfx/skyline_trees.png')] private const SkylineTreesImg:Class; [Embed(source='/assets/gfx/hill.png')] public const HillImg:Class; // Sounds [Embed(source="/assets/sound/hit.mp3")] private var HitSound:Class; [Embed(source="/assets/sound/hitbig.mp3")] private var HitbigSound:Class; // Env sounds [Embed(source="/assets/sound/cicada.mp3")] private var CicadaSound:Class; [Embed(source="/assets/sound/owls.mp3")] private var OwlsSound:Class; [Embed(source="/assets/sound/birds.mp3")] private var BirdsSound:Class; //Music [Embed(source="/assets/music/night1.mp3")] private var MusicNight1:Class; [Embed(source="/assets/music/night2.mp3")] private var MusicNight2:Class; [Embed(source="/assets/music/night3.mp3")] private var MusicNight3:Class; [Embed(source="/assets/music/night4.mp3")] private var MusicNight4:Class; [Embed(source="/assets/music/night5.mp3")] private var MusicNight5:Class; [Embed(source="/assets/music/day1.mp3")] private var MusicDay1:Class; [Embed(source="/assets/music/day2.mp3")] private var MusicDay2:Class; [Embed(source="/assets/music/day3.mp3")] private var MusicDay3:Class; [Embed(source="/assets/music/day4.mp3")] private var MusicDay4:Class; [Embed(source="/assets/music/day5.mp3")] private var MusicDay5:Class; // DISPLAY GROUPS public var sky:Sky; public var sunmoon:SunMoon; public var backdropFar:FlxBackdrop; public var backdropClose:FlxBackdrop; public var backdrop:FlxGroup; public var haze:Haze; public var player:FlxSprite; public var bunnies:FlxGroup; public var farmland:FlxGroup; public var coins:FlxGroup; public var beggars:FlxGroup; public var characters:FlxGroup; public var trolls:FlxGroup; public var trollsNoCollide:FlxGroup; public var gibs:FlxGroup; public var indicators:FlxGroup; public var walls:FlxGroup; public var level:FlxGroup; public var archers:FlxGroup; public var objects:FlxGroup; public var shops:FlxGroup; public var floor:FlxTilemap; public var farmlands:FlxGroup; public var props:FlxGroup; public var lights:FlxGroup; public var darkness:FlxSprite; public var water:Water; public var arrows:FlxGroup; public var fx:FlxGroup; public var fog:Fog; public var text:FlxText; public var centerText:FlxText; public var sack:Coinsack; public var noise:FlxSprite; public var weather:Weather; // Extra references public var castle:Castle; public var minimap:Minimap; public var weatherInput:FlxInputText; //CONSTANTS public static const CHEATS:Boolean = false; public static const WEATHERCONTROLS:Boolean = false; public static const GAME_WIDTH:int = 3840; public static const MIN_KINGDOM_WIDTH:int = 200; public static const MAX_BUNNIES:int = 50; public static const MIN_BUNNY_SPAWNTIME:Number = 6.0; public static const MIN_TROLL_SPAWNTIME:Number = 1.0; public static const TROLL_WALL_DAMAGE:Number = 2.0; public static const TEXT_MAX_ALPHA:Number = 0.7; public static const TEXT_READ_SPEED:Number = 0.20; public static const TEXT_MIN_TIME:Number = 6; // Game vars public var kingdomLeft:Number = 1920-200; public var kingdomRight:Number = 1920+200; public var groundHeight:int = 132; public var phase:int = 0; public var phasesPaused:Boolean = false; public var timeToNextPhase:Number = 0; public var bunnySpawnTimer:Number = 0.0; public var trollSpawnTimer:Number = 0.0; public var trollsToSpawn:Array = []; public var minBeggars:int = 0; public var retreatDelay:Number = 0; public var gameover:Boolean = false; public var day:MochiDigits = new MochiDigits(0) public var trollHealth:Number = 1; public var trollMaxSpeed:Number = 20; public var trollJumpHeight:Number = 20; public var trollJumpiness:Number = 30; public var trollConfusion:Number = 30; public var trollBig:Boolean = false; public var grassTiles:Array; // Progress variables public var reachedVillage:Boolean = false; public var recruitedCitizen:Boolean = false; public var boughtItem:Boolean = false; public var buyBowAdvice:Boolean = false; public var buyScytheAdvice:Boolean = false; public var expandedKingdomAdvice:Boolean = false; public var horseAdvice:Boolean = false; public var outOfGoldAdvice:Boolean = false; public var savedProgress:String = null; public var restoreProgress:String = null; // Internals public var textTimeout:Number = 0; public var textQueue:Array = []; public var cameraTarget:CameraTarget; public var cameraTimeout:Number = 0; public var music:FlxSound = null; public var cicada:FlxSound = null; public var owls:FlxSound = null; public var birds:FlxSound = null; // Cheatvars private var cheatNoTrolls:Boolean = false; private var untouchable:Boolean = false; public function PlayState(progress:String=null){ super(); restoreProgress = progress; } //=== INITIALIZATION ==// override public function create():void { FlxG.camera.bgColor = 0xFFafb4c2; FlxG.camera.bounds = new FlxRect(0,0,GAME_WIDTH,196) FlxG.worldBounds.width = GAME_WIDTH; FlxG.worldBounds.height = 300; /*FlxG.framerate = 30;*/ buildLevel(LevelCity); weather.tweenTo(WeatherPresets.FOGGY, 0); if (CHEATS){ add(minimap = new Minimap(0, FlxG.height - 1 ,FlxG.width, 1)); minimap.add(trolls, 0xFF87B587); minimap.add(trollsNoCollide, 0xFF0000FF); minimap.add(player, 0xff765DB3); minimap.add(beggars, 0xFF7D6841); minimap.add(characters, 0xFFA281F8); minimap.add(walls, 0xFF969696); } showCoins(); // Load up environment sounds cicada = FlxG.play(CicadaSound, 0.0, true); owls = FlxG.play(OwlsSound, 0.0, true); birds = FlxG.play(BirdsSound, 0.0, true); // Camera add(cameraTarget = new CameraTarget()); cameraTarget.target = player; cameraTarget.offset.y = -4; cameraTarget.snap(); FlxG.camera.follow(cameraTarget,FlxCamera.STYLE_LOCKON); // Set up some debugging FlxG.watch(this, 'timeToNextPhase'); FlxG.watch(weather, 'timeOfDay'); FlxG.watch(weather, 'progress'); FlxG.watch(weather, 'ambient'); FlxG.watch(weather, 'ambientAmount'); FlxG.watch(this, 'phase'); // Set up weathercontrols if (WEATHERCONTROLS){ weatherInput = new FlxInputText(10, 10, 400, 32, '',0, null, 16); weatherInput.scrollFactor.x = weatherInput.scrollFactor.y = 0; add(weatherInput); // var setWeatherButton:FlxButton = new FlxButton(10,30,"SET", setWeatherFromInput); // setWeatherButton.scrollFactor.x = setWeatherButton.scrollFactor.y = 0; // add(setWeatherButton); FlxG.mouse.show() } } public function setWeatherFromInput():void{ var txt:String = weatherInput.textField.text; weatherInput.textField.text = ''; var object:Object = JSON.parse(txt) FlxG.stage.focus = weatherInput.textField; var w:Object = {'sky':0,'horizon':0,'haze':0,'darknessColor':0,'darkness':0, 'contrast':-0,'saturation':0,'ambient':0,'wind':0, 'fog':0,'timeOfDay':0,'sunTint':0} for (var k:String in w){ w[k] = weather.targetState[k]; if (k in object){ if (object[k].substr(0, 2) == '0x'){ var col:uint = parseInt(object[k]); FlxG.log(k + ': ' + col.toString(16)); w[k] = col; } else { var f:Number = parseFloat(object[k]); w[k] = f; FlxG.log(k + ': ' + f); } } } weather.tweenTo(w, 10); } public function progressAll():void{ reachedVillage = true; recruitedCitizen = true; boughtItem = true; buyBowAdvice = true; buyScytheAdvice = true; expandedKingdomAdvice = true; } public function buildLevel(levelXML:Class):void{ //Load XML var oel:XML = new XML(new levelXML); //Variables var backdropFarGraphic:Class = this[oel.@backdropFarImg] as Class; var backdropCloseGraphic:Class = this[oel.@backdropCloseImg] as Class; var waterHeight:int = oel.@waterHeight; darkness = new FlxSprite(0,0).makeGraphic(FlxG.width, FlxG.height,0x88000000) //Basic setup weather = new Weather(); add(sky = new Sky(weather)); add(sunmoon = new SunMoon(weather)); add(backdropFar = new FlxBackdrop(backdropFarGraphic, 0.15, 0.2, 0xFF717565)); add(backdropClose = new FlxBackdrop(backdropCloseGraphic, 0.3, 0.2, 0xFF555849)); add(backdrop = new FlxGroup()); add(haze = new Haze(0,0,weather)); // Movables add(archers = new FlxGroup(10)) add(objects = new FlxGroup()); add(shops = new FlxGroup()); add(bunnies = new FlxGroup()); add(beggars = new FlxGroup()); add(player = new Player(100,68)); add(characters = new FlxGroup()); add(trolls = new FlxGroup()); add(trollsNoCollide = new FlxGroup()); add(walls = new FlxGroup()); add(coins = new FlxGroup(100)); add(gibs = new FlxGroup(200)); add(indicators = new FlxGroup()); // Level add(level = new FlxGroup()); add(floor = new FlxTilemap()); add(farmlands = new FlxGroup()) add(props = new FlxGroup()); // Effects add(lights = new FlxGroup()); darkness.scrollFactor.x = darkness.scrollFactor.y = 0; darkness.blend = 'multiply'; add(darkness); add(text = new FlxText(10, 138, FlxG.width, "TEXT")); // FlxG.log(font) text.setFormat("04b03", 8, 0xFFFFFFFF, "left", 0xCC333333); text.visible = false; text.scrollFactor.x = 0; text.alpha = 1.0; add(centerText = new FlxText(0, FlxG.height/2 - 32, FlxG.width, "TEXT")); centerText.setFormat("04b03", 32, 0xFFFFFFFF, "center", 0xAA333333); centerText.visible = false; centerText.scrollFactor.x = 0; centerText.alpha = 1.0; add(water = new Water(-4,waterHeight,FlxG.width+8,44,lights,weather)); add(arrows = new FlxGroup(64)); add(fx = new FlxGroup()); add(sack = new Coinsack(270, 2)); add(fog = new Fog(weather)); add(noise = new FlxSprite(0,0)); noise.scrollFactor.x = noise.scrollFactor.y = 0; noise.makeGraphic(FlxG.width,FlxG.height,0xFFFF00FF) noise.pixels.noise(0,0,255,7,true); noise.alpha = 0.015; //Add backdrop objects var o:XML; if (oel.backdrop != undefined){ buildObjects(oel.backdrop[0].*,backdrop); for (var i:int = 0; i < backdrop.length; i ++){ backdrop.members[i].scrollFactor.x = 0.5; } } // Add Ground Tiles if (oel.ground != undefined){ var tileWidth:uint = oel.ground[0].@tileWidth; var tileHeight:uint = oel.ground[0].@tileHeight; var mapData:String = oel.ground.toString(); floor.loadMap(mapData, TilesImg, tileWidth, tileHeight); } grassTiles = new Array(); for (i = 0; i < floor.widthInTiles; i++){ var t:int = floor.getTile(i, 4); if ((t >= 7 && t <= 11) || (t >= 17 && t <= 18)){ grassTiles.push(i); } } // Add ground collision proxy because this is a flat level. var collider:FlxSprite = new FlxSprite(0,132.2).makeGraphic(FlxG.worldBounds.width,32,0x00FF00FF) collider.immovable = true; level.add(collider); collider = new FlxSprite(0,0).makeGraphic(8,200,0x00FF00FF); collider.immovable = true; level.add(collider); collider = new FlxSprite(FlxG.worldBounds.width - 8,0).makeGraphic(8,200,0x00FF00FF); collider.immovable = true; level.add(collider); // Add Walls if (oel.walls != undefined){ buildObjects(oel.walls[0].*,walls); } // Set the closest walls to a first build stage for (i = 0; i < walls.length; i ++){ var w:Wall = walls.members[i] as Wall; if ((w.x + w.width) > kingdomLeft && w.x < kingdomRight){ w.build() } } // Add level objects if (oel.objects != undefined){ buildObjects(oel.objects[0].Shop,shops); buildObjects(oel.objects[0].Castle,objects); } // Add level objects if (oel.farmlands != undefined){ buildObjects(oel.farmlands[0].*,farmlands); } // Add props if (oel.props != undefined){ buildObjects(oel.props[0].*,props); } // Add lights if (oel.lights != undefined){ buildObjects(oel.lights[0].*,lights); } } /** * Builds and adds to groups the objects from given xml nodes */ public function buildObjects(nodes:XMLList, group:FlxGroup):void{ for each(var node:XML in nodes){ var objType:String = node.name(); var obj:FlxSprite; try { var classRef:Class = getDefinitionByName(objType) as Class; obj = new classRef(node.@x, node.@y); } catch(error:ReferenceError) { var simpleGraphic:Class = this[objType+"Img"]; //getDefinitionByName(objType+"Img") as Class; obj = new FlxSprite(node.@x, node.@y, simpleGraphic) } group.add(obj); } } //=== GAME LOGIC ===// override public function update():void{ // Collisions if (restoreProgress){ setProgress(restoreProgress); restoreProgress = null; } FlxG.collide(level, coins); FlxG.collide(level, trolls); FlxG.collide(level, trollsNoCollide); FlxG.collide(level, gibs); FlxG.collide(trolls, trolls); FlxG.overlap(trolls, walls, this.trollWall); FlxG.overlap(trollsNoCollide, walls, this.trollWall); FlxG.overlap(arrows, trolls, this.trollShot); FlxG.overlap(arrows, trollsNoCollide, this.trollShot); FlxG.overlap(arrows, bunnies, this.bunnyShot); FlxG.overlap(coins, characters,this.pickUpCoin); FlxG.overlap(coins, player,this.pickUpCoin); FlxG.overlap(coins, beggars, this.pickUpCoin); FlxG.overlap(coins, trolls, this.pickUpCoin); FlxG.overlap(coins, trollsNoCollide, this.pickUpCoin); FlxG.overlap(trolls, characters, this.trollHit); FlxG.overlap(trollsNoCollide, characters, this.trollHit); FlxG.overlap(trolls, beggars, this.trollHit); FlxG.overlap(trollsNoCollide, beggars, this.trollHit); if (!(CHEATS && untouchable)){ FlxG.overlap(trolls, player, this.trollHit); FlxG.overlap(trollsNoCollide, player, this.trollHit); } FlxG.overlap(characters, player, this.giveTaxes); // Update weather weather.update(); // Gamestate if (timeToNextPhase <= 0){ nextPhase(); } else if (!phasesPaused){ timeToNextPhase -= FlxG.elapsed; } kingdomRight = Math.max(GAME_WIDTH/2 + MIN_KINGDOM_WIDTH/2, kingdomRight - FlxG.elapsed*4); kingdomLeft = Math.min(GAME_WIDTH/2 - MIN_KINGDOM_WIDTH/2, kingdomLeft + FlxG.elapsed*4); // Spawn bunnies using logistic growth var p:Number = (bunnies.countLiving() + 2) / (MAX_BUNNIES + 2); if (bunnySpawnTimer <= 0){ bunnySpawnTimer = MIN_BUNNY_SPAWNTIME; var probAdd:Number = 0.5 + 2*p*(1-p); if (FlxG.random() < probAdd){ var rx:int = int(FlxG.random()*grassTiles.length); bunnies.add(new Bunny(grassTiles[rx]*32,0)); } } else { bunnySpawnTimer -= FlxG.elapsed; } // Spawn beggars if (beggars.countLiving() < minBeggars){ beggars.add(new Citizen((FlxG.random() < 0.5) ? 16 : GAME_WIDTH-16,0)); } // Spawn trolls updateTrollSpawn() trollSpawnTimer -= FlxG.elapsed; if (retreatDelay > 0){ retreatDelay -= FlxG.elapsed if (retreatDelay <= 0){ trolls.callAll("retreat"); trollsNoCollide.callAll("retreat"); } } // Text update if (textTimeout <= 0){ showText() } else { text.alpha = Math.min(TEXT_MAX_ALPHA, textTimeout); textTimeout -= FlxG.elapsed; } if (centerText.visible && centerText.alpha < 0.001){ centerText.visible = false; } else { centerText.alpha -= 0.05 * FlxG.elapsed; } // Camera follow timeout if (cameraTarget.target != player){ if (cameraTimeout <= 0){ // Reset the cameratarget. cameraTarget.target = player; cameraTarget.lead = 48; } else { cameraTimeout -= FlxG.elapsed; } } // Progress update if (player.x > GAME_WIDTH/2 && !reachedVillage) { reachedVillage = true; if (beggars.length > 0){ panTo(beggars.members[0], 5.0); showText("Throw some coins [DOWN] near them."); } } if (recruitedCitizen && !boughtItem && !buyBowAdvice){ buyBowAdvice = true; showText("Buy them bows to defend and hunt for you."); panTo(shops.members[1], 7.5); } if (buyBowAdvice && !buyScytheAdvice && cameraTarget.target == player){ buyScytheAdvice = true; showText("Buy them scythes to build and farm for you."); panTo(shops.members[0], 7.5); } if (boughtItem && !expandedKingdomAdvice && characters.length >= 4 && weather.timeOfDay > 0.3 && weather.timeOfDay < 0.6){ expandedKingdomAdvice = true; showText("Expand your kingdom by building a wall here."); panTo(walls.members[1], 5.0, -12); } this.updateEnvironmentSounds(); if(gameover && FlxG.mouse.justPressed()) { FlxG.mouse.hide(); // var newState:PlayState = new PlayState(savedProgress); // FlxG.switchState(newState); FlxG.switchState(new PlayState(savedProgress)); // FlxG.log("Switching gamestate") } super.update(); if (FlxG.keys.justPressed("S")) { if (FlxG.stage.displayState == 'normal') { FlxG.stage.displayState = 'fullScreen'; } else { FlxG.stage.displayState = 'normal'; } } if (CHEATS){ if (FlxG.keys.justPressed("F")) { var c:Citizen = new Citizen ((kingdomRight+kingdomLeft) / 2, 0); characters.add(c); c.morph(Citizen.FARMER); showText("Spawned farmer.") } if (FlxG.keys.justPressed("H")) { var h:Citizen = new Citizen ((kingdomRight+kingdomLeft) / 2, 0); characters.add(h); h.morph(Citizen.HUNTER); showText("Spawned farmer.") } if (FlxG.keys.justPressed("T")) { cheatNoTrolls = !cheatNoTrolls; showText("Trolls " + (cheatNoTrolls ? "disabled" : "enabled")) } if (FlxG.keys.justPressed("U")) { untouchable = !untouchable; showText("Untouchable " + (untouchable ? "enabled" : "disabled")) } if (FlxG.keys.justPressed("N")) { timeToNextPhase = 1.0; showText("Skip phase."); } if (FlxG.keys.justPressed("B")) { beggars.add( new Citizen ((kingdomRight+kingdomLeft) / 2, 0)); showText("Spawned beggar.") } if (FlxG.keys.justPressed("I")) { trollBig = !trollBig; showText("Trolls " + (trollBig ? "big." : "normal.")); } if (FlxG.keys.justPressed('A')) { progressAll(); showText("Full progress") } if (FlxG.keys.justPressed("R")){ spawnTrolls(2) showText("Spawned 2 trolls") } if (FlxG.keys.justPressed("P")){ phasesPaused = !phasesPaused; showText("Phases " + (phasesPaused ? "paused" : "resumed")) } if (FlxG.keys.justPressed("ENTER")){ setWeatherFromInput(); } if (FlxG.keys.justPressed("C")){ (player as Player).coins += 1; showText((player as Player).coins + " coins.") } if (FlxG.keys.justPressed("ONE")){ // setProgress('D1 A2 X1000 B2 P0 F0 H0 W000011 C0 G7 S00'); } if (FlxG.keys.justPressed("TWO")){ // setProgress('D2 A7 X1000 B2 P0 F1 H2 W000011 C0 G4 S00'); } if (FlxG.keys.justPressed("THREE")){ setProgress('D3 A12 X1713 B2 P0 F2 H4 W010011 C1 S01 G3'); } if (FlxG.keys.justPressed("FOUR")){ setProgress('D4 A17 X1932 B2 P1 F3 H6 W220021 C1 S02 G7'); } if (FlxG.keys.justPressed("FIVE")){ setProgress('D5 A21 X1899 B4 P1 F3 H6 W010031 C2 S00 G0'); } if (FlxG.keys.justPressed("SIX")){ setProgress('D6 A25 X2235 B2 P2 F1 H10 W010031 C2 S11 G0'); } if (FlxG.keys.justPressed("SEVEN")){ setProgress('D7 A29 X2146 B2 P5 F2 H8 W030031 C2 S00 G0'); } if (FlxG.keys.justPressed("EIGHT")){ setProgress('D8 A33 X2318 B3 P1 F6 H9 W040011 C2 S01 G2'); } if (FlxG.keys.justPressed("NINE")){ setProgress('D9 A37 X1467 B2 P1 F6 H7 W140041 C2 S02 G0'); } } } public function phaseFirst():void{ beggars.add( new Citizen (kingdomRight+580, 0)); beggars.add( new Citizen (kingdomRight+600, 0)); minBeggars = 2; } public function phaseBeforeNightOne():void{ showText("Night comes, be careful."); } public function phaseNightOne():void{ trollStats(24, 1, 20, 999999, false, 16.0); // Nojump spawnTrolls(2); if (player.x < GAME_WIDTH / 2){ panTo(trolls.members[0]); } else { panTo(trolls.members[1]); } showText("They will noodle your stuff away.") } // These trolls still won't scale your lowest walls public function phaseNightTwo():void{ trollStats(26, 1, 20, 2, false, 12.0); //Jump0 spawnTrolls(12); } // These WILL scale the lowest walls public function phaseNightThree():void{ trollStats(26, 1, 30, 2, false, 12.0); // Grunts spawnTrolls(20); } // The trolls are a little tougher now. public function phaseNightFour():void{ trollStats(26, 3, 30, 2, false, 12.0); spawnTrolls(24); } // They are faster but more chaotic, they might // break your walls, which will kill you in the next wave. public function phaseNightFive():void{ trollStats(35, 2, 38, 2, false, 4.0); // Chaotic spawnTrolls(36); } // These trolls will scale the stone walls public function phaseNightSix():void{ trollStats(30, 3, 45, 2, false, 10.0); spawnTrolls(8); } // Boss wave trolls public function phaseNightSeven():void{ // trollMaxSpeed = 30; // trollHealth = 1 // spawnTrolls(32); trollStats(20, 30, 10, 999999, true, 16.0) spawnTrolls(2); } // Since the boss probably broke your walls // these trolls jump very high, there is no // disadvantage to not having walls. // You will need them back in the next wave though. public function phaseNightEight():void{ trollStats(40, 4, 50, 3, false, 12.0) spawnTrolls(16); } // You need the highest walls here public function phaseNightNine():void{ trollStats(30, 4, 45, 4, false, 8.0) spawnTrolls(24); } // Kill the player off public function phaseNightTen():void{ trollStats(20, 30, 10, 999999, true, 16.0); // Boss spawnTrolls(4); trollStats(30, 4, 45, 4, false, 8.0); // Strong spawnTrolls(20); trollStats(40, 2, 50, 3, false, 12.0); // Jumper spawnTrolls(10); trollStats(26, 1, 30, 2, false, 12.0); // Grunts spawnTrolls(40); } public function phaseNightCycle():void{ var difficulty:Number = day.value - 10; trollStats(30, 3 + 2 * difficulty, 45, 4, false, 8.0); // Strong spawnTrolls(int(10 + difficulty)); if (day.value % 2 == 0){ trollStats(20, 30, 10, 999999, true, 16.0); // Boss spawnTrolls(int(2 * difficulty)); } } public const PHASES:Array = [ // INTRO (0-3) [WeatherPresets.FOGGY, 10, null, phaseFirst, null], // ONE (4-9) [WeatherPresets.DAWN, 25, null, daybreak, null], [WeatherPresets.SUNNY, 30, null, null, null], [WeatherPresets.EVENING, 20, null, null, null], [WeatherPresets.NIGHT, 20, null, phaseBeforeNightOne, MusicNight2], [null, 50, null, phaseNightOne, null], // TWO (10-14) [WeatherPresets.DAWNLIGHTPINK, 20, null, daybreak, null], [WeatherPresets.DAYWINDYCLEAR, 30, null, null, MusicDay1], [WeatherPresets.DUSKYELLOW, 20, null, null, null], [WeatherPresets.EVENINGORANGE, 20, null, null, MusicNight3], [WeatherPresets.NIGHTGREEN, 60, 30, phaseNightTwo, null], // GREEN // THREE (15-18) [WeatherPresets.DAWNGREY, 20, null, daybreak, null], [WeatherPresets.DAYBLEAK, 50, null, null, MusicDay2], [WeatherPresets.DUSKWARM, 20, null, null, null], [WeatherPresets.EVENINGBLACK, 20, null, null, MusicNight4], [WeatherPresets.NIGHTDARK, 60, 30, phaseNightThree, null], // FOUR (19-22) [WeatherPresets.DAWNBLEAK, 20, null, daybreak, null], [WeatherPresets.DAYSOFT, 40, null, null, null], [WeatherPresets.EVENINGMONOTONE, 30, null, null, MusicNight5], [WeatherPresets.NIGHTSUPERDARK, 65, 30, phaseNightFour, null], // FIVE (23-26) [WeatherPresets.DAWNLIGHTPINK, 20, null, daybreak, MusicDay3], [WeatherPresets.DAYBLEAK, 55, null, null, null], [WeatherPresets.EVENINGFOGGY, 40, null, null, MusicNight4], [WeatherPresets.NIGHTFOGGY, 60, 30, phaseNightFive, null], // SIX (27-30) [WeatherPresets.DAWNBLEAK, 25, null, daybreak, MusicDay4], [WeatherPresets.DAYMONOCHROME, 60, null, null, null], [WeatherPresets.DUSKPINK, 15, null, null, null], [WeatherPresets.NIGHTCLEAR, 70, 30, phaseNightSix, MusicNight4], // SEVEN (31-34) [WeatherPresets.DAWNCLEARORANGE, 20, null, daybreak, null], [WeatherPresets.DAYCLEARCOLD, 40, null, null, MusicDay3], [WeatherPresets.DUSKCLEAR, 20, null, null, null], [WeatherPresets.NIGHTSHINE, 70, 30, phaseNightSeven, MusicNight3], // EIGHT (35-38) [WeatherPresets.DAWNREDMOON, 40, null, daybreak, MusicDay5], [WeatherPresets.DAYORANGESKY, 40, null, null, null], [WeatherPresets.DUSKFOGGY, 20, null, null, null], // BIG WAVE [WeatherPresets.NIGHTPURPLE, 80, 30, phaseNightEight, MusicNight4], // NINE (39-42) [WeatherPresets.DAWNBRIGHT, 20, null, daybreak, null], [WeatherPresets.DAYPASTEL, 75, null, null, MusicDay2], [WeatherPresets.DUSKTAN, 20, null, null, MusicNight4], // SINGLE TROLL, MASSIVE HEALTH [WeatherPresets.NIGHTREDMOON, 60, 30, phaseNightNine, null], // TEN (43) [WeatherPresets.DAWNBROWN, 20, null, daybreak, null], [WeatherPresets.DAYDUSTY, 40, null, null, null], [WeatherPresets.DUSKRED, 20, null, null, MusicNight3], // EVERYTHING, YOU DIE HERE. [WeatherPresets.NIGHTLONG, 60, 30, phaseNightTen, null], [WeatherPresets.DAWNEARLY, 15, null, trollRetreat, null], ]; public const PHASES_CYCLE:Array = [ [WeatherPresets.DAWNREDMOON, 20, null, daybreak, null], [WeatherPresets.DAYPASTEL, 40, null, null, null], [WeatherPresets.DUSKTAN, 20, null, null, null], [WeatherPresets.NIGHTLONG, 30, null, null, MusicNight5], [null, 55, null, phaseNightCycle, null] ]; public function nextPhase():void{ if (phasesPaused){ return; } var currentPhase:Array; if (phase < PHASES.length){ currentPhase = PHASES[phase]; } else { var p:int = (phase - PHASES.length) % 5; currentPhase = PHASES_CYCLE[p] } var weatherTweenTime:Number; timeToNextPhase = currentPhase[1]; // Transform weather if (currentPhase[2] == null){ weatherTweenTime = timeToNextPhase * 0.7; } else { weatherTweenTime = currentPhase[2] } if (currentPhase[0] != null){ weather.tweenTo(currentPhase[0], weatherTweenTime); } phase += 1; // Call the function to do custom actions if there is one if (currentPhase[3] != null){ currentPhase[3](); } // Play music if (currentPhase[4] != null){ if (this.music != null){ this.music.stop(); } this.music = FlxG.play(currentPhase[4]); FlxG.log("Playing " + currentPhase[4]); } } public function updateEnvironmentSounds():void{ var v:Number; v = 1 - Math.pow(Math.abs(weather.timeOfDay - 0.7) / 0.1, 2); this.cicada.volume = v; v = 1 - Math.pow(Math.min(weather.timeOfDay, Math.abs(weather.timeOfDay - 1.0)) / 0.2, 2); this.owls.volume = v; v = 1 - Math.pow(Math.abs(weather.timeOfDay - 0.4) / 0.25, 2); this.birds.volume = v; // if (v > 0){ // this.cicadas.resume(); // } else { // this.cicadas.pause(); // } } public function trollStats(speed:Number, health:Number, jumpheight:Number, jumpiness:Number=2, big:Boolean=false, confusion:Number=3):void{ trollMaxSpeed = speed; trollHealth = health; trollJumpHeight = jumpheight; trollJumpiness = jumpiness; trollBig = big; trollConfusion = confusion; } public function spawnTrolls(amount:int):void{ if (cheatNoTrolls) return; while(amount){ amount -= 2; var troll:Troll = (trolls.recycle(Troll) as Troll); troll.reset(64, groundHeight - 40) trollsToSpawn.push(troll); troll = (trolls.recycle(Troll) as Troll); troll.reset(GAME_WIDTH - 64, groundHeight - 40); trollsToSpawn.push(troll); updateTrollSpawn(); } } public function updateTrollSpawn():void{ if (trollsToSpawn.length > 0 && trollSpawnTimer <= 0){ (trollsToSpawn.shift() as Troll).go(); (trollsToSpawn.shift() as Troll).go(); trollSpawnTimer = MIN_TROLL_SPAWNTIME; } } public function daybreak():void{ trollRetreat(); (coins.recycle(Coin) as Coin).drop(castle, player); if (castle.stage >= 2) { (coins.recycle(Coin) as Coin).drop(castle, player); } day.addValue(1); showCenterText(Utils.toRoman(day.value)); saveProgress(); } public function saveProgress():void{ var numBeggars:int = beggars.countLiving(); var numCitizens:Array = [0,0,0,0]; for (var i:int = 0; i < characters.length; i ++){ if (characters.members[i] != null && (characters.members[i].alive)){ numCitizens[(characters.members[i] as Citizen).occupation] ++; } } numCitizens[Citizen.HUNTER] += Math.max(0, archers.countLiving()); var wallStages:Array = []; for (i = 0; i < walls.length; i ++){ wallStages.push((walls.members[i] as Wall).stage); } var s:String = ''; s += 'D' + day.value + ' '; s += 'A' + phase + ' '; s += 'X' + int(player.x) + ' '; s += 'B' + numBeggars + ' '; s += 'P' + numCitizens[Citizen.POOR] + ' '; s += 'F' + numCitizens[Citizen.FARMER] + ' '; s += 'H' + numCitizens[Citizen.HUNTER] + ' '; s += 'W' + wallStages.join('') + ' '; s += 'C' + castle.stage + ' '; s += 'S' + (shops.members[0] as Shop).supply + (shops.members[1] as Shop).supply + ' '; s += 'G' + (player as Player).coins FlxG.log(s); savedProgress = s; } public function setProgress(s:String):void{ // Parse the string // 'N1 X1 B2 P0 F0 H0 W000011 C0 G7' progressAll(); FlxG.flash(0xFFFFFFFF, 3); FlxG.log("Skip to " + s); var newDay:int = parseInt(s.match(/D(\d+)/)[1]); var ph:int = parseInt(s.match(/A(\d+)/)[1]); var playerX:int = parseInt(s.match(/X(\d+)/)[1]); var numBeggars:int = parseInt(s.match(/B(\d+)/)[1]); var numPoor:int = parseInt(s.match(/P(\d+)/)[1]); var numFarmers:int = parseInt(s.match(/F(\d+)/)[1]); var numHunters:int = parseInt(s.match(/H(\d+)/)[1]); var wallStages:Array = s.match(/W(\d)(\d)(\d)(\d)(\d)(\d)/); var castleStage:int = parseInt(s.match(/C(\d)/)[1]); var shopSupply:Array = s.match(/S(\d)(\d)/); var gold:int = parseInt(s.match(/G(\d)/)[1]); while (beggars.countLiving() < numBeggars){ beggars.add(new Citizen((kingdomRight + kingdomLeft) / 2,0)); } player.x = playerX; characters.callAll('kill'); archers.callAll('kill'); var c:Citizen; while (numPoor) { c = new Citizen ((kingdomRight+kingdomLeft) / 2, 0); c.morph(Citizen.POOR); characters.add(c); numPoor --; } while (numFarmers) { c = new Citizen ((kingdomRight+kingdomLeft) / 2, 0); c.morph(Citizen.FARMER); characters.add(c); numFarmers --; } while (numHunters) { c = new Citizen ((kingdomRight+kingdomLeft) / 2, 0); c.morph(Citizen.HUNTER); characters.add(c); numHunters --; } for (var i:int = 0; i < walls.length; i ++){ (walls.members[i] as Wall).buildTo(parseInt(wallStages[i+1]), true); } (shops.members[0] as Shop).setSupply(parseInt(shopSupply[0])); (shops.members[1] as Shop).setSupply(parseInt(shopSupply[1])); castle.morph(castleStage); (player as Player).changeCoins(gold - (player as Player).coins); phase = ph - 1; day.setValue(newDay - 1); nextPhase(); trolls.callAll("kill"); trollsNoCollide.callAll("kill"); gibs.callAll("kill"); } public function trollRetreat(delay:Number=10):void{ retreatDelay = delay; if (retreatDelay <= 0){ trollsToSpawn.splice(0); trolls.callAll("retreat"); trollsNoCollide.callAll("retreat"); } } public function pickUpCoin(coin:FlxObject, char:FlxObject):void{ if (char is Player){ (char as Player).pickup(coin); } else if (char is Citizen){ (char as Citizen).pickup(coin); } else if (char is Troll){ (char as Troll).pickup(coin); } } public function giveTaxes(char:FlxObject, player:FlxObject):void{ if (char != player){ (char as Citizen).giveTaxes(player as Player); } } public function trollWall(troll:FlxObject, wall:FlxObject):void{ FlxObject.separate(troll, wall); wall.hurt((troll as Troll).big ? 2 * TROLL_WALL_DAMAGE : TROLL_WALL_DAMAGE); } public function trollShot(arrow:FlxObject, troll:Troll):void{ if (troll.alive && arrow.exists){ FlxG.play(HitbigSound).proximity(arrow.x, arrow.y, player, FlxG.width); arrow.kill(); (troll as Troll).getShot(); } } public function bunnyShot(arrow:FlxObject, bunny:FlxObject):void{ if (bunny.alive && arrow.exists){ FlxG.play(HitSound).proximity(arrow.x, arrow.y, player, FlxG.width); arrow.kill(); (bunny as Bunny).getShot(arrow as Arrow); } } public function trollHit(troll:FlxObject, char:FlxObject):void{ if (char is Citizen){ (char as Citizen).hitByTroll(troll as Troll); } if (char == player){ (char as Player).hitByTroll(troll as Troll); } } public function crownStolen():void{ gameover = true; phasesPaused = true; trollRetreat(0); FlxG.mouse.show(); showText("No crown, no king. Game over."); showText("Click to continue or wait to enter highscore."); showText("Click to continue or wait to enter highscore."); FlxG.fade(0, 20, endGame); } public function endGame():void{ FlxG.switchState(new GameOverState(day.value - 1)); } //=== RENDERING ==// override public function draw():void{ darkness.dirty = true; darkness.fill(weather.darknessColor); super.draw(); weather.ambientTransform.applyFilter(FlxG.camera.buffer); } public function showCoins():void{ var c:int = (player as Player).coins; sack.show(c); } public function showText(t:String=null):void{ if (t != null){ textQueue = textQueue.concat(t.split('\n')) } if (textQueue.length > 0 && textTimeout <= 0){ text.text = textQueue.shift(); text.visible = true; textTimeout = Math.max(TEXT_MIN_TIME, TEXT_READ_SPEED * text.text.length); } } public function showCenterText(t:String):void{ centerText.text = t; centerText.visible = true; centerText.alpha = 0.999; } public function panTo(o:FlxSprite, duration:Number=8.0, lead:Number=0):void{ cameraTimeout = duration; cameraTarget.target = o; cameraTarget.lead = lead; } } } ================================================ FILE: Player.as ================================================ package { import flash.geom.Point; import org.flixel.FlxSprite; import org.flixel.FlxG; import org.flixel.FlxCamera; import org.flixel.FlxGroup; import org.flixel.FlxObject; import org.flixel.FlxPoint; import org.flixel.FlxSound; public class Player extends FlxSprite{ [Embed(source='/assets/gfx/king.png')] private var PlayerImg:Class; [Embed(source="/assets/sound/pickup.mp3")] public static const PickupSound:Class; [Embed(source="/assets/sound/build.mp3")] private var BuildSound:Class; [Embed(source="/assets/sound/throw.mp3")] private var ThrowSound:Class; [Embed(source="/assets/sound/stolen.mp3")] private var StolenSound:Class; public static var pickupSound:FlxSound = FlxG.loadSound(PickupSound); public static const BASE_SKIN:uint = 0xFFedbebf; public static const BASE_DARK:uint = 0xFFbd9898; public static const BASE_EYES:uint = 0xFFa18383; public static const MAX_SPEED:Number = 80; public static const MIN_SPEED:Number = 25; public static const MAX_FOOD_BONUS:Number = 50; public static const MAX_FOOD:Number = 100; public static const HIT_RATE:Number = 0.2; public static const SELECT_DISTANCE:Number = 10; private var playstate:PlayState; private var selectedBuilding:FlxSprite = null; private var floatCoin:CoinFloat = null; private var lastMoved:Number = 0; public var food:Number = 100; public var hasCrown:Boolean = true; public var lastTrollHit:Number = 0; public var coins:int = 7; public function Player(X:int,Y:int){ super(X,Y); y = 100 // 132 (land height) - 32 (player height) loadGraphic(PlayerImg,true,true,64,64); width = 20; height = 32; offset.x = 22; offset.y = 32; maxVelocity.x = MAX_SPEED; drag.x = maxVelocity.x*5; playstate = (FlxG.state as PlayState); addAnimation('walk_slow',[0,1,2,3,4,5,6,7],10,true); addAnimation('walk_fast',[0,1,2,3,4,5,6,7],15,true); addAnimation('stand',[8],10,true); addAnimation('eat',[9,10],5,true); addAnimation('nocrown',[11],10,true); play('stand'); playstate.player = this; var d:Number = Math.random() * 20; var skin:uint = Utils.HSVtoRGB(d, 0.19 + (d / 100), 0.97 - (d / 33)); Utils.replaceColor(pixels, BASE_SKIN, skin); Utils.replaceColor(pixels, BASE_DARK, Utils.interpolateColor(skin,0xFF000000,0.2)); Utils.replaceColor(pixels, BASE_EYES, Utils.interpolateColor(skin,0xFF000000,0.5)); } public function changeCoins(amt:int):void{ if (amt > 0) { pickupSound.play(false); pickupSound.proximity(x, y, this, FlxG.width); } coins += amt; playstate.showCoins(); } public function hitByTroll(troll:Troll):void{ if (troll.hasCoin) return; if (lastTrollHit < HIT_RATE) return; lastTrollHit = 0; // If the player has coins, lose one and return. if (coins > 0){ var c:Coin = (playstate.coins.recycle(Coin) as Coin); c.drop(this, troll, true); c.justThrown = true; FlxG.play(StolenSound).proximity(x, y, this, FlxG.width); changeCoins(-1); FlxG.shake(); return; } if (hasCrown){ FlxG.flash(0xFFFFFFFF, 0.1); troll.stealCrown(); lostCrown(troll); playstate.crownStolen(); } } public function lostCrown(troll:FlxObject):void{ hasCrown = false; facing = troll.x > x ? RIGHT : LEFT; play('nocrown'); FlxG.play(StolenSound).proximity(x, y, this, FlxG.width); Utils.explode(this, playstate.gibs); } public function pickup(coin:FlxObject):void{ if (!coin.alive) return; var c:Coin = coin as Coin; // Return if the coin doesn't belong to me. if (c.justThrown){ return; } c.kill(); changeCoins(1); } override public function update():void { lastTrollHit += FlxG.elapsed; // Check for movement input acceleration.x = 0; if (!hasCrown){ return; } if(FlxG.keys.LEFT || FlxG.keys.RIGHT){ lastMoved = 0; if (food > 0){ food -= FlxG.elapsed; } maxVelocity.x = MIN_SPEED + Math.min(1,food/MAX_FOOD_BONUS) * (MAX_SPEED-MIN_SPEED); if (!playstate.horseAdvice && food < 10){ playstate.horseAdvice = true; playstate.showText("Horse is tired. Let him rest on the grass.") } } if(FlxG.keys.LEFT){ acceleration.x = -maxVelocity.x*4; facing = LEFT; if (maxVelocity.x > MIN_SPEED + 15){ play('walk_fast'); } else { play('walk_slow'); } } else if(FlxG.keys.RIGHT){ acceleration.x = maxVelocity.x*4; facing = RIGHT; if (maxVelocity.x > MIN_SPEED + 15){ play('walk_fast'); } else { play('walk_slow'); } } else { lastMoved += FlxG.elapsed; if (lastMoved > 1 && food < MAX_FOOD){ // Check if on grass var headPos:Number = (x+width/2) + (facing == RIGHT ? 25: -25) var onTile:int = playstate.floor.getTile(headPos/32,4) if ((onTile >= 7 && onTile <= 11) || (onTile >= 17 && onTile <= 18)){ play('eat',false); food += FlxG.elapsed*10; } else { play('stand'); } } else { play('stand'); } } if (FlxG.keys.SHIFT && PlayState.CHEATS) { velocity.x *= 10 } if (FlxG.keys.justPressed("DOWN")){ if (coins <= 0){ playstate.showCoins(); } else { if (selectedBuilding != null){ changeCoins(-1); giveCoin(selectedBuilding); } else { var cit:Citizen; var closestCitizen:Citizen = null; var closest:Number = 1000000; for (var i:int = 0; i < playstate.beggars.length; i++){ cit = (playstate.beggars.members[i] as Citizen) if (Math.abs((cit.x + cit.width/2) - (x + width/2)) < closest){ closestCitizen = cit; closest = Math.abs((cit.x + cit.width/2) - (x + width/2)); } } if (playstate.recruitedCitizen || closest < 64){ var c:Coin = (playstate.coins.recycle(Coin) as Coin); c.drop(this, closestCitizen); c.justThrown = true; FlxG.play(ThrowSound).proximity(x, y, this, FlxG.width); changeCoins(-1); } } } } super.update(); //Find selected shop/wall if (selectedBuilding != null){ if (Math.abs((selectedBuilding.x + selectedBuilding.width/2) - (x + width/2)) > SELECT_DISTANCE * 2){ deselect(selectedBuilding); } } else if (playstate.recruitedCitizen) { checkSelectable(playstate.objects) checkSelectable(playstate.shops); checkSelectable(playstate.walls); } // CAP WALKING AT LEVEL ENDS if (x < 0){ velocity.x = Math.max(velocity.x, 0); } else if (x + width > PlayState.GAME_WIDTH) { velocity.x = Math.min(velocity.x, 0); } } private function checkSelectable(group:FlxGroup):void{ for (var i:int = 0; i < group.length; i ++){ var b:FlxSprite = group.members[i]; if (b != null && Math.abs((b.x + b.width/2) - (x + width/2)) <= SELECT_DISTANCE){ if ((b as Buildable).canBuild()) select(b); } } } private function select(building:FlxSprite):void{ selectedBuilding = building; if (floatCoin == null){ playstate.add(floatCoin = new CoinFloat()); } floatCoin.visible = true; floatCoin.float(selectedBuilding); } private function deselect(building:FlxSprite):void{ selectedBuilding.color = 0xFFFFFFFF; selectedBuilding = null; floatCoin.visible = false; } private function giveCoin(building:FlxSprite):void{ Buildable(building).build(); FlxG.play(BuildSound).proximity(x, y, this, FlxG.width); deselect(building); } } } ================================================ FILE: Preloader.as ================================================ package { import org.flixel.system.FlxPreloader; public class Preloader extends FlxPreloader { public function Preloader() { className = "king"; super(); } } } ================================================ FILE: Reed.as ================================================ package { import flash.geom.Point; import org.flixel.FlxSprite; import org.flixel.FlxG; import org.flixel.FlxObject; import org.flixel.FlxPoint; public class Reed extends FlxSprite{ [Embed(source='/assets/gfx/reed.png')] private var ReedImg:Class; private var weather:Weather; private var weatherChanged:Number = 0; private var t:Number = 0; public function Reed(X:int, Y:int){ super(X,Y); loadGraphic(ReedImg, true, false, 32, 32); this.weather = (FlxG.state as PlayState).weather; } override public function update():void{ t += weather.wind; frame = int(3 * (0.5 + 0.5*Math.sin(0.05*t + x)) + 0.3 * Math.sin(0.2*t)); /*if (weather.changed > weatherChanged) { weatherChanged = weather.t; var wind:Number = weather.wind; wind = wind * (0.5 + 0.5*Math.sin(x + weather.t*wind*3) + 0.3*Math.sin(x + weather.t*wind*5)); frame = int(3*(1-wind)) }*/ } } } ================================================ FILE: Scaffold.as ================================================ package { import flash.geom.Point; import org.flixel.FlxSprite; import org.flixel.FlxGroup; import org.flixel.FlxG; import org.flixel.FlxObject; import org.flixel.FlxPoint; public class Scaffold extends FlxSprite{ [Embed(source='/assets/gfx/scaffold.png')] public var Img:Class; public function Scaffold(){ super(0,0); loadGraphic(Img); offset.x = 4; width = 24; } public function build(over:FlxSprite):Scaffold { revive(); x = over.x; y = over.y; return this; } } } ================================================ FILE: Shop.as ================================================ package { import flash.geom.Point; import org.flixel.FlxSprite; import org.flixel.FlxGroup; import org.flixel.FlxG; import org.flixel.FlxObject; import org.flixel.FlxPoint; public class Shop extends FlxSprite implements Buildable{ [Embed(source='/assets/gfx/shop.png')] public var Img:Class; public static const SCYTHES:int = 1; public static const BOWS:int = 2; public var type:int; public var supply:int = 0; public function Shop(X:int, Y:int){ super(X,Y+2); if (X > PlayState.GAME_WIDTH/2){ type = BOWS; } else { type = SCYTHES; } loadGraphic(Img,true); width = 56; offset.x = 4; x += 4; height = 46; offset.y = 18; y += 18; moves = false; updateAppearance(); } override public function update():void{ if (supply > 0){ FlxG.overlap(this, (FlxG.state as PlayState).characters, equip) } } public function equip(shop:FlxObject, char:FlxObject):void{ if (supply <= 0) return; var cit:Citizen = char as Citizen; if (cit.occupation == Citizen.POOR){ supply --; if (type == BOWS){ cit.morph(Citizen.HUNTER); } else { cit.morph(Citizen.FARMER); } updateAppearance(); } } public function setSupply(s:int):void{ supply = s; updateAppearance(); } public function updateAppearance():void{ frame = supply; if (type == BOWS){ frame += 5; } } public function canBuild():Boolean{ return (supply < 4); } public function build():void{ (FlxG.state as PlayState).boughtItem = true; supply += 1; flicker(0.3); updateAppearance(); } } } ================================================ FILE: Sky.as ================================================ package{ import org.flixel.FlxSprite; import org.flixel.FlxG; public class Sky extends FlxSprite{ private var weather:Weather; private var weatherChanged:Number = 0; public function Sky(weather:Weather):void{ this.weather = weather; scrollFactor.x = scrollFactor.y = 0; makeGraphic(FlxG.width,FlxG.height,0x00000000,true); } override public function update():void{ if (weather.changed > weatherChanged) { Utils.gradientOverlay(pixels, [weather.sky, weather.horizon, weather.haze],90, 1); dirty = true; weatherChanged = weather.t; } } } } ================================================ FILE: Sparkle.as ================================================ package { import flash.geom.Point; import org.flixel.FlxParticle; import org.flixel.FlxG; import org.flixel.FlxObject; import org.flixel.FlxPoint; import org.flixel.FlxSprite; public class Sparkle extends FlxParticle{ [Embed(source='/assets/gfx/sparkle.png')] private var Img:Class; public function Sparkle(){ super(); loadGraphic(Img,true); addAnimation('splash',[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14],30,false); } override public function reset(X:Number, Y:Number):void{ super.reset(X,Y); lifespan = 1.0; play('splash', true); } } } ================================================ FILE: Splash.as ================================================ package { import flash.geom.Point; import org.flixel.FlxParticle; import org.flixel.FlxG; import org.flixel.FlxObject; import org.flixel.FlxPoint; import org.flixel.FlxSprite; public class Splash extends FlxParticle{ [Embed(source='/assets/gfx/splash.png')] private var Img:Class; public function Splash(){ super(); loadGraphic(Img,true); addAnimation('splash',[0,1,2,3,4],10); } override public function reset(X:Number, Y:Number):void{ super.reset(X,Y); lifespan = 0.5; play('splash'); } } } ================================================ FILE: SunMoon.as ================================================ package{ import org.flixel.FlxSprite; import org.flixel.FlxG; public class SunMoon extends Light{ [Embed(source='/assets/gfx/sunmoon.png')] public var Img:Class; [Embed(source='/assets/gfx/light_mid.png')] private var LightMidImg:Class; [Embed(source='/assets/gfx/light_reflect_wide.png')] private var LightReflectWideImg:Class; public static const ZENITH:Number = 20 // Highest point public static const HORIZON:Number = 100 // Sun "extinguishes" below horizon private var weatherChanged:Number = 0; public function SunMoon(weather:Weather):void{ super(0,0); offset.x = offset.y = 16; scrollFactor.x = scrollFactor.y = 0.0; loadGraphic(Img,true); beam.loadGraphic(LightMidImg); reflected.loadGraphic(LightReflectWideImg); reflected.color = 0xFFfc8f53; beam.blend = 'screen'; } override public function update():void{ /** * the timeOfDay works as follows: * 0 and 1 are night. 0.5 is mid-day. */ if (weather.changed > weatherChanged) { var progressX:Number = (weather.timeOfDay*2+0.5)%1; var progressY:Number = Math.sin(Math.PI*progressX) x = width + (FlxG.width - 2*width) * progressX; y = HORIZON - progressY*(HORIZON-ZENITH); color = weather.sunTint; beam.alpha = progressY * 2; frame = (weather.timeOfDay > 0.25 && weather.timeOfDay < 0.75) ? 0 : 1; dirty = true; beam.drawFrame(true); weatherChanged = weather.t; } super.update(); } } } ================================================ FILE: Torch.as ================================================ package{ import org.flixel.FlxSprite; import org.flixel.FlxG; public class Torch extends Light{ [Embed(source='/assets/gfx/torch.png')] private var TorchImg:Class; [Embed(source='/assets/gfx/light_mid.png')] private var LightMidImg:Class; [Embed(source='/assets/gfx/light_reflect_small.png')] private var LightReflectSmallImg:Class; public function Torch(X:Number, Y:Number){ Y += 8; super(X,Y); offset.x = width/2; offset.y = 8; loadGraphic(TorchImg, true, true, 16, 32); beam.loadGraphic(LightMidImg); reflected.loadGraphic(LightReflectSmallImg); reflected.color = 0xFFfc8f53; if (FlxG.random()<0.5){ addAnimation('on', [0,1,2,3,4,5,6,7], 6, true); } else { addAnimation('on', [8,9,10,11,12,13,14,15], 6, true); } facing = (FlxG.random() < 0.5) ? LEFT : RIGHT; addAnimationCallback(this.dim); play('on'); setLight(); } override public function update():void{ if (x < playstate.kingdomRight + 64 && x > playstate.kingdomLeft - 64){ visible = true; } else { visible = false; } super.update() } } } ================================================ FILE: Treeline.as ================================================ package { import flash.geom.Point; import org.flixel.FlxSprite; import org.flixel.FlxG; import org.flixel.FlxObject; import org.flixel.FlxPoint; public class Treeline extends FlxSprite{ [Embed(source='/assets/gfx/treeline.png')] private var TreelineImg:Class; public function Treeline(X:int, Y:int){ super(X,Y); loadGraphic(TreelineImg, false, true, 96, 160); if (X == 0) facing = LEFT; } } } ================================================ FILE: Troll.as ================================================ package { import flash.geom.Point; import org.flixel.FlxSprite; import org.flixel.FlxG; import org.flixel.FlxObject; import org.flixel.FlxPoint; import org.flixel.FlxCamera; public class Troll extends FlxSprite{ [Embed(source='/assets/gfx/troll.png')] private var Img:Class; [Embed(source='/assets/gfx/trollbig.png')] private var ImgBig:Class; public var t:Number = 0; public var goal:Number = 1600; public var hasCoin:Boolean = false; public var hasCrown:Boolean = false; public var wait:Boolean = false; public var retreating:Boolean = false; public var maxSpeed:Number = 20; public var jumpHeight:Number = 100; public var jumpiness:Number = 0.01; public var confusion:Number = 0.01; public var big:Boolean = false; public var safeDistance:Number = 200; private var maxHeightReached:Number = 0; private var jumpCooldown:Number = 0; private var confuseCooldown:Number = 0; private var playstate:PlayState; public function Troll(){ super(0,0); maxVelocity.y = 275; maxVelocity.x = 60; acceleration.y = 900; loadAnims(); playstate = (FlxG.state as PlayState); // allowCollisions = UP|DOWN; } override public function reset(X:Number,Y:Number):void{ retreating = false; hasCoin = false; wait = true; visible = false; velocity.x = velocity.y = 0; if (! big == playstate.trollBig){ big = playstate.trollBig; loadAnims(); } X -= width / 2; super.reset(X - width / 2, Y); health = playstate.trollHealth; maxSpeed = playstate.trollMaxSpeed; jumpHeight = playstate.trollJumpHeight; jumpiness = playstate.trollJumpiness; jumpCooldown = jumpiness + Math.random() * 2 * jumpiness; confusion = playstate.trollConfusion; confuseCooldown = confusion + Math.random() * 2 * confusion; t = 1; if (playstate.trollsNoCollide.remove(this)){ playstate.trolls.add(this); } } private function loadAnims():void{ if (big){ // scale.x = scale.y = 2; loadGraphic(ImgBig,true,true,64,64); offset.x = 24; offset.y = 24; width = 16; height = 40; addAnimation('walk',[0,1,2,3,4,5,6,7,8], 7, true); addAnimation('walk_crown',[9,10,11,12,13,14,15,16,17], 7, true); addAnimation('stand',[0],10,true); } else { // scale.x = scale.y = 1; loadGraphic(Img,true,true,32,32); offset.x = 12; offset.y = 12; width = 8; height = 20; addAnimation('walk',[0,1,2,3,4,5,6,7,8],(10+FlxG.random()*5),true); addAnimation('walk_crown',[9,10,11,12,13,14,15,16,17],(10+FlxG.random()*5),true); addAnimation('walk_coin',[18,19,20,21,22,23,24,25,26],(10+FlxG.random()*5),true); addAnimation('stand',[0],10,true); } } public function getsCoin():void{ hasCoin = true; retreat(); } public function pickup(coin:FlxObject):void{ if (!hasCoin && coin.alive && !retreating && !big){ coin.kill(); hasCoin = true; retreat(); } } public function stealCrown():void{ hasCrown = true; playstate.panTo(this, 20); retreat(); } public function getShot():void{ if (hasCrown) return; health --; if (health > 0) { // flicker(); } else { Utils.explode(this, playstate.gibs, 1.0); if (hasCoin) { (playstate.coins.recycle(Coin) as Coin).drop(this); } kill(); } } override public function kill():void{ playstate.trollsNoCollide.remove(this); playstate.trolls.add(this); super.kill(); } public function retreat():void{ retreating = true; goal = (x < playstate.player.x) ? 0 : FlxG.worldBounds.width; wait = false; playstate.trolls.remove(this); playstate.trollsNoCollide.add(this); } public function go():void{ wait = false; visible = true; goal = playstate.player.x; if (big) { playstate.trolls.remove(this); playstate.trollsNoCollide.add(this); } } override public function update():void { if (wait){ acceleration.x = 0; velocity.x = 0; return; } confuseCooldown -= FlxG.elapsed; jumpCooldown -= FlxG.elapsed; // Check for movement input acceleration.x = 0; t += FlxG.elapsed; if (!hasCoin && t > 1.8){ if (retreating || confuseCooldown < 0){ goal = (x < playstate.player.x) ? 0 : FlxG.worldBounds.width; confuseCooldown = confusion + Math.random() * 2 * confusion; } else { goal = playstate.player.x; } t = 0 } // I don't know why I need this, but apparently trolls can fall of the world. if (x <= 24 || x + width >= FlxG.worldBounds.width - 24){ if (retreating) kill(); } if (y > 200){ // throw new Error("TROLL FELL OFF :("); FlxG.log("TROLL FELL " + x + " , " + y) FlxG.log("Retreating: " + retreating); FlxG.log("Big: " + big); FlxG.log("Wait: " + wait); kill(); } facing = (goal > x) ? RIGHT : LEFT; if(touching & FLOOR){ maxVelocity.x = maxSpeed; // Sprint outside of kingdom. if (x > playstate.kingdomRight + safeDistance || x < playstate.kingdomLeft - safeDistance){ maxVelocity.x += 40; } drag.x = maxVelocity.x*10; if(facing == LEFT){ acceleration.x = -maxVelocity.x*4; } else { acceleration.x = maxVelocity.x*4; } if (hasCrown) play('walk_crown'); else if (hasCoin) play('walk_coin'); else play('walk') // Jump if(jumpCooldown < 0){ maxHeightReached = 0; var v:Number = Math.sqrt(jumpHeight * 2 * acceleration.y) velocity.y = -v; maxVelocity.x = maxSpeed * 2; velocity.x *= 2 jumpCooldown = jumpiness + Math.random() * 2 * jumpiness; } } else { maxHeightReached = Math.max(112 - y, maxHeightReached) drag.x = maxVelocity.x*0.1; maxVelocity.x = maxSpeed * 2; if(facing == LEFT) acceleration.x = -maxVelocity.x; else acceleration.x = maxVelocity.x; } super.update(); } } } ================================================ FILE: Utils.as ================================================ package { import flash.display.BitmapData; import flash.display.Shape; import flash.geom.Rectangle; import flash.geom.Matrix; import org.flixel.FlxPoint; import org.flixel.FlxSprite; import org.flixel.FlxParticle; import org.flixel.FlxGroup; import org.flixel.FlxG; public class Utils{ public static const DEG_TO_RAD:Number = Math.PI/180; public static var ROMAN_VALUES:Array = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1] public static var ROMAN_LETTERS:Array = ['M', 'CM', 'D', 'CD','C', 'XC','L','XL','X','IX','V','IV','I'] /** * Shrinks the hitbox (x,y and offset) of given FlxSprite to the pixels that are actually * covered by a greater portion than min{X,Y}Cover of the pixels. */ static public function shrinkHitbox(sprite:FlxSprite, minXCover:Number=0, minYCover:Number=0.5):void{ var pix:BitmapData = sprite.pixels; var x:int, y:int, sum:int = 0; var minx:int = pix.width; var maxx:int = 0; var miny:int = pix.height; var maxy:int = 0; for (x = 0; x < pix.width; x++){ sum = 0 for (y = 0; y < pix.height; y++){ if (pix.getPixel32(x,y) > 0x00FFFFFF) sum ++; } if (sum >= minXCover*pix.height) { minx = Math.min(minx,x); maxx = Math.max(maxx,x); } } for (y = 0; y < pix.height; y++){ sum = 0 for (x = 0; x < pix.width; x++){ if (pix.getPixel32(x,y) > 0x00FFFFFF) sum ++; } if (sum >= minYCover*pix.width) { miny = Math.min(miny,y); maxy = Math.max(maxy,y); } } sprite.width = maxx-minx; sprite.height = maxy-miny; sprite.offset.x = minx sprite.offset.y = miny; sprite.x += minx/2; sprite.y += miny/2; } static public function findRectanglesWithColor(bitmap:BitmapData, color:uint):Vector. { var cur:Rectangle; var rects:Vector. = new Vector.(); var i:int,j:int,k:int; for(i = 0; i < bitmap.height; i++){ for(j = 0; j < bitmap.width; j++){ for(k = 0; k < rects.length; k++){ cur = rects[k]; // If we are in a rect, skip to the right side. if (cur.contains(j,i)){ j = cur.right; // If we are outside of the image, next line. if (j >= bitmap.width){ j = 0; i++; } } } if (bitmap.getPixel32(j,i) == color){ rects.push(cur = new Rectangle(j,i,1,1)); // Traverse to right while( bitmap.getPixel32(j,i) == color){ j++; } cur.width = j - cur.x; // Traverse down while( bitmap.getPixel32(j-1,i) == color){ i++; } cur.height = i - cur.y; // Reset I and J j = cur.right; i = cur.y; } } } return rects; } /** * Replace one color with another in bitmap. */ static public function replaceColor(bitmap:BitmapData,fromColor:uint, toColor:uint):void{ for (var i:int = 0; i < bitmap.height; i++){ for( var j:int = 0; j < bitmap.width; j++){ if (bitmap.getPixel32(j,i) == fromColor){ bitmap.setPixel32(j, i, toColor); } } } } /** * Fills a bitmap with a gradient with given colors */ static public function gradientOverlay(bitmap:BitmapData, colors:Array, rotation:Number=90, chunks:int = 1):void{ var matrix:Matrix = new Matrix(); matrix.createGradientBox(bitmap.width/chunks, bitmap.height/chunks, rotation*DEG_TO_RAD); var s:Shape = new Shape(); var ratios:Array = new Array(colors.length); var alphas:Array = new Array(colors.length); for (var i:int = 0; i < colors.length; i ++){ alphas[i] = (colors[i] >>> 24)/255; ratios[i] = (i * (1/(colors.length-1)))*255; } s.graphics.beginGradientFill("linear", colors, alphas, ratios, matrix, "pad", "rgb"); s.graphics.drawRect(0, 0, bitmap.width/chunks, bitmap.height/chunks); if (chunks == 1) { bitmap.draw(s); } else { var transform:Matrix = new Matrix(); var tempBitmap:BitmapData = new BitmapData(bitmap.width/chunks,bitmap.height/chunks,true,0x000000); tempBitmap.draw(s); transform.scale(bitmap.width/tempBitmap.width,bitmap.height/tempBitmap.height); bitmap.draw(tempBitmap,transform); } } /** * Convert a HSV (hue, saturation, lightness) color space value to an RGB color * * @param h Hue degree, between 0 and 359 * @param s Saturation, between 0.0 (grey) and 1.0 * @param v Value, between 0.0 (black) and 1.0 * * @return 32-bit RGB colour value (0xAARRGGBB) */ public static function HSVtoRGB(h:Number, s:Number, v:Number):uint { var result:uint; if (s == 0.0) { result = getColor32(255, v * 255, v * 255, v * 255); } else { h = h / 60.0; var f:Number = h - int(h); var p:Number = v * (1.0 - s); var q:Number = v * (1.0 - s * f); var t:Number = v * (1.0 - s * (1.0 - f)); switch (int(h)) { case 0: result = getColor32(255, v * 255, t * 255, p * 255); break; case 1: result = getColor32(255, q * 255, v * 255, p * 255); break; case 2: result = getColor32(255, p * 255, v * 255, t * 255); break; case 3: result = getColor32(255, p * 255, q * 255, v * 255); break; case 4: result = getColor32(255, t * 255, p * 255, v * 255); break; case 5: result = getColor32(255, v * 255, p * 255, q * 255); break; default: FlxG.log("FlxColor Error: HSVtoRGB : Unknown color"); } } return result; } public static function RGBtoHSV(color:uint):Object { var rgb:Object = getRGB(color); var red:Number = rgb.red / 255; var green:Number = rgb.green / 255; var blue:Number = rgb.blue / 255; var min:Number = Math.min(red, green, blue); var max:Number = Math.max(red, green, blue); var delta:Number = max - min; var lightness:Number = (max + min) / 2; var hue:Number; var saturation:Number; // Grey color, no chroma if (delta == 0) { hue = 0; saturation = 0; } else { if (lightness < 0.5) { saturation = delta / (max + min); } else { saturation = delta / (2 - max - min); } var delta_r:Number = (((max - red) / 6) + (delta / 2)) / delta; var delta_g:Number = (((max - green) / 6) + (delta / 2)) / delta; var delta_b:Number = (((max - blue) / 6) + (delta / 2)) / delta; if (red == max) { hue = delta_b - delta_g; } else if (green == max) { hue = (1 / 3) + delta_r - delta_b; } else if (blue == max) { hue = (2 / 3) + delta_g - delta_r; } if (hue < 0) { hue += 1; } if (hue > 1) { hue -= 1; } } // Keep the value with 0 to 359 hue *= 360; hue = Math.round(hue); // Testing //saturation *= 100; //lightness *= 100; return { hue: hue, saturation: saturation, lightness: lightness, value: lightness }; } public static function interpolateColor(color1:uint, color2:uint, f:Number):uint { var a1:uint = color1 >>> 24; var r1:uint = color1 >> 16 & 0xFF; var g1:uint = color1 >> 8 & 0xFF; var b1:uint = color1 & 0xFF; var a2:uint = color2 >>> 24; var r2:uint = color2 >> 16 & 0xFF; var g2:uint = color2 >> 8 & 0xFF; var b2:uint = color2 & 0xFF; var fi:Number = (1-f); a1 = (fi * a1) + (f * a2); r1 = (fi * r1) + (f * r2); g1 = (fi * g1) + (f * g2); b1 = (fi * b1) + (f * b2); return a1 << 24 | r1 << 16 | g1 << 8 | b1; } public static function interpolateColorAndAlpha(color1:uint, color2:uint, steps:uint, currentStep:uint):uint { var src1:Object = getRGB(color1); var src2:Object = getRGB(color2); var a:uint = (((src2.alpha - src1.alpha) * currentStep) / steps) + src1.alpha; var r:uint = (((src2.red - src1.red) * currentStep) / steps) + src1.red; var g:uint = (((src2.green - src1.green) * currentStep) / steps) + src1.green; var b:uint = (((src2.blue - src1.blue) * currentStep) / steps) + src1.blue; return getColor32(a, r, g, b); } /** * Return the component parts of a color as an Object with the properties alpha, red, green, blue * *

Alpha will only be set if it exist in the given color (0xAARRGGBB)

* * @param color in RGB (0xRRGGBB) or ARGB format (0xAARRGGBB) * * @return Object with properties: alpha, red, green, blue */ public static function getRGB(color:uint):Object { var alpha:uint = color >>> 24; var red:uint = color >> 16 & 0xFF; var green:uint = color >> 8 & 0xFF; var blue:uint = color & 0xFF; return { alpha: alpha, red: red, green: green, blue: blue }; } /** * Given an alpha and 3 color values this will return an integer representation of it * * @param alpha The Alpha value (between 0 and 255) * @param red The Red channel value (between 0 and 255) * @param green The Green channel value (between 0 and 255) * @param blue The Blue channel value (between 0 and 255) * * @return A native color value integer (format: 0xAARRGGBB) */ public static function getColor32(alpha:uint, red:uint, green:uint, blue:uint):uint { return alpha << 24 | red << 16 | green << 8 | blue; } public static function explode(object:FlxSprite, group:FlxGroup, portion:Number = 1, gibsize:int=4, rounded:Boolean=true):Vector.{ var gibs:Vector. = new Vector.() var gib:FlxParticle; for (var x:int = 0; x < object.framePixels.width; x += gibsize){ for (var y:int = 0; y < object.framePixels.height; y += gibsize){ if ((object.framePixels.getPixel32(x+gibsize/2,y+gibsize/2) >>> 24) > 0){ if (FlxG.random() < portion){ gib = group.recycle(FlxParticle) as FlxParticle; if (gib.frameWidth != gibsize || gib.frameHeight != gibsize){ gib.makeGraphic(gibsize,gibsize,0,true); } gib.revive(); // _flashPoint.x = X; // _flashPoint.y = Y; // _flashRect2.width = bitmapData.width; // _flashRect2.height = bitmapData.height; // gib.framePixels.copyPixels(object._framePixels,rect,point,null,null,true); gib.stamp(object, -x, -y); if (rounded){ gib.framePixels.setPixel32(0,0,0); gib.framePixels.setPixel32(0,gibsize-1,0); gib.framePixels.setPixel32(gibsize-1,0,0); gib.framePixels.setPixel32(gibsize-1,gibsize-1,0); } gibs.push(gib); gib.elasticity = 0.5; gib.lifespan = 7; gib.x = object.x - object.offset.x + x; gib.y = object.y - object.offset.y + y; gib.acceleration.y = 900; gib.velocity.x = FlxG.random()*80 - 40; gib.velocity.y = -130-FlxG.random()*30; } } } } return gibs; } /** * Returns a roman numeral string */ public static function toRoman(n:int):String{ var s:String = '' for (var i:int = 0; i < ROMAN_LETTERS.length; i ++){ var c:int = Math.floor(n / ROMAN_VALUES[i]) n -= c * ROMAN_VALUES[i]; while (c > 0){ s += ROMAN_LETTERS[i]; c --; } } return s; } } } ================================================ FILE: Wall.as ================================================ package { import flash.geom.Point; import org.flixel.FlxSprite; import org.flixel.FlxG; import org.flixel.FlxObject; import org.flixel.FlxPoint; public class Wall extends FlxSprite implements Workable, Buildable{ [Embed(source='/assets/gfx/wall.png')] private var WallImg:Class; [Embed(source="/assets/sound/hitwall.mp3")] private var HitwallSound:Class; public const HEIGHT:Array = [11,38,46,54,59]; // Effective Height: [26, 34, 42, 47] public const HEALTH:Array = [2,38,50,60,75]; public const HURT_COOLDOWN:Number = 1; public const WORK_BUILD_HEIGHT:int = 10; public const WORK_HEAL_AMOUNT:int = 4; private var playstate:PlayState; public var scaffold:Scaffold = null; public var building:Boolean = false; public var heightToBuild:int = 0; public var baseY:Number; public var stage:int = 0; private var t:Number = 0; public function Wall(X:Number, Y:Number){ baseY = Y; super(X,Y); immovable = true; moves = false; solid = false; loadGraphic(WallImg, true, true, 32, 64); addAnimation("grow",[0,1,2,3,4,5,6,7,8,9],1); if (X > 1920){ facing = LEFT; } offset.x = 4; width = 24; health = HEALTH[stage]; updateAppearance(); playstate = FlxG.state as PlayState; } public function build():void{ buildTo(stage + 1); } public function buildTo(s:int, instant:Boolean=false):void{ if (!instant && s < stage) return; building = true; stage = s; heightToBuild = HEIGHT[stage]; health = HEALTH[stage]; updateAppearance(); if (scaffold != null){ scaffold.kill(); } scaffold = (playstate.indicators.recycle(Scaffold) as Scaffold).build(this); scaffold.y = y - HEIGHT[stage]; solid = false; // Kind of hacky this. if (instant){ heightToBuild = 1; work(null); } // flicker(); } public function work(citizen:Citizen=null):void{ if (heightToBuild > 0) { heightToBuild -= WORK_BUILD_HEIGHT; if (heightToBuild <= 0){ heightToBuild = 0; building = false; solid = true; Utils.explode(scaffold, playstate.gibs, 1); scaffold.kill(); scaffold = null; } } else { if (health < HEALTH[stage]){ health += Math.min(WORK_HEAL_AMOUNT, HEALTH[stage] - health); } } updateAppearance(); } override public function hurt(Damage:Number):void{ if (t > HURT_COOLDOWN){ health -= Damage; FlxG.play(HitwallSound).proximity(x, y, playstate.player, FlxG.width) Utils.explode(this, playstate.gibs, 0.1); t = 0; } if (health <= 0 && stage > 0){ Utils.explode(this, playstate.gibs, 1.0); stage = 0; health = HEALTH[stage]; } health = Math.max(health, HEALTH[0]); updateAppearance(); } public function needsWork():Boolean{ return ((building || health < HEALTH[stage]) && t < HURT_COOLDOWN); } public function canBuild():Boolean{ return (!building && stage < 4) } private function updateAppearance():void{ height = HEIGHT[stage] - heightToBuild; y = baseY + 64 - height; offset.y = 64 - HEIGHT[stage]; if (health < HEALTH[stage] / 2){ frame = stage + 5; } else { frame = stage; } } override public function update():void{ t += FlxG.elapsed; if (this.stage > 0 && !building){ if (this.x > PlayState.GAME_WIDTH/2){ playstate.kingdomRight = Math.max(playstate.kingdomRight, x) } else { playstate.kingdomLeft = Math.min(playstate.kingdomLeft, x+width) } } } } } ================================================ FILE: Water.as ================================================ package { import flash.display.BitmapData; import flash.display.BitmapDataChannel; import flash.filters.DisplacementMapFilter; import flash.filters.DisplacementMapFilterMode; import flash.geom.ColorTransform; import flash.geom.Matrix; import flash.geom.Point; import flash.geom.Rectangle; import org.flixel.FlxSprite; import org.flixel.FlxGroup; import org.flixel.FlxG; public class Water extends FlxSprite { public static const NOISE_BIAS:int = 100; public static const WIND_RIPPLE_MULTIPLIER:Number = 25; private var rect:Rectangle = new Rectangle(0, 0, 480, 160); //private var point:Point = new Point(0, 160); private var zeroPoint:Point = new Point(0, 0); private var perlinOffset:Point = new Point(0,0) private var matrix:Matrix = new Matrix(); private var transform:ColorTransform; private var noiseRange:ColorTransform; private var displacementFilter:DisplacementMapFilter; private var displacementBitmap:BitmapData; private var baseColor:uint; private var currentBase:uint; private var timer:Number = 0; private var lights:FlxGroup; private var weather:Weather; private var weatherChanged:Number = -1; public function Water(x:int,y:int,width:int,height:int,lights:FlxGroup,weather:Weather,baseColor:uint=0xFF686C53,reflectivity:Number=0.3) { // Set height/width/x/y this.x = x; this.y = y; moves = false; scrollFactor.x = 0; transform = new ColorTransform(1,1,1,reflectivity); rect = new Rectangle(0,0,width,height); displacementBitmap = new BitmapData(width, height, false, 0); makeGraphic(width,height,baseColor); this.lights = lights; this.weather = weather; this.baseColor = baseColor; // This is the filter that makes the reflection ripple displacementFilter = new DisplacementMapFilter(displacementBitmap, zeroPoint, 1, 2, 256, 256, DisplacementMapFilterMode.COLOR, baseColor, 0.5); // Reduce the range of perlin transform } override public function update():void { timer += FlxG.elapsed; if (weather.changed > weatherChanged){ currentBase = 0xFF000000 | Utils.interpolateColor(baseColor, weather.darknessColor, weather.darkness) var rippleScale:int = int(weather.wind*WIND_RIPPLE_MULTIPLIER); var xscale:int = rippleScale/2; var yscale:int = rippleScale; noiseRange = new ColorTransform(xscale/128,yscale/128,1,1,(128-xscale+(NOISE_BIAS*xscale/128)),(128-yscale+(NOISE_BIAS*yscale/128)),1,1) weatherChanged = weather.t } } override public function draw():void { if (timer > 0.1) { // Update the water ripple perlinOffset.y += 1/5; perlinOffset.x = FlxG.camera.scroll.x*1.5; displacementBitmap.perlinNoise(32, 4, 1, 12312, false, false, 1|2, true, [perlinOffset]); displacementBitmap.colorTransform(rect,noiseRange); // Adjust the base color according to the weather. displacementFilter.color = currentBase; timer = 0; } var px:BitmapData = pixels; matrix.identity(); matrix.scale(1, -1); getScreenXY(_point); matrix.translate(-_point.x, _point.y); // Clear the reflection px.fillRect(rect, currentBase); Utils.gradientOverlay(px, [0x00000000,0x66000000], 90, 4); // Flip the screen and copy it to the reflection px.draw(FlxG.camera.buffer, matrix, transform); // Draw the lights var l:Light; for (var i:int = 0; i < lights.length; i++){ l = lights.members[i] as Light; l.getScreenXY(_point); if(l.visible && -64 < _point.x && _point.x < FlxG.width + 64){ l.reflected.alpha = weather.darkness * 0.8; l.reflected.alpha *= Math.min(1.0, (weather.wind * 10)); l.reflected.drawFrame(); stamp(l.reflected, _point.x - l.reflected.width/2 + 4, (y - l.y) * 0.3); } } // Apply the ripple filter px.applyFilter(px, rect, zeroPoint, displacementFilter); dirty = true; super.draw() } } } ================================================ FILE: Weather.as ================================================ package{ import org.flixel.FlxG; import com.quasimondo.geom.ColorMatrix public class Weather extends Object{ public var sky:uint = 0xFF8C8CA6; public var horizon:uint = 0xFFCF7968; public var haze:uint = 0xAAf3f1e8; public var darknessColor:uint = 0x88111114; public var darkness:Number = 1.0; public var contrast:Number = 0.3; public var saturation:Number = 1.0 public var ambient:uint = 0x11FF0000; public var wind:Number = 0.0; public var fog:Number = 0.5; public var rain:Number = 0.5; public var timeOfDay:Number = 0.5; public var sunTint:uint = 0xFFFFFF; public var ambientTransform:ColorMatrix = new ColorMatrix(); public var t:Number = 0; public var changed:Number = 0; public var progress:Number = 0; public var ambientAmount:Number = 0; public var tweenStart:Number = 0; public var tweenDuration:Number = 0.0; public var previousState:Object = WeatherPresets.SUNNY; public var targetState:Object = WeatherPresets.SUNNY; public function Weather(){ setVariables(WeatherPresets.SUNNY); } public function update():void{ t += FlxG.elapsed; if (t - changed > 1/30){ updateTween(); changed = t; } } public function tweenTo(state:Object, d:Number=30):void{ targetState = state; if (d == 0){ setVariables(state) previousState = state; } else { tweenDuration = d; tweenStart = t; } } public function updateTween():void{ if (targetState === previousState) return; // Compute the tween factor progress = (t - tweenStart)/tweenDuration; if (tweenDuration == 0 || progress >= 1) { previousState = targetState; progress = 1; } setVariables(targetState, previousState, progress); } private function setVariables(target:Object, previous:Object=null, f:Number = 1):void{ // Very ugly if (!target.hasOwnProperty('ambientAmount')){ target['ambientAmount'] = ((target['ambient'] >> 24) / 0xFF); } if (previous === null){ previous = target; } var fi:Number = 1 - f; // Loop through the variables and tween them for (var v:String in target){ // List non-color props here. if (v == 'darkness' || v == 'contrast' || v == 'saturation' || v == 'fog' || v == 'rain' || v == 'wind' || v == 'ambientAmount'){ this[v] = (fi * previous[v]) + (f * target[v]); // timeOfDay is weird and circular. } else if (v == 'timeOfDay'){ if (target[v] > previous[v]) this[v] = (previous[v] + (target[v]-previous[v])*f)%1; else this[v] = (previous[v] + (target[v]+1-previous[v])*f)%1; } else { // Interpolate a color. this[v] = Utils.interpolateColor(previous[v], target[v], f); } } // Set the other vars ambientTransform.reset(); ambientTransform.colorize(ambient, ambientAmount); ambientTransform.adjustContrast(contrast); ambientTransform.adjustSaturation(saturation); // Set opacity of the darknessColor to darkness. darknessColor = (darknessColor&0x00FFFFFF) | (uint(0xFF*darkness) << 24) ; } } } ================================================ FILE: WeatherPresets.as ================================================ // THIS FILE IS AUTOGENERATED, MODIFY weathers.json IN STEAD. package { public class WeatherPresets extends Object{ public static const NIGHTGREEN:Object = { 'saturation': 0.7, 'darkness': 0.45, 'sky': 0xFF005EA5, 'haze': 0xFFB8F2BB, 'sunTint': 0xDDDDFF, 'fog': 0.4, 'contrast': 0.0, 'horizon': 0xFF002E80, 'ambient': 0x2254FFAF, 'timeOfDay': 0.85, 'wind': 0.1, 'darknessColor': 0x88263529 } public static const NIGHTFOGGY:Object = { 'saturation': 0.7, 'darkness': 0.7, 'sky': 0xFF25229d, 'haze': 0xFFd5d9ff, 'sunTint': 0xffd9c8, 'fog': 1.0, 'contrast': -0.1, 'horizon': 0xFF6a6d55, 'ambient': 0x7763709d, 'timeOfDay': 0.8, 'wind': 0.2, 'darknessColor': 0x88111114 } public static const NIGHTLONG:Object = { 'saturation': 1.0, 'darkness': 0.4, 'sky': 0xFF7399c8, 'sunTint': 0x162039, 'haze': 0xFF000000, 'fog': 0.0, 'wind': 0.1, 'horizon': 0xFF7399c8, 'ambient': 0x55acc857, 'timeOfDay': 0.9, 'contrast': 0.3, 'darknessColor': 0x88000000 } public static const DAYCLEARCOLD:Object = { 'saturation': 0.7, 'darkness': 0.0, 'sky': 0xFFC9E3EA, 'haze': 0x33C9E3EA, 'sunTint': 0xF7E9AA, 'fog': 0.0, 'contrast': 0.2, 'horizon': 0xFFC9E3EA, 'ambient': 0x33F7E0C3, 'timeOfDay': 0.45, 'wind': 0.5, 'darknessColor': 0x88111114 } public static const DAWNBLEAK:Object = { 'saturation': 0.9, 'darkness': 0.2, 'sky': 0xFFC4AD99, 'haze': 0xAAf3f1e8, 'sunTint': 0xF9B340, 'fog': 0.5, 'contrast': 0.5, 'horizon': 0xFFCECECE, 'ambient': 0x22FF84DA, 'timeOfDay': 0.31, 'wind': 0.1, 'darknessColor': 0x88111114 } public static const DAYORANGESKY:Object = { 'saturation': 0.7, 'darkness': 0.1, 'sky': 0xFFADA290, 'haze': 0x445A432C, 'sunTint': 0xF9F8E6, 'fog': 0.3, 'contrast': 0.2, 'horizon': 0xFFDCAB4F, 'ambient': 0x110E0B22, 'timeOfDay': 0.4, 'wind': 0.7, 'darknessColor': 0x880E0B62 } public static const DAYPASTEL:Object = { 'saturation': 1.0, 'darkness': 0.0, 'sky': 0xFF3090F6, 'sunTint': 0xF9F8E6, 'haze': 0xDD657A8F, 'fog': 0.0, 'wind': 0.3, 'horizon': 0xFFEEEE88, 'ambient': 0x44886A00, 'timeOfDay': 0.55, 'contrast': 1.0, 'darknessColor': 0x88000BBB } public static const DUSKPINK:Object = { 'saturation': 0.9, 'darkness': 0.0, 'sky': 0xFF8C8CA6, 'haze': 0xAAf3c1e8, 'sunTint': 0xF9B340, 'fog': 0.0, 'contrast': 0.8, 'horizon': 0xFFEDC99A, 'ambient': 0x44F79A42, 'timeOfDay': 0.651, 'wind': 0.1, 'darknessColor': 0x88111114 } public static const DAWNCLEARORANGE:Object = { 'saturation': 0.8, 'darkness': 0.2, 'sky': 0xFF97A7B4, 'haze': 0x88FDB24C, 'sunTint': 0xFAFDC9, 'fog': 0.0, 'contrast': 0.5, 'horizon': 0xFFF9A04F, 'ambient': 0x11FF0000, 'timeOfDay': 0.28, 'wind': 0.1, 'darknessColor': 0x88111114 } public static const EVENINGORANGE:Object = { 'saturation': 0.8, 'darkness': 0.1, 'sky': 0xFF8BB8E8, 'haze': 0xFFFF9068, 'sunTint': 0xFFFF7038, 'fog': 0.0, 'contrast': 0.7, 'horizon': 0xFFEDC99A, 'ambient': 0x33DE5E37, 'timeOfDay': 0.70, 'wind': 0.1, 'darknessColor': 0x88111114 } public static const NIGHTSHINE:Object = { 'saturation': 0.5, 'darkness': 0.35, 'sky': 0xFF000000, 'sunTint': 0xF9F8E6, 'haze': 0x00886AAA, 'fog': 0.0, 'wind': 0.2, 'horizon': 0xFFAAAAFF, 'ambient': 0x00000000, 'timeOfDay': 0.9, 'contrast': 4.0, 'darknessColor': 0x88222255 } public static const EVENINGFOGGY:Object = { 'saturation': 0.7, 'darkness': 0.3, 'sky': 0xFFd56c47, 'haze': 0xFFd5d9ff, 'sunTint': 0xffd9c8, 'fog': 1.0, 'contrast': -0.1, 'horizon': 0xFF6a6d55, 'ambient': 0x440000FF, 'timeOfDay': 0.7, 'wind': 0.2, 'darknessColor': 0x88111114 } public static const NIGHTPURPLE:Object = { 'saturation': 0.8, 'darkness': 0.3, 'sky': 0xFF57577D, 'sunTint': 0xF9F8E6, 'haze': 0x88886AAA, 'fog': 0.0, 'wind': 0.2, 'horizon': 0xFF4D4658, 'ambient': 0x33886AAA, 'timeOfDay': 0.9, 'contrast': 1.4, 'darknessColor': 0x88990BBB } public static const EVENINGBLACK:Object = { 'saturation': 0.7, 'darkness': 0.3, 'sky': 0xFF333333, 'haze': 0xFFFF9090, 'sunTint': 0xFFFF7038, 'fog': 0.0, 'contrast': 0.0, 'horizon': 0xFFEDC99A, 'ambient': 0x339f6b5c, 'timeOfDay': 0.70, 'wind': 0.1, 'darknessColor': 0x88111114 } public static const EVENINGMONOTONE:Object = { 'saturation': 0.7, 'darkness': 0.3, 'sky': 0xFF333333, 'haze': 0xFFFF9090, 'sunTint': 0xAAAAAA, 'fog': 0.0, 'contrast': 0.0, 'horizon': 0xFFEDEDED, 'ambient': 0x33666666, 'timeOfDay': 0.70, 'wind': 0.1, 'darknessColor': 0x88111114 } public static const NIGHTCLEAR:Object = { 'saturation': 0.8, 'darkness': 0.4, 'sky': 0xFF005EA5, 'haze': 0x44434e87, 'sunTint': 0xDDDDFF, 'fog': 0.1, 'contrast': -0.1, 'horizon': 0xFF002E80, 'ambient': 0x110000FF, 'timeOfDay': 0.1, 'wind': 0.3, 'darknessColor': 0x88111114 } public static const SUNNY:Object = { 'saturation': 0.8, 'darkness': 0.0, 'sky': 0xFF98BEEC, 'haze': 0xCCf3f1e8, 'sunTint': 0xfff766, 'fog': 0.0, 'contrast': 0.8, 'horizon': 0xFFC4DAF1, 'ambient': 0x44FFBB7F, 'timeOfDay': 0.6, 'wind': 1.0, 'darknessColor': 0x88111114 } public static const DAYBLEAK:Object = { 'saturation': 0.7, 'darkness': 0.0, 'sky': 0xFFA0C2E8, 'haze': 0xFFf3f1e8, 'sunTint': 0xF7E9AA, 'fog': 1.0, 'contrast': 0.0, 'horizon': 0xFFA6C9ED, 'ambient': 0x33F7E0C3, 'timeOfDay': 0.45, 'wind': 0.5, 'darknessColor': 0x88111114 } public static const NIGHTREDMOON:Object = { 'saturation': 0.9, 'darkness': 0.4, 'sky': 0xFF142744, 'haze': 0x665C5D9E, 'sunTint': 0xC73800, 'fog': 0.1, 'contrast': 0.2, 'horizon': 0xFFAD2E21, 'ambient': 0x220E0B62, 'timeOfDay': 0.1, 'wind': 0.1, 'darknessColor': 0x880E0B62 } public static const DAYTEMP:Object = { 'saturation': 0.0, 'darkness': 0.2, 'sky': 0xFF6a6d55, 'haze': 0xFF999d7c, 'sunTint': 0xffffff, 'fog': 1.0, 'contrast': -0.2, 'horizon': 0xFF6a6d55, 'ambient': 0xFFFF0000, 'timeOfDay': 0.5, 'wind': 0.2, 'darknessColor': 0x88111114 } public static const FOGGY:Object = { 'saturation': 0.7, 'darkness': 0.2, 'sky': 0xFF6a6d55, 'haze': 0xFF999d7c, 'sunTint': 0xffffff, 'fog': 1.0, 'contrast': -0.2, 'horizon': 0xFF6a6d55, 'ambient': 0x330000FF, 'timeOfDay': 0.25, 'wind': 0.2, 'darknessColor': 0x88111114 } public static const DAWNBROWN:Object = { 'saturation': 0.8, 'darkness': 0.0, 'sky': 0xFFC0AFBD, 'sunTint': 0xF9F8E6, 'haze': 0x99AAAAAA, 'fog': 0.2, 'wind': 0.2, 'horizon': 0xFF94A9B6, 'ambient': 0x55AD3200, 'timeOfDay': 0.28, 'contrast': 0.7, 'darknessColor': 0x88222255 } public static const DUSKFOGGY:Object = { 'saturation': 0.6, 'darkness': 0.25, 'sky': 0xFFB29C8F, 'sunTint': 0xF9F8E6, 'haze': 0x00000000, 'fog': 0.8, 'wind': 0.4, 'horizon': 0xFF3D6BCD, 'ambient': 0x330E0B22, 'timeOfDay': 0.75, 'contrast': 0.1, 'darknessColor': 0x880E0B62 } public static const DAWNLIGHTPINK:Object = { 'saturation': 0.8, 'darkness': 0.1, 'sky': 0xFF8C8CA6, 'haze': 0xAAf3f1e8, 'sunTint': 0xF9B340, 'fog': 0.5, 'contrast': 0.5, 'horizon': 0xFFCF7968, 'ambient': 0x22FF84DA, 'timeOfDay': 0.28, 'wind': 0.1, 'darknessColor': 0x88111114 } public static const NIGHTDARK:Object = { 'saturation': 0.7, 'darkness': 0.75, 'sky': 0xFF002E33, 'haze': 0xFF555555, 'sunTint': 0x65a2cb, 'fog': 0.0, 'contrast': 0.4, 'horizon': 0xFF002E80, 'ambient': 0x4454AACF, 'timeOfDay': 0.85, 'wind': 0.1, 'darknessColor': 0xFF263529 } public static const DUSKWARM:Object = { 'saturation': 0.9, 'darkness': 0.0, 'sky': 0xFF8BB8E8, 'haze': 0x88f3f1e8, 'sunTint': 0xF4EED0, 'fog': 0.0, 'contrast': 0.8, 'horizon': 0xFFEDC99A, 'ambient': 0x44F79A42, 'timeOfDay': 0.651, 'wind': 0.1, 'darknessColor': 0x88111114 } public static const DAWNEARLY:Object = { 'saturation': 0.9, 'darkness': 0.2, 'sky': 0xFFC4AD99, 'haze': 0xAAf3f1e8, 'sunTint': 0xF9B340, 'fog': 0.5, 'contrast': 0.5, 'horizon': 0xFFCECECE, 'ambient': 0x22FF84DA, 'timeOfDay': 0.19, 'wind': 0.1, 'darknessColor': 0x88111114 } public static const DAWNBRIGHT:Object = { 'saturation': 0.8, 'darkness': 0.15, 'sky': 0xFF57577D, 'sunTint': 0xF9F8E6, 'haze': 0x00886AAA, 'fog': 0.0, 'wind': 0.2, 'horizon': 0xFFEEEE88, 'ambient': 0xff886A00, 'timeOfDay': 0.3, 'contrast': 1.4, 'darknessColor': 0x88000BBB } public static const DAWNGREY:Object = { 'saturation': 0.5, 'darkness': 0.2, 'sky': 0xFFC4AD99, 'haze': 0xAAf3f1e8, 'sunTint': 0xF9B340, 'fog': 0.5, 'contrast': 0.5, 'horizon': 0xFFCECECE, 'ambient': 0x22FF84DA, 'timeOfDay': 0.31, 'wind': 0.1, 'darknessColor': 0x88111114 } public static const NIGHTSUPERDARK:Object = { 'saturation': 0.7, 'darkness': 0.7, 'sky': 0xFF000000, 'haze': 0xFFFFFFFF, 'sunTint': 0x65a2cb, 'fog': 0.0, 'contrast': 0.4, 'horizon': 0xFF002E80, 'ambient': 0x4454AACF, 'timeOfDay': 0.85, 'wind': 0.1, 'darknessColor': 0xFF263529 } public static const DUSKCLEAR:Object = { 'saturation': 0.9, 'darkness': 0.2, 'sky': 0xFF513744, 'haze': 0xAAC9976D, 'sunTint': 0xF9B340, 'fog': 0.0, 'contrast': 0.45, 'horizon': 0xFFC69875, 'ambient': 0x22360A00, 'timeOfDay': 0.651, 'wind': 0.1, 'darknessColor': 0x88111114 } public static const NIGHT:Object = { 'saturation': 0.8, 'darkness': 0.4, 'sky': 0xFF005EA5, 'haze': 0xFF333333, 'sunTint': 0xDDDDFF, 'fog': 0.2, 'contrast': 0.5, 'horizon': 0xFF002E80, 'ambient': 0x110000FF, 'timeOfDay': 0, 'wind': 0.2, 'darknessColor': 0x88111114 } public static const DAWNTEMP:Object = { 'saturation': 0.0, 'darkness': 0.2, 'sky': 0xFF6a6d55, 'haze': 0xFF999d7c, 'sunTint': 0xffffff, 'fog': 1.0, 'contrast': -0.2, 'horizon': 0xFF6a6d55, 'ambient': 0xFFFF0000, 'timeOfDay': 0.25, 'wind': 0.2, 'darknessColor': 0x88111114 } public static const DAYWINDYCLEAR:Object = { 'saturation': 1.0, 'darkness': 0.0, 'sky': 0xFF64A3EA, 'haze': 0x22f3f1e8, 'sunTint': 0xF7E9AA, 'fog': 0.0, 'contrast': 1.0, 'horizon': 0xFF86BAEF, 'ambient': 0x33F99100, 'timeOfDay': 0.45, 'wind': 0.5, 'darknessColor': 0x88111114 } public static const DAWN:Object = { 'saturation': 0.8, 'darkness': 0.2, 'sky': 0xFF8C8CA6, 'haze': 0x66f3f1e8, 'sunTint': 0xff6d40, 'fog': 0.0, 'contrast': 0.5, 'horizon': 0xFFCF7968, 'ambient': 0x11FF0000, 'timeOfDay': 0.28, 'wind': 0.1, 'darknessColor': 0x88111114 } public static const DAYSOFT:Object = { 'saturation': 0.7, 'darkness': 0.0, 'sky': 0xFFA0C2E8, 'haze': 0x22f3f1e8, 'sunTint': 0xF7E9AA, 'fog': 0.0, 'contrast': 0.0, 'horizon': 0xFFA6C9ED, 'ambient': 0x33F7E0C3, 'timeOfDay': 0.45, 'wind': 0.1, 'darknessColor': 0x88111114 } public static const EVENING:Object = { 'saturation': 0.8, 'darkness': 0.1, 'sky': 0xFFFF7F51, 'haze': 0x99FF9068, 'sunTint': 0xFFFF7038, 'fog': 0.0, 'contrast': 0.7, 'horizon': 0xFFFFDF54, 'ambient': 0x33DE5E37, 'timeOfDay': 0.70, 'wind': 0.1, 'darknessColor': 0x88111114 } public static const DAYMONOCHROME:Object = { 'saturation': 0.25, 'darkness': 0.0, 'sky': 0xFF80A2C8, 'haze': 0xFFf3f1e8, 'sunTint': 0xF7E9AA, 'fog': 1.0, 'contrast': 0.4, 'horizon': 0xFFA6C9ED, 'ambient': 0x33F7E0C3, 'timeOfDay': 0.45, 'wind': 0.5, 'darknessColor': 0x88111114 } public static const DAWNREDMOON:Object = { 'saturation': 0.9, 'darkness': 0.2, 'sky': 0xFF16549F, 'haze': 0xbb5C5D9E, 'sunTint': 0xE76833, 'fog': 0.1, 'contrast': 0.0, 'horizon': 0xFFFF9286, 'ambient': 0x110E0B22, 'timeOfDay': 0.2, 'wind': 0.1, 'darknessColor': 0x880E0B62 } public static const DUSKTAN:Object = { 'saturation': 0.8, 'darkness': 0.1, 'sky': 0xFF52424C, 'sunTint': 0xF9F8E6, 'haze': 0x55C18F90, 'fog': 0.0, 'wind': 0.3, 'horizon': 0xFFFFBA3B, 'ambient': 0xFF886A00, 'timeOfDay': 0.7, 'contrast': 0.5, 'darknessColor': 0x88330B33 } public static const DUSKTEMP:Object = { 'saturation': 0.0, 'darkness': 0.2, 'sky': 0xFF6a6d55, 'haze': 0xFF999d7c, 'sunTint': 0xffffff, 'fog': 1.0, 'contrast': -0.2, 'horizon': 0xFF6a6d55, 'ambient': 0xFFFF0000, 'timeOfDay': 0.75, 'wind': 0.2, 'darknessColor': 0x88111114 } public static const DUSKYELLOW:Object = { 'saturation': 0.9, 'darkness': 0.0, 'sky': 0xFF8BB8E8, 'haze': 0x88f3f1e8, 'sunTint': 0xF4EED0, 'fog': 0.0, 'contrast': 0.8, 'horizon': 0xFFEDC99A, 'ambient': 0x44F79A42, 'timeOfDay': 0.651, 'wind': 0.1, 'darknessColor': 0x88111114 } public static const NIGHTTEMP:Object = { 'saturation': 0.0, 'darkness': 0.2, 'sky': 0xFF6a6d55, 'haze': 0xFF999d7c, 'sunTint': 0xffffff, 'fog': 1.0, 'contrast': -0.2, 'horizon': 0xFF6a6d55, 'ambient': 0xFFFF0000, 'timeOfDay': 0.0, 'wind': 0.2, 'darknessColor': 0x88111114 } public static const DAYDUSTY:Object = { 'saturation': 0.8, 'darkness': 0.0, 'sky': 0xFF5d9df5, 'sunTint': 0xF9F8E6, 'haze': 0x99AAAAAA, 'fog': 0.2, 'wind': 0.2, 'horizon': 0xFF94A9B6, 'ambient': 0x55f5db04, 'timeOfDay': 0.4, 'contrast': 0.2, 'darknessColor': 0x88222255 } public static const DUSKRED:Object = { 'saturation': 0.8, 'darkness': 0.2, 'sky': 0xFFd06219, 'sunTint': 0xf27612, 'haze': 0x99AAAAAA, 'fog': 0.2, 'wind': 0.2, 'horizon': 0xFFf2d407, 'ambient': 0x55f5db04, 'timeOfDay': 0.651, 'contrast': 0.1, 'darknessColor': 0x88000000 } } } ================================================ FILE: Workable.as ================================================ package { public interface Workable { function needsWork():Boolean; function work(citizen:Citizen=null):void; } } ================================================ FILE: assets/levels/compiled/fields.oel ================================================ 3840 192 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,10,18,1,5,4,3,4,5,2,4,3,4,5,6,14,16,15,15,16,15,17,9,10,8,10,9,8,8,10,9,10,10,9,8,9,11,12,13,14,15,17,10,8,9,10,9,8,10,10,9,11,15,12,16,15,12,12,15,12,15,12,16,15,17,10,10,9,10,8,9,8,9,9,11,12,13,14,16,17,9,10,9,9,10,9,10,8,9,10,9,8,10,10,11,15,16,15,12,15,13,1,3,4,2,6,4,5,2,3,4,5,6,7,8,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, ================================================ FILE: assets/levels/compiled/fields_alt.oel ================================================ 3840 192 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,10,18,1,5,4,3,4,5,2,4,3,4,5,6,14,16,15,15,16,15,17,9,10,8,10,9,8,8,10,9,10,10,9,8,9,11,12,13,14,15,17,10,8,9,10,9,8,10,10,9,9,8,11,16,15,12,12,15,12,15,12,16,15,17,10,10,9,10,8,9,8,9,9,11,12,13,14,16,17,9,10,9,9,10,9,10,8,9,10,9,8,10,10,11,15,16,15,12,15,13,1,3,4,2,6,4,5,2,3,4,5,6,7,8,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, ================================================ FILE: assets/levels/compiled/fields_loose.oel ================================================ 3840 192 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,10,18,1,5,4,3,4,5,2,4,3,4,5,6,14,16,15,15,16,15,17,9,10,8,10,9,8,8,10,9,10,10,9,8,9,11,12,13,14,15,17,10,8,9,10,9,8,10,10,9,11,15,12,16,15,12,12,15,12,15,12,16,15,17,10,10,9,10,8,9,8,9,9,11,12,13,14,16,17,9,10,9,9,10,9,10,8,9,10,9,8,10,10,11,15,16,15,12,15,13,1,3,4,2,6,4,5,2,3,4,5,6,7,8,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, ================================================ FILE: assets/levels/compiled/fields_old.oel ================================================ 3840 192 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,10,18,1,5,4,3,4,5,2,4,3,4,5,6,14,16,15,15,16,15,17,9,10,8,10,9,8,8,10,9,10,10,9,8,9,11,12,13,14,15,17,10,8,9,10,9,8,10,10,9,11,15,12,16,15,12,12,15,12,15,12,16,15,17,10,10,9,10,8,9,8,9,9,11,12,13,14,16,17,9,10,9,9,10,9,10,8,9,10,9,8,10,10,11,15,16,15,12,15,13,1,3,4,2,6,4,5,2,3,4,5,6,7,8,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, ================================================ FILE: assets/levels/fields.oel ================================================ 3840 192 ================================================ FILE: assets/levels/fields_alt.oel ================================================ 3840 192 ================================================ FILE: assets/levels/fields_loose.oel ================================================ 3840 192 ================================================ FILE: assets/levels/fields_old.oel ================================================ 3840 192 ================================================ FILE: assets/levels/ogmoconfig.oep ================================================ Kingdom 3840 192 ../gfx ================================================ FILE: assets/sound/build.bfxrsound ================================================ 2,0.5,,0.0781,0.3599,0.1093,0.3,0.413,,,,,,,,,0.125,0.6214,,,,,,,,1,,,,,,,masterVolume ================================================ FILE: assets/sound/hit.bfxrsound ================================================ 1,0.5,,0.0695,,0.1577,0.3,0.4565,,-0.515,,,,,,,,,,,,,,,,1,,,0.2087,,,,masterVolume ================================================ FILE: assets/sound/hitbig.bfxrsound ================================================ 1.0413,0.5,0.175,0.295,0.055,0.13,0.22,0.53,,-0.36,0.0402,,0.0232,,,0.0081,,,,0.0451,,,,,,1,-0.0025,,0.2087,,,,masterVolume ================================================ FILE: assets/sound/hitcitizen.bfxrsound ================================================ 2,0.21,0.11,0.035,,0.265,0.185,0.155,,-0.2099,-0.0625,0.0402,,0.165,,0.0375,0.0148,0.0419,0.0449,,,-0.0012,0.0103,-0.0678,0.0435,0.958,-0.0205,0.0185,0.1,-0.0265,,-0.0264,masterVolume,attackTime,sustainTime,decayTime,compressionAmount,startFrequency,minFrequency,slide,overtones ================================================ FILE: assets/sound/hitwall.bfxrsound ================================================ 1,0.2,,0.0695,0.395,0.23,0.3,0.4565,,-0.395,,0.395,,0.12,0.415,,,,,,,,,0.2649,-0.145,0.35,-0.155,,0.2087,,0.25,-0.045,masterVolume ================================================ FILE: assets/sound/pickup.bfxrsound ================================================ 2,0.5,,0.055,0.5256,0.2387,0.3,0.55,,,,,,,,,0.4,0.6179,,,,,,,,1,,,,,,,masterVolume ================================================ FILE: assets/sound/powerup.bfxrsound ================================================ 1,0.32,,0.195,,0.45,0.3,0.14,,0.1997,,,,,,,,,,,,,,,,1,,,,,,,masterVolume ================================================ FILE: assets/sound/stolen.bfxrsound ================================================ ,0.29,,0.515,0.165,0.25,0.3,0.13,,-0.175,,,,,,,,,,,0.1867,,,,0.195,1,,,0.1,0.2649,0.375,0.03,masterVolume ================================================ FILE: assets/sound/throw.bfxrsound ================================================ 2,0.5,0.12,0.065,0.165,0.165,0.3,0.45,,0.2299,0.03,,,,,,,,,,,,,,,1,,,,,,,masterVolume ================================================ FILE: com/quasimondo/geom/ColorMatrix.as ================================================ /* ColorMatrix Class v2.41 released under MIT License (X11) http://www.opensource.org/licenses/mit-license.php Author: Mario Klingemann http://www.quasimondo.com Big parts of this class are based on information found in "Matrix Operations for Image Processing" by Paul Haeberli http://web.archive.org/web/20060110044204/http://www.sgi.com/misc/grafica/matrix/ Matrix factors for the applyColorDeficiency() method have been copied from http://www.nofunc.com/Color_Matrix_Library/ Copyright (c) 2006-2010 Mario Klingemann Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ // Changes in v1.1: // Changed the RGB to luminance constants // Added colorize() method // Changes in v1.2: // Added clone() // Added randomize() // Added blend() // Added "filter" property // Changes in v1.3: // Added invertAlpha() // Added thresholdAlpha() // Changes in v1.4: // Added luminance2Alpha() //Changes in v1.5 // Added rotateX(); // Added rotateY(); // Added rotateZ(); // Added shearZ(); //changes in v2.0 // AS3 optimizations // Added setMultiplicators() // Added clearChannels() // Added rotateHue() // Added transformVector() // Added applyMatrix() // Added rotateRed() // Added rotateGreen() // Added rotateBlue() // Added shearRed() // Added shearGreen() // Added shearBlue() //changes in v2.1 // Added applyColorDeficiency() //changes in v2.2 // Added applyFilter() //changes in v2.3 // Added threshold_rgb() // Added RGB2YUV() // Added YUV2RGB() // Added invertMatrix() // Added normalize() // Added fitRange() // Added toString() // fixed factor in threshold //changes in v2.4 // Added autoDesaturate() //changes in v2.41 // Added several default values to methods package com.quasimondo.geom { import __AS3__.vec.Vector; import flash.display.BitmapData; import flash.filters.ColorMatrixFilter; import flash.geom.Matrix3D; import flash.geom.Point; public class ColorMatrix { public static const COLOR_DEFICIENCY_TYPES:Array = [ 'Protanopia', 'Protanomaly', 'Deuteranopia', 'Deuteranomaly', 'Tritanopia', 'Tritanomaly', 'Achromatopsia', 'Achromatomaly' ]; // Estimated occurences of color deficiencies: // Protanopia: 1.32% // Protanomaly: 1.32% // Deuteranopia: 1.21% // Deuteranomaly: 5.35% // Tritanopia: 0.031% // Tritanomaly: 0.0002% // Achromatopsia: 0.00002% // Achromatomaly: 0.00002% // RGB to Luminance conversion constants as found on // Charles A. Poynton's colorspace-faq: // http://www.faqs.org/faqs/graphics/colorspace-faq/ private static const LUMA_R:Number = 0.212671; private static const LUMA_G:Number = 0.71516; private static const LUMA_B:Number = 0.072169; // There seem different standards for converting RGB // values to Luminance. This is the one by Paul Haeberli: private static const LUMA_R2:Number = 0.3086; private static const LUMA_G2:Number = 0.6094; private static const LUMA_B2:Number = 0.0820; private static const ONETHIRD:Number = 1 / 3; private static const IDENTITY:Array = [1,0,0,0,0, 0,1,0,0,0, 0,0,1,0,0, 0,0,0,1,0]; private static const RAD:Number = Math.PI / 180; public var matrix:Array; private var preHue:ColorMatrix; private var postHue:ColorMatrix; private var hueInitialized:Boolean; /* Function: ColorMatrix Constructor Parameters: mat - if omitted matrix gets initialized with an identity matrix. Alternatively it can be initialized with another ColorMatrix or an array (there is currently no check if the array is valid. A correct array contains 20 elements.) */ public function ColorMatrix ( mat:Object = null ) { if (mat is ColorMatrix ) { matrix = mat.matrix.concat(); } else if (mat is Array ) { matrix = mat.concat(); } else { reset(); } } /* Function: reset resets the matrix to the neutral identity matrix. Applying this matrix to an image will not make any changes to it. Parameters: none Returns: nothing */ public function reset():void { matrix = IDENTITY.concat(); } public function clone():ColorMatrix { return new ColorMatrix( matrix ); } public function invert():void { concat([ -1 , 0, 0, 0, 255, 0 , -1, 0, 0, 255, 0 , 0, -1, 0, 255, 0, 0, 0, 1, 0]); } /* Function: adjustSaturation changes the saturation Parameters: s - typical values come in the range 0.0 ... 2.0 where 0.0 means 0% Saturation 0.5 means 50% Saturation 1.0 is 100% Saturation (aka no change) 2.0 is 200% Saturation Other values outside of this range are possible -1.0 will invert the hue but keep the luminance Returns: nothing */ public function adjustSaturation( s:Number = 1 ):void{ var sInv:Number; var irlum:Number; var iglum:Number; var iblum:Number; sInv = (1 - s); irlum = (sInv * LUMA_R); iglum = (sInv * LUMA_G); iblum = (sInv * LUMA_B); concat([(irlum + s), iglum, iblum, 0, 0, irlum, (iglum + s), iblum, 0, 0, irlum, iglum, (iblum + s), 0, 0, 0, 0, 0, 1, 0]); } /* Function: adjustContrast changes the contrast Parameters: s - typical values come in the range -1.0 ... 1.0 where -1.0 means no contrast (grey) 0 means no change 1.0 is high contrast Returns: nothing */ public function adjustContrast( r:Number = 0, g:Number = NaN, b:Number = NaN ):void { if (isNaN(g)) g = r; if (isNaN(b)) b = r; r += 1; g += 1; b += 1; concat([r, 0, 0, 0, (128 * (1 - r)), 0, g, 0, 0, (128 * (1 - g)), 0, 0, b, 0, (128 * (1 - b)), 0, 0, 0, 1, 0]); } public function adjustBrightness(r:Number = 0, g:Number=NaN, b:Number=NaN):void { if (isNaN(g)) g = r; if (isNaN(b)) b = r; concat([1, 0, 0, 0, r, 0, 1, 0, 0, g, 0, 0, 1, 0, b, 0, 0, 0, 1, 0]); } public function toGreyscale( r:Number = LUMA_R, g:Number = LUMA_G, b:Number = LUMA_B ):void { concat([r, g, b, 0, 0, r, g, b, 0, 0, r, g, b, 0, 0, 0, 0, 0, 1, 0]); } public function adjustHue( degrees:Number = 0 ):void { degrees *= RAD; var cos:Number = Math.cos(degrees); var sin:Number = Math.sin(degrees); concat([((LUMA_R + (cos * (1 - LUMA_R))) + (sin * -(LUMA_R))), ((LUMA_G + (cos * -(LUMA_G))) + (sin * -(LUMA_G))), ((LUMA_B + (cos * -(LUMA_B))) + (sin * (1 - LUMA_B))), 0, 0, ((LUMA_R + (cos * -(LUMA_R))) + (sin * 0.143)), ((LUMA_G + (cos * (1 - LUMA_G))) + (sin * 0.14)), ((LUMA_B + (cos * -(LUMA_B))) + (sin * -0.283)), 0, 0, ((LUMA_R + (cos * -(LUMA_R))) + (sin * -((1 - LUMA_R)))), ((LUMA_G + (cos * -(LUMA_G))) + (sin * LUMA_G)), ((LUMA_B + (cos * (1 - LUMA_B))) + (sin * LUMA_B)), 0, 0, 0, 0, 0, 1, 0]); } public function rotateHue( degrees:Number = 0 ):void { initHue(); concat( preHue.matrix ); rotateBlue( degrees ); concat( postHue.matrix ); } public function luminance2Alpha():void { concat([0, 0, 0, 0, 255, 0, 0, 0, 0, 255, 0, 0, 0, 0, 255, LUMA_R, LUMA_G, LUMA_B, 0, 0]); } public function adjustAlphaContrast( amount:Number = 0 ):void { amount += 1; concat([1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, amount, (128 * (1 - amount))]); } public function colorize( rgb:uint, amount:Number = -1 ):void { var a:Number; var r:Number; var g:Number; var b:Number; var inv_amount:Number; a = ((rgb >> 24) / 0xFF); r = (((rgb >> 16) & 0xFF) / 0xFF); g = (((rgb >> 8) & 0xFF) / 0xFF); b = ((rgb & 0xFF) / 0xFF); if (amount == -1){ amount = a; } inv_amount = (1 - amount); concat([(inv_amount + ((amount * r) * LUMA_R)), ((amount * r) * LUMA_G), ((amount * r) * LUMA_B), 0, 0, ((amount * g) * LUMA_R), (inv_amount + ((amount * g) * LUMA_G)), ((amount * g) * LUMA_B), 0, 0, ((amount * b) * LUMA_R), ((amount * b) * LUMA_G), (inv_amount + ((amount * b) * LUMA_B)), 0, 0, 0, 0, 0, 1, 0]); } public function setChannels( r:int = 1, g:int = 2, b:int = 4, a:int = 8 ):void { var rf:Number = ((((((r & 1) == 1)) ? 1 : 0 + (((r & 2) == 2)) ? 1 : 0) + (((r & 4) == 4)) ? 1 : 0) + (((r & 8) == 8)) ? 1 : 0); if (rf > 0){ rf = (1 / rf); }; var gf:Number = ((((((g & 1) == 1)) ? 1 : 0 + (((g & 2) == 2)) ? 1 : 0) + (((g & 4) == 4)) ? 1 : 0) + (((g & 8) == 8)) ? 1 : 0); if (gf > 0){ gf = (1 / gf); }; var bf:Number = ((((((b & 1) == 1)) ? 1 : 0 + (((b & 2) == 2)) ? 1 : 0) + (((b & 4) == 4)) ? 1 : 0) + (((b & 8) == 8)) ? 1 : 0); if (bf > 0){ bf = (1 / bf); }; var af:Number = ((((((a & 1) == 1)) ? 1 : 0 + (((a & 2) == 2)) ? 1 : 0) + (((a & 4) == 4)) ? 1 : 0) + (((a & 8) == 8)) ? 1 : 0); if (af > 0){ af = (1 / af); }; concat([(((r & 1) == 1)) ? rf : 0, (((r & 2) == 2)) ? rf : 0, (((r & 4) == 4)) ? rf : 0, (((r & 8) == 8)) ? rf : 0, 0, (((g & 1) == 1)) ? gf : 0, (((g & 2) == 2)) ? gf : 0, (((g & 4) == 4)) ? gf : 0, (((g & 8) == 8)) ? gf : 0, 0, (((b & 1) == 1)) ? bf : 0, (((b & 2) == 2)) ? bf : 0, (((b & 4) == 4)) ? bf : 0, (((b & 8) == 8)) ? bf : 0, 0, (((a & 1) == 1)) ? af : 0, (((a & 2) == 2)) ? af : 0, (((a & 4) == 4)) ? af : 0, (((a & 8) == 8)) ? af : 0, 0]); } public function blend( mat:ColorMatrix, amount:Number ):void { var inv_amount:Number = (1 - amount); var i:int = 0; while (i < 20) { matrix[i] = ((inv_amount * Number(matrix[i])) + (amount * Number(mat.matrix[i]))); i++; }; } public function average( r:Number = ONETHIRD, g:Number = ONETHIRD, b:Number = ONETHIRD ):void { concat([r, g, b, 0, 0, r, g, b, 0, 0, r, g, b, 0, 0, 0, 0, 0, 1, 0]); } public function threshold(threshold:Number, factor:Number=256):void { concat([(LUMA_R * factor), (LUMA_G * factor), (LUMA_B * factor), 0, (-(factor-1) * threshold), (LUMA_R * factor), (LUMA_G * factor), (LUMA_B * factor), 0, (-(factor-1) * threshold), (LUMA_R * factor), (LUMA_G * factor), (LUMA_B * factor), 0, (-(factor-1) * threshold), 0, 0, 0, 1, 0]); } public function threshold_rgb(threshold:Number, factor:Number=256):void { concat([factor, 0, 0, 0, (-(factor-1) * threshold), 0, factor, 0, 0, (-(factor-1) * threshold), 0, 0, factor, 0, (-(factor-1) * threshold), 0, 0, 0, 1, 0]); } public function desaturate():void { concat([LUMA_R, LUMA_G, LUMA_B, 0, 0, LUMA_R, LUMA_G, LUMA_B, 0, 0, LUMA_R, LUMA_G, LUMA_B, 0, 0, 0, 0, 0, 1, 0]); } public function randomize( amount:Number = 1, normalize:Boolean = false ):void { var inv_amount:Number = (1 - amount); var r1:Number = (inv_amount + (amount * (Math.random() - Math.random()))); var g1:Number = (amount * (Math.random() - Math.random())); var b1:Number = (amount * (Math.random() - Math.random())); var o1:Number = ((amount * 0xFF) * (Math.random() - Math.random())); var r2:Number = (amount * (Math.random() - Math.random())); var g2:Number = (inv_amount + (amount * (Math.random() - Math.random()))); var b2:Number = (amount * (Math.random() - Math.random())); var o2:Number = ((amount * 0xFF) * (Math.random() - Math.random())); var r3:Number = (amount * (Math.random() - Math.random())); var g3:Number = (amount * (Math.random() - Math.random())); var b3:Number = (inv_amount + (amount * (Math.random() - Math.random()))); var o3:Number = ((amount * 0xFF) * (Math.random() - Math.random())); concat([r1, g1, b1, 0, o1, r2, g2, b2, 0, o2, r3, g3, b3, 0, o3, 0, 0, 0, 1, 0]); if ( normalize ) this.normalize(); } public function setMultiplicators( red:Number = 1, green:Number = 1, blue:Number = 1, alpha:Number = 1 ):void { var mat:Array = new Array ( red, 0, 0, 0, 0, 0, green, 0, 0, 0, 0, 0, blue, 0, 0, 0, 0, 0, alpha, 0 ); concat(mat); } public function clearChannels( red:Boolean = false, green:Boolean = false, blue:Boolean = false, alpha:Boolean = false ):void { if ( red ) { matrix[0] = matrix[1] = matrix[2] = matrix[3] = matrix[4] = 0; } if ( green ) { matrix[5] = matrix[6] = matrix[7] = matrix[8] = matrix[9] = 0; } if ( blue ) { matrix[10] = matrix[11] = matrix[12] = matrix[13] = matrix[14] = 0; } if ( alpha ) { matrix[15] = matrix[16] = matrix[17] = matrix[18] = matrix[19] = 0; } } public function thresholdAlpha( threshold:Number = 0.5, factor:Number = 256):void { concat([1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, factor, (-factor * threshold)]); } public function averageRGB2Alpha():void { concat([0, 0, 0, 0, 255, 0, 0, 0, 0, 255, 0, 0, 0, 0, 255, ONETHIRD, ONETHIRD, ONETHIRD, 0, 0]); } public function invertAlpha():void { concat([1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, -1, 255]); } public function rgb2Alpha( r:Number = ONETHIRD, g:Number = ONETHIRD, b:Number = ONETHIRD ):void { concat([0, 0, 0, 0, 255, 0, 0, 0, 0, 255, 0, 0, 0, 0, 255, r, g, b, 0, 0]); } public function setAlpha( alpha:Number = 1 ):void { concat([1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, alpha, 0]); } public function get filter():ColorMatrixFilter { return new ColorMatrixFilter( matrix ); } public function applyFilter( bitmapData:BitmapData ):void { bitmapData.applyFilter( bitmapData, bitmapData.rect, new Point(), filter ); } public function concat( mat:Array ):void { var temp:Array = []; var i:int = 0; var x:int, y:int; for (y = 0; y < 4; y++ ) { for (x = 0; x < 5; x++ ) { temp[ int( i + x) ] = Number(mat[i ]) * Number(matrix[x]) + Number(mat[int(i+1)]) * Number(matrix[int(x + 5)]) + Number(mat[int(i+2)]) * Number(matrix[int(x + 10)]) + Number(mat[int(i+3)]) * Number(matrix[int(x + 15)]) + (x == 4 ? Number(mat[int(i+4)]) : 0); } i+=5; } matrix = temp; } public function rotateRed( degrees:Number = 0 ):void { rotateColor( degrees, 2, 1 ); } public function rotateGreen( degrees:Number = 0 ):void { rotateColor( degrees, 0, 2 ); } public function rotateBlue( degrees:Number = 0 ):void { rotateColor( degrees, 1, 0 ); } public function normalize():void { for ( var i:int = 0; i < 4; i++ ) { var sum:Number = 0; for ( var j:int = 0; j < 4; j++ ) { sum += matrix[i*5+j] * matrix[i*5+j]; } sum = 1 / Math.sqrt( sum ); if ( sum != 1 ) { for ( j = 0; j < 4; j++ ) { matrix[i*5+j] *= sum; } } } } public function fitRange():void { for ( var i:int = 0; i < 4; i++ ) { var minFactor:Number = 0; var maxFactor:Number = 0; for ( var j:int = 0; j < 4; j++ ) { if ( matrix[int(i*5+j)] < 0 ) minFactor += matrix[int(i*5+j)]; else maxFactor += matrix[int(i*5+j)]; } var range:Number = maxFactor * 255 - minFactor * 255; var rangeCorrection:Number = 255 / range; if ( rangeCorrection != 1 ) { for ( j = 0; j < 4; j++ ) { matrix[int(i*5+j)] *= rangeCorrection; } } minFactor = 0; maxFactor = 0; for ( j = 0; j < 4; j++ ) { if ( matrix[int(i*5+j)] < 0 ) minFactor += matrix[int(i*5+j)]; else maxFactor += matrix[int(i*5+j)]; } var worstMin:Number = minFactor * 255; var worstMax:Number = maxFactor * 255; matrix[int(i*5+4)] = - ( worstMin + ( worstMax - worstMin ) * 0.5 - 127.5 ); } } public function shearRed( green:Number, blue:Number ):void { shearColor( 0, 1, green, 2, blue ); } public function shearGreen( red:Number, blue:Number ):void { shearColor( 1, 0, red, 2, blue ); } public function shearBlue( red:Number, green:Number ):void { shearColor( 2, 0, red, 1, green ); } public function applyColorDeficiency( type:String ):void { switch ( type ) { case 'Protanopia': concat([0.567,0.433,0,0,0, 0.558,0.442,0,0,0, 0,0.242,0.758,0,0, 0,0,0,1,0]); break; case 'Protanomaly': concat([0.817,0.183,0,0,0, 0.333,0.667,0,0,0, 0,0.125,0.875,0,0, 0,0,0,1,0]); break; case 'Deuteranopia': concat([0.625,0.375,0,0,0, 0.7,0.3,0,0,0, 0,0.3,0.7,0,0, 0,0,0,1,0]); break; case 'Deuteranomaly': concat([0.8,0.2,0,0,0, 0.258,0.742,0,0,0, 0,0.142,0.858,0,0, 0,0,0,1,0]); break; case 'Tritanopia': concat([0.95,0.05,0,0,0, 0,0.433,0.567,0,0, 0,0.475,0.525,0,0, 0,0,0,1,0]); break; case 'Tritanomaly': concat([0.967,0.033,0,0,0, 0,0.733,0.267,0,0, 0,0.183,0.817,0,0, 0,0,0,1,0]); break; case 'Achromatopsia': concat([0.299,0.587,0.114,0,0, 0.299,0.587,0.114,0,0, 0.299,0.587,0.114,0,0, 0,0,0,1,0]); break; case 'Achromatomaly': concat([0.618,0.320,0.062,0,0, 0.163,0.775,0.062,0,0, 0.163,0.320,0.516,0,0, 0,0,0,1,0]); break; } } public function RGB2YUV():void { concat([ 0.29900, 0.58700, 0.11400, 0, 0, -0.16874, -0.33126, 0.50000, 0, 128, 0.50000, -0.41869, -0.08131, 0, 128, 0 , 0 , 0 , 1, 0 ]); } public function YUV2RGB():void { concat([ 1 , -0.000007154783816076815, 1.4019975662231445 , 0, -179.45477266423404, 1 , -0.3441331386566162 , -0.7141380310058594 , 0, 135.45870971679688, 1 , 1.7720025777816772 , 0.00001542569043522235, 0, -226.8183044444304, 0 , 0 , 0 , 1, 0 ]); } public function RGB2YIQ():void { concat([ 0.2990, 0.5870, 0.1140, 0, 0, 0.595716, -0.274453, -0.321263, 0, 128, 0.211456, -0.522591, -0.311135, 0, 128, 0 , 0 , 0 , 1, 0 ]); } /* public function YIQ2RGB():void { concat([ 1, ,-0.000007154783816076815, 1.4019975662231445 , 0, -179.45477266423404, 1 , -0.3441331386566162 , -0.7141380310058594 , 0, 135.45870971679688, 1 , 1.7720025777816772 , 0.00001542569043522235, 0, -226.8183044444304, 0 , 0 , 0 , 1, 0 ]); } */ public function autoDesaturate( bitmapData:BitmapData, stretchLevels:Boolean = false, outputToBlueOnly:Boolean = false ):void { var histogram:Vector.> = bitmapData.histogram(bitmapData.rect ); var sum_r:Number = 0; var sum_g:Number = 0; var sum_b:Number = 0; var min:Number; var max:Number; var minFound:Boolean = false; for ( var i:int = 0; i < 256; i++ ) { sum_r += histogram[0][i] * i; sum_g += histogram[1][i] * i; sum_b += histogram[2][i] * i; if ( stretchLevels ) { if ( histogram[0][i] != 0 || histogram[1][i] != 0 || histogram[2][i] != 0 ) { max = i if ( !minFound ) { min = i; minFound = true; } } } } var total:Number = sum_r + sum_g + sum_b; if ( total == 0 ) { total = 3; sum_r = sum_g = sum_b = 3; } sum_r /= total; sum_g /= total; sum_b /= total; var offset:Number = 0; if ( stretchLevels && max - min < 255) { var f:Number = 256 / ((max - min) + 1); sum_r *= f; sum_g *= f; sum_b *= f; offset = -min; } f = 1 / Math.sqrt(sum_r * sum_r + sum_g * sum_g + sum_b * sum_b); sum_r *= f; sum_g *= f; sum_b *= f; if ( !outputToBlueOnly ) concat([sum_r,sum_g,sum_b,0,offset, sum_r,sum_g,sum_b,0,offset, sum_r,sum_g,sum_b,0,offset, 0,0,0,1,0]); else concat([0,0,0,0,0, 0,0,0,0,0, sum_r,sum_g,sum_b,0,offset, 0,0,0,1,0]); } public function invertMatrix():Boolean { var coeffs:Matrix3D = new Matrix3D( Vector.( [matrix[0],matrix[1],matrix[2],matrix[3], matrix[5],matrix[6],matrix[7],matrix[8], matrix[10],matrix[11],matrix[12],matrix[13], matrix[15],matrix[16],matrix[17],matrix[18]] ) ); var check:Boolean = coeffs.invert(); if (!check) return false; matrix[0] = coeffs.rawData[0]; matrix[1] = coeffs.rawData[1]; matrix[2] = coeffs.rawData[2]; matrix[3] = coeffs.rawData[3]; var tmp1:Number = -( coeffs.rawData[0] * matrix[4] + coeffs.rawData[1] * matrix[9] + coeffs.rawData[2] * matrix[14] + coeffs.rawData[3] * matrix[15] ); matrix[5] = coeffs.rawData[4]; matrix[6] = coeffs.rawData[5]; matrix[7] = coeffs.rawData[6]; matrix[8] = coeffs.rawData[7]; var tmp2:Number = -( coeffs.rawData[4] * matrix[4] + coeffs.rawData[5] * matrix[9] + coeffs.rawData[6] * matrix[14] + coeffs.rawData[7] * matrix[15] ); matrix[10] = coeffs.rawData[8]; matrix[11] = coeffs.rawData[9]; matrix[12] = coeffs.rawData[10]; matrix[13] = coeffs.rawData[11]; var tmp3:Number = -( coeffs.rawData[8] * matrix[4] + coeffs.rawData[9] * matrix[9] + coeffs.rawData[10] * matrix[14] + coeffs.rawData[11] * matrix[15] ); matrix[15] = coeffs.rawData[12]; matrix[16] = coeffs.rawData[13]; matrix[17] = coeffs.rawData[14]; matrix[18] = coeffs.rawData[15]; var tmp4:Number = -( coeffs.rawData[12] * matrix[4] + coeffs.rawData[13] * matrix[9] + coeffs.rawData[14] * matrix[14] + coeffs.rawData[15] * matrix[15] ); matrix[4] = tmp1; matrix[9] = tmp2; matrix[14] = tmp3; matrix[19] = tmp4; return true; } public function applyMatrix( rgba:uint ):uint { var a:Number = ( rgba >>> 24 ) & 0xff; var r:Number = ( rgba >>> 16 ) & 0xff; var g:Number = ( rgba >>> 8 ) & 0xff; var b:Number = rgba & 0xff; var r2:int = 0.5 + r * matrix[0] + g * matrix[1] + b * matrix[2] + a * matrix[3] + matrix[4]; var g2:int = 0.5 + r * matrix[5] + g * matrix[6] + b * matrix[7] + a * matrix[8] + matrix[9]; var b2:int = 0.5 + r * matrix[10] + g * matrix[11] + b * matrix[12] + a * matrix[13] + matrix[14]; var a2:int = 0.5 + r * matrix[15] + g * matrix[16] + b * matrix[17] + a * matrix[18] + matrix[19]; if ( a2 < 0 ) a2 = 0; if ( a2 > 255 ) a2 = 255; if ( r2 < 0 ) r2 = 0; if ( r2 > 255 ) r2 = 255; if ( g2 < 0 ) g2 = 0; if ( g2 > 255 ) g2 = 255; if ( b2 < 0 ) b2 = 0; if ( b2 > 255 ) b2 = 255; return a2<<24 | r2<<16 | g2<<8 | b2; } public function transformVector( values:Array ):void { if ( values.length != 4) return; var r:Number = values[0] * matrix[0] + values[1] * matrix[1] + values[2] * matrix[2] + values[3] * matrix[3] + matrix[4]; var g:Number = values[0] * matrix[5] + values[1] * matrix[6] + values[2] * matrix[7] + values[3] * matrix[8] + matrix[9]; var b:Number = values[0] * matrix[10] + values[1] * matrix[11] + values[2] * matrix[12] + values[3] * matrix[13] + matrix[14]; var a:Number = values[0] * matrix[15] + values[1] * matrix[16] + values[2] * matrix[17] + values[3] * matrix[18] + matrix[19]; values[0] = r; values[1] = g; values[2] = b; values[3] = a; } private function initHue():void { //var greenRotation:Number = 35.0; var greenRotation:Number = 39.182655; if (!hueInitialized) { hueInitialized = true; preHue = new ColorMatrix(); preHue.rotateRed( 45 ); preHue.rotateGreen(- greenRotation ); var lum:Array = [ LUMA_R2, LUMA_G2, LUMA_B2, 1.0 ]; preHue.transformVector(lum); var red:Number = lum[0] / lum[2]; var green:Number = lum[1] / lum[2]; preHue.shearBlue(red, green); postHue = new ColorMatrix(); postHue.shearBlue( -red, -green); postHue.rotateGreen(greenRotation); postHue.rotateRed(- 45.0 ); } } private function rotateColor( degrees:Number, x:int, y:int ):void { degrees *= RAD; var mat:Array = IDENTITY.concat(); mat[ x + x * 5 ] = mat[ y + y * 5 ] = Math.cos( degrees ); mat[ y + x * 5 ] = Math.sin( degrees ); mat[ x + y * 5 ] = -Math.sin( degrees ); concat( mat ); } private function shearColor( x:int, y1:int, d1:Number, y2:int, d2:Number ):void { var mat:Array = IDENTITY.concat(); mat[ y1 + x * 5 ] = d1; mat[ y2 + x * 5 ] = d2; concat( mat ); } public function toString():String { return matrix.toString(); } } } ================================================ FILE: convert_sounds.sh ================================================ #! /bin/bash for i in assets/sound/*.{wav,aiff}; do echo "converting $i ..."; filename=$(basename "$i") filename="${filename%.*}" sox "$i" assets/sound/${filename}.mp3 rate 44100 reverse silence 1 0.005 0.1% reverse; done ================================================ FILE: convert_tiles.py ================================================ #!/usr/bin/python import os, sys from xml.dom import minidom #-------------------------------------- BASE_PATH = os.path.dirname(__file__) MAP_SRC_DIR = os.path.join(BASE_PATH, 'assets/levels') MAP_COMPILED_DIR = os.path.join(BASE_PATH, 'assets/levels/compiled') TILE_LAYER_NAMES = ["ground"] #-------------------------------------- def compile_levels(): """ Flatten OGMO's XML tilemaps into comma-separated strings. This is done in order to cut down processing at runtime - parsing tilemap data from a list of xml nodes into a CSV is too costly to do at runtime without noticeable lag. """ for ogmo_filename in [x for x in os.listdir(MAP_SRC_DIR) if x.endswith('.oel')]: ogmo_path = os.path.join(MAP_SRC_DIR, ogmo_filename) ogmo_flattened_path = os.path.join(MAP_COMPILED_DIR, ogmo_filename) if os.path.exists(ogmo_flattened_path): if os.path.getmtime(ogmo_flattened_path) > os.path.getmtime(ogmo_path): sys.stdout.write("--%s up to date\n" % ogmo_flattened_path) continue flatten_ogmo_tilemaps(ogmo_path, ogmo_flattened_path) def flatten_ogmo_tilemaps(ogmo_path, ogmo_flattened_path): ogmo_dom = minidom.parse(ogmo_path) map_data = dict(ogmo_dom.getElementsByTagName('level')[0].attributes.items()) map_data['width'] = ogmo_dom.getElementsByTagName('width')[0].firstChild.data map_data['height'] = ogmo_dom.getElementsByTagName('height')[0].firstChild.data # load tiles for tile_layer_name in TILE_LAYER_NAMES: map_data[tile_layer_name] = dict(ogmo_dom.getElementsByTagName(tile_layer_name)[0].attributes.items()) map_data[tile_layer_name]['tiles'] = '' tileWidth = int(map_data[tile_layer_name]['tileWidth']) tileHeight = int(map_data[tile_layer_name]['tileHeight']) widthInTiles = int(map_data['width']) / tileWidth heightInTiles = int(map_data['height']) / tileHeight tiles = {} for tileNode in ogmo_dom.getElementsByTagName(tile_layer_name)[0].getElementsByTagName('tile'): tileId = tileNode.getAttribute('id') tileX = int(tileNode.getAttribute('x')) / tileWidth tileY = int(tileNode.getAttribute('y')) / tileHeight tiles[str(tileX) + '@' + str(tileY)] = tileId for y in range(0, heightInTiles): for x in range(0, widthInTiles): tileId = tiles.get(str(x) + '@' + str(y), '0') map_data[tile_layer_name]['tiles'] += tileId map_data[tile_layer_name]['tiles'] += "," map_data[tile_layer_name]['tiles'] += "\n" # clear out old tiles node & add a new flattened one ogmo_dom.getElementsByTagName(tile_layer_name)[0].childNodes[:] = [] # shorcut to clear all child nodes flattenedTextNode = ogmo_dom.createTextNode(map_data[tile_layer_name]['tiles']) ogmo_dom.getElementsByTagName(tile_layer_name)[0].appendChild(flattenedTextNode) f = open(ogmo_flattened_path, 'w') f.write(ogmo_dom.toxml()) f.close() if __name__ == '__main__': compile_levels() ================================================ FILE: convert_weather.py ================================================ #! /usr/bin/env python import json import re WEATHER_RX = r'public static const ([A-Z_]*):Object = ({[^\}]*})' PROP_RX = r':([^,\n]*)([,\n])' # weatherfile = open('Weather.as').read() # jsonfile = open("weathers.json",'w') # weathers = {} # for a in re.findall(WEATHER_RX, weatherfile, re.DOTALL): # print a[0] # w = re.sub(PROP_RX, r':"\1"\2', a[1]) # w = w.replace("'",'"') # weathers[a[0]] = json.loads(w) # # json.dump(weathers, jsonfile, indent=2) # presets = open('WeatherPresets.as', 'w') presets.write("// THIS FILE IS AUTOGENERATED, MODIFY weathers.json IN STEAD.\n\n") presets.write("package {\n") presets.write("public class WeatherPresets extends Object{\n") weathers = json.load(open('weathers.json')) print "Found: \n- " + '\n- '.join(sorted(weathers.keys())) for weather, content in weathers.items(): presets.write("\tpublic static const %s:Object = {\n" % (weather)) presets.write(',\n'.join(["\t\t'%s': %s" % (key,val) for key, val in content.items()])) presets.write('\n\t}\n') presets.write("}\n}") ================================================ FILE: king.as ================================================ package { import org.flixel.*; [SWF(width="864", height="480", backgroundColor="#000000")] [Frame(factoryClass="Preloader")] public class king extends FlxGame { public static const VERSION:String = 'v1.1.3'; public function king() { super(288,160,MenuState,3, 60, 60, false); FlxG.debug = true; } } } ================================================ FILE: org/flixel/FlxBasic.as ================================================ package org.flixel { /** * This is a useful "generic" Flixel object. * Both FlxObject and FlxGroup extend this class, * as do the plugins. Has no size, position or graphical data. * * @author Adam Atomic */ public class FlxBasic { static internal var _ACTIVECOUNT:uint; static internal var _VISIBLECOUNT:uint; /** * IDs seem like they could be pretty useful, huh? * They're not actually used for anything yet though. */ public var ID:int; /** * Controls whether update() and draw() are automatically called by FlxState/FlxGroup. */ public var exists:Boolean; /** * Controls whether update() is automatically called by FlxState/FlxGroup. */ public var active:Boolean; /** * Controls whether draw() is automatically called by FlxState/FlxGroup. */ public var visible:Boolean; /** * Useful state for many game objects - "dead" (!alive) vs alive. * kill() and revive() both flip this switch (along with exists, but you can override that). */ public var alive:Boolean; /** * An array of camera objects that this object will use during draw(). * This value will initialize itself during the first draw to automatically * point at the main camera list out in FlxG unless you already set it. * You can also change it afterward too, very flexible! */ public var cameras:Array; /** * Setting this to true will prevent the object from appearing * when the visual debug mode in the debugger overlay is toggled on. */ public var ignoreDrawDebug:Boolean; /** * Instantiate the basic flixel object. */ public function FlxBasic() { ID = -1; exists = true; active = true; visible = true; alive = true; ignoreDrawDebug = false; } /** * Override this function to null out variables or manually call * destroy() on class members if necessary. * Don't forget to call super.destroy()! */ public function destroy():void {} /** * Pre-update is called right before update() on each object in the game loop. */ public function preUpdate():void { _ACTIVECOUNT++; } /** * Override this function to update your class's position and appearance. * This is where most of your game rules and behavioral code will go. */ public function update():void { } /** * Post-update is called right after update() on each object in the game loop. */ public function postUpdate():void { } /** * Override this function to control how the object is drawn. * Overriding draw() is rarely necessary, but can be very useful. */ public function draw():void { if(cameras == null) cameras = FlxG.cameras; var camera:FlxCamera; var i:uint = 0; var l:uint = cameras.length; while(i < l) { camera = cameras[i++]; _VISIBLECOUNT++; if(FlxG.visualDebug && !ignoreDrawDebug) drawDebug(camera); } } /** * Override this function to draw custom "debug mode" graphics to the * specified camera while the debugger's visual mode is toggled on. * * @param Camera Which camera to draw the debug visuals to. */ public function drawDebug(Camera:FlxCamera=null):void { } /** * Handy function for "killing" game objects. * Default behavior is to flag them as nonexistent AND dead. * However, if you want the "corpse" to remain in the game, * like to animate an effect or whatever, you should override this, * setting only alive to false, and leaving exists true. */ public function kill():void { alive = false; exists = false; } /** * Handy function for bringing game objects "back to life". Just sets alive and exists back to true. * In practice, this function is most often called by FlxObject.reset(). */ public function revive():void { alive = true; exists = true; } /** * Convert object to readable string name. Useful for debugging, save games, etc. */ public function toString():String { return FlxU.getClassName(this,true); } } } ================================================ FILE: org/flixel/FlxButton.as ================================================ package org.flixel { import flash.events.MouseEvent; /** * A simple button class that calls a function when clicked by the mouse. * * @author Adam Atomic */ public class FlxButton extends FlxSprite { [Embed(source="data/button.png")] protected var ImgDefaultButton:Class; [Embed(source="data/beep.mp3")] protected var SndBeep:Class; /** * Used with public variable status, means not highlighted or pressed. */ static public var NORMAL:uint = 0; /** * Used with public variable status, means highlighted (usually from mouse over). */ static public var HIGHLIGHT:uint = 1; /** * Used with public variable status, means pressed (usually from mouse click). */ static public var PRESSED:uint = 2; /** * The text that appears on the button. */ public var label:FlxText; /** * Controls the offset (from top left) of the text from the button. */ public var labelOffset:FlxPoint; /** * This function is called when the button is released. * We recommend assigning your main button behavior to this function * via the FlxButton constructor. */ public var onUp:Function; /** * This function is called when the button is pressed down. */ public var onDown:Function; /** * This function is called when the mouse goes over the button. */ public var onOver:Function; /** * This function is called when the mouse leaves the button area. */ public var onOut:Function; /** * Shows the current state of the button. */ public var status:uint; /** * Set this to play a sound when the mouse goes over the button. * We recommend using the helper function setSounds()! */ public var soundOver:FlxSound; /** * Set this to play a sound when the mouse leaves the button. * We recommend using the helper function setSounds()! */ public var soundOut:FlxSound; /** * Set this to play a sound when the button is pressed down. * We recommend using the helper function setSounds()! */ public var soundDown:FlxSound; /** * Set this to play a sound when the button is released. * We recommend using the helper function setSounds()! */ public var soundUp:FlxSound; /** * Used for checkbox-style behavior. */ protected var _onToggle:Boolean; /** * Tracks whether or not the button is currently pressed. */ protected var _pressed:Boolean; /** * Whether or not the button has initialized itself yet. */ protected var _initialized:Boolean; /** * Creates a new FlxButton object with a gray background * and a callback function on the UI thread. * * @param X The X position of the button. * @param Y The Y position of the button. * @param Label The text that you want to appear on the button. * @param OnClick The function to call whenever the button is clicked. */ public function FlxButton(X:Number=0,Y:Number=0,Label:String=null,OnClick:Function=null) { super(X,Y); if(Label != null) { label = new FlxText(0,0,80,Label); label.setFormat(null,8,0x333333,"center"); labelOffset = new FlxPoint(-1,3); } loadGraphic(ImgDefaultButton,true,false,80,20); onUp = OnClick; onDown = null; onOut = null; onOver = null; soundOver = null; soundOut = null; soundDown = null; soundUp = null; status = NORMAL; _onToggle = false; _pressed = false; _initialized = false; } /** * Called by the game state when state is changed (if this object belongs to the state) */ override public function destroy():void { if(FlxG.stage != null) FlxG.stage.removeEventListener(MouseEvent.MOUSE_UP, handleMouseUp); if(label != null) { label.destroy(); label = null; } onUp = null; onDown = null; onOut = null; onOver = null; if(soundOver != null) soundOver.destroy(); if(soundOut != null) soundOut.destroy(); if(soundDown != null) soundDown.destroy(); if(soundUp != null) soundUp.destroy(); super.destroy(); } /** * Since button uses its own mouse handler for thread reasons, * we run a little pre-check here to make sure that we only add * the mouse handler when it is actually safe to do so. */ override public function preUpdate():void { super.preUpdate(); if(!_initialized) { if(FlxG.stage != null) { FlxG.stage.addEventListener(MouseEvent.MOUSE_UP, handleMouseUp); _initialized = true; } } } /** * Called by the game loop automatically, handles mouseover and click detection. */ override public function update():void { updateButton(); //Basic button logic //Default button appearance is to simply update // the label appearance based on animation frame. if(label == null) return; switch(frame) { case HIGHLIGHT: //Extra behavior to accomodate checkbox logic. label.alpha = 1.0; break; case PRESSED: label.alpha = 0.5; label.y++; break; case NORMAL: default: label.alpha = 0.8; break; } } /** * Basic button update logic */ protected function updateButton():void { //Figure out if the button is highlighted or pressed or what // (ignore checkbox behavior for now). if(FlxG.mouse.visible) { if(cameras == null) cameras = FlxG.cameras; var camera:FlxCamera; var i:uint = 0; var l:uint = cameras.length; var offAll:Boolean = true; while(i < l) { camera = cameras[i++] as FlxCamera; FlxG.mouse.getWorldPosition(camera,_point); if(overlapsPoint(_point,true,camera)) { offAll = false; if(FlxG.mouse.justPressed()) { status = PRESSED; if(onDown != null) onDown(); if(soundDown != null) soundDown.play(true); } if(status == NORMAL) { status = HIGHLIGHT; if(onOver != null) onOver(); if(soundOver != null) soundOver.play(true); } } } if(offAll) { if(status != NORMAL) { if(onOut != null) onOut(); if(soundOut != null) soundOut.play(true); } status = NORMAL; } } //Then if the label and/or the label offset exist, // position them to match the button. if(label != null) { label.x = x; label.y = y; } if(labelOffset != null) { label.x += labelOffset.x; label.y += labelOffset.y; } //Then pick the appropriate frame of animation if((status == HIGHLIGHT) && _onToggle) frame = NORMAL; else frame = status; } /** * Just draws the button graphic and text label to the screen. */ override public function draw():void { super.draw(); if(label != null) { label.scrollFactor = scrollFactor; label.cameras = cameras; label.draw(); } } /** * Updates the size of the text field to match the button. */ override protected function resetHelpers():void { super.resetHelpers(); if(label != null) label.width = width; } /** * Set sounds to play during mouse-button interactions. * These operations can be done manually as well, and the public * sound variables can be used after this for more fine-tuning, * such as positional audio, etc. * * @param SoundOver What embedded sound effect to play when the mouse goes over the button. Default is null, or no sound. * @param SoundOverVolume How load the that sound should be. * @param SoundOut What embedded sound effect to play when the mouse leaves the button area. Default is null, or no sound. * @param SoundOutVolume How load the that sound should be. * @param SoundDown What embedded sound effect to play when the mouse presses the button down. Default is null, or no sound. * @param SoundDownVolume How load the that sound should be. * @param SoundUp What embedded sound effect to play when the mouse releases the button. Default is null, or no sound. * @param SoundUpVolume How load the that sound should be. */ public function setSounds(SoundOver:Class=null, SoundOverVolume:Number=1.0, SoundOut:Class=null, SoundOutVolume:Number=1.0, SoundDown:Class=null, SoundDownVolume:Number=1.0, SoundUp:Class=null, SoundUpVolume:Number=1.0):void { if(SoundOver != null) soundOver = FlxG.loadSound(SoundOver, SoundOverVolume); if(SoundOut != null) soundOut = FlxG.loadSound(SoundOut, SoundOutVolume); if(SoundDown != null) soundDown = FlxG.loadSound(SoundDown, SoundDownVolume); if(SoundUp != null) soundUp = FlxG.loadSound(SoundUp, SoundUpVolume); } /** * Use this to toggle checkbox-style behavior. */ public function get on():Boolean { return _onToggle; } /** * @private */ public function set on(On:Boolean):void { _onToggle = On; } /** * Internal function for handling the actual callback call (for UI thread dependent calls like FlxU.openURL()). */ protected function handleMouseUp(event:MouseEvent):void { if(!exists || !visible || !active || (status != PRESSED)) return; if(onUp != null) onUp(); if(soundUp != null) soundUp.play(true); } } } ================================================ FILE: org/flixel/FlxCamera.as ================================================ package org.flixel { import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.Sprite; import flash.geom.ColorTransform; import flash.geom.Point; import flash.geom.Rectangle; /** * The camera class is used to display the game's visuals in the Flash player. * By default one camera is created automatically, that is the same size as the Flash player. * You can add more cameras or even replace the main camera using utilities in FlxG. * * @author Adam Atomic */ public class FlxCamera extends FlxBasic { /** * Camera "follow" style preset: camera has no deadzone, just tracks the focus object directly. */ static public const STYLE_LOCKON:uint = 0; /** * Camera "follow" style preset: camera deadzone is narrow but tall. */ static public const STYLE_PLATFORMER:uint = 1; /** * Camera "follow" style preset: camera deadzone is a medium-size square around the focus object. */ static public const STYLE_TOPDOWN:uint = 2; /** * Camera "follow" style preset: camera deadzone is a small square around the focus object. */ static public const STYLE_TOPDOWN_TIGHT:uint = 3; /** * Camera "shake" effect preset: shake camera on both the X and Y axes. */ static public const SHAKE_BOTH_AXES:uint = 0; /** * Camera "shake" effect preset: shake camera on the X axis only. */ static public const SHAKE_HORIZONTAL_ONLY:uint = 1; /** * Camera "shake" effect preset: shake camera on the Y axis only. */ static public const SHAKE_VERTICAL_ONLY:uint = 2; /** * While you can alter the zoom of each camera after the fact, * this variable determines what value the camera will start at when created. */ static public var defaultZoom:Number; /** * The X position of this camera's display. Zoom does NOT affect this number. * Measured in pixels from the left side of the flash window. */ public var x:Number; /** * The Y position of this camera's display. Zoom does NOT affect this number. * Measured in pixels from the top of the flash window. */ public var y:Number; /** * How wide the camera display is, in game pixels. */ public var width:uint; /** * How tall the camera display is, in game pixels. */ public var height:uint; /** * Tells the camera to follow this FlxObject object around. */ public var target:FlxObject; /** * You can assign a "dead zone" to the camera in order to better control its movement. * The camera will always keep the focus object inside the dead zone, * unless it is bumping up against the bounds rectangle's edges. * The deadzone's coordinates are measured from the camera's upper left corner in game pixels. * For rapid prototyping, you can use the preset deadzones (e.g. STYLE_PLATFORMER) with follow(). */ public var deadzone:FlxRect; /** * The edges of the camera's range, i.e. where to stop scrolling. * Measured in game pixels and world coordinates. */ public var bounds:FlxRect; /** * Stores the basic parallax scrolling values. */ public var scroll:FlxPoint; /** * The actual bitmap data of the camera display itself. */ public var buffer:BitmapData; /** * The natural background color of the camera. Defaults to FlxG.bgColor. * NOTE: can be transparent for crazy FX! */ public var bgColor:uint; /** * Sometimes it's easier to just work with a FlxSprite than it is to work * directly with the BitmapData buffer. This sprite reference will * allow you to do exactly that. */ public var screen:FlxSprite; /** * Indicates how far the camera is zoomed in. */ protected var _zoom:Number; /** * Internal, to help avoid costly allocations. */ protected var _point:FlxPoint; /** * Internal, help with color transforming the flash bitmap. */ protected var _color:uint; /** * Internal, used to render buffer to screen space. */ protected var _flashBitmap:Bitmap; /** * Internal, used to render buffer to screen space. */ internal var _flashSprite:Sprite; /** * Internal, used to render buffer to screen space. */ internal var _flashOffsetX:Number; /** * Internal, used to render buffer to screen space. */ internal var _flashOffsetY:Number; /** * Internal, used to render buffer to screen space. */ protected var _flashRect:Rectangle; /** * Internal, used to render buffer to screen space. */ protected var _flashPoint:Point; /** * Internal, used to control the "flash" special effect. */ protected var _fxFlashColor:uint; /** * Internal, used to control the "flash" special effect. */ protected var _fxFlashDuration:Number; /** * Internal, used to control the "flash" special effect. */ protected var _fxFlashComplete:Function; /** * Internal, used to control the "flash" special effect. */ protected var _fxFlashAlpha:Number; /** * Internal, used to control the "fade" special effect. */ protected var _fxFadeColor:uint; /** * Internal, used to control the "fade" special effect. */ protected var _fxFadeDuration:Number; /** * Internal, used to control the "fade" special effect. */ protected var _fxFadeComplete:Function; /** * Internal, used to control the "fade" special effect. */ protected var _fxFadeAlpha:Number; /** * Internal, used to control the "shake" special effect. */ protected var _fxShakeIntensity:Number; /** * Internal, used to control the "shake" special effect. */ protected var _fxShakeDuration:Number; /** * Internal, used to control the "shake" special effect. */ protected var _fxShakeComplete:Function; /** * Internal, used to control the "shake" special effect. */ protected var _fxShakeOffset:FlxPoint; /** * Internal, used to control the "shake" special effect. */ protected var _fxShakeDirection:uint; /** * Internal helper variable for doing better wipes/fills between renders. */ protected var _fill:BitmapData; /** * Instantiates a new camera at the specified location, with the specified size and zoom level. * * @param X X location of the camera's display in pixels. Uses native, 1:1 resolution, ignores zoom. * @param Y Y location of the camera's display in pixels. Uses native, 1:1 resolution, ignores zoom. * @param Width The width of the camera display in pixels. * @param Height The height of the camera display in pixels. * @param Zoom The initial zoom level of the camera. A zoom level of 2 will make all pixels display at 2x resolution. */ public function FlxCamera(X:int,Y:int,Width:int,Height:int,Zoom:Number=0) { x = X; y = Y; width = Width; height = Height; target = null; deadzone = null; scroll = new FlxPoint(); _point = new FlxPoint(); bounds = null; screen = new FlxSprite(); screen.makeGraphic(width,height,0,true); screen.setOriginToCorner(); buffer = screen.pixels; bgColor = FlxG.bgColor; _color = 0xffffff; _flashBitmap = new Bitmap(buffer); _flashBitmap.x = -width*0.5; _flashBitmap.y = -height*0.5; _flashSprite = new Sprite(); zoom = Zoom; //sets the scale of flash sprite, which in turn loads flashoffset values _flashOffsetX = width*0.5*zoom; _flashOffsetY = height*0.5*zoom; _flashSprite.x = x + _flashOffsetX; _flashSprite.y = y + _flashOffsetY; _flashSprite.addChild(_flashBitmap); _flashRect = new Rectangle(0,0,width,height); _flashPoint = new Point(); _fxFlashColor = 0; _fxFlashDuration = 0.0; _fxFlashComplete = null; _fxFlashAlpha = 0.0; _fxFadeColor = 0; _fxFadeDuration = 0.0; _fxFadeComplete = null; _fxFadeAlpha = 0.0; _fxShakeIntensity = 0.0; _fxShakeDuration = 0.0; _fxShakeComplete = null; _fxShakeOffset = new FlxPoint(); _fxShakeDirection = 0; _fill = new BitmapData(width,height,true,0); } /** * Clean up memory. */ override public function destroy():void { screen.destroy(); screen = null; target = null; scroll = null; deadzone = null; bounds = null; buffer = null; _flashBitmap = null; _flashRect = null; _flashPoint = null; _fxFlashComplete = null; _fxFadeComplete = null; _fxShakeComplete = null; _fxShakeOffset = null; _fill = null; } /** * Updates the camera scroll as well as special effects like screen-shake or fades. */ override public function update():void { //Either follow the object closely, //or doublecheck our deadzone and update accordingly. if(target != null) { if(deadzone == null) focusOn(target.getMidpoint(_point)); else { var edge:Number; var targetX:Number = target.x + ((target.x > 0)?0.0000001:-0.0000001); var targetY:Number = target.y + ((target.y > 0)?0.0000001:-0.0000001); edge = targetX - deadzone.x; if(scroll.x > edge) scroll.x = edge; edge = targetX + target.width - deadzone.x - deadzone.width; if(scroll.x < edge) scroll.x = edge; edge = targetY - deadzone.y; if(scroll.y > edge) scroll.y = edge; edge = targetY + target.height - deadzone.y - deadzone.height; if(scroll.y < edge) scroll.y = edge; } } //Make sure we didn't go outside the camera's bounds if(bounds != null) { if(scroll.x < bounds.left) scroll.x = bounds.left; if(scroll.x > bounds.right - width) scroll.x = bounds.right - width; if(scroll.y < bounds.top) scroll.y = bounds.top; if(scroll.y > bounds.bottom - height) scroll.y = bounds.bottom - height; } //Update the "flash" special effect if(_fxFlashAlpha > 0.0) { _fxFlashAlpha -= FlxG.elapsed/_fxFlashDuration; if((_fxFlashAlpha <= 0) && (_fxFlashComplete != null)) _fxFlashComplete(); } //Update the "fade" special effect if((_fxFadeAlpha > 0.0) && (_fxFadeAlpha < 1.0)) { _fxFadeAlpha += FlxG.elapsed/_fxFadeDuration; if(_fxFadeAlpha >= 1.0) { _fxFadeAlpha = 1.0; if(_fxFadeComplete != null) _fxFadeComplete(); } } //Update the "shake" special effect if(_fxShakeDuration > 0) { _fxShakeDuration -= FlxG.elapsed; if(_fxShakeDuration <= 0) { _fxShakeOffset.make(); if(_fxShakeComplete != null) _fxShakeComplete(); } else { if((_fxShakeDirection == SHAKE_BOTH_AXES) || (_fxShakeDirection == SHAKE_HORIZONTAL_ONLY)) _fxShakeOffset.x = (FlxG.random()*_fxShakeIntensity*width*2-_fxShakeIntensity*width)*_zoom; if((_fxShakeDirection == SHAKE_BOTH_AXES) || (_fxShakeDirection == SHAKE_VERTICAL_ONLY)) _fxShakeOffset.y = (FlxG.random()*_fxShakeIntensity*height*2-_fxShakeIntensity*height)*_zoom; } } } /** * Tells this camera object what FlxObject to track. * * @param Target The object you want the camera to track. Set to null to not follow anything. * @param Style Leverage one of the existing "deadzone" presets. If you use a custom deadzone, ignore this parameter and manually specify the deadzone after calling follow(). */ public function follow(Target:FlxObject, Style:uint=STYLE_LOCKON):void { target = Target; var helper:Number; switch(Style) { case STYLE_PLATFORMER: var w:Number = width/8; var h:Number = height/3; deadzone = new FlxRect((width-w)/2,(height-h)/2 - h*0.25,w,h); break; case STYLE_TOPDOWN: helper = FlxU.max(width,height)/4; deadzone = new FlxRect((width-helper)/2,(height-helper)/2,helper,helper); break; case STYLE_TOPDOWN_TIGHT: helper = FlxU.max(width,height)/8; deadzone = new FlxRect((width-helper)/2,(height-helper)/2,helper,helper); break; case STYLE_LOCKON: default: deadzone = null; break; } } /** * Move the camera focus to this location instantly. * * @param Point Where you want the camera to focus. */ public function focusOn(Point:FlxPoint):void { Point.x += (Point.x > 0)?0.0000001:-0.0000001; Point.y += (Point.y > 0)?0.0000001:-0.0000001; scroll.make(Point.x - width*0.5,Point.y - height*0.5); } /** * Specify the boundaries of the level or where the camera is allowed to move. * * @param X The smallest X value of your level (usually 0). * @param Y The smallest Y value of your level (usually 0). * @param Width The largest X value of your level (usually the level width). * @param Height The largest Y value of your level (usually the level height). * @param UpdateWorld Whether the global quad-tree's dimensions should be updated to match (default: false). */ public function setBounds(X:Number=0, Y:Number=0, Width:Number=0, Height:Number=0, UpdateWorld:Boolean=false):void { if(bounds == null) bounds = new FlxRect(); bounds.make(X,Y,Width,Height); if(UpdateWorld) FlxG.worldBounds.copyFrom(bounds); update(); } /** * The screen is filled with this color and gradually returns to normal. * * @param Color The color you want to use. * @param Duration How long it takes for the flash to fade. * @param OnComplete A function you want to run when the flash finishes. * @param Force Force the effect to reset. */ public function flash(Color:uint=0xffffffff, Duration:Number=1, OnComplete:Function=null, Force:Boolean=false):void { if(!Force && (_fxFlashAlpha > 0.0)) return; _fxFlashColor = Color; if(Duration <= 0) Duration = Number.MIN_VALUE; _fxFlashDuration = Duration; _fxFlashComplete = OnComplete; _fxFlashAlpha = 1.0; } /** * The screen is gradually filled with this color. * * @param Color The color you want to use. * @param Duration How long it takes for the fade to finish. * @param OnComplete A function you want to run when the fade finishes. * @param Force Force the effect to reset. */ public function fade(Color:uint=0xff000000, Duration:Number=1, OnComplete:Function=null, Force:Boolean=false):void { if(!Force && (_fxFadeAlpha > 0.0)) return; _fxFadeColor = Color; if(Duration <= 0) Duration = Number.MIN_VALUE; _fxFadeDuration = Duration; _fxFadeComplete = OnComplete; _fxFadeAlpha = Number.MIN_VALUE; } /** * A simple screen-shake effect. * * @param Intensity Percentage of screen size representing the maximum distance that the screen can move while shaking. * @param Duration The length in seconds that the shaking effect should last. * @param OnComplete A function you want to run when the shake effect finishes. * @param Force Force the effect to reset (default = true, unlike flash() and fade()!). * @param Direction Whether to shake on both axes, just up and down, or just side to side (use class constants SHAKE_BOTH_AXES, SHAKE_VERTICAL_ONLY, or SHAKE_HORIZONTAL_ONLY). */ public function shake(Intensity:Number=0.05, Duration:Number=0.5, OnComplete:Function=null, Force:Boolean=true, Direction:uint=SHAKE_BOTH_AXES):void { if(!Force && ((_fxShakeOffset.x != 0) || (_fxShakeOffset.y != 0))) return; _fxShakeIntensity = Intensity; _fxShakeDuration = Duration; _fxShakeComplete = OnComplete; _fxShakeDirection = Direction; _fxShakeOffset.make(); } /** * Just turns off all the camera effects instantly. */ public function stopFX():void { _fxFlashAlpha = 0.0; _fxFadeAlpha = 0.0; _fxShakeDuration = 0; _flashSprite.x = x + width*0.5; _flashSprite.y = y + height*0.5; } /** * Copy the bounds, focus object, and deadzone info from an existing camera. * * @param Camera The camera you want to copy from. * * @return A reference to this FlxCamera object. */ public function copyFrom(Camera:FlxCamera):FlxCamera { if(Camera.bounds == null) bounds = null; else { if(bounds == null) bounds = new FlxRect(); bounds.copyFrom(Camera.bounds); } target = Camera.target; if(target != null) { if(Camera.deadzone == null) deadzone = null; else { if(deadzone == null) deadzone = new FlxRect(); deadzone.copyFrom(Camera.deadzone); } } return this; } /** * The zoom level of this camera. 1 = 1:1, 2 = 2x zoom, etc. */ public function get zoom():Number { return _zoom; } /** * @private */ public function set zoom(Zoom:Number):void { if(Zoom == 0) _zoom = defaultZoom; else _zoom = Zoom; setScale(_zoom,_zoom); } /** * The alpha value of this camera display (a Number between 0.0 and 1.0). */ public function get alpha():Number { return _flashBitmap.alpha; } /** * @private */ public function set alpha(Alpha:Number):void { _flashBitmap.alpha = Alpha; } /** * The angle of the camera display (in degrees). * Currently yields weird display results, * since cameras aren't nested in an extra display object yet. */ public function get angle():Number { return _flashSprite.rotation; } /** * @private */ public function set angle(Angle:Number):void { _flashSprite.rotation = Angle; } /** * The color tint of the camera display. */ public function get color():uint { return _color; } /** * @private */ public function set color(Color:uint):void { _color = Color; var colorTransform:ColorTransform = _flashBitmap.transform.colorTransform; colorTransform.redMultiplier = (_color>>16)*0.00392; colorTransform.greenMultiplier = (_color>>8&0xff)*0.00392; colorTransform.blueMultiplier = (_color&0xff)*0.00392; _flashBitmap.transform.colorTransform = colorTransform; } /** * Whether the camera display is smooth and filtered, or chunky and pixelated. * Default behavior is chunky-style. */ public function get antialiasing():Boolean { return _flashBitmap.smoothing; } /** * @private */ public function set antialiasing(Antialiasing:Boolean):void { _flashBitmap.smoothing = Antialiasing; } /** * The scale of the camera object, irrespective of zoom. * Currently yields weird display results, * since cameras aren't nested in an extra display object yet. */ public function getScale():FlxPoint { return _point.make(_flashSprite.scaleX,_flashSprite.scaleY); } /** * @private */ public function setScale(X:Number,Y:Number):void { _flashSprite.scaleX = X; _flashSprite.scaleY = Y; } /** * Fetches a reference to the Flash Sprite object * that contains the camera display in the Flash display list. * Uses include 3D projection, advanced display list modification, and more. * NOTE: We don't recommend modifying this directly unless you are * fairly experienced. For simple changes to the camera display, * like scaling, rotation, and color tinting, we recommend * using the existing FlxCamera variables. * * @return A Flash Sprite object containing the camera display. */ public function getContainerSprite():Sprite { return _flashSprite; } /** * Fill the camera with the specified color. * * @param Color The color to fill with in 0xAARRGGBB hex format. * @param BlendAlpha Whether to blend the alpha value or just wipe the previous contents. Default is true. */ public function fill(Color:uint,BlendAlpha:Boolean=true):void { _fill.fillRect(_flashRect,Color); buffer.copyPixels(_fill,_flashRect,_flashPoint,null,null,BlendAlpha); } /** * Internal helper function, handles the actual drawing of all the special effects. */ internal function drawFX():void { var alphaComponent:Number; //Draw the "flash" special effect onto the buffer if(_fxFlashAlpha > 0.0) { alphaComponent = _fxFlashColor>>24; fill((uint(((alphaComponent <= 0)?0xff:alphaComponent)*_fxFlashAlpha)<<24)+(_fxFlashColor&0x00ffffff)); } //Draw the "fade" special effect onto the buffer if(_fxFadeAlpha > 0.0) { alphaComponent = _fxFadeColor>>24; fill((uint(((alphaComponent <= 0)?0xff:alphaComponent)*_fxFadeAlpha)<<24)+(_fxFadeColor&0x00ffffff)); } if((_fxShakeOffset.x != 0) || (_fxShakeOffset.y != 0)) { _flashSprite.x = x + _flashOffsetX + _fxShakeOffset.x; _flashSprite.y = y + _flashOffsetY + _fxShakeOffset.y; } } } } ================================================ FILE: org/flixel/FlxEmitter.as ================================================ package org.flixel { /** * FlxEmitter is a lightweight particle emitter. * It can be used for one-time explosions or for * continuous fx like rain and fire. FlxEmitter * is not optimized or anything; all it does is launch * FlxParticle objects out at set intervals * by setting their positions and velocities accordingly. * It is easy to use and relatively efficient, * relying on FlxGroup's RECYCLE POWERS. * * @author Adam Atomic */ public class FlxEmitter extends FlxGroup { /** * The X position of the top left corner of the emitter in world space. */ public var x:Number; /** * The Y position of the top left corner of emitter in world space. */ public var y:Number; /** * The width of the emitter. Particles can be randomly generated from anywhere within this box. */ public var width:Number; /** * The height of the emitter. Particles can be randomly generated from anywhere within this box. */ public var height:Number; /** * The minimum possible velocity of a particle. * The default value is (-100,-100). */ public var minParticleSpeed:FlxPoint; /** * The maximum possible velocity of a particle. * The default value is (100,100). */ public var maxParticleSpeed:FlxPoint; /** * The X and Y drag component of particles launched from the emitter. */ public var particleDrag:FlxPoint; /** * The minimum possible angular velocity of a particle. The default value is -360. * NOTE: rotating particles are more expensive to draw than non-rotating ones! */ public var minRotation:Number; /** * The maximum possible angular velocity of a particle. The default value is 360. * NOTE: rotating particles are more expensive to draw than non-rotating ones! */ public var maxRotation:Number; /** * Sets the acceleration.y member of each particle to this value on launch. */ public var gravity:Number; /** * Determines whether the emitter is currently emitting particles. * It is totally safe to directly toggle this. */ public var on:Boolean; /** * How often a particle is emitted (if emitter is started with Explode == false). */ public var frequency:Number; /** * How long each particle lives once it is emitted. * Set lifespan to 'zero' for particles to live forever. */ public var lifespan:Number; /** * How much each particle should bounce. 1 = full bounce, 0 = no bounce. */ public var bounce:Number; /** * Set your own particle class type here. * Default is FlxParticle. */ public var particleClass:Class; /** * Internal helper for deciding how many particles to launch. */ protected var _quantity:uint; /** * Internal helper for the style of particle emission (all at once, or one at a time). */ protected var _explode:Boolean; /** * Internal helper for deciding when to launch particles or kill them. */ protected var _timer:Number; /** * Internal counter for figuring out how many particles to launch. */ protected var _counter:uint; /** * Internal point object, handy for reusing for memory mgmt purposes. */ protected var _point:FlxPoint; /** * Creates a new FlxEmitter object at a specific position. * Does NOT automatically generate or attach particles! * * @param X The X position of the emitter. * @param Y The Y position of the emitter. * @param Size Optional, specifies a maximum capacity for this emitter. */ public function FlxEmitter(X:Number=0, Y:Number=0, Size:Number=0) { super(Size); x = X; y = Y; width = 0; height = 0; minParticleSpeed = new FlxPoint(-100,-100); maxParticleSpeed = new FlxPoint(100,100); minRotation = -360; maxRotation = 360; gravity = 0; particleClass = null; particleDrag = new FlxPoint(); frequency = 0.1; lifespan = 3; bounce = 0; _quantity = 0; _counter = 0; _explode = true; on = false; _point = new FlxPoint(); } /** * Clean up memory. */ override public function destroy():void { minParticleSpeed = null; maxParticleSpeed = null; particleDrag = null; particleClass = null; _point = null; super.destroy(); } /** * This function generates a new array of particle sprites to attach to the emitter. * * @param Graphics If you opted to not pre-configure an array of FlxSprite objects, you can simply pass in a particle image or sprite sheet. * @param Quantity The number of particles to generate when using the "create from image" option. * @param BakedRotations How many frames of baked rotation to use (boosts performance). Set to zero to not use baked rotations. * @param Multiple Whether the image in the Graphics param is a single particle or a bunch of particles (if it's a bunch, they need to be square!). * @param Collide Whether the particles should be flagged as not 'dead' (non-colliding particles are higher performance). 0 means no collisions, 0-1 controls scale of particle's bounding box. * * @return This FlxEmitter instance (nice for chaining stuff together, if you're into that). */ public function makeParticles(Graphics:Class, Quantity:uint=50, BakedRotations:uint=16, Multiple:Boolean=false, Collide:Number=0.8):FlxEmitter { maxSize = Quantity; var totalFrames:uint = 1; if(Multiple) { var sprite:FlxSprite = new FlxSprite(); sprite.loadGraphic(Graphics,true); totalFrames = sprite.frames; sprite.destroy(); } var randomFrame:uint; var particle:FlxParticle; var i:uint = 0; while(i < Quantity) { if(particleClass == null) particle = new FlxParticle(); else particle = new particleClass(); if(Multiple) { randomFrame = FlxG.random()*totalFrames; if(BakedRotations > 0) particle.loadRotatedGraphic(Graphics,BakedRotations,randomFrame); else { particle.loadGraphic(Graphics,true); particle.frame = randomFrame; } } else { if(BakedRotations > 0) particle.loadRotatedGraphic(Graphics,BakedRotations); else particle.loadGraphic(Graphics); } if(Collide > 0) { particle.width *= Collide; particle.height *= Collide; particle.centerOffsets(); } else particle.allowCollisions = FlxObject.NONE; particle.exists = false; add(particle); i++; } return this; } /** * Called automatically by the game loop, decides when to launch particles and when to "die". */ override public function update():void { if(on) { if(_explode) { on = false; var i:uint = 0; var l:uint = _quantity; if((l <= 0) || (l > length)) l = length; while(i < l) { emitParticle(); i++; } _quantity = 0; } else { _timer += FlxG.elapsed; while((frequency > 0) && (_timer > frequency) && on) { _timer -= frequency; emitParticle(); if((_quantity > 0) && (++_counter >= _quantity)) { on = false; _quantity = 0; } } } } super.update(); } /** * Call this function to turn off all the particles and the emitter. */ override public function kill():void { on = false; super.kill(); } /** * Call this function to start emitting particles. * * @param Explode Whether the particles should all burst out at once. * @param Lifespan How long each particle lives once emitted. 0 = forever. * @param Frequency Ignored if Explode is set to true. Frequency is how often to emit a particle. 0 = never emit, 0.1 = 1 particle every 0.1 seconds, 5 = 1 particle every 5 seconds. * @param Quantity How many particles to launch. 0 = "all of the particles". */ public function start(Explode:Boolean=true,Lifespan:Number=0,Frequency:Number=0.1,Quantity:uint=0):void { revive(); visible = true; on = true; _explode = Explode; lifespan = Lifespan; frequency = Frequency; _quantity += Quantity; _counter = 0; _timer = 0; } /** * This function can be used both internally and externally to emit the next particle. */ public function emitParticle():void { var particle:FlxParticle = recycle(FlxParticle) as FlxParticle; particle.lifespan = lifespan; particle.elasticity = bounce; particle.reset(x - (particle.width>>1) + FlxG.random()*width, y - (particle.height>>1) + FlxG.random()*height); particle.visible = true; if(minParticleSpeed.x != maxParticleSpeed.x) particle.velocity.x = minParticleSpeed.x + FlxG.random()*(maxParticleSpeed.x-minParticleSpeed.x); else particle.velocity.x = minParticleSpeed.x; if(minParticleSpeed.y != maxParticleSpeed.y) particle.velocity.y = minParticleSpeed.y + FlxG.random()*(maxParticleSpeed.y-minParticleSpeed.y); else particle.velocity.y = minParticleSpeed.y; particle.acceleration.y = gravity; if(minRotation != maxRotation) particle.angularVelocity = minRotation + FlxG.random()*(maxRotation-minRotation); else particle.angularVelocity = minRotation; if(particle.angularVelocity != 0) particle.angle = FlxG.random()*360-180; particle.drag.x = particleDrag.x; particle.drag.y = particleDrag.y; particle.onEmit(); } /** * A more compact way of setting the width and height of the emitter. * * @param Width The desired width of the emitter (particles are spawned randomly within these dimensions). * @param Height The desired height of the emitter. */ public function setSize(Width:uint,Height:uint):void { width = Width; height = Height; } /** * A more compact way of setting the X velocity range of the emitter. * * @param Min The minimum value for this range. * @param Max The maximum value for this range. */ public function setXSpeed(Min:Number=0,Max:Number=0):void { minParticleSpeed.x = Min; maxParticleSpeed.x = Max; } /** * A more compact way of setting the Y velocity range of the emitter. * * @param Min The minimum value for this range. * @param Max The maximum value for this range. */ public function setYSpeed(Min:Number=0,Max:Number=0):void { minParticleSpeed.y = Min; maxParticleSpeed.y = Max; } /** * A more compact way of setting the angular velocity constraints of the emitter. * * @param Min The minimum value for this range. * @param Max The maximum value for this range. */ public function setRotation(Min:Number=0,Max:Number=0):void { minRotation = Min; maxRotation = Max; } /** * Change the emitter's midpoint to match the midpoint of a FlxObject. * * @param Object The FlxObject that you want to sync up with. */ public function at(Object:FlxObject):void { Object.getMidpoint(_point); x = _point.x - (width>>1); y = _point.y - (height>>1); } } } ================================================ FILE: org/flixel/FlxG.as ================================================ package org.flixel { import flash.display.BitmapData; import flash.display.Graphics; import flash.display.Sprite; import flash.display.Stage; import flash.geom.Matrix; import flash.geom.Point; import flash.geom.Rectangle; import org.flixel.plugin.DebugPathDisplay; import org.flixel.plugin.TimerManager; import org.flixel.system.FlxDebugger; import org.flixel.system.FlxQuadTree; import org.flixel.system.input.*; /** * This is a global helper class full of useful functions for audio, * input, basic info, and the camera system among other things. * Utilities for maths and color and things can be found in FlxU. * FlxG is specifically for Flixel-specific properties. * * @author Adam Atomic */ public class FlxG { /** * If you build and maintain your own version of flixel, * you can give it your own name here. */ static public var LIBRARY_NAME:String = "flixel"; /** * Assign a major version to your library. * Appears before the decimal in the console. */ static public var LIBRARY_MAJOR_VERSION:uint = 2; /** * Assign a minor version to your library. * Appears after the decimal in the console. */ static public var LIBRARY_MINOR_VERSION:uint = 55; /** * Debugger overlay layout preset: Wide but low windows at the bottom of the screen. */ static public const DEBUGGER_STANDARD:uint = 0; /** * Debugger overlay layout preset: Tiny windows in the screen corners. */ static public const DEBUGGER_MICRO:uint = 1; /** * Debugger overlay layout preset: Large windows taking up bottom half of screen. */ static public const DEBUGGER_BIG:uint = 2; /** * Debugger overlay layout preset: Wide but low windows at the top of the screen. */ static public const DEBUGGER_TOP:uint = 3; /** * Debugger overlay layout preset: Large windows taking up left third of screen. */ static public const DEBUGGER_LEFT:uint = 4; /** * Debugger overlay layout preset: Large windows taking up right third of screen. */ static public const DEBUGGER_RIGHT:uint = 5; /** * Some handy color presets. Less glaring than pure RGB full values. * Primarily used in the visual debugger mode for bounding box displays. * Red is used to indicate an active, movable, solid object. */ static public const RED:uint = 0xffff0012; /** * Green is used to indicate solid but immovable objects. */ static public const GREEN:uint = 0xff00f225; /** * Blue is used to indicate non-solid objects. */ static public const BLUE:uint = 0xff0090e9; /** * Pink is used to indicate objects that are only partially solid, like one-way platforms. */ static public const PINK:uint = 0xfff01eff; /** * White... for white stuff. */ static public const WHITE:uint = 0xffffffff; /** * And black too. */ static public const BLACK:uint = 0xff000000; /** * Internal tracker for game object. */ static internal var _game:FlxGame; /** * Handy shared variable for implementing your own pause behavior. */ static public var paused:Boolean; /** * Whether you are running in Debug or Release mode. * Set automatically by FlxPreloader during startup. */ static public var debug:Boolean; /** * Represents the amount of time in seconds that passed since last frame. */ static public var elapsed:Number; /** * How fast or slow time should pass in the game; default is 1.0. */ static public var timeScale:Number; /** * The width of the screen in game pixels. */ static public var width:uint; /** * The height of the screen in game pixels. */ static public var height:uint; /** * The dimensions of the game world, used by the quad tree for collisions and overlap checks. */ static public var worldBounds:FlxRect; /** * How many times the quad tree should divide the world on each axis. * Generally, sparse collisions can have fewer divisons, * while denser collision activity usually profits from more. * Default value is 6. */ static public var worldDivisions:uint; /** * Whether to show visual debug displays or not. * Default = false. */ static public var visualDebug:Boolean; /** * Setting this to true will disable/skip stuff that isn't necessary for mobile platforms like Android. [BETA] */ static public var mobile:Boolean; /** * The global random number generator seed (for deterministic behavior in recordings and saves). */ static public var globalSeed:Number; /** * FlxG.levels and FlxG.scores are generic * global variables that can be used for various cross-state stuff. */ static public var levels:Array; static public var level:int; static public var scores:Array; static public var score:int; /** * FlxG.saves is a generic bucket for storing * FlxSaves so you can access them whenever you want. */ static public var saves:Array; static public var save:int; /** * A reference to a FlxMouse object. Important for input! */ static public var mouse:Mouse; /** * A reference to a FlxKeyboard object. Important for input! */ static public var keys:Keyboard; /** * A handy container for a background music object. */ static public var music:FlxSound; /** * A list of all the sounds being played in the game. */ static public var sounds:FlxGroup; /** * Whether or not the game sounds are muted. */ static public var mute:Boolean; /** * Internal volume level, used for global sound control. */ static protected var _volume:Number; /** * An array of FlxCamera objects that are used to draw stuff. * By default flixel creates one camera the size of the screen. */ static public var cameras:Array; /** * By default this just refers to the first entry in the cameras array * declared above, but you can do what you like with it. */ static public var camera:FlxCamera; /** * Allows you to possibly slightly optimize the rendering process IF * you are not doing any pre-processing in your game state's draw() call. * @default false */ static public var useBufferLocking:Boolean; /** * Internal helper variable for clearing the cameras each frame. */ static protected var _cameraRect:Rectangle; /** * An array container for plugins. * By default flixel uses a couple of plugins: * DebugPathDisplay, and TimerManager. */ static public var plugins:Array; /** * Set this hook to get a callback whenever the volume changes. * Function should take the form myVolumeHandler(Volume:Number). */ static public var volumeHandler:Function; /** * Useful helper objects for doing Flash-specific rendering. * Primarily used for "debug visuals" like drawing bounding boxes directly to the screen buffer. */ static public var flashGfxSprite:Sprite; static public var flashGfx:Graphics; /** * Internal storage system to prevent graphics from being used repeatedly in memory. */ static protected var _cache:Object; static public function getLibraryName():String { return FlxG.LIBRARY_NAME + " v" + FlxG.LIBRARY_MAJOR_VERSION + "." + FlxG.LIBRARY_MINOR_VERSION; } /** * Log data to the debugger. * * @param Data Anything you want to log to the console. */ static public function log(Data:Object):void { if((_game != null) && (_game._debugger != null)) _game._debugger.log.add((Data == null)?"ERROR: null object":((Data is Array)?FlxU.formatArray(Data as Array):Data.toString())); } /** * Add a variable to the watch list in the debugger. * This lets you see the value of the variable all the time. * * @param AnyObject A reference to any object in your game, e.g. Player or Robot or this. * @param VariableName The name of the variable you want to watch, in quotes, as a string: e.g. "speed" or "health". * @param DisplayName Optional, display your own string instead of the class name + variable name: e.g. "enemy count". */ static public function watch(AnyObject:Object,VariableName:String,DisplayName:String=null):void { if((_game != null) && (_game._debugger != null)) _game._debugger.watch.add(AnyObject,VariableName,DisplayName); } /** * Remove a variable from the watch list in the debugger. * Don't pass a Variable Name to remove all watched variables for the specified object. * * @param AnyObject A reference to any object in your game, e.g. Player or Robot or this. * @param VariableName The name of the variable you want to watch, in quotes, as a string: e.g. "speed" or "health". */ static public function unwatch(AnyObject:Object,VariableName:String=null):void { if((_game != null) && (_game._debugger != null)) _game._debugger.watch.remove(AnyObject,VariableName); } /** * How many times you want your game to update each second. * More updates usually means better collisions and smoother motion. * NOTE: This is NOT the same thing as the Flash Player framerate! */ static public function get framerate():Number { return 1000/_game._step; } /** * @private */ static public function set framerate(Framerate:Number):void { _game._step = 1000/Framerate; if(_game._maxAccumulation < _game._step) _game._maxAccumulation = _game._step; } /** * How many times you want your game to update each second. * More updates usually means better collisions and smoother motion. * NOTE: This is NOT the same thing as the Flash Player framerate! */ static public function get flashFramerate():Number { if(_game.root != null) return _game.stage.frameRate; else return 0; } /** * @private */ static public function set flashFramerate(Framerate:Number):void { _game._flashFramerate = Framerate; if(_game.root != null) _game.stage.frameRate = _game._flashFramerate; _game._maxAccumulation = 2000/_game._flashFramerate - 1; if(_game._maxAccumulation < _game._step) _game._maxAccumulation = _game._step; } /** * Switch to full-screen display. */ static public function fullscreen():void { FlxG.stage.displayState = "fullScreen"; var fsw:uint = FlxG.width*FlxG.camera.zoom; var fsh:uint = FlxG.height*FlxG.camera.zoom; FlxG.camera.x = (FlxG.stage.fullScreenWidth - fsw)/2; FlxG.camera.y = (FlxG.stage.fullScreenHeight - fsh)/2; } /** * Generates a random number. Deterministic, meaning safe * to use if you want to record replays in random environments. * * @return A Number between 0 and 1. */ static public function random():Number { return globalSeed = FlxU.srand(globalSeed); } /** * Shuffles the entries in an array into a new random order. * FlxG.shuffle() is deterministic and safe for use with replays/recordings. * HOWEVER, FlxU.shuffle() is NOT deterministic and unsafe for use with replays/recordings. * * @param A A Flash Array object containing...stuff. * @param HowManyTimes How many swaps to perform during the shuffle operation. Good rule of thumb is 2-4 times as many objects are in the list. * * @return The same Flash Array object that you passed in in the first place. */ static public function shuffle(Objects:Array,HowManyTimes:uint):Array { var i:uint = 0; var index1:uint; var index2:uint; var object:Object; while(i < HowManyTimes) { index1 = FlxG.random()*Objects.length; index2 = FlxG.random()*Objects.length; object = Objects[index2]; Objects[index2] = Objects[index1]; Objects[index1] = object; i++; } return Objects; } /** * Fetch a random entry from the given array. * Will return null if random selection is missing, or array has no entries. * FlxG.getRandom() is deterministic and safe for use with replays/recordings. * HOWEVER, FlxU.getRandom() is NOT deterministic and unsafe for use with replays/recordings. * * @param Objects A Flash array of objects. * @param StartIndex Optional offset off the front of the array. Default value is 0, or the beginning of the array. * @param Length Optional restriction on the number of values you want to randomly select from. * * @return The random object that was selected. */ static public function getRandom(Objects:Array,StartIndex:uint=0,Length:uint=0):Object { if(Objects != null) { var l:uint = Length; if((l == 0) || (l > Objects.length - StartIndex)) l = Objects.length - StartIndex; if(l > 0) return Objects[StartIndex + uint(FlxG.random()*l)]; } return null; } /** * Load replay data from a string and play it back. * * @param Data The replay that you want to load. * @param State Optional parameter: if you recorded a state-specific demo or cutscene, pass a new instance of that state here. * @param CancelKeys Optional parameter: an array of string names of keys (see FlxKeyboard) that can be pressed to cancel the playback, e.g. ["ESCAPE","ENTER"]. Also accepts 2 custom key names: "ANY" and "MOUSE" (fairly self-explanatory I hope!). * @param Timeout Optional parameter: set a time limit for the replay. CancelKeys will override this if pressed. * @param Callback Optional parameter: if set, called when the replay finishes. Running to the end, CancelKeys, and Timeout will all trigger Callback(), but only once, and CancelKeys and Timeout will NOT call FlxG.stopReplay() if Callback is set! */ static public function loadReplay(Data:String,State:FlxState=null,CancelKeys:Array=null,Timeout:Number=0,Callback:Function=null):void { _game._replay.load(Data); if(State == null) FlxG.resetGame(); else FlxG.switchState(State); _game._replayCancelKeys = CancelKeys; _game._replayTimer = Timeout*1000; _game._replayCallback = Callback; _game._replayRequested = true; } /** * Resets the game or state and replay requested flag. * * @param StandardMode If true, reload entire game, else just reload current game state. */ static public function reloadReplay(StandardMode:Boolean=true):void { if(StandardMode) FlxG.resetGame(); else FlxG.resetState(); if(_game._replay.frameCount > 0) _game._replayRequested = true; } /** * Stops the current replay. */ static public function stopReplay():void { _game._replaying = false; if(_game._debugger != null) _game._debugger.vcr.stopped(); resetInput(); } /** * Resets the game or state and requests a new recording. * * @param StandardMode If true, reset the entire game, else just reset the current state. */ static public function recordReplay(StandardMode:Boolean=true):void { if(StandardMode) FlxG.resetGame(); else FlxG.resetState(); _game._recordingRequested = true; } /** * Stop recording the current replay and return the replay data. * * @return The replay data in simple ASCII format (see FlxReplay.save()). */ static public function stopRecording():String { _game._recording = false; if(_game._debugger != null) _game._debugger.vcr.stopped(); return _game._replay.save(); } /** * Request a reset of the current game state. */ static public function resetState():void { _game._requestedState = new (FlxU.getClass(FlxU.getClassName(_game._state,false)))(); } /** * Like hitting the reset button on a game console, this will re-launch the game as if it just started. */ static public function resetGame():void { _game._requestedReset = true; } /** * Reset the input helper objects (useful when changing screens or states) */ static public function resetInput():void { keys.reset(); mouse.reset(); } /** * Set up and play a looping background soundtrack. * * @param Music The sound file you want to loop in the background. * @param Volume How loud the sound should be, from 0 to 1. */ static public function playMusic(Music:Class,Volume:Number=1.0):void { if(music == null) music = new FlxSound(); else if(music.active) music.stop(); music.loadEmbedded(Music,true); music.volume = Volume; music.survive = true; music.play(); } /** * Creates a new sound object. * * @param EmbeddedSound The embedded sound resource you want to play. To stream, use the optional URL parameter instead. * @param Volume How loud to play it (0 to 1). * @param Looped Whether to loop this sound. * @param AutoDestroy Whether to destroy this sound when it finishes playing. Leave this value set to "false" if you want to re-use this FlxSound instance. * @param AutoPlay Whether to play the sound. * @param URL Load a sound from an external web resource instead. Only used if EmbeddedSound = null. * * @return A FlxSound object. */ static public function loadSound(EmbeddedSound:Class=null,Volume:Number=1.0,Looped:Boolean=false,AutoDestroy:Boolean=false,AutoPlay:Boolean=false,URL:String=null):FlxSound { if((EmbeddedSound == null) && (URL == null)) { FlxG.log("WARNING: FlxG.loadSound() requires either\nan embedded sound or a URL to work."); return null; } var sound:FlxSound = sounds.recycle(FlxSound) as FlxSound; if(EmbeddedSound != null) sound.loadEmbedded(EmbeddedSound,Looped,AutoDestroy); else sound.loadStream(URL,Looped,AutoDestroy); sound.volume = Volume; if(AutoPlay) sound.play(); return sound; } /** * Creates a new sound object from an embedded Class object. * NOTE: Just calls FlxG.loadSound() with AutoPlay == true. * * @param EmbeddedSound The sound you want to play. * @param Volume How loud to play it (0 to 1). * @param Looped Whether to loop this sound. * @param AutoDestroy Whether to destroy this sound when it finishes playing. Leave this value set to "false" if you want to re-use this FlxSound instance. * * @return A FlxSound object. */ static public function play(EmbeddedSound:Class,Volume:Number=1.0,Looped:Boolean=false,AutoDestroy:Boolean=true):FlxSound { return FlxG.loadSound(EmbeddedSound,Volume,Looped,AutoDestroy,true); } /** * Creates a new sound object from a URL. * NOTE: Just calls FlxG.loadSound() with AutoPlay == true. * * @param URL The URL of the sound you want to play. * @param Volume How loud to play it (0 to 1). * @param Looped Whether or not to loop this sound. * @param AutoDestroy Whether to destroy this sound when it finishes playing. Leave this value set to "false" if you want to re-use this FlxSound instance. * * @return A FlxSound object. */ static public function stream(URL:String,Volume:Number=1.0,Looped:Boolean=false,AutoDestroy:Boolean=true):FlxSound { return FlxG.loadSound(null,Volume,Looped,AutoDestroy,true,URL); } /** * Set volume to a number between 0 and 1 to change the global volume. * * @default 0.5 */ static public function get volume():Number { return _volume; } /** * @private */ static public function set volume(Volume:Number):void { _volume = Volume; if(_volume < 0) _volume = 0; else if(_volume > 1) _volume = 1; if(volumeHandler != null) volumeHandler(FlxG.mute?0:_volume); } /** * Called by FlxGame on state changes to stop and destroy sounds. * * @param ForceDestroy Kill sounds even if they're flagged survive. */ static internal function destroySounds(ForceDestroy:Boolean=false):void { if((music != null) && (ForceDestroy || !music.survive)) { music.destroy(); music = null; } var i:uint = 0; var sound:FlxSound; var l:uint = sounds.members.length; while(i < l) { sound = sounds.members[i++] as FlxSound; if((sound != null) && (ForceDestroy || !sound.survive)) sound.destroy(); } } /** * Called by the game loop to make sure the sounds get updated each frame. */ static internal function updateSounds():void { if((music != null) && music.active) music.update(); if((sounds != null) && sounds.active) sounds.update(); } /** * Pause all sounds currently playing. */ static public function pauseSounds():void { if((music != null) && music.exists && music.active) music.pause(); var i:uint = 0; var sound:FlxSound; var l:uint = sounds.length; while(i < l) { sound = sounds.members[i++] as FlxSound; if((sound != null) && sound.exists && sound.active) sound.pause(); } } /** * Resume playing existing sounds. */ static public function resumeSounds():void { if((music != null) && music.exists) music.play(); var i:uint = 0; var sound:FlxSound; var l:uint = sounds.length; while(i < l) { sound = sounds.members[i++] as FlxSound; if((sound != null) && sound.exists) sound.resume(); } } /** * Check the local bitmap cache to see if a bitmap with this key has been loaded already. * * @param Key The string key identifying the bitmap. * * @return Whether or not this file can be found in the cache. */ static public function checkBitmapCache(Key:String):Boolean { return (_cache[Key] != undefined) && (_cache[Key] != null); } /** * Generates a new BitmapData object (a colored square) and caches it. * * @param Width How wide the square should be. * @param Height How high the square should be. * @param Color What color the square should be (0xAARRGGBB) * @param Unique Ensures that the bitmap data uses a new slot in the cache. * @param Key Force the cache to use a specific Key to index the bitmap. * * @return The BitmapData we just created. */ static public function createBitmap(Width:uint, Height:uint, Color:uint, Unique:Boolean=false, Key:String=null):BitmapData { if(Key == null) { Key = Width+"x"+Height+":"+Color; if(Unique && checkBitmapCache(Key)) { var inc:uint = 0; var ukey:String; do { ukey = Key + inc++; } while(checkBitmapCache(ukey)); Key = ukey; } } if(!checkBitmapCache(Key)) _cache[Key] = new BitmapData(Width,Height,true,Color); return _cache[Key]; } /** * Loads a bitmap from a file, caches it, and generates a horizontally flipped version if necessary. * * @param Graphic The image file that you want to load. * @param Reverse Whether to generate a flipped version. * @param Unique Ensures that the bitmap data uses a new slot in the cache. * @param Key Force the cache to use a specific Key to index the bitmap. * * @return The BitmapData we just created. */ static public function addBitmap(Graphic:Class, Reverse:Boolean=false, Unique:Boolean=false, Key:String=null):BitmapData { var needReverse:Boolean = false; if(Key == null) { Key = String(Graphic)+(Reverse?"_REVERSE_":""); if(Unique && checkBitmapCache(Key)) { var inc:uint = 0; var ukey:String; do { ukey = Key + inc++; } while(checkBitmapCache(ukey)); Key = ukey; } } //If there is no data for this key, generate the requested graphic if(!checkBitmapCache(Key)) { _cache[Key] = (new Graphic).bitmapData; if(Reverse) needReverse = true; } var pixels:BitmapData = _cache[Key]; if(!needReverse && Reverse && (pixels.width == (new Graphic).bitmapData.width)) needReverse = true; if(needReverse) { var newPixels:BitmapData = new BitmapData(pixels.width<<1,pixels.height,true,0x00000000); newPixels.draw(pixels); var mtx:Matrix = new Matrix(); mtx.scale(-1,1); mtx.translate(newPixels.width,0); newPixels.draw(pixels,mtx); pixels = newPixels; _cache[Key] = pixels; } return pixels; } /** * Dumps the cache's image references. */ static public function clearBitmapCache():void { _cache = new Object(); } /** * Read-only: retrieves the Flash stage object (required for event listeners) * Will be null if it's not safe/useful yet. */ static public function get stage():Stage { if(_game.root != null) return _game.stage; return null; } /** * Read-only: access the current game state from anywhere. */ static public function get state():FlxState { return _game._state; } /** * Switch from the current game state to the one specified here. */ static public function switchState(State:FlxState):void { _game._requestedState = State; } /** * Change the way the debugger's windows are laid out. * * @param Layout See the presets above (e.g. DEBUGGER_MICRO, etc). */ static public function setDebuggerLayout(Layout:uint):void { if(_game._debugger != null) _game._debugger.setLayout(Layout); } /** * Just resets the debugger windows to whatever the last selected layout was (DEBUGGER_STANDARD by default). */ static public function resetDebuggerLayout():void { if(_game._debugger != null) _game._debugger.resetLayout(); } /** * Add a new camera object to the game. * Handy for PiP, split-screen, etc. * * @param NewCamera The camera you want to add. * * @return This FlxCamera instance. */ static public function addCamera(NewCamera:FlxCamera):FlxCamera { FlxG._game.addChildAt(NewCamera._flashSprite,FlxG._game.getChildIndex(FlxG._game._mouse)); FlxG.cameras.push(NewCamera); return NewCamera; } /** * Remove a camera from the game. * * @param Camera The camera you want to remove. * @param Destroy Whether to call destroy() on the camera, default value is true. */ static public function removeCamera(Camera:FlxCamera,Destroy:Boolean=true):void { try { FlxG._game.removeChild(Camera._flashSprite); } catch(E:Error) { FlxG.log("Error removing camera, not part of game."); } if(Destroy) Camera.destroy(); } /** * Dumps all the current cameras and resets to just one camera. * Handy for doing split-screen especially. * * @param NewCamera Optional; specify a specific camera object to be the new main camera. */ static public function resetCameras(NewCamera:FlxCamera=null):void { var cam:FlxCamera; var i:uint = 0; var l:uint = cameras.length; while(i < l) { cam = FlxG.cameras[i++] as FlxCamera; FlxG._game.removeChild(cam._flashSprite); cam.destroy(); } FlxG.cameras.length = 0; if(NewCamera == null) NewCamera = new FlxCamera(0,0,FlxG.width,FlxG.height) FlxG.camera = FlxG.addCamera(NewCamera); } /** * All screens are filled with this color and gradually return to normal. * * @param Color The color you want to use. * @param Duration How long it takes for the flash to fade. * @param OnComplete A function you want to run when the flash finishes. * @param Force Force the effect to reset. */ static public function flash(Color:uint=0xffffffff, Duration:Number=1, OnComplete:Function=null, Force:Boolean=false):void { var i:uint = 0; var l:uint = FlxG.cameras.length; while(i < l) (FlxG.cameras[i++] as FlxCamera).flash(Color,Duration,OnComplete,Force); } /** * The screen is gradually filled with this color. * * @param Color The color you want to use. * @param Duration How long it takes for the fade to finish. * @param OnComplete A function you want to run when the fade finishes. * @param Force Force the effect to reset. */ static public function fade(Color:uint=0xff000000, Duration:Number=1, OnComplete:Function=null, Force:Boolean=false):void { var i:uint = 0; var l:uint = FlxG.cameras.length; while(i < l) (FlxG.cameras[i++] as FlxCamera).fade(Color,Duration,OnComplete,Force); } /** * A simple screen-shake effect. * * @param Intensity Percentage of screen size representing the maximum distance that the screen can move while shaking. * @param Duration The length in seconds that the shaking effect should last. * @param OnComplete A function you want to run when the shake effect finishes. * @param Force Force the effect to reset (default = true, unlike flash() and fade()!). * @param Direction Whether to shake on both axes, just up and down, or just side to side (use class constants SHAKE_BOTH_AXES, SHAKE_VERTICAL_ONLY, or SHAKE_HORIZONTAL_ONLY). Default value is SHAKE_BOTH_AXES (0). */ static public function shake(Intensity:Number=0.05, Duration:Number=0.5, OnComplete:Function=null, Force:Boolean=true, Direction:uint=0):void { var i:uint = 0; var l:uint = FlxG.cameras.length; while(i < l) (FlxG.cameras[i++] as FlxCamera).shake(Intensity,Duration,OnComplete,Force,Direction); } /** * Get and set the background color of the game. * Get functionality is equivalent to FlxG.camera.bgColor. * Set functionality sets the background color of all the current cameras. */ static public function get bgColor():uint { if(FlxG.camera == null) return 0xff000000; else return FlxG.camera.bgColor; } static public function set bgColor(Color:uint):void { var i:uint = 0; var l:uint = FlxG.cameras.length; while(i < l) (FlxG.cameras[i++] as FlxCamera).bgColor = Color; } /** * Call this function to see if one FlxObject overlaps another. * Can be called with one object and one group, or two groups, or two objects, * whatever floats your boat! For maximum performance try bundling a lot of objects * together using a FlxGroup (or even bundling groups together!). * *

NOTE: does NOT take objects' scrollfactor into account, all overlaps are checked in world space.

* * @param ObjectOrGroup1 The first object or group you want to check. * @param ObjectOrGroup2 The second object or group you want to check. If it is the same as the first, flixel knows to just do a comparison within that group. * @param NotifyCallback A function with two FlxObject parameters - e.g. myOverlapFunction(Object1:FlxObject,Object2:FlxObject) - that is called if those two objects overlap. * @param ProcessCallback A function with two FlxObject parameters - e.g. myOverlapFunction(Object1:FlxObject,Object2:FlxObject) - that is called if those two objects overlap. If a ProcessCallback is provided, then NotifyCallback will only be called if ProcessCallback returns true for those objects! * * @return Whether any oevrlaps were detected. */ static public function overlap(ObjectOrGroup1:FlxBasic=null,ObjectOrGroup2:FlxBasic=null,NotifyCallback:Function=null,ProcessCallback:Function=null):Boolean { if(ObjectOrGroup1 == null) ObjectOrGroup1 = FlxG.state; if(ObjectOrGroup2 === ObjectOrGroup1) ObjectOrGroup2 = null; FlxQuadTree.divisions = FlxG.worldDivisions; var quadTree:FlxQuadTree = new FlxQuadTree(FlxG.worldBounds.x,FlxG.worldBounds.y,FlxG.worldBounds.width,FlxG.worldBounds.height); quadTree.load(ObjectOrGroup1,ObjectOrGroup2,NotifyCallback,ProcessCallback); var result:Boolean = quadTree.execute(); quadTree.destroy(); return result; } /** * Call this function to see if one FlxObject collides with another. * Can be called with one object and one group, or two groups, or two objects, * whatever floats your boat! For maximum performance try bundling a lot of objects * together using a FlxGroup (or even bundling groups together!). * *

This function just calls FlxG.overlap and presets the ProcessCallback parameter to FlxObject.separate. * To create your own collision logic, write your own ProcessCallback and use FlxG.overlap to set it up.

* *

NOTE: does NOT take objects' scrollfactor into account, all overlaps are checked in world space.

* * @param ObjectOrGroup1 The first object or group you want to check. * @param ObjectOrGroup2 The second object or group you want to check. If it is the same as the first, flixel knows to just do a comparison within that group. * @param NotifyCallback A function with two FlxObject parameters - e.g. myOverlapFunction(Object1:FlxObject,Object2:FlxObject) - that is called if those two objects overlap. * * @return Whether any objects were successfully collided/separated. */ static public function collide(ObjectOrGroup1:FlxBasic=null, ObjectOrGroup2:FlxBasic=null, NotifyCallback:Function=null):Boolean { return overlap(ObjectOrGroup1,ObjectOrGroup2,NotifyCallback,FlxObject.separate); } /** * Adds a new plugin to the global plugin array. * * @param Plugin Any object that extends FlxBasic. Useful for managers and other things. See org.flixel.plugin for some examples! * * @return The same FlxBasic-based plugin you passed in. */ static public function addPlugin(Plugin:FlxBasic):FlxBasic { //Don't add repeats var pluginList:Array = FlxG.plugins; var i:uint = 0; var l:uint = pluginList.length; while(i < l) { if(pluginList[i++].toString() == Plugin.toString()) return Plugin; } //no repeats! safe to add a new instance of this plugin pluginList.push(Plugin); return Plugin; } /** * Retrieves a plugin based on its class name from the global plugin array. * * @param ClassType The class name of the plugin you want to retrieve. See the FlxPath or FlxTimer constructors for example usage. * * @return The plugin object, or null if no matching plugin was found. */ static public function getPlugin(ClassType:Class):FlxBasic { var pluginList:Array = FlxG.plugins; var i:uint = 0; var l:uint = pluginList.length; while(i < l) { if(pluginList[i] is ClassType) return plugins[i]; i++; } return null; } /** * Removes an instance of a plugin from the global plugin array. * * @param Plugin The plugin instance you want to remove. * * @return The same FlxBasic-based plugin you passed in. */ static public function removePlugin(Plugin:FlxBasic):FlxBasic { //Don't add repeats var pluginList:Array = FlxG.plugins; var i:int = pluginList.length-1; while(i >= 0) { if(pluginList[i] == Plugin) pluginList.splice(i,1); i--; } return Plugin; } /** * Removes an instance of a plugin from the global plugin array. * * @param ClassType The class name of the plugin type you want removed from the array. * * @return Whether or not at least one instance of this plugin type was removed. */ static public function removePluginType(ClassType:Class):Boolean { //Don't add repeats var results:Boolean = false; var pluginList:Array = FlxG.plugins; var i:int = pluginList.length-1; while(i >= 0) { if(pluginList[i] is ClassType) { pluginList.splice(i,1); results = true; } i--; } return results; } /** * Called by FlxGame to set up FlxG during FlxGame's constructor. */ static internal function init(Game:FlxGame,Width:uint,Height:uint,Zoom:Number):void { FlxG._game = Game; FlxG.width = Width; FlxG.height = Height; FlxG.mute = false; FlxG._volume = 0.5; FlxG.sounds = new FlxGroup(); FlxG.volumeHandler = null; FlxG.clearBitmapCache(); if(flashGfxSprite == null) { flashGfxSprite = new Sprite(); flashGfx = flashGfxSprite.graphics; } FlxCamera.defaultZoom = Zoom; FlxG._cameraRect = new Rectangle(); FlxG.cameras = new Array(); useBufferLocking = false; plugins = new Array(); addPlugin(new DebugPathDisplay()); addPlugin(new TimerManager()); FlxG.mouse = new Mouse(FlxG._game._mouse); FlxG.keys = new Keyboard(); FlxG.mobile = false; FlxG.levels = new Array(); FlxG.scores = new Array(); FlxG.visualDebug = false; } /** * Called whenever the game is reset, doesn't have to do quite as much work as the basic initialization stuff. */ static internal function reset():void { FlxG.clearBitmapCache(); FlxG.resetInput(); FlxG.destroySounds(true); FlxG.levels.length = 0; FlxG.scores.length = 0; FlxG.level = 0; FlxG.score = 0; FlxG.paused = false; FlxG.timeScale = 1.0; FlxG.elapsed = 0; FlxG.globalSeed = Math.random(); FlxG.worldBounds = new FlxRect(-10,-10,FlxG.width+20,FlxG.height+20); FlxG.worldDivisions = 6; var debugPathDisplay:DebugPathDisplay = FlxG.getPlugin(DebugPathDisplay) as DebugPathDisplay; if(debugPathDisplay != null) debugPathDisplay.clear(); } /** * Called by the game object to update the keyboard and mouse input tracking objects. */ static internal function updateInput():void { FlxG.keys.update(); if(!_game._debuggerUp || !_game._debugger.hasMouse) FlxG.mouse.update(FlxG._game.mouseX,FlxG._game.mouseY); } /** * Called by the game object to lock all the camera buffers and clear them for the next draw pass. */ static internal function lockCameras():void { var cam:FlxCamera; var cams:Array = FlxG.cameras; var i:uint = 0; var l:uint = cams.length; while(i < l) { cam = cams[i++] as FlxCamera; if((cam == null) || !cam.exists || !cam.visible) continue; if(useBufferLocking) cam.buffer.lock(); cam.fill(cam.bgColor); cam.screen.dirty = true; } } /** * Called by the game object to draw the special FX and unlock all the camera buffers. */ static internal function unlockCameras():void { var cam:FlxCamera; var cams:Array = FlxG.cameras; var i:uint = 0; var l:uint = cams.length; while(i < l) { cam = cams[i++] as FlxCamera; if((cam == null) || !cam.exists || !cam.visible) continue; cam.drawFX(); if(useBufferLocking) cam.buffer.unlock(); } } /** * Called by the game object to update the cameras and their tracking/special effects logic. */ static internal function updateCameras():void { var cam:FlxCamera; var cams:Array = FlxG.cameras; var i:uint = 0; var l:uint = cams.length; while(i < l) { cam = cams[i++] as FlxCamera; if((cam != null) && cam.exists) { if(cam.active) cam.update(); cam._flashSprite.x = cam.x + cam._flashOffsetX; cam._flashSprite.y = cam.y + cam._flashOffsetY; cam._flashSprite.visible = cam.visible; } } } /** * Used by the game object to call update() on all the plugins. */ static internal function updatePlugins():void { var plugin:FlxBasic; var pluginList:Array = FlxG.plugins; var i:uint = 0; var l:uint = pluginList.length; while(i < l) { plugin = pluginList[i++] as FlxBasic; if(plugin.exists && plugin.active) plugin.update(); } } /** * Used by the game object to call draw() on all the plugins. */ static internal function drawPlugins():void { var plugin:FlxBasic; var pluginList:Array = FlxG.plugins; var i:uint = 0; var l:uint = pluginList.length; while(i < l) { plugin = pluginList[i++] as FlxBasic; if(plugin.exists && plugin.visible) plugin.draw(); } } } } ================================================ FILE: org/flixel/FlxGame.as ================================================ package org.flixel { import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.Graphics; import flash.display.Sprite; import flash.display.StageAlign; import flash.display.StageScaleMode; import flash.events.*; import flash.geom.Point; import flash.text.AntiAliasType; import flash.text.GridFitType; import flash.text.TextField; import flash.text.TextFormat; import flash.ui.Mouse; import flash.utils.Timer; import flash.utils.getTimer; import org.flixel.plugin.TimerManager; import org.flixel.system.FlxDebugger; import org.flixel.system.FlxReplay; /** * FlxGame is the heart of all flixel games, and contains a bunch of basic game loops and things. * It is a long and sloppy file that you shouldn't have to worry about too much! * It is basically only used to create your game object in the first place, * after that FlxG and FlxState have all the useful stuff you actually need. * * @author Adam Atomic */ public class FlxGame extends Sprite { [Embed(source="data/nokiafc22.ttf",fontFamily="system",embedAsCFF="false")] protected var junk:String; [Embed(source="data/beep.mp3")] protected var SndBeep:Class; [Embed(source="data/logo.png")] protected var ImgLogo:Class; /** * Sets 0, -, and + to control the global volume sound volume. * @default true */ public var useSoundHotKeys:Boolean; /** * Tells flixel to use the default system mouse cursor instead of custom Flixel mouse cursors. * @default false */ public var useSystemCursor:Boolean; /** * Initialize and allow the flixel debugger overlay even in release mode. * Also useful if you don't use FlxPreloader! * @default false */ public var forceDebugger:Boolean; /** * Current game state. */ internal var _state:FlxState; /** * Mouse cursor. */ internal var _mouse:Sprite; /** * Class type of the initial/first game state for the game, usually MenuState or something like that. */ protected var _iState:Class; /** * Whether the game object's basic initialization has finished yet. */ protected var _created:Boolean; /** * Total number of milliseconds elapsed since game start. */ protected var _total:uint; /** * Total number of milliseconds elapsed since last update loop. * Counts down as we step through the game loop. */ protected var _accumulator:int; /** * Whether the Flash player lost focus. */ protected var _lostFocus:Boolean; /** * Milliseconds of time per step of the game loop. FlashEvent.g. 60 fps = 16ms. */ internal var _step:uint; /** * Framerate of the Flash player (NOT the game loop). Default = 30. */ internal var _flashFramerate:uint; /** * Max allowable accumulation (see _accumulator). * Should always (and automatically) be set to roughly 2x the flash player framerate. */ internal var _maxAccumulation:uint; /** * If a state change was requested, the new state object is stored here until we switch to it. */ internal var _requestedState:FlxState; /** * A flag for keeping track of whether a game reset was requested or not. */ internal var _requestedReset:Boolean; /** * The "focus lost" screen (see createFocusScreen()). */ protected var _focus:Sprite; /** * The sound tray display container (see createSoundTray()). */ protected var _soundTray:Sprite; /** * Helps us auto-hide the sound tray after a volume change. */ protected var _soundTrayTimer:Number; /** * Helps display the volume bars on the sound tray. */ protected var _soundTrayBars:Array; /** * The debugger overlay object. */ internal var _debugger:FlxDebugger; /** * A handy boolean that keeps track of whether the debugger exists and is currently visible. */ internal var _debuggerUp:Boolean; /** * Container for a game replay object. */ internal var _replay:FlxReplay; /** * Flag for whether a playback of a recording was requested. */ internal var _replayRequested:Boolean; /** * Flag for whether a new recording was requested. */ internal var _recordingRequested:Boolean; /** * Flag for whether a replay is currently playing. */ internal var _replaying:Boolean; /** * Flag for whether a new recording is being made. */ internal var _recording:Boolean; /** * Array that keeps track of keypresses that can cancel a replay. * Handy for skipping cutscenes or getting out of attract modes! */ internal var _replayCancelKeys:Array; /** * Helps time out a replay if necessary. */ internal var _replayTimer:int; /** * This function, if set, is triggered when the callback stops playing. */ internal var _replayCallback:Function; /** * Instantiate a new game object. * * @param GameSizeX The width of your game in game pixels, not necessarily final display pixels (see Zoom). * @param GameSizeY The height of your game in game pixels, not necessarily final display pixels (see Zoom). * @param InitialState The class name of the state you want to create and switch to first (e.g. MenuState). * @param Zoom The default level of zoom for the game's cameras (e.g. 2 = all pixels are now drawn at 2x). Default = 1. * @param GameFramerate How frequently the game should update (default is 60 times per second). * @param FlashFramerate Sets the actual display framerate for Flash player (default is 30 times per second). * @param UseSystemCursor Whether to use the default OS mouse pointer, or to use custom flixel ones. */ public function FlxGame(GameSizeX:uint,GameSizeY:uint,InitialState:Class,Zoom:Number=1,GameFramerate:uint=60,FlashFramerate:uint=30,UseSystemCursor:Boolean=false) { //super high priority init stuff (focus, mouse, etc) _lostFocus = false; _focus = new Sprite(); _focus.visible = false; _soundTray = new Sprite(); _mouse = new Sprite() //basic display and update setup stuff FlxG.init(this,GameSizeX,GameSizeY,Zoom); FlxG.framerate = GameFramerate; FlxG.flashFramerate = FlashFramerate; _accumulator = _step; _total = 0; _state = null; useSoundHotKeys = true; useSystemCursor = UseSystemCursor; if(!useSystemCursor) flash.ui.Mouse.hide(); forceDebugger = false; _debuggerUp = false; //replay data _replay = new FlxReplay(); _replayRequested = false; _recordingRequested = false; _replaying = false; _recording = false; //then get ready to create the game object for real _iState = InitialState; _requestedState = null; _requestedReset = true; _created = false; addEventListener(Event.ENTER_FRAME, create); } /** * Makes the little volume tray slide out. * * @param Silent Whether or not it should beep. */ internal function showSoundTray(Silent:Boolean=false):void { if(!Silent) FlxG.play(SndBeep); _soundTrayTimer = 1; _soundTray.y = 0; _soundTray.visible = true; var globalVolume:uint = Math.round(FlxG.volume*10); if(FlxG.mute) globalVolume = 0; for (var i:uint = 0; i < _soundTrayBars.length; i++) { if(i < globalVolume) _soundTrayBars[i].alpha = 1; else _soundTrayBars[i].alpha = 0.5; } } /** * Internal event handler for input and focus. * * @param FlashEvent Flash keyboard event. */ protected function handleKeyUp(FlashEvent:KeyboardEvent):void { if(_debuggerUp && _debugger.watch.editing) return; if(!FlxG.mobile) { if((_debugger != null) && ((FlashEvent.keyCode == 192) || (FlashEvent.keyCode == 220))) { _debugger.visible = !_debugger.visible; _debuggerUp = _debugger.visible; if(_debugger.visible) flash.ui.Mouse.show(); else if(!useSystemCursor) flash.ui.Mouse.hide(); //_console.toggle(); return; } if(useSoundHotKeys) { var c:int = FlashEvent.keyCode; var code:String = String.fromCharCode(FlashEvent.charCode); switch(c) { case 48: case 96: FlxG.mute = !FlxG.mute; if(FlxG.volumeHandler != null) FlxG.volumeHandler(FlxG.mute?0:FlxG.volume); showSoundTray(); return; case 109: case 189: FlxG.mute = false; FlxG.volume = FlxG.volume - 0.1; showSoundTray(); return; case 107: case 187: FlxG.mute = false; FlxG.volume = FlxG.volume + 0.1; showSoundTray(); return; default: break; } } } if(_replaying) return; FlxG.keys.handleKeyUp(FlashEvent); } /** * Internal event handler for input and focus. * * @param FlashEvent Flash keyboard event. */ protected function handleKeyDown(FlashEvent:KeyboardEvent):void { if(_debuggerUp && _debugger.watch.editing) return; if(_replaying && (_replayCancelKeys != null) && (_debugger == null) && (FlashEvent.keyCode != 192) && (FlashEvent.keyCode != 220)) { var cancel:Boolean = false; var replayCancelKey:String; var i:uint = 0; var l:uint = _replayCancelKeys.length; while(i < l) { replayCancelKey = _replayCancelKeys[i++]; if((replayCancelKey == "ANY") || (FlxG.keys.getKeyCode(replayCancelKey) == FlashEvent.keyCode)) { if(_replayCallback != null) { _replayCallback(); _replayCallback = null; } else FlxG.stopReplay(); break; } } return; } FlxG.keys.handleKeyDown(FlashEvent); } /** * Internal event handler for input and focus. * * @param FlashEvent Flash mouse event. */ protected function handleMouseDown(FlashEvent:MouseEvent):void { if(_debuggerUp) { if(_debugger.hasMouse) return; if(_debugger.watch.editing) _debugger.watch.submit(); } if(_replaying && (_replayCancelKeys != null)) { var replayCancelKey:String; var i:uint = 0; var l:uint = _replayCancelKeys.length; while(i < l) { replayCancelKey = _replayCancelKeys[i++] as String; if((replayCancelKey == "MOUSE") || (replayCancelKey == "ANY")) { if(_replayCallback != null) { _replayCallback(); _replayCallback = null; } else FlxG.stopReplay(); break; } } return; } FlxG.mouse.handleMouseDown(FlashEvent); } /** * Internal event handler for input and focus. * * @param FlashEvent Flash mouse event. */ protected function handleMouseUp(FlashEvent:MouseEvent):void { if((_debuggerUp && _debugger.hasMouse) || _replaying) return; FlxG.mouse.handleMouseUp(FlashEvent); } /** * Internal event handler for input and focus. * * @param FlashEvent Flash mouse event. */ protected function handleMouseWheel(FlashEvent:MouseEvent):void { if((_debuggerUp && _debugger.hasMouse) || _replaying) return; FlxG.mouse.handleMouseWheel(FlashEvent); } /** * Internal event handler for input and focus. * * @param FlashEvent Flash event. */ protected function onFocus(FlashEvent:Event=null):void { if(!_debuggerUp && !useSystemCursor) flash.ui.Mouse.hide(); FlxG.resetInput(); _lostFocus = _focus.visible = false; stage.frameRate = _flashFramerate; FlxG.resumeSounds(); } /** * Internal event handler for input and focus. * * @param FlashEvent Flash event. */ protected function onFocusLost(FlashEvent:Event=null):void { if((x != 0) || (y != 0)) { x = 0; y = 0; } flash.ui.Mouse.show(); _lostFocus = _focus.visible = true; stage.frameRate = 10; FlxG.pauseSounds(); } /** * Handles the onEnterFrame call and figures out how many updates and draw calls to do. * * @param FlashEvent Flash event. */ protected function onEnterFrame(FlashEvent:Event=null):void { var mark:uint = getTimer(); var elapsedMS:uint = mark-_total; _total = mark; updateSoundTray(elapsedMS); if(!_lostFocus) { if((_debugger != null) && _debugger.vcr.paused) { if(_debugger.vcr.stepRequested) { _debugger.vcr.stepRequested = false; step(); } } else { _accumulator += elapsedMS; if(_accumulator > _maxAccumulation) _accumulator = _maxAccumulation; while(_accumulator >= _step) { step(); _accumulator = _accumulator - _step; } } FlxBasic._VISIBLECOUNT = 0; draw(); if(_debuggerUp) { _debugger.perf.flash(elapsedMS); _debugger.perf.visibleObjects(FlxBasic._VISIBLECOUNT); _debugger.perf.update(); _debugger.watch.update(); } } } /** * If there is a state change requested during the update loop, * this function handles actual destroying the old state and related processes, * and calls creates on the new state and plugs it into the game object. */ protected function switchState():void { //Basic reset stuff FlxG.resetCameras(); FlxG.resetInput(); FlxG.destroySounds(); FlxG.clearBitmapCache(); //Clear the debugger overlay's Watch window if(_debugger != null) _debugger.watch.removeAll(); //Clear any timers left in the timer manager var timerManager:TimerManager = FlxTimer.manager; if(timerManager != null) timerManager.clear(); //Destroy the old state (if there is an old state) if(_state != null) _state.destroy(); //Finally assign and create the new state _state = _requestedState; _state.create(); } /** * This is the main game update logic section. * The onEnterFrame() handler is in charge of calling this * the appropriate number of times each frame. * This block handles state changes, replays, all that good stuff. */ protected function step():void { //handle game reset request if(_requestedReset) { _requestedReset = false; _requestedState = new _iState(); _replayTimer = 0; _replayCancelKeys = null; FlxG.reset(); } //handle replay-related requests if(_recordingRequested) { _recordingRequested = false; _replay.create(FlxG.globalSeed); _recording = true; if(_debugger != null) { _debugger.vcr.recording(); FlxG.log("FLIXEL: starting new flixel gameplay record."); } } else if(_replayRequested) { _replayRequested = false; _replay.rewind(); FlxG.globalSeed = _replay.seed; if(_debugger != null) _debugger.vcr.playing(); _replaying = true; } //handle state switching requests if(_state != _requestedState) switchState(); //finally actually step through the game physics FlxBasic._ACTIVECOUNT = 0; if(_replaying) { _replay.playNextFrame(); if(_replayTimer > 0) { _replayTimer -= _step; if(_replayTimer <= 0) { if(_replayCallback != null) { _replayCallback(); _replayCallback = null; } else FlxG.stopReplay(); } } if(_replaying && _replay.finished) { FlxG.stopReplay(); if(_replayCallback != null) { _replayCallback(); _replayCallback = null; } } if(_debugger != null) _debugger.vcr.updateRuntime(_step); } else FlxG.updateInput(); if(_recording) { _replay.recordFrame(); if(_debugger != null) _debugger.vcr.updateRuntime(_step); } update(); FlxG.mouse.wheel = 0; if(_debuggerUp) _debugger.perf.activeObjects(FlxBasic._ACTIVECOUNT); } /** * This function just updates the soundtray object. */ protected function updateSoundTray(MS:Number):void { //animate stupid sound tray thing if(_soundTray != null) { if(_soundTrayTimer > 0) _soundTrayTimer -= MS/1000; else if(_soundTray.y > -_soundTray.height) { _soundTray.y -= (MS/1000)*FlxG.height*2; if(_soundTray.y <= -_soundTray.height) { _soundTray.visible = false; //Save sound preferences var soundPrefs:FlxSave = new FlxSave(); if(soundPrefs.bind("flixel")) { if(soundPrefs.data.sound == null) soundPrefs.data.sound = new Object; soundPrefs.data.sound.mute = FlxG.mute; soundPrefs.data.sound.volume = FlxG.volume; soundPrefs.close(); } } } } } /** * This function is called by step() and updates the actual game state. * May be called multiple times per "frame" or draw call. */ protected function update():void { var mark:uint = getTimer(); FlxG.elapsed = FlxG.timeScale*(_step/1000); FlxG.updateSounds(); FlxG.updatePlugins(); _state.update(); FlxG.updateCameras(); if(_debuggerUp) _debugger.perf.flixelUpdate(getTimer()-mark); } /** * Goes through the game state and draws all the game objects and special effects. */ protected function draw():void { var w:Number = FlxG.width * FlxG.camera.zoom; var h:Number = FlxG.height * FlxG.camera.zoom; /* keep aspect ratio (letter/pillarBox) */ if (FlxG.stage.stageWidth / FlxG.stage.stageHeight < (w / h)) { // letter-box scaleX = (FlxG.stage.stageWidth / w); scaleY = ((h / w * FlxG.stage.stageWidth) / h); }else { // pillar-box scaleX = ((w / h * FlxG.stage.stageHeight) / w); scaleY = (FlxG.stage.stageHeight / h); } x = Math.round((FlxG.stage.stageWidth / 2) - (w * scaleX / 2)); y = Math.round((FlxG.stage.stageHeight / 2) - (h * scaleY / 2)); var mark:uint = getTimer(); FlxG.lockCameras(); _state.draw(); FlxG.drawPlugins(); FlxG.unlockCameras(); if(_debuggerUp) _debugger.perf.flixelDraw(getTimer()-mark); } /** * Used to instantiate the guts of the flixel game object once we have a valid reference to the root. * * @param FlashEvent Just a Flash system event, not too important for our purposes. */ protected function create(FlashEvent:Event):void { if(root == null) return; removeEventListener(Event.ENTER_FRAME, create); _total = getTimer(); //Set up the view window and double buffering stage.scaleMode = StageScaleMode.NO_SCALE; stage.align = StageAlign.TOP_LEFT; stage.frameRate = _flashFramerate; //Add basic input event listeners and mouse container stage.addEventListener(MouseEvent.MOUSE_DOWN, handleMouseDown); stage.addEventListener(MouseEvent.MOUSE_UP, handleMouseUp); stage.addEventListener(MouseEvent.MOUSE_WHEEL, handleMouseWheel); stage.addEventListener(KeyboardEvent.KEY_DOWN, handleKeyDown); stage.addEventListener(KeyboardEvent.KEY_UP, handleKeyUp); addChild(_mouse); //Let mobile devs opt out of unnecessary overlays. if(!FlxG.mobile) { //Debugger overlay if(FlxG.debug || forceDebugger) { _debugger = new FlxDebugger(FlxG.width*FlxCamera.defaultZoom,FlxG.height*FlxCamera.defaultZoom); addChild(_debugger); } //Volume display tab createSoundTray(); //Focus gained/lost monitoring stage.addEventListener(Event.DEACTIVATE, onFocusLost); stage.addEventListener(Event.ACTIVATE, onFocus); createFocusScreen(); } //Finally, set up an event for the actual game loop stuff. addEventListener(Event.ENTER_FRAME, onEnterFrame); } /** * Sets up the "sound tray", the little volume meter that pops down sometimes. */ protected function createSoundTray():void { _soundTray.visible = false; _soundTray.scaleX = 2; _soundTray.scaleY = 2; var tmp:Bitmap = new Bitmap(new BitmapData(80,30,true,0x7F000000)); _soundTray.x = (FlxG.width/2)*FlxCamera.defaultZoom-(tmp.width/2)*_soundTray.scaleX; _soundTray.addChild(tmp); var text:TextField = new TextField(); text.width = tmp.width; text.height = tmp.height; text.multiline = true; text.wordWrap = true; text.selectable = false; text.embedFonts = true; text.antiAliasType = AntiAliasType.NORMAL; text.gridFitType = GridFitType.PIXEL; text.defaultTextFormat = new TextFormat("system",8,0xffffff,null,null,null,null,null,"center");; _soundTray.addChild(text); text.text = "VOLUME"; text.y = 16; var bx:uint = 10; var by:uint = 14; _soundTrayBars = new Array(); var i:uint = 0; while(i < 10) { tmp = new Bitmap(new BitmapData(4,++i,false,0xffffff)); tmp.x = bx; tmp.y = by; _soundTrayBars.push(_soundTray.addChild(tmp)); bx += 6; by--; } _soundTray.y = -_soundTray.height; _soundTray.visible = false; addChild(_soundTray); //load saved sound preferences for this game if they exist var soundPrefs:FlxSave = new FlxSave(); if(soundPrefs.bind("flixel") && (soundPrefs.data.sound != null)) { if(soundPrefs.data.sound.volume != null) FlxG.volume = soundPrefs.data.sound.volume; if(soundPrefs.data.sound.mute != null) FlxG.mute = soundPrefs.data.sound.mute; soundPrefs.destroy(); } } /** * Sets up the darkened overlay with the big white "play" button that appears when a flixel game loses focus. */ protected function createFocusScreen():void { var gfx:Graphics = _focus.graphics; var screenWidth:uint = FlxG.width*FlxCamera.defaultZoom; var screenHeight:uint = FlxG.height*FlxCamera.defaultZoom; //draw transparent black backdrop gfx.moveTo(0,0); gfx.beginFill(0,0.5); gfx.lineTo(screenWidth,0); gfx.lineTo(screenWidth,screenHeight); gfx.lineTo(0,screenHeight); gfx.lineTo(0,0); gfx.endFill(); //draw white arrow var halfWidth:uint = screenWidth/2; var halfHeight:uint = screenHeight/2; var helper:uint = FlxU.min(halfWidth,halfHeight)/3; gfx.moveTo(halfWidth-helper,halfHeight-helper); gfx.beginFill(0xffffff,0.65); gfx.lineTo(halfWidth+helper,halfHeight); gfx.lineTo(halfWidth-helper,halfHeight+helper); gfx.lineTo(halfWidth-helper,halfHeight-helper); gfx.endFill(); var logo:Bitmap = new ImgLogo(); logo.scaleX = int(helper/10); if(logo.scaleX < 1) logo.scaleX = 1; logo.scaleY = logo.scaleX; logo.x -= logo.scaleX; logo.alpha = 0.35; _focus.addChild(logo); addChild(_focus); } } } ================================================ FILE: org/flixel/FlxGroup.as ================================================ package org.flixel { /** * This is an organizational class that can update and render a bunch of FlxBasics. * NOTE: Although FlxGroup extends FlxBasic, it will not automatically * add itself to the global collisions quad tree, it will only add its members. * * @author Adam Atomic */ public class FlxGroup extends FlxBasic { /** * Use with sort() to sort in ascending order. */ static public const ASCENDING:int = -1; /** * Use with sort() to sort in descending order. */ static public const DESCENDING:int = 1; /** * Array of all the FlxBasics that exist in this group. */ public var members:Array; /** * The number of entries in the members array. * For performance and safety you should check this variable * instead of members.length unless you really know what you're doing! */ public var length:Number; /** * Internal tracker for the maximum capacity of the group. * Default is 0, or no max capacity. */ protected var _maxSize:uint; /** * Internal helper variable for recycling objects a la FlxEmitter. */ protected var _marker:uint; /** * Helper for sort. */ protected var _sortIndex:String; /** * Helper for sort. */ protected var _sortOrder:int; /** * Constructor */ public function FlxGroup(MaxSize:uint=0) { super(); members = new Array(); length = 0; _maxSize = MaxSize; _marker = 0; _sortIndex = null; } /** * Override this function to handle any deleting or "shutdown" type operations you might need, * such as removing traditional Flash children like Sprite objects. */ override public function destroy():void { if(members != null) { var basic:FlxBasic; var i:uint = 0; while(i < length) { basic = members[i++] as FlxBasic; if(basic != null) basic.destroy(); } members.length = 0; members = null; } _sortIndex = null; } /** * Just making sure we don't increment the active objects count. */ override public function preUpdate():void { } /** * Automatically goes through and calls update on everything you added. */ override public function update():void { var basic:FlxBasic; var i:uint = 0; while(i < length) { basic = members[i++] as FlxBasic; if((basic != null) && basic.exists && basic.active) { basic.preUpdate(); basic.update(); basic.postUpdate(); } } } /** * Automatically goes through and calls render on everything you added. */ override public function draw():void { var basic:FlxBasic; var i:uint = 0; while(i < length) { basic = members[i++] as FlxBasic; if((basic != null) && basic.exists && basic.visible) basic.draw(); } } /** * The maximum capacity of this group. Default is 0, meaning no max capacity, and the group can just grow. */ public function get maxSize():uint { return _maxSize; } /** * @private */ public function set maxSize(Size:uint):void { _maxSize = Size; if(_marker >= _maxSize) _marker = 0; if((_maxSize == 0) || (members == null) || (_maxSize >= members.length)) return; //If the max size has shrunk, we need to get rid of some objects var basic:FlxBasic; var i:uint = _maxSize; var l:uint = members.length; while(i < l) { basic = members[i++] as FlxBasic; if(basic != null) basic.destroy(); } length = members.length = _maxSize; } /** * Adds a new FlxBasic subclass (FlxBasic, FlxSprite, Enemy, etc) to the group. * FlxGroup will try to replace a null member of the array first. * Failing that, FlxGroup will add it to the end of the member array, * assuming there is room for it, and doubling the size of the array if necessary. * *

WARNING: If the group has a maxSize that has already been met, * the object will NOT be added to the group!

* * @param Object The object you want to add to the group. * * @return The same FlxBasic object that was passed in. */ public function add(Object:FlxBasic):FlxBasic { //Don't bother adding an object twice. if(members.indexOf(Object) >= 0) return Object; //First, look for a null entry where we can add the object. var i:int = 0; var l:uint = members.length; for (i = 0; i < l; i++){ // while(i < l){ if(members[i] == null) { members[i] = Object; if(i >= length) length = i+1; return Object; } // i++; } //Failing that, expand the array (if we can) and add the object. if(_maxSize > 0) { if(members.length >= _maxSize) return Object; else if(members.length * 2 <= _maxSize) members.length *= 2; else members.length = _maxSize; } else members.length *= 2; //If we made it this far, then we successfully grew the group, //and we can go ahead and add the object at the first open slot. members[i] = Object; length = i+1; return Object; } /** * Recycling is designed to help you reuse game objects without always re-allocating or "newing" them. * *

If you specified a maximum size for this group (like in FlxEmitter), * then recycle will employ what we're calling "rotating" recycling. * Recycle() will first check to see if the group is at capacity yet. * If group is not yet at capacity, recycle() returns a new object. * If the group IS at capacity, then recycle() just returns the next object in line.

* *

If you did NOT specify a maximum size for this group, * then recycle() will employ what we're calling "grow-style" recycling. * Recycle() will return either the first object with exists == false, * or, finding none, add a new object to the array, * doubling the size of the array if necessary.

* *

WARNING: If this function needs to create a new object, * and no object class was provided, it will return null * instead of a valid object!

* * @param ObjectClass The class type you want to recycle (e.g. FlxSprite, EvilRobot, etc). Do NOT "new" the class in the parameter! * * @return A reference to the object that was created. Don't forget to cast it back to the Class you want (e.g. myObject = myGroup.recycle(myObjectClass) as myObjectClass;). */ public function recycle(ObjectClass:Class=null):FlxBasic { var basic:FlxBasic; if(_maxSize > 0) { if(length < _maxSize) { if(ObjectClass == null) return null; return add(new ObjectClass() as FlxBasic); } else { basic = members[_marker++]; if(_marker >= _maxSize) _marker = 0; return basic; } } else { basic = getFirstAvailable(ObjectClass); if(basic != null) return basic; if(ObjectClass == null) return null; return add(new ObjectClass() as FlxBasic); } } /** * Removes an object from the group. * * @param Object The FlxBasic you want to remove. * @param Splice Whether the object should be cut from the array entirely or not. * * @return The removed object. */ public function remove(Object:FlxBasic,Splice:Boolean=false):FlxBasic { var index:int = members.indexOf(Object); if((index < 0) || (index >= members.length)) return null; if(Splice) { members.splice(index,1); length--; } else members[index] = null; return Object; } /** * Replaces an existing FlxBasic with a new one. * * @param OldObject The object you want to replace. * @param NewObject The new object you want to use instead. * * @return The new object. */ public function replace(OldObject:FlxBasic,NewObject:FlxBasic):FlxBasic { var index:int = members.indexOf(OldObject); if((index < 0) || (index >= members.length)) return null; members[index] = NewObject; return NewObject; } /** * Call this function to sort the group according to a particular value and order. * For example, to sort game objects for Zelda-style overlaps you might call * myGroup.sort("y",ASCENDING) at the bottom of your * FlxState.update() override. To sort all existing objects after * a big explosion or bomb attack, you might call myGroup.sort("exists",DESCENDING). * * @param Index The String name of the member variable you want to sort on. Default value is "y". * @param Order A FlxGroup constant that defines the sort order. Possible values are ASCENDING and DESCENDING. Default value is ASCENDING. */ public function sort(Index:String="y",Order:int=ASCENDING):void { _sortIndex = Index; _sortOrder = Order; members.sort(sortHandler); } /** * Go through and set the specified variable to the specified value on all members of the group. * * @param VariableName The string representation of the variable name you want to modify, for example "visible" or "scrollFactor". * @param Value The value you want to assign to that variable. * @param Recurse Default value is true, meaning if setAll() encounters a member that is a group, it will call setAll() on that group rather than modifying its variable. */ public function setAll(VariableName:String,Value:Object,Recurse:Boolean=true):void { var basic:FlxBasic; var i:uint = 0; while(i < length) { basic = members[i++] as FlxBasic; if(basic != null) { if(Recurse && (basic is FlxGroup)) (basic as FlxGroup).setAll(VariableName,Value,Recurse); else basic[VariableName] = Value; } } } /** * Go through and call the specified function on all members of the group. * Currently only works on functions that have no required parameters. * * @param FunctionName The string representation of the function you want to call on each object, for example "kill()" or "init()". * @param Recurse Default value is true, meaning if callAll() encounters a member that is a group, it will call callAll() on that group rather than calling the group's function. */ public function callAll(FunctionName:String,Recurse:Boolean=true):void { var basic:FlxBasic; var i:uint = 0; while(i < length) { basic = members[i++] as FlxBasic; if(basic != null) { if(Recurse && (basic is FlxGroup)) (basic as FlxGroup).callAll(FunctionName,Recurse); else basic[FunctionName](); } } } /** * Call this function to retrieve the first object with exists == false in the group. * This is handy for recycling in general, e.g. respawning enemies. * * @param ObjectClass An optional parameter that lets you narrow the results to instances of this particular class. * * @return A FlxBasic currently flagged as not existing. */ public function getFirstAvailable(ObjectClass:Class=null):FlxBasic { var basic:FlxBasic; var i:uint = 0; while(i < length) { basic = members[i++] as FlxBasic; if((basic != null) && !basic.exists && ((ObjectClass == null) || (basic is ObjectClass))) return basic; } return null; } /** * Call this function to retrieve the first index set to 'null'. * Returns -1 if no index stores a null object. * * @return An int indicating the first null slot in the group. */ public function getFirstNull():int { var basic:FlxBasic; var i:uint = 0; var l:uint = members.length; while(i < l) { if(members[i] == null) return i; else i++; } return -1; } /** * Call this function to retrieve the first object with exists == true in the group. * This is handy for checking if everything's wiped out, or choosing a squad leader, etc. * * @return A FlxBasic currently flagged as existing. */ public function getFirstExtant():FlxBasic { var basic:FlxBasic; var i:uint = 0; while(i < length) { basic = members[i++] as FlxBasic; if((basic != null) && basic.exists) return basic; } return null; } /** * Call this function to retrieve the first object with dead == false in the group. * This is handy for checking if everything's wiped out, or choosing a squad leader, etc. * * @return A FlxBasic currently flagged as not dead. */ public function getFirstAlive():FlxBasic { var basic:FlxBasic; var i:uint = 0; while(i < length) { basic = members[i++] as FlxBasic; if((basic != null) && basic.exists && basic.alive) return basic; } return null; } /** * Call this function to retrieve the first object with dead == true in the group. * This is handy for checking if everything's wiped out, or choosing a squad leader, etc. * * @return A FlxBasic currently flagged as dead. */ public function getFirstDead():FlxBasic { var basic:FlxBasic; var i:uint = 0; while(i < length) { basic = members[i++] as FlxBasic; if((basic != null) && !basic.alive) return basic; } return null; } /** * Call this function to find out how many members of the group are not dead. * * @return The number of FlxBasics flagged as not dead. Returns -1 if group is empty. */ public function countLiving():int { var count:int = -1; var basic:FlxBasic; var i:uint = 0; while(i < length) { basic = members[i++] as FlxBasic; if(basic != null) { if(count < 0) count = 0; if(basic.exists && basic.alive) count++; } } return count; } /** * Call this function to find out how many members of the group are dead. * * @return The number of FlxBasics flagged as dead. Returns -1 if group is empty. */ public function countDead():int { var count:int = -1; var basic:FlxBasic; var i:uint = 0; while(i < length) { basic = members[i++] as FlxBasic; if(basic != null) { if(count < 0) count = 0; if(!basic.alive) count++; } } return count; } /** * Returns a member at random from the group. * * @param StartIndex Optional offset off the front of the array. Default value is 0, or the beginning of the array. * @param Length Optional restriction on the number of values you want to randomly select from. * * @return A FlxBasic from the members list. */ public function getRandom(StartIndex:uint=0,Length:uint=0):FlxBasic { if(Length == 0) Length = length; return FlxG.getRandom(members,StartIndex,Length) as FlxBasic; } /** * Remove all instances of FlxBasic subclass (FlxSprite, FlxBlock, etc) from the list. * WARNING: does not destroy() or kill() any of these objects! */ public function clear():void { length = members.length = 0; } /** * Calls kill on the group's members and then on the group itself. */ override public function kill():void { var basic:FlxBasic; var i:uint = 0; while(i < length) { basic = members[i++] as FlxBasic; if((basic != null) && basic.exists) basic.kill(); } super.kill(); } /** * Helper function for the sort process. * * @param Obj1 The first object being sorted. * @param Obj2 The second object being sorted. * * @return An integer value: -1 (Obj1 before Obj2), 0 (same), or 1 (Obj1 after Obj2). */ protected function sortHandler(Obj1:FlxBasic,Obj2:FlxBasic):int { if(Obj1[_sortIndex] < Obj2[_sortIndex]) return _sortOrder; else if(Obj1[_sortIndex] > Obj2[_sortIndex]) return -_sortOrder; return 0; } } } ================================================ FILE: org/flixel/FlxInputText.as ================================================ package org.flixel { import flash.text.TextField; import org.flixel.*; import flash.events.Event; import flash.text.TextFieldType; /** FlxInputText v0.92, Input text field extension for Flixel (author Nitram_cero, Martin Sebastian Wain) (slight modification by Wurmy, seems to work on version 2.34 of Flixel) (little insignificant update by Tyranus, now works with scroll) (minor 2.5. update by Kevin Prein) (minor update by Mr_Walrus, IDE-friendly and plays nicer with Flixel) New members: getText() setMaxLength() backgroundColor borderColor backgroundVisible borderVisible forceUpperCase filterMode customFilterPattern Copyright (c) 2009 Martin Sebastian Wain License: Creative Commons Attribution 3.0 United States (http://creativecommons.org/licenses/by/3.0/us/) (A tiny "single line comment" reference in the source code is more than sufficient as attribution :) */ public class FlxInputText extends FlxText { static public const NO_FILTER:uint = 0; static public const ONLY_ALPHA:uint = 1; static public const ONLY_NUMERIC:uint = 2; static public const ONLY_ALPHANUMERIC:uint = 3; static public const CUSTOM_FILTER:uint = 4; //@desc Defines what text to filter. It can be NO_FILTER, ONLY_ALPHA, ONLY_NUMERIC, ONLY_ALPHA_NUMERIC or CUSTOM_FILTER // (Remember to append "FlxInputText." as a prefix to those constants) public var filterMode:uint = NO_FILTER; //@desc This regular expression will filter out (remove) everything that matches. This is activated by setting filterMode = FlxInputText.CUSTOM_FILTER. public var customFilterPattern:RegExp = /[]*/g; //@desc If this is set to true, text typed is forced to be uppercase public var forceUpperCase:Boolean = false; /** * Input field class that "fits in" with Flixel's workflow * * @param X The X position of the text. * @param Y The Y position of the text. * @param Width The width of the text box. * @param Text The text to be displayed. * @param Color The text color. * @param Font The text font. * @param Size The width of the text box. * @param Justification How to center the text with with regards to the box. */ public function FlxInputText(X:Number, Y:Number, Width:uint, Height:uint, Text:String, Color:uint=0x000000, Font:String=null, Size:uint=8, Justification:String=null) { //super(X, Y, Width, Height, Text, Color, Font, Size, Justification, Angle); super(X, Y, Width, Text); setFormat(Font, Size) _textField.selectable = true; _textField.type = TextFieldType.INPUT; _textField.background = false; _textField.backgroundColor = (~Color) & 0xffffff; _textField.textColor = Color; _textField.border = true; _textField.borderColor = Color; _textField.height = Height; this.height = Height; _textField.x = X; _textField.y = Y; _textField.addEventListener(Event.CHANGE, onTextChange); FlxG.stage.addChild(_textField); } /** * Override the draw function so that the flash object shows, not the FlxSprite */ override public function draw():void { } /** * Clean up after ourselves */ override public function destroy():void { _textField.removeEventListener(Event.CHANGE, onTextChange); FlxG.stage.removeChild(_textField); super.destroy(); } /** * Handles the text of the object being changed, * checks it against the text field filters * @param event */ private function onTextChange(event:Event):void { if(forceUpperCase) _textField.text = _textField.text.toUpperCase(); if(filterMode != NO_FILTER) { var pattern:RegExp; switch(filterMode) { case ONLY_ALPHA: pattern = /[^a-zA-Z]*/g; break; case ONLY_NUMERIC: pattern = /[^0-9]*/g; break; case ONLY_ALPHANUMERIC: pattern = /[^a-zA-Z0-9]*/g; break; case CUSTOM_FILTER: pattern = customFilterPattern; break; default: throw new Error("FlxInputText: Unknown filterMode ("+filterMode+")"); } _textField.text = _textField.text.replace(pattern, ""); } } /** * The color of the background of the text box */ public function get backgroundColor():uint { return _textField.backgroundColor; } /** * @private */ public function set backgroundColor(Color:uint):void { _textField.backgroundColor = Color; } /** * The color of the border around the text box. */ public function get borderColor():uint { return _textField.borderColor; } /** * @private */ public function set borderColor(Color:uint):void { _textField.borderColor = Color; } /** * Whether the textbox has a background. */ public function get backgroundVisible():Boolean { return _textField.background; } /** * @private */ public function set backgroundVisible(Enabled:Boolean):void { _textField.background = Enabled; } /** * Whether the textbox has a border. */ public function get borderVisible():Boolean { return _textField.border; } /** * @private */ public function set borderVisible(Enabled:Boolean):void { _textField.border = Enabled; } /** * The read-only flash object text field, compare this to * stage.focus to see if this text box is selected. */ public function get textField():TextField { return _textField; } /** * Set the maximum length for the field (e.g. "3" * for Arcade type hi-score initials) * @param Length The maximum length. 0 means unlimited. */ public function setMaxLength(Length:uint):void { _textField.maxChars = Length; } } } ================================================ FILE: org/flixel/FlxObject.as ================================================ package org.flixel { import flash.display.Graphics; import flash.display.Sprite; import flash.geom.Point; import org.flixel.FlxBasic; /** * This is the base class for most of the display objects (FlxSprite, FlxText, etc). * It includes some basic attributes about game objects, including retro-style flickering, * basic state information, sizes, scrolling, and basic physics and motion. * * @author Adam Atomic */ public class FlxObject extends FlxBasic { /** * Generic value for "left" Used by facing, allowCollisions, and touching. */ static public const LEFT:uint = 0x0001; /** * Generic value for "right" Used by facing, allowCollisions, and touching. */ static public const RIGHT:uint = 0x0010; /** * Generic value for "up" Used by facing, allowCollisions, and touching. */ static public const UP:uint = 0x0100; /** * Generic value for "down" Used by facing, allowCollisions, and touching. */ static public const DOWN:uint = 0x1000; /** * Special-case constant meaning no collisions, used mainly by allowCollisions and touching. */ static public const NONE:uint = 0; /** * Special-case constant meaning up, used mainly by allowCollisions and touching. */ static public const CEILING:uint= UP; /** * Special-case constant meaning down, used mainly by allowCollisions and touching. */ static public const FLOOR:uint = DOWN; /** * Special-case constant meaning only the left and right sides, used mainly by allowCollisions and touching. */ static public const WALL:uint = LEFT | RIGHT; /** * Special-case constant meaning any direction, used mainly by allowCollisions and touching. */ static public const ANY:uint = LEFT | RIGHT | UP | DOWN; /** * Handy constant used during collision resolution (see separateX() and separateY()). */ static public const OVERLAP_BIAS:Number = 4; /** * Path behavior controls: move from the start of the path to the end then stop. */ static public const PATH_FORWARD:uint = 0x000000; /** * Path behavior controls: move from the end of the path to the start then stop. */ static public const PATH_BACKWARD:uint = 0x000001; /** * Path behavior controls: move from the start of the path to the end then directly back to the start, and start over. */ static public const PATH_LOOP_FORWARD:uint = 0x000010; /** * Path behavior controls: move from the end of the path to the start then directly back to the end, and start over. */ static public const PATH_LOOP_BACKWARD:uint = 0x000100; /** * Path behavior controls: move from the start of the path to the end then turn around and go back to the start, over and over. */ static public const PATH_YOYO:uint = 0x001000; /** * Path behavior controls: ignores any vertical component to the path data, only follows side to side. */ static public const PATH_HORIZONTAL_ONLY:uint = 0x010000; /** * Path behavior controls: ignores any horizontal component to the path data, only follows up and down. */ static public const PATH_VERTICAL_ONLY:uint = 0x100000; /** * X position of the upper left corner of this object in world space. */ public var x:Number; /** * Y position of the upper left corner of this object in world space. */ public var y:Number; /** * The width of this object. */ public var width:Number; /** * The height of this object. */ public var height:Number; /** * Whether an object will move/alter position after a collision. */ public var immovable:Boolean; /** * The basic speed of this object. */ public var velocity:FlxPoint; /** * The virtual mass of the object. Default value is 1. * Currently only used with elasticity during collision resolution. * Change at your own risk; effects seem crazy unpredictable so far! */ public var mass:Number; /** * The bounciness of this object. Only affects collisions. Default value is 0, or "not bouncy at all." */ public var elasticity:Number; /** * How fast the speed of this object is changing. * Useful for smooth movement and gravity. */ public var acceleration:FlxPoint; /** * This isn't drag exactly, more like deceleration that is only applied * when acceleration is not affecting the sprite. */ public var drag:FlxPoint; /** * If you are using acceleration, you can use maxVelocity with it * to cap the speed automatically (very useful!). */ public var maxVelocity:FlxPoint; /** * Set the angle of a sprite to rotate it. * WARNING: rotating sprites decreases rendering * performance for this sprite by a factor of 10x! */ public var angle:Number; /** * This is how fast you want this sprite to spin. */ public var angularVelocity:Number; /** * How fast the spin speed should change. */ public var angularAcceleration:Number; /** * Like drag but for spinning. */ public var angularDrag:Number; /** * Use in conjunction with angularAcceleration for fluid spin speed control. */ public var maxAngular:Number; /** * Should always represent (0,0) - useful for different things, for avoiding unnecessary new calls. */ static protected const _pZero:FlxPoint = new FlxPoint(); /** * A point that can store numbers from 0 to 1 (for X and Y independently) * that governs how much this object is affected by the camera subsystem. * 0 means it never moves, like a HUD element or far background graphic. * 1 means it scrolls along a the same speed as the foreground layer. * scrollFactor is initialized as (1,1) by default. */ public var scrollFactor:FlxPoint; /** * Internal helper used for retro-style flickering. */ protected var _flicker:Boolean; /** * Internal helper used for retro-style flickering. */ protected var _flickerTimer:Number; /** * Handy for storing health percentage or armor points or whatever. */ public var health:Number; /** * This is just a pre-allocated x-y point container to be used however you like */ protected var _point:FlxPoint; /** * This is just a pre-allocated rectangle container to be used however you like */ protected var _rect:FlxRect; /** * Set this to false if you want to skip the automatic motion/movement stuff (see updateMotion()). * FlxObject and FlxSprite default to true. * FlxText, FlxTileblock, FlxTilemap and FlxSound default to false. */ public var moves:Boolean; /** * Bit field of flags (use with UP, DOWN, LEFT, RIGHT, etc) indicating surface contacts. * Use bitwise operators to check the values stored here, or use touching(), justStartedTouching(), etc. * You can even use them broadly as boolean values if you're feeling saucy! */ public var touching:uint; /** * Bit field of flags (use with UP, DOWN, LEFT, RIGHT, etc) indicating surface contacts from the previous game loop step. * Use bitwise operators to check the values stored here, or use touching(), justStartedTouching(), etc. * You can even use them broadly as boolean values if you're feeling saucy! */ public var wasTouching:uint; /** * Bit field of flags (use with UP, DOWN, LEFT, RIGHT, etc) indicating collision directions. * Use bitwise operators to check the values stored here. * Useful for things like one-way platforms (e.g. allowCollisions = UP;) * The accessor "solid" just flips this variable between NONE and ANY. */ public var allowCollisions:uint; /** * Important variable for collision processing. * By default this value is set automatically during preUpdate(). */ public var last:FlxPoint; /** * A reference to a path object. Null by default, assigned by followPath(). */ public var path:FlxPath; /** * The speed at which the object is moving on the path. * When an object completes a non-looping path circuit, * the pathSpeed will be zeroed out, but the path reference * will NOT be nulled out. So pathSpeed is a good way * to check if this object is currently following a path or not. */ public var pathSpeed:Number; /** * The angle in degrees between this object and the next node, where 0 is directly upward, and 90 is to the right. */ public var pathAngle:Number; /** * Internal helper, tracks which node of the path this object is moving toward. */ protected var _pathNodeIndex:int; /** * Internal tracker for path behavior flags (like looping, horizontal only, etc). */ protected var _pathMode:uint; /** * Internal helper for node navigation, specifically yo-yo and backwards movement. */ protected var _pathInc:int; /** * Internal flag for whether the object's angle should be adjusted to the path angle during path follow behavior. */ protected var _pathRotate:Boolean; /** * Internal flag for whether the object's should stop once it has reached the end of the path. */ protected var _pathAutoStop:Boolean; /** * Instantiates a FlxObject. * * @param X The X-coordinate of the point in space. * @param Y The Y-coordinate of the point in space. * @param Width Desired width of the rectangle. * @param Height Desired height of the rectangle. */ public function FlxObject(X:Number=0,Y:Number=0,Width:Number=0,Height:Number=0) { x = X; y = Y; last = new FlxPoint(x,y); width = Width; height = Height; mass = 1.0; elasticity = 0.0; health = 1; immovable = false; moves = true; touching = NONE; wasTouching = NONE; allowCollisions = ANY; velocity = new FlxPoint(); acceleration = new FlxPoint(); drag = new FlxPoint(); maxVelocity = new FlxPoint(10000,10000); angle = 0; angularVelocity = 0; angularAcceleration = 0; angularDrag = 0; maxAngular = 10000; scrollFactor = new FlxPoint(1.0,1.0); _flicker = false; _flickerTimer = 0; _point = new FlxPoint(); _rect = new FlxRect(); path = null; pathSpeed = 0; pathAngle = 0; } /** * Override this function to null out variables or * manually call destroy() on class members if necessary. * Don't forget to call super.destroy()! */ override public function destroy():void { velocity = null; acceleration = null; drag = null; maxVelocity = null; scrollFactor = null; _point = null; _rect = null; last = null; cameras = null; if(path != null) path.destroy(); path = null; } /** * Pre-update is called right before update() on each object in the game loop. * In FlxObject it controls the flicker timer, * tracking the last coordinates for collision purposes, * and checking if the object is moving along a path or not. */ override public function preUpdate():void { _ACTIVECOUNT++; if(_flickerTimer != 0) { if(_flickerTimer > 0) { _flickerTimer = _flickerTimer - FlxG.elapsed; if(_flickerTimer <= 0) { _flickerTimer = 0; _flicker = false; } } } last.x = x; last.y = y; if((path != null) && (pathSpeed != 0) && (path.nodes[_pathNodeIndex] != null)) updatePathMotion(); } /** * Post-update is called right after update() on each object in the game loop. * In FlxObject this function handles integrating the objects motion * based on the velocity and acceleration settings, and tracking/clearing the touching flags. */ override public function postUpdate():void { if(moves) updateMotion(); wasTouching = touching; touching = NONE; } /** * Internal function for updating the position and speed of this object. * Useful for cases when you need to update this but are buried down in too many supers. * Does a slightly fancier-than-normal integration to help with higher fidelity framerate-independenct motion. */ protected function updateMotion():void { var delta:Number; var velocityDelta:Number; velocityDelta = (FlxU.computeVelocity(angularVelocity,angularAcceleration,angularDrag,maxAngular) - angularVelocity)/2; angularVelocity += velocityDelta; angle += angularVelocity*FlxG.elapsed; angularVelocity += velocityDelta; velocityDelta = (FlxU.computeVelocity(velocity.x,acceleration.x,drag.x,maxVelocity.x) - velocity.x)/2; velocity.x += velocityDelta; delta = velocity.x*FlxG.elapsed; velocity.x += velocityDelta; x += delta; velocityDelta = (FlxU.computeVelocity(velocity.y,acceleration.y,drag.y,maxVelocity.y) - velocity.y)/2; velocity.y += velocityDelta; delta = velocity.y*FlxG.elapsed; velocity.y += velocityDelta; y += delta; } /** * Rarely called, and in this case just increments the visible objects count and calls drawDebug() if necessary. */ override public function draw():void { if(cameras == null) cameras = FlxG.cameras; var camera:FlxCamera; var i:uint = 0; var l:uint = cameras.length; while(i < l) { camera = cameras[i++]; if(!onScreen(camera)) continue; _VISIBLECOUNT++; if(FlxG.visualDebug && !ignoreDrawDebug) drawDebug(camera); } } /** * Override this function to draw custom "debug mode" graphics to the * specified camera while the debugger's visual mode is toggled on. * * @param Camera Which camera to draw the debug visuals to. */ override public function drawDebug(Camera:FlxCamera=null):void { if(Camera == null) Camera = FlxG.camera; //get bounding box coordinates var boundingBoxX:Number = x - int(Camera.scroll.x*scrollFactor.x); //copied from getScreenXY() var boundingBoxY:Number = y - int(Camera.scroll.y*scrollFactor.y); boundingBoxX = int(boundingBoxX + ((boundingBoxX > 0)?0.0000001:-0.0000001)); boundingBoxY = int(boundingBoxY + ((boundingBoxY > 0)?0.0000001:-0.0000001)); var boundingBoxWidth:int = (width != int(width))?width:width-1; var boundingBoxHeight:int = (height != int(height))?height:height-1; //fill static graphics object with square shape var gfx:Graphics = FlxG.flashGfx; gfx.clear(); gfx.moveTo(boundingBoxX,boundingBoxY); var boundingBoxColor:uint; if(allowCollisions) { if(allowCollisions != ANY) boundingBoxColor = FlxG.PINK; if(immovable) boundingBoxColor = FlxG.GREEN; else boundingBoxColor = FlxG.RED; } else boundingBoxColor = FlxG.BLUE; gfx.lineStyle(1,boundingBoxColor,0.5); gfx.lineTo(boundingBoxX+boundingBoxWidth,boundingBoxY); gfx.lineTo(boundingBoxX+boundingBoxWidth,boundingBoxY+boundingBoxHeight); gfx.lineTo(boundingBoxX,boundingBoxY+boundingBoxHeight); gfx.lineTo(boundingBoxX,boundingBoxY); //draw graphics shape to camera buffer Camera.buffer.draw(FlxG.flashGfxSprite); } /** * Call this function to give this object a path to follow. * If the path does not have at least one node in it, this function * will log a warning message and return. * * @param Path The FlxPath you want this object to follow. * @param Speed How fast to travel along the path in pixels per second. * @param Mode Optional, controls the behavior of the object following the path using the path behavior constants. Can use multiple flags at once, for example PATH_YOYO|PATH_HORIZONTAL_ONLY will make an object move back and forth along the X axis of the path only. * @param AutoRotate Automatically point the object toward the next node. Assumes the graphic is pointing upward. Default behavior is false, or no automatic rotation. * @param StopWhenFinished Automatically stop the player from moving once they have reached the final node in the path. Default is "false" just so it won't conflict with code written for previous versions. */ public function followPath(Path:FlxPath,Speed:Number=100,Mode:uint=PATH_FORWARD,AutoRotate:Boolean=false,StopWhenFinished:Boolean=false):void { if(Path.nodes.length <= 0) { FlxG.log("WARNING: Paths need at least one node in them to be followed."); return; } path = Path; pathSpeed = FlxU.abs(Speed); _pathMode = Mode; _pathRotate = AutoRotate; _pathAutoStop = StopWhenFinished; //get starting node if((_pathMode == PATH_BACKWARD) || (_pathMode == PATH_LOOP_BACKWARD)) { _pathNodeIndex = path.nodes.length-1; _pathInc = -1; } else { _pathNodeIndex = 0; _pathInc = 1; } } /** * Tells this object to stop following the path its on. * * @param DestroyPath Tells this function whether to call destroy on the path object. Default value is false. */ public function stopFollowingPath(DestroyPath:Boolean=false,StopMoving:Boolean=false):void { pathSpeed = 0; if (StopMoving) { velocity.x = 0; velocity.y = 0; } if(DestroyPath && (path != null)) { path.destroy(); path = null; } } /** * Internal function that decides what node in the path to aim for next based on the behavior flags. * * @return The node (a FlxPoint object) we are aiming for next. */ protected function advancePath(Snap:Boolean=true):FlxPoint { if(Snap) { var oldNode:FlxPoint = path.nodes[_pathNodeIndex]; if(oldNode != null) { if((_pathMode & PATH_VERTICAL_ONLY) == 0) x = oldNode.x - width*0.5; if((_pathMode & PATH_HORIZONTAL_ONLY) == 0) y = oldNode.y - height*0.5; } } _pathNodeIndex += _pathInc; if((_pathMode & PATH_BACKWARD) > 0) { if(_pathNodeIndex < 0) { _pathNodeIndex = 0; stopFollowingPath(false, _pathAutoStop); } } else if((_pathMode & PATH_LOOP_FORWARD) > 0) { if(_pathNodeIndex >= path.nodes.length) _pathNodeIndex = 0; } else if((_pathMode & PATH_LOOP_BACKWARD) > 0) { if(_pathNodeIndex < 0) { _pathNodeIndex = path.nodes.length-1; if(_pathNodeIndex < 0) _pathNodeIndex = 0; } } else if((_pathMode & PATH_YOYO) > 0) { if(_pathInc > 0) { if(_pathNodeIndex >= path.nodes.length) { _pathNodeIndex = path.nodes.length-2; if(_pathNodeIndex < 0) _pathNodeIndex = 0; _pathInc = -_pathInc; } } else if(_pathNodeIndex < 0) { _pathNodeIndex = 1; if(_pathNodeIndex >= path.nodes.length) _pathNodeIndex = path.nodes.length-1; if(_pathNodeIndex < 0) _pathNodeIndex = 0; _pathInc = -_pathInc; } } else { if(_pathNodeIndex >= path.nodes.length) { _pathNodeIndex = path.nodes.length-1; stopFollowingPath(false, _pathAutoStop); } } return path.nodes[_pathNodeIndex]; } /** * Internal function for moving the object along the path. * Generally this function is called automatically by preUpdate(). * The first half of the function decides if the object can advance to the next node in the path, * while the second half handles actually picking a velocity toward the next node. */ protected function updatePathMotion():void { //first check if we need to be pointing at the next node yet _point.x = x + width*0.5; _point.y = y + height*0.5; var node:FlxPoint = path.nodes[_pathNodeIndex]; var deltaX:Number = node.x - _point.x; var deltaY:Number = node.y - _point.y; var horizontalOnly:Boolean = (_pathMode & PATH_HORIZONTAL_ONLY) > 0; var verticalOnly:Boolean = (_pathMode & PATH_VERTICAL_ONLY) > 0; if(horizontalOnly) { if(((deltaX>0)?deltaX:-deltaX) < pathSpeed*FlxG.elapsed) node = advancePath(); } else if(verticalOnly) { if(((deltaY>0)?deltaY:-deltaY) < pathSpeed*FlxG.elapsed) node = advancePath(); } else { if(Math.sqrt(deltaX*deltaX + deltaY*deltaY) < pathSpeed*FlxG.elapsed) node = advancePath(); } //then just move toward the current node at the requested speed if(pathSpeed != 0) { //set velocity based on path mode _point.x = x + width*0.5; _point.y = y + height*0.5; if(horizontalOnly || (_point.y == node.y)) { velocity.x = (_point.x < node.x)?pathSpeed:-pathSpeed; if(velocity.x < 0) pathAngle = -90; else pathAngle = 90; if(!horizontalOnly) velocity.y = 0; } else if(verticalOnly || (_point.x == node.x)) { velocity.y = (_point.y < node.y)?pathSpeed:-pathSpeed; if(velocity.y < 0) pathAngle = 0; else pathAngle = 180; if(!verticalOnly) velocity.x = 0; } else { pathAngle = FlxU.getAngle(_point,node); FlxU.rotatePoint(0,pathSpeed,0,0,pathAngle,velocity); } //then set object rotation if necessary if(_pathRotate) { angularVelocity = 0; angularAcceleration = 0; angle = pathAngle; } } } /** * Checks to see if some FlxObject overlaps this FlxObject or FlxGroup. * If the group has a LOT of things in it, it might be faster to use FlxG.overlaps(). * WARNING: Currently tilemaps do NOT support screen space overlap checks! * * @param ObjectOrGroup The object or group being tested. * @param InScreenSpace Whether to take scroll factors into account when checking for overlap. Default is false, or "only compare in world space." * @param Camera Specify which game camera you want. If null getScreenXY() will just grab the first global camera. * * @return Whether or not the two objects overlap. */ public function overlaps(ObjectOrGroup:FlxBasic,InScreenSpace:Boolean=false,Camera:FlxCamera=null):Boolean { if(ObjectOrGroup is FlxGroup) { var results:Boolean = false; var i:uint = 0; var members:Array = (ObjectOrGroup as FlxGroup).members; while(i < length) { if(overlaps(members[i++],InScreenSpace,Camera)) results = true; } return results; } if(ObjectOrGroup is FlxTilemap) { //Since tilemap's have to be the caller, not the target, to do proper tile-based collisions, // we redirect the call to the tilemap overlap here. return (ObjectOrGroup as FlxTilemap).overlaps(this,InScreenSpace,Camera); } var object:FlxObject = ObjectOrGroup as FlxObject; if(!InScreenSpace) { return (object.x + object.width > x) && (object.x < x + width) && (object.y + object.height > y) && (object.y < y + height); } if(Camera == null) Camera = FlxG.camera; var objectScreenPos:FlxPoint = object.getScreenXY(null,Camera); getScreenXY(_point,Camera); return (objectScreenPos.x + object.width > _point.x) && (objectScreenPos.x < _point.x + width) && (objectScreenPos.y + object.height > _point.y) && (objectScreenPos.y < _point.y + height); } /** * Checks to see if this FlxObject were located at the given position, would it overlap the FlxObject or FlxGroup? * This is distinct from overlapsPoint(), which just checks that point, rather than taking the object's size into account. * WARNING: Currently tilemaps do NOT support screen space overlap checks! * * @param X The X position you want to check. Pretends this object (the caller, not the parameter) is located here. * @param Y The Y position you want to check. Pretends this object (the caller, not the parameter) is located here. * @param ObjectOrGroup The object or group being tested. * @param InScreenSpace Whether to take scroll factors into account when checking for overlap. Default is false, or "only compare in world space." * @param Camera Specify which game camera you want. If null getScreenXY() will just grab the first global camera. * * @return Whether or not the two objects overlap. */ public function overlapsAt(X:Number,Y:Number,ObjectOrGroup:FlxBasic,InScreenSpace:Boolean=false,Camera:FlxCamera=null):Boolean { if(ObjectOrGroup is FlxGroup) { var results:Boolean = false; var basic:FlxBasic; var i:uint = 0; var members:Array = (ObjectOrGroup as FlxGroup).members; while(i < length) { if(overlapsAt(X,Y,members[i++],InScreenSpace,Camera)) results = true; } return results; } if(ObjectOrGroup is FlxTilemap) { //Since tilemap's have to be the caller, not the target, to do proper tile-based collisions, // we redirect the call to the tilemap overlap here. //However, since this is overlapsAt(), we also have to invent the appropriate position for the tilemap. //So we calculate the offset between the player and the requested position, and subtract that from the tilemap. var tilemap:FlxTilemap = ObjectOrGroup as FlxTilemap; return tilemap.overlapsAt(tilemap.x - (X - x),tilemap.y - (Y - y),this,InScreenSpace,Camera); } var object:FlxObject = ObjectOrGroup as FlxObject; if(!InScreenSpace) { return (object.x + object.width > X) && (object.x < X + width) && (object.y + object.height > Y) && (object.y < Y + height); } if(Camera == null) Camera = FlxG.camera; var objectScreenPos:FlxPoint = object.getScreenXY(null,Camera); _point.x = X - int(Camera.scroll.x*scrollFactor.x); //copied from getScreenXY() _point.y = Y - int(Camera.scroll.y*scrollFactor.y); _point.x += (_point.x > 0)?0.0000001:-0.0000001; _point.y += (_point.y > 0)?0.0000001:-0.0000001; return (objectScreenPos.x + object.width > _point.x) && (objectScreenPos.x < _point.x + width) && (objectScreenPos.y + object.height > _point.y) && (objectScreenPos.y < _point.y + height); } /** * Checks to see if a point in 2D world space overlaps this FlxObject object. * * @param Point The point in world space you want to check. * @param InScreenSpace Whether to take scroll factors into account when checking for overlap. * @param Camera Specify which game camera you want. If null getScreenXY() will just grab the first global camera. * * @return Whether or not the point overlaps this object. */ public function overlapsPoint(Point:FlxPoint,InScreenSpace:Boolean=false,Camera:FlxCamera=null):Boolean { if(!InScreenSpace) return (Point.x > x) && (Point.x < x + width) && (Point.y > y) && (Point.y < y + height); if(Camera == null) Camera = FlxG.camera; var X:Number = Point.x - Camera.scroll.x; var Y:Number = Point.y - Camera.scroll.y; getScreenXY(_point,Camera); return (X > _point.x) && (X < _point.x+width) && (Y > _point.y) && (Y < _point.y+height); } /** * Check and see if this object is currently on screen. * * @param Camera Specify which game camera you want. If null getScreenXY() will just grab the first global camera. * * @return Whether the object is on screen or not. */ public function onScreen(Camera:FlxCamera=null):Boolean { if(Camera == null) Camera = FlxG.camera; getScreenXY(_point,Camera); return (_point.x + width > 0) && (_point.x < Camera.width) && (_point.y + height > 0) && (_point.y < Camera.height); } /** * Call this function to figure out the on-screen position of the object. * * @param Camera Specify which game camera you want. If null getScreenXY() will just grab the first global camera. * @param Point Takes a FlxPoint object and assigns the post-scrolled X and Y values of this object to it. * * @return The Point you passed in, or a new Point if you didn't pass one, containing the screen X and Y position of this object. */ public function getScreenXY(Point:FlxPoint=null,Camera:FlxCamera=null):FlxPoint { if(Point == null) Point = new FlxPoint(); if(Camera == null) Camera = FlxG.camera; Point.x = x - int(Camera.scroll.x*scrollFactor.x); Point.y = y - int(Camera.scroll.y*scrollFactor.y); Point.x += (Point.x > 0)?0.0000001:-0.0000001; Point.y += (Point.y > 0)?0.0000001:-0.0000001; return Point; } /** * Tells this object to flicker, retro-style. * Pass a negative value to flicker forever. * * @param Duration How many seconds to flicker for. */ public function flicker(Duration:Number=1):void { _flickerTimer = Duration; if(_flickerTimer == 0) _flicker = false; } /** * Check to see if the object is still flickering. * * @return Whether the object is flickering or not. */ public function get flickering():Boolean { return _flickerTimer != 0; } /** * Whether the object collides or not. For more control over what directions * the object will collide from, use collision constants (like LEFT, FLOOR, etc) * to set the value of allowCollisions directly. */ public function get solid():Boolean { return (allowCollisions & ANY) > NONE; } /** * @private */ public function set solid(Solid:Boolean):void { if(Solid) allowCollisions = ANY; else allowCollisions = NONE; } /** * Retrieve the midpoint of this object in world coordinates. * * @Point Allows you to pass in an existing FlxPoint object if you're so inclined. Otherwise a new one is created. * * @return A FlxPoint object containing the midpoint of this object in world coordinates. */ public function getMidpoint(Point:FlxPoint=null):FlxPoint { if(Point == null) Point = new FlxPoint(); Point.x = x + width*0.5; Point.y = y + height*0.5; return Point; } /** * Handy function for reviving game objects. * Resets their existence flags and position. * * @param X The new X position of this object. * @param Y The new Y position of this object. */ public function reset(X:Number,Y:Number):void { revive(); touching = NONE; wasTouching = NONE; x = X; y = Y; last.x = x; last.y = y; velocity.x = 0; velocity.y = 0; } /** * Handy function for checking if this object is touching a particular surface. * For slightly better performance you can just & the value directly into touching. * However, this method is good for readability and accessibility. * * @param Direction Any of the collision flags (e.g. LEFT, FLOOR, etc). * * @return Whether the object is touching an object in (any of) the specified direction(s) this frame. */ public function isTouching(Direction:uint):Boolean { return (touching & Direction) > NONE; } /** * Handy function for checking if this object is just landed on a particular surface. * * @param Direction Any of the collision flags (e.g. LEFT, FLOOR, etc). * * @return Whether the object just landed on (any of) the specified surface(s) this frame. */ public function justTouched(Direction:uint):Boolean { return ((touching & Direction) > NONE) && ((wasTouching & Direction) <= NONE); } /** * Reduces the "health" variable of this sprite by the amount specified in Damage. * Calls kill() if health drops to or below zero. * * @param Damage How much health to take away (use a negative number to give a health bonus). */ public function hurt(Damage:Number):void { health = health - Damage; if(health <= 0) kill(); } /** * The main collision resolution function in flixel. * * @param Object1 Any FlxObject. * @param Object2 Any other FlxObject. * * @return Whether the objects in fact touched and were separated. */ static public function separate(Object1:FlxObject, Object2:FlxObject):Boolean { var separatedX:Boolean = separateX(Object1,Object2); var separatedY:Boolean = separateY(Object1,Object2); return separatedX || separatedY; } /** * The X-axis component of the object separation process. * * @param Object1 Any FlxObject. * @param Object2 Any other FlxObject. * * @return Whether the objects in fact touched and were separated along the X axis. */ static public function separateX(Object1:FlxObject, Object2:FlxObject):Boolean { //can't separate two immovable objects var obj1immovable:Boolean = Object1.immovable; var obj2immovable:Boolean = Object2.immovable; if(obj1immovable && obj2immovable) return false; //If one of the objects is a tilemap, just pass it off. if(Object1 is FlxTilemap) return (Object1 as FlxTilemap).overlapsWithCallback(Object2,separateX); if(Object2 is FlxTilemap) return (Object2 as FlxTilemap).overlapsWithCallback(Object1,separateX,true); //First, get the two object deltas var overlap:Number = 0; var obj1delta:Number = Object1.x - Object1.last.x; var obj2delta:Number = Object2.x - Object2.last.x; if(obj1delta != obj2delta) { //Check if the X hulls actually overlap var obj1deltaAbs:Number = (obj1delta > 0)?obj1delta:-obj1delta; var obj2deltaAbs:Number = (obj2delta > 0)?obj2delta:-obj2delta; var obj1rect:FlxRect = new FlxRect(Object1.x-((obj1delta > 0)?obj1delta:0),Object1.last.y,Object1.width+((obj1delta > 0)?obj1delta:-obj1delta),Object1.height); var obj2rect:FlxRect = new FlxRect(Object2.x-((obj2delta > 0)?obj2delta:0),Object2.last.y,Object2.width+((obj2delta > 0)?obj2delta:-obj2delta),Object2.height); if((obj1rect.x + obj1rect.width > obj2rect.x) && (obj1rect.x < obj2rect.x + obj2rect.width) && (obj1rect.y + obj1rect.height > obj2rect.y) && (obj1rect.y < obj2rect.y + obj2rect.height)) { var maxOverlap:Number = obj1deltaAbs + obj2deltaAbs + OVERLAP_BIAS; //If they did overlap (and can), figure out by how much and flip the corresponding flags if(obj1delta > obj2delta) { overlap = Object1.x + Object1.width - Object2.x; if((overlap > maxOverlap) || !(Object1.allowCollisions & RIGHT) || !(Object2.allowCollisions & LEFT)) overlap = 0; else { Object1.touching |= RIGHT; Object2.touching |= LEFT; } } else if(obj1delta < obj2delta) { overlap = Object1.x - Object2.width - Object2.x; if((-overlap > maxOverlap) || !(Object1.allowCollisions & LEFT) || !(Object2.allowCollisions & RIGHT)) overlap = 0; else { Object1.touching |= LEFT; Object2.touching |= RIGHT; } } } } //Then adjust their positions and velocities accordingly (if there was any overlap) if(overlap != 0) { var obj1v:Number = Object1.velocity.x; var obj2v:Number = Object2.velocity.x; if(!obj1immovable && !obj2immovable) { overlap *= 0.5; Object1.x = Object1.x - overlap; Object2.x += overlap; var obj1velocity:Number = Math.sqrt((obj2v * obj2v * Object2.mass)/Object1.mass) * ((obj2v > 0)?1:-1); var obj2velocity:Number = Math.sqrt((obj1v * obj1v * Object1.mass)/Object2.mass) * ((obj1v > 0)?1:-1); var average:Number = (obj1velocity + obj2velocity)*0.5; obj1velocity -= average; obj2velocity -= average; Object1.velocity.x = average + obj1velocity * Object1.elasticity; Object2.velocity.x = average + obj2velocity * Object2.elasticity; } else if(!obj1immovable) { Object1.x = Object1.x - overlap; Object1.velocity.x = obj2v - obj1v*Object1.elasticity; } else if(!obj2immovable) { Object2.x += overlap; Object2.velocity.x = obj1v - obj2v*Object2.elasticity; } return true; } else return false; } /** * The Y-axis component of the object separation process. * * @param Object1 Any FlxObject. * @param Object2 Any other FlxObject. * * @return Whether the objects in fact touched and were separated along the Y axis. */ static public function separateY(Object1:FlxObject, Object2:FlxObject):Boolean { //can't separate two immovable objects var obj1immovable:Boolean = Object1.immovable; var obj2immovable:Boolean = Object2.immovable; if(obj1immovable && obj2immovable) return false; //If one of the objects is a tilemap, just pass it off. if(Object1 is FlxTilemap) return (Object1 as FlxTilemap).overlapsWithCallback(Object2,separateY); if(Object2 is FlxTilemap) return (Object2 as FlxTilemap).overlapsWithCallback(Object1,separateY,true); //First, get the two object deltas var overlap:Number = 0; var obj1delta:Number = Object1.y - Object1.last.y; var obj2delta:Number = Object2.y - Object2.last.y; if(obj1delta != obj2delta) { //Check if the Y hulls actually overlap var obj1deltaAbs:Number = (obj1delta > 0)?obj1delta:-obj1delta; var obj2deltaAbs:Number = (obj2delta > 0)?obj2delta:-obj2delta; var obj1rect:FlxRect = new FlxRect(Object1.x,Object1.y-((obj1delta > 0)?obj1delta:0),Object1.width,Object1.height+obj1deltaAbs); var obj2rect:FlxRect = new FlxRect(Object2.x,Object2.y-((obj2delta > 0)?obj2delta:0),Object2.width,Object2.height+obj2deltaAbs); if((obj1rect.x + obj1rect.width > obj2rect.x) && (obj1rect.x < obj2rect.x + obj2rect.width) && (obj1rect.y + obj1rect.height > obj2rect.y) && (obj1rect.y < obj2rect.y + obj2rect.height)) { var maxOverlap:Number = obj1deltaAbs + obj2deltaAbs + OVERLAP_BIAS; //If they did overlap (and can), figure out by how much and flip the corresponding flags if(obj1delta > obj2delta) { overlap = Object1.y + Object1.height - Object2.y; if((overlap > maxOverlap) || !(Object1.allowCollisions & DOWN) || !(Object2.allowCollisions & UP)) overlap = 0; else { Object1.touching |= DOWN; Object2.touching |= UP; } } else if(obj1delta < obj2delta) { overlap = Object1.y - Object2.height - Object2.y; if((-overlap > maxOverlap) || !(Object1.allowCollisions & UP) || !(Object2.allowCollisions & DOWN)) overlap = 0; else { Object1.touching |= UP; Object2.touching |= DOWN; } } } } //Then adjust their positions and velocities accordingly (if there was any overlap) if(overlap != 0) { var obj1v:Number = Object1.velocity.y; var obj2v:Number = Object2.velocity.y; if(!obj1immovable && !obj2immovable) { overlap *= 0.5; Object1.y = Object1.y - overlap; Object2.y += overlap; var obj1velocity:Number = Math.sqrt((obj2v * obj2v * Object2.mass)/Object1.mass) * ((obj2v > 0)?1:-1); var obj2velocity:Number = Math.sqrt((obj1v * obj1v * Object1.mass)/Object2.mass) * ((obj1v > 0)?1:-1); var average:Number = (obj1velocity + obj2velocity)*0.5; obj1velocity -= average; obj2velocity -= average; Object1.velocity.y = average + obj1velocity * Object1.elasticity; Object2.velocity.y = average + obj2velocity * Object2.elasticity; } else if(!obj1immovable) { Object1.y = Object1.y - overlap; Object1.velocity.y = obj2v - obj1v*Object1.elasticity; //This is special case code that handles cases like horizontal moving platforms you can ride if(Object2.active && Object2.moves && (obj1delta > obj2delta)) Object1.x += Object2.x - Object2.last.x; } else if(!obj2immovable) { Object2.y += overlap; Object2.velocity.y = obj1v - obj2v*Object2.elasticity; //This is special case code that handles cases like horizontal moving platforms you can ride if(Object1.active && Object1.moves && (obj1delta < obj2delta)) Object2.x += Object1.x - Object1.last.x; } return true; } else return false; } } } ================================================ FILE: org/flixel/FlxParticle.as ================================================ package org.flixel { /** * This is a simple particle class that extends the default behavior * of FlxSprite to have slightly more specialized behavior * common to many game scenarios. You can override and extend this class * just like you would FlxSprite. While FlxEmitter * used to work with just any old sprite, it now requires a * FlxParticle based class. * * @author Adam Atomic */ public class FlxParticle extends FlxSprite { /** * How long this particle lives before it disappears. * NOTE: this is a maximum, not a minimum; the object * could get recycled before its lifespan is up. */ public var lifespan:Number; /** * Determines how quickly the particles come to rest on the ground. * Only used if the particle has gravity-like acceleration applied. * @default 500 */ public var friction:Number; /** * Instantiate a new particle. Like FlxSprite, all meaningful creation * happens during loadGraphic() or makeGraphic() or whatever. */ public function FlxParticle() { super(); lifespan = 0; friction = 500; } /** * The particle's main update logic. Basically it checks to see if it should * be dead yet, and then has some special bounce behavior if there is some gravity on it. */ override public function update():void { //lifespan behavior if(lifespan <= 0) return; lifespan -= FlxG.elapsed; if(lifespan <= 0) kill(); //simpler bounce/spin behavior for now if(touching) { if(angularVelocity != 0) angularVelocity = -angularVelocity; } if(acceleration.y > 0) //special behavior for particles with gravity { if(touching & FLOOR) { drag.x = friction; if(!(wasTouching & FLOOR)) { if(velocity.y < -elasticity*10) { if(angularVelocity != 0) angularVelocity *= -elasticity; } else { velocity.y = 0; angularVelocity = 0; } } } else drag.x = 0; } } /** * Triggered whenever this object is launched by a FlxEmitter. * You can override this to add custom behavior like a sound or AI or something. */ public function onEmit():void { } } } ================================================ FILE: org/flixel/FlxPath.as ================================================ package org.flixel { import flash.display.Graphics; import org.flixel.plugin.DebugPathDisplay; /** * This is a simple path data container. Basically a list of points that * a FlxObject can follow. Also has code for drawing debug visuals. * FlxTilemap.findPath() returns a path object, but you can * also just make your own, using the add() functions below * or by creating your own array of points. * * @author Adam Atomic */ public class FlxPath { /** * The list of FlxPoints that make up the path data. */ public var nodes:Array; /** * Specify a debug display color for the path. Default is white. */ public var debugColor:uint; /** * Specify a debug display scroll factor for the path. Default is (1,1). * NOTE: does not affect world movement! Object scroll factors take care of that. */ public var debugScrollFactor:FlxPoint; /** * Setting this to true will prevent the object from appearing * when the visual debug mode in the debugger overlay is toggled on. * @default false */ public var ignoreDrawDebug:Boolean; /** * Internal helper for keeping new variable instantiations under control. */ protected var _point:FlxPoint; /** * Instantiate a new path object. * * @param Nodes Optional, can specify all the points for the path up front if you want. */ public function FlxPath(Nodes:Array=null) { if(Nodes == null) nodes = new Array(); else nodes = Nodes; _point = new FlxPoint(); debugScrollFactor = new FlxPoint(1.0,1.0); debugColor = 0xffffff; ignoreDrawDebug = false; var debugPathDisplay:DebugPathDisplay = manager; if(debugPathDisplay != null) debugPathDisplay.add(this); } /** * Clean up memory. */ public function destroy():void { var debugPathDisplay:DebugPathDisplay = manager; if(debugPathDisplay != null) debugPathDisplay.remove(this); debugScrollFactor = null; _point = null; nodes = null; } /** * Add a new node to the end of the path at the specified location. * * @param X X position of the new path point in world coordinates. * @param Y Y position of the new path point in world coordinates. */ public function add(X:Number,Y:Number):void { nodes.push(new FlxPoint(X,Y)); } /** * Add a new node to the path at the specified location and index within the path. * * @param X X position of the new path point in world coordinates. * @param Y Y position of the new path point in world coordinates. * @param Index Where within the list of path nodes to insert this new point. */ public function addAt(X:Number, Y:Number, Index:uint):void { if(Index > nodes.length) Index = nodes.length; nodes.splice(Index,0,new FlxPoint(X,Y)); } /** * Sometimes its easier or faster to just pass a point object instead of separate X and Y coordinates. * This also gives you the option of not creating a new node but actually adding that specific * FlxPoint object to the path. This allows you to do neat things, like dynamic paths. * * @param Node The point in world coordinates you want to add to the path. * @param AsReference Whether to add the point as a reference, or to create a new point with the specified values. */ public function addPoint(Node:FlxPoint,AsReference:Boolean=false):void { if(AsReference) nodes.push(Node); else nodes.push(new FlxPoint(Node.x,Node.y)); } /** * Sometimes its easier or faster to just pass a point object instead of separate X and Y coordinates. * This also gives you the option of not creating a new node but actually adding that specific * FlxPoint object to the path. This allows you to do neat things, like dynamic paths. * * @param Node The point in world coordinates you want to add to the path. * @param Index Where within the list of path nodes to insert this new point. * @param AsReference Whether to add the point as a reference, or to create a new point with the specified values. */ public function addPointAt(Node:FlxPoint,Index:uint,AsReference:Boolean=false):void { if(Index > nodes.length) Index = nodes.length; if(AsReference) nodes.splice(Index,0,Node); else nodes.splice(Index,0,new FlxPoint(Node.x,Node.y)); } /** * Remove a node from the path. * NOTE: only works with points added by reference or with references from nodes itself! * * @param Node The point object you want to remove from the path. * * @return The node that was excised. Returns null if the node was not found. */ public function remove(Node:FlxPoint):FlxPoint { var index:int = nodes.indexOf(Node); if(index >= 0) return nodes.splice(index,1)[0]; else return null; } /** * Remove a node from the path using the specified position in the list of path nodes. * * @param Index Where within the list of path nodes you want to remove a node. * * @return The node that was excised. Returns null if there were no nodes in the path. */ public function removeAt(Index:uint):FlxPoint { if(nodes.length <= 0) return null; if(Index >= nodes.length) Index = nodes.length-1; return nodes.splice(Index,1)[0]; } /** * Get the first node in the list. * * @return The first node in the path. */ public function head():FlxPoint { if(nodes.length > 0) return nodes[0]; return null; } /** * Get the last node in the list. * * @return The last node in the path. */ public function tail():FlxPoint { if(nodes.length > 0) return nodes[nodes.length-1]; return null; } /** * While this doesn't override FlxBasic.drawDebug(), the behavior is very similar. * Based on this path data, it draws a simple lines-and-boxes representation of the path * if the visual debug mode was toggled in the debugger overlay. You can use debugColor * and debugScrollFactor to control the path's appearance. * * @param Camera The camera object the path will draw to. */ public function drawDebug(Camera:FlxCamera=null):void { if(nodes.length <= 0) return; if(Camera == null) Camera = FlxG.camera; //Set up our global flash graphics object to draw out the path var gfx:Graphics = FlxG.flashGfx; gfx.clear(); //Then fill up the object with node and path graphics var node:FlxPoint; var nextNode:FlxPoint; var i:uint = 0; var l:uint = nodes.length; while(i < l) { //get a reference to the current node node = nodes[i] as FlxPoint; //find the screen position of the node on this camera _point.x = node.x - int(Camera.scroll.x*debugScrollFactor.x); //copied from getScreenXY() _point.y = node.y - int(Camera.scroll.y*debugScrollFactor.y); _point.x = int(_point.x + ((_point.x > 0)?0.0000001:-0.0000001)); _point.y = int(_point.y + ((_point.y > 0)?0.0000001:-0.0000001)); //decide what color this node should be var nodeSize:uint = 2; if((i == 0) || (i == l-1)) nodeSize *= 2; var nodeColor:uint = debugColor; if(l > 1) { if(i == 0) nodeColor = FlxG.GREEN; else if(i == l-1) nodeColor = FlxG.RED; } //draw a box for the node gfx.beginFill(nodeColor,0.5); gfx.lineStyle(); gfx.drawRect(_point.x-nodeSize*0.5,_point.y-nodeSize*0.5,nodeSize,nodeSize); gfx.endFill(); //then find the next node in the path var linealpha:Number = 0.3; if(i < l-1) nextNode = nodes[i+1]; else { nextNode = nodes[0]; linealpha = 0.15; } //then draw a line to the next node gfx.moveTo(_point.x,_point.y); gfx.lineStyle(1,debugColor,linealpha); _point.x = nextNode.x - int(Camera.scroll.x*debugScrollFactor.x); //copied from getScreenXY() _point.y = nextNode.y - int(Camera.scroll.y*debugScrollFactor.y); _point.x = int(_point.x + ((_point.x > 0)?0.0000001:-0.0000001)); _point.y = int(_point.y + ((_point.y > 0)?0.0000001:-0.0000001)); gfx.lineTo(_point.x,_point.y); i++; } //then stamp the path down onto the game buffer Camera.buffer.draw(FlxG.flashGfxSprite); } static public function get manager():DebugPathDisplay { return FlxG.getPlugin(DebugPathDisplay) as DebugPathDisplay; } } } ================================================ FILE: org/flixel/FlxPoint.as ================================================ package org.flixel { import flash.geom.Point; /** * Stores a 2D floating point coordinate. * * @author Adam Atomic */ public class FlxPoint { /** * @default 0 */ public var x:Number; /** * @default 0 */ public var y:Number; /** * Instantiate a new point object. * * @param X The X-coordinate of the point in space. * @param Y The Y-coordinate of the point in space. */ public function FlxPoint(X:Number=0, Y:Number=0) { x = X; y = Y; } /** * Instantiate a new point object. * * @param X The X-coordinate of the point in space. * @param Y The Y-coordinate of the point in space. */ public function make(X:Number=0, Y:Number=0):FlxPoint { x = X; y = Y; return this; } /** * Helper function, just copies the values from the specified point. * * @param Point Any FlxPoint. * * @return A reference to itself. */ public function copyFrom(Point:FlxPoint):FlxPoint { x = Point.x; y = Point.y; return this; } /** * Helper function, just copies the values from this point to the specified point. * * @param Point Any FlxPoint. * * @return A reference to the altered point parameter. */ public function copyTo(Point:FlxPoint):FlxPoint { Point.x = x; Point.y = y; return Point; } /** * Helper function, just copies the values from the specified Flash point. * * @param Point Any Point. * * @return A reference to itself. */ public function copyFromFlash(FlashPoint:Point):FlxPoint { x = FlashPoint.x; y = FlashPoint.y; return this; } /** * Helper function, just copies the values from this point to the specified Flash point. * * @param Point Any Point. * * @return A reference to the altered point parameter. */ public function copyToFlash(FlashPoint:Point):Point { FlashPoint.x = x; FlashPoint.y = y; return FlashPoint; } } } ================================================ FILE: org/flixel/FlxRect.as ================================================ package org.flixel { import flash.geom.Rectangle; /** * Stores a rectangle. * * @author Adam Atomic */ public class FlxRect { /** * @default 0 */ public var x:Number; /** * @default 0 */ public var y:Number; /** * @default 0 */ public var width:Number; /** * @default 0 */ public var height:Number; /** * Instantiate a new rectangle. * * @param X The X-coordinate of the point in space. * @param Y The Y-coordinate of the point in space. * @param Width Desired width of the rectangle. * @param Height Desired height of the rectangle. */ public function FlxRect(X:Number=0, Y:Number=0, Width:Number=0, Height:Number=0) { x = X; y = Y; width = Width; height = Height; } /** * The X coordinate of the left side of the rectangle. Read-only. */ public function get left():Number { return x; } /** * The X coordinate of the right side of the rectangle. Read-only. */ public function get right():Number { return x + width; } /** * The Y coordinate of the top of the rectangle. Read-only. */ public function get top():Number { return y; } /** * The Y coordinate of the bottom of the rectangle. Read-only. */ public function get bottom():Number { return y + height; } /** * Instantiate a new rectangle. * * @param X The X-coordinate of the point in space. * @param Y The Y-coordinate of the point in space. * @param Width Desired width of the rectangle. * @param Height Desired height of the rectangle. * * @return A reference to itself. */ public function make(X:Number=0, Y:Number=0, Width:Number=0, Height:Number=0):FlxRect { x = X; y = Y; width = Width; height = Height; return this; } /** * Helper function, just copies the values from the specified rectangle. * * @param Rect Any FlxRect. * * @return A reference to itself. */ public function copyFrom(Rect:FlxRect):FlxRect { x = Rect.x; y = Rect.y; width = Rect.width; height = Rect.height; return this; } /** * Helper function, just copies the values from this rectangle to the specified rectangle. * * @param Point Any FlxRect. * * @return A reference to the altered rectangle parameter. */ public function copyTo(Rect:FlxRect):FlxRect { Rect.x = x; Rect.y = y; Rect.width = width; Rect.height = height; return Rect; } /** * Helper function, just copies the values from the specified Flash rectangle. * * @param FlashRect Any Rectangle. * * @return A reference to itself. */ public function copyFromFlash(FlashRect:Rectangle):FlxRect { x = FlashRect.x; y = FlashRect.y; width = FlashRect.width; height = FlashRect.height; return this; } /** * Helper function, just copies the values from this rectangle to the specified Flash rectangle. * * @param Point Any Rectangle. * * @return A reference to the altered rectangle parameter. */ public function copyToFlash(FlashRect:Rectangle):Rectangle { FlashRect.x = x; FlashRect.y = y; FlashRect.width = width; FlashRect.height = height; return FlashRect; } /** * Checks to see if some FlxRect object overlaps this FlxRect object. * * @param Rect The rectangle being tested. * * @return Whether or not the two rectangles overlap. */ public function overlaps(Rect:FlxRect):Boolean { return (Rect.x + Rect.width > x) && (Rect.x < x+width) && (Rect.y + Rect.height > y) && (Rect.y < y+height); } } } ================================================ FILE: org/flixel/FlxSave.as ================================================ package org.flixel { import flash.events.NetStatusEvent; import flash.net.SharedObject; import flash.net.SharedObjectFlushStatus; /** * A class to help automate and simplify save game functionality. * Basicaly a wrapper for the Flash SharedObject thing, but * handles some annoying storage request stuff too. * * @author Adam Atomic */ public class FlxSave extends Object { static protected var SUCCESS:uint = 0; static protected var PENDING:uint = 1; static protected var ERROR:uint = 2; /** * Allows you to directly access the data container in the local shared object. * @default null */ public var data:Object; /** * The name of the local shared object. * @default null */ public var name:String; /** * The local shared object itself. * @default null */ protected var _sharedObject:SharedObject; /** * Internal tracker for callback function in case save takes too long. */ protected var _onComplete:Function; /** * Internal tracker for save object close request. */ protected var _closeRequested:Boolean; /** * Blanks out the containers. */ public function FlxSave() { destroy(); } /** * Clean up memory. */ public function destroy():void { _sharedObject = null; name = null; data = null; _onComplete = null; _closeRequested = false; } /** * Automatically creates or reconnects to locally saved data. * * @param Name The name of the object (should be the same each time to access old data). * * @return Whether or not you successfully connected to the save data. */ public function bind(Name:String):Boolean { destroy(); name = Name; try { _sharedObject = SharedObject.getLocal(name); } catch(e:Error) { FlxG.log("ERROR: There was a problem binding to\nthe shared object data from FlxSave."); destroy(); return false; } data = _sharedObject.data; return true; } /** * A way to safely call flush() and destroy() on your save file. * Will correctly handle storage size popups and all that good stuff. * If you don't want to save your changes first, just call destroy() instead. * * @param MinFileSize If you need X amount of space for your save, specify it here. * @param OnComplete This callback will be triggered when the data is written successfully. * * @return The result of result of the flush() call (see below for more details). */ public function close(MinFileSize:uint=0,OnComplete:Function=null):Boolean { _closeRequested = true; return flush(MinFileSize,OnComplete); } /** * Writes the local shared object to disk immediately. Leaves the object open in memory. * * @param MinFileSize If you need X amount of space for your save, specify it here. * @param OnComplete This callback will be triggered when the data is written successfully. * * @return Whether or not the data was written immediately. False could be an error OR a storage request popup. */ public function flush(MinFileSize:uint=0,OnComplete:Function=null):Boolean { if(!checkBinding()) return false; _onComplete = OnComplete; var result:String = null; try { result = _sharedObject.flush(MinFileSize); } catch (e:Error) { return onDone(ERROR); } if(result == SharedObjectFlushStatus.PENDING) _sharedObject.addEventListener(NetStatusEvent.NET_STATUS,onFlushStatus); return onDone((result == SharedObjectFlushStatus.FLUSHED)?SUCCESS:PENDING); } /** * Erases everything stored in the local shared object. * Data is immediately erased and the object is saved that way, * so use with caution! * * @return Returns false if the save object is not bound yet. */ public function erase():Boolean { if(!checkBinding()) return false; _sharedObject.clear(); return true; } /** * Event handler for special case storage requests. * * @param E Flash net status event. */ protected function onFlushStatus(E:NetStatusEvent):void { _sharedObject.removeEventListener(NetStatusEvent.NET_STATUS,onFlushStatus); onDone((E.info.code == "SharedObject.Flush.Success")?SUCCESS:ERROR); } /** * Event handler for special case storage requests. * Handles logging of errors and calling of callback. * * @param Result One of the result codes (PENDING, ERROR, or SUCCESS). * * @return Whether the operation was a success or not. */ protected function onDone(Result:uint):Boolean { switch(Result) { case PENDING: FlxG.log("FLIXEL: FlxSave is requesting extra storage space."); break; case ERROR: FlxG.log("ERROR: There was a problem flushing\nthe shared object data from FlxSave."); break; default: break; } if(_onComplete != null) _onComplete(Result == SUCCESS); if(_closeRequested) destroy(); return Result == SUCCESS; } /** * Handy utility function for checking and warning if the shared object is bound yet or not. * * @return Whether the shared object was bound yet. */ protected function checkBinding():Boolean { if(_sharedObject == null) { FlxG.log("FLIXEL: You must call FlxSave.bind()\nbefore you can read or write data."); return false; } return true; } } } ================================================ FILE: org/flixel/FlxSound.as ================================================ package org.flixel { import flash.events.Event; import flash.media.Sound; import flash.media.SoundChannel; import flash.media.SoundTransform; import flash.net.URLRequest; /** * This is the universal flixel sound object, used for streaming, music, and sound effects. * * @author Adam Atomic */ public class FlxSound extends FlxBasic { /** * The X position of this sound in world coordinates. * Only really matters if you are doing proximity/panning stuff. */ public var x:Number; /** * The Y position of this sound in world coordinates. * Only really matters if you are doing proximity/panning stuff. */ public var y:Number; /** * Whether or not this sound should be automatically destroyed when you switch states. */ public var survive:Boolean; /** * The ID3 song name. Defaults to null. Currently only works for streamed sounds. */ public var name:String; /** * The ID3 artist name. Defaults to null. Currently only works for streamed sounds. */ public var artist:String; /** * Stores the average wave amplitude of both stereo channels */ public var amplitude:Number; /** * Just the amplitude of the left stereo channel */ public var amplitudeLeft:Number; /** * Just the amplitude of the left stereo channel */ public var amplitudeRight:Number; /** * Whether to call destroy() when the sound has finished. */ public var autoDestroy:Boolean; /** * Internal tracker for a Flash sound object. */ protected var _sound:Sound; /** * Internal tracker for a Flash sound channel object. */ protected var _channel:SoundChannel; /** * Internal tracker for a Flash sound transform object. */ protected var _transform:SoundTransform; /** * Internal tracker for the position in runtime of the music playback. */ protected var _position:Number; /** * Internal tracker for how loud the sound is. */ protected var _volume:Number; /** * Internal tracker for total volume adjustment. */ protected var _volumeAdjust:Number; /** * Internal tracker for whether the sound is looping or not. */ protected var _looped:Boolean; /** * Internal tracker for the sound's "target" (for proximity and panning). */ protected var _target:FlxObject; /** * Internal tracker for the maximum effective radius of this sound (for proximity and panning). */ protected var _radius:Number; /** * Internal tracker for whether to pan the sound left and right. Default is false. */ protected var _pan:Boolean; /** * Internal timer used to keep track of requests to fade out the sound playback. */ protected var _fadeOutTimer:Number; /** * Internal helper for fading out sounds. */ protected var _fadeOutTotal:Number; /** * Internal flag for whether to pause or stop the sound when it's done fading out. */ protected var _pauseOnFadeOut:Boolean; /** * Internal timer for fading in the sound playback. */ protected var _fadeInTimer:Number; /** * Internal helper for fading in sounds. */ protected var _fadeInTotal:Number; /** * The FlxSound constructor gets all the variables initialized, but NOT ready to play a sound yet. */ public function FlxSound() { super(); createSound(); } /** * An internal function for clearing all the variables used by sounds. */ protected function createSound():void { destroy(); x = 0; y = 0; if(_transform == null) _transform = new SoundTransform(); _transform.pan = 0; _sound = null; _position = 0; _volume = 1.0; _volumeAdjust = 1.0; _looped = false; _target = null; _radius = 0; _pan = false; _fadeOutTimer = 0; _fadeOutTotal = 0; _pauseOnFadeOut = false; _fadeInTimer = 0; _fadeInTotal = 0; exists = false; active = false; visible = false; name = null; artist = null; amplitude = 0; amplitudeLeft = 0; amplitudeRight = 0; autoDestroy = false; } /** * Clean up memory. */ override public function destroy():void { kill(); _transform = null; _sound = null; _channel = null; _target = null; name = null; artist = null; super.destroy(); } /** * Handles fade out, fade in, panning, proximity, and amplitude operations each frame. */ override public function update():void { if(_position != 0) return; var radial:Number = 0.0; var fade:Number = 1.0; //Distance-based volume control if(_target != null) { radial = FlxU.getDistance(new FlxPoint(_target.x,_target.y), new FlxPoint(x,y))/_radius; if(radial < 0) radial = 0; if(radial > 1) radial = 1; if(_pan) { var d:Number = (x - _target.x) / _radius; if(d < -1) d = -1; else if(d > 1) d = 1; _transform.pan = d; } } //Cross-fading volume control if(_fadeOutTimer > 0) { _fadeOutTimer -= FlxG.elapsed; if(_fadeOutTimer <= 0) { if(_pauseOnFadeOut) pause(); else stop(); } fade = _fadeOutTimer/_fadeOutTotal; if(fade < 0) fade = 0; } else if(_fadeInTimer > 0) { _fadeInTimer -= FlxG.elapsed; fade = _fadeInTimer/_fadeInTotal; if(fade < 0) fade = 0; fade = 1 - fade; } _volumeAdjust = (1 - radial) * fade; updateTransform(); if((_transform.volume > 0) && (_channel != null)) { amplitudeLeft = _channel.leftPeak/_transform.volume; amplitudeRight = _channel.rightPeak/_transform.volume; amplitude = (amplitudeLeft+amplitudeRight)*0.5; } } override public function kill():void { super.kill(); if(_channel != null) stop(); } /** * One of two main setup functions for sounds, this function loads a sound from an embedded MP3. * * @param EmbeddedSound An embedded Class object representing an MP3 file. * @param Looped Whether or not this sound should loop endlessly. * @param AutoDestroy Whether or not this FlxSound instance should be destroyed when the sound finishes playing. Default value is false, but FlxG.play() and FlxG.stream() will set it to true by default. * * @return This FlxSound instance (nice for chaining stuff together, if you're into that). */ public function loadEmbedded(EmbeddedSound:Class, Looped:Boolean=false, AutoDestroy:Boolean=false):FlxSound { stop(); createSound(); _sound = new EmbeddedSound(); //NOTE: can't pull ID3 info from embedded sound currently _looped = Looped; updateTransform(); exists = true; return this; } /** * One of two main setup functions for sounds, this function loads a sound from a URL. * * @param EmbeddedSound A string representing the URL of the MP3 file you want to play. * @param Looped Whether or not this sound should loop endlessly. * @param AutoDestroy Whether or not this FlxSound instance should be destroyed when the sound finishes playing. Default value is false, but FlxG.play() and FlxG.stream() will set it to true by default. * * @return This FlxSound instance (nice for chaining stuff together, if you're into that). */ public function loadStream(SoundURL:String, Looped:Boolean=false, AutoDestroy:Boolean=false):FlxSound { stop(); createSound(); _sound = new Sound(); _sound.addEventListener(Event.ID3, gotID3); _sound.load(new URLRequest(SoundURL)); _looped = Looped; updateTransform(); exists = true; return this; } /** * Call this function if you want this sound's volume to change * based on distance from a particular FlxCore object. * * @param X The X position of the sound. * @param Y The Y position of the sound. * @param Object The object you want to track. * @param Radius The maximum distance this sound can travel. * @param Pan Whether the sound should pan in addition to the volume changes (default: true). * * @return This FlxSound instance (nice for chaining stuff together, if you're into that). */ public function proximity(X:Number,Y:Number,Object:FlxObject,Radius:Number,Pan:Boolean=true):FlxSound { x = X; y = Y; _target = Object; _radius = Radius; _pan = Pan; return this; } /** * Call this function to play the sound - also works on paused sounds. * * @param ForceRestart Whether to start the sound over or not. Default value is false, meaning if the sound is already playing or was paused when you call play(), it will continue playing from its current position, NOT start again from the beginning. */ public function play(ForceRestart:Boolean=false):void { if(_position < 0) return; if(ForceRestart) { var oldAutoDestroy:Boolean = autoDestroy; autoDestroy = false; stop(); autoDestroy = oldAutoDestroy; } if(_looped) { if(_position == 0) { if(_channel == null) _channel = _sound.play(0,9999,_transform); if(_channel == null) exists = false; } else { _channel = _sound.play(_position,0,_transform); if(_channel == null) exists = false; else _channel.addEventListener(Event.SOUND_COMPLETE, looped); } } else { if(_position == 0) { if(_channel == null) { _channel = _sound.play(0,0,_transform); if(_channel == null) exists = false; else _channel.addEventListener(Event.SOUND_COMPLETE, stopped); } } else { _channel = _sound.play(_position,0,_transform); if(_channel == null) exists = false; } } active = (_channel != null); _position = 0; } /** * Unpause a sound. Only works on sounds that have been paused. */ public function resume():void { if(_position <= 0) return; if(_looped) { _channel = _sound.play(_position,0,_transform); if(_channel == null) exists = false; else _channel.addEventListener(Event.SOUND_COMPLETE, looped); } else { _channel = _sound.play(_position,0,_transform); if(_channel == null) exists = false; } active = (_channel != null); } /** * Call this function to pause this sound. */ public function pause():void { if(_channel == null) { _position = -1; return; } _position = _channel.position; _channel.stop(); if(_looped) { while(_position >= _sound.length) _position -= _sound.length; } if(_position <= 0) _position = 1; _channel = null; active = false; } /** * Call this function to stop this sound. */ public function stop():void { _position = 0; if(_channel != null) { _channel.stop(); stopped(); } } /** * Call this function to make this sound fade out over a certain time interval. * * @param Seconds The amount of time the fade out operation should take. * @param PauseInstead Tells the sound to pause on fadeout, instead of stopping. */ public function fadeOut(Seconds:Number,PauseInstead:Boolean=false):void { _pauseOnFadeOut = PauseInstead; _fadeInTimer = 0; _fadeOutTimer = Seconds; _fadeOutTotal = _fadeOutTimer; } /** * Call this function to make a sound fade in over a certain * time interval (calls play() automatically). * * @param Seconds The amount of time the fade-in operation should take. */ public function fadeIn(Seconds:Number):void { _fadeOutTimer = 0; _fadeInTimer = Seconds; _fadeInTotal = _fadeInTimer; play(); } /** * Set volume to a value between 0 and 1 to change how this sound is. */ public function get volume():Number { return _volume; } /** * @private */ public function set volume(Volume:Number):void { _volume = Volume; if(_volume < 0) _volume = 0; else if(_volume > 1) _volume = 1; updateTransform(); } /** * Returns the currently selected "real" volume of the sound (takes fades and proximity into account). * * @return The adjusted volume of the sound. */ public function getActualVolume():Number { return _volume*_volumeAdjust; } /** * Call after adjusting the volume to update the sound channel's settings. */ internal function updateTransform():void { _transform.volume = (FlxG.mute?0:1)*FlxG.volume*_volume*_volumeAdjust; if(_channel != null) _channel.soundTransform = _transform; } /** * An internal helper function used to help Flash resume playing a looped sound. * * @param event An Event object. */ protected function looped(event:Event=null):void { if (_channel == null) return; _channel.removeEventListener(Event.SOUND_COMPLETE,looped); _channel = null; play(); } /** * An internal helper function used to help Flash clean up and re-use finished sounds. * * @param event An Event object. */ protected function stopped(event:Event=null):void { if(!_looped) _channel.removeEventListener(Event.SOUND_COMPLETE,stopped); else _channel.removeEventListener(Event.SOUND_COMPLETE,looped); _channel = null; active = false; if(autoDestroy) destroy(); } /** * Internal event handler for ID3 info (i.e. fetching the song name). * * @param event An Event object. */ protected function gotID3(event:Event=null):void { FlxG.log("got ID3 info!"); if(_sound.id3.songName.length > 0) name = _sound.id3.songName; if(_sound.id3.artist.length > 0) artist = _sound.id3.artist; _sound.removeEventListener(Event.ID3, gotID3); } } } ================================================ FILE: org/flixel/FlxSprite.as ================================================ package org.flixel { import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.Graphics; import flash.geom.ColorTransform; import flash.geom.Matrix; import flash.geom.Point; import flash.geom.Rectangle; import org.flixel.system.FlxAnim; /** * The main "game object" class, the sprite is a FlxObject * with a bunch of graphics options and abilities, like animation and stamping. * * @author Adam Atomic */ public class FlxSprite extends FlxObject { [Embed(source="data/default.png")] protected var ImgDefault:Class; /** * WARNING: The origin of the sprite will default to its center. * If you change this, the visuals and the collisions will likely be * pretty out-of-sync if you do any rotation. */ public var origin:FlxPoint; /** * If you changed the size of your sprite object after loading or making the graphic, * you might need to offset the graphic away from the bound box to center it the way you want. */ public var offset:FlxPoint; /** * Change the size of your sprite's graphic. * NOTE: Scale doesn't currently affect collisions automatically, * you will need to adjust the width, height and offset manually. * WARNING: scaling sprites decreases rendering performance for this sprite by a factor of 10x! */ public var scale:FlxPoint; /** * Blending modes, just like Photoshop or whatever. * E.g. "multiply", "screen", etc. * @default null */ public var blend:String; /** * Controls whether the object is smoothed when rotated, affects performance. * @default false */ public var antialiasing:Boolean; /** * Whether the current animation has finished its first (or only) loop. */ public var finished:Boolean; /** * The width of the actual graphic or image being displayed (not necessarily the game object/bounding box). * NOTE: Edit at your own risk!! This is intended to be read-only. */ public var frameWidth:uint; /** * The height of the actual graphic or image being displayed (not necessarily the game object/bounding box). * NOTE: Edit at your own risk!! This is intended to be read-only. */ public var frameHeight:uint; /** * The total number of frames in this image. WARNING: assumes each row in the sprite sheet is full! */ public var frames:uint; /** * The actual Flash BitmapData object representing the current display state of the sprite. */ public var framePixels:BitmapData; /** * Set this flag to true to force the sprite to update during the draw() call. * NOTE: Rarely if ever necessary, most sprite operations will flip this flag automatically. */ public var dirty:Boolean; /** * Internal, stores all the animations that were added to this sprite. */ protected var _animations:Array; /** * Internal, keeps track of whether the sprite was loaded with support for automatic reverse/mirroring. */ protected var _flipped:uint; /** * Internal, keeps track of the current animation being played. */ protected var _curAnim:FlxAnim; /** * Internal, keeps track of the current frame of animation. * This is NOT an index into the tile sheet, but the frame number in the animation object. */ protected var _curFrame:uint; /** * Internal, keeps track of the current index into the tile sheet based on animation or rotation. */ protected var _curIndex:uint; /** * Internal, used to time each frame of animation. */ protected var _frameTimer:Number; /** * Internal tracker for the animation callback. Default is null. * If assigned, will be called each time the current frame changes. * A function that has 3 parameters: a string name, a uint frame number, and a uint frame index. */ protected var _callback:Function; /** * Internal tracker for what direction the sprite is currently facing, used with Flash getter/setter. */ protected var _facing:uint; /** * Internal tracker for opacity, used with Flash getter/setter. */ protected var _alpha:Number; /** * Internal tracker for color tint, used with Flash getter/setter. */ protected var _color:uint; /** * Internal tracker for how many frames of "baked" rotation there are (if any). */ protected var _bakedRotation:Number; /** * Internal, stores the entire source graphic (not the current displayed animation frame), used with Flash getter/setter. */ protected var _pixels:BitmapData; /** * Internal, reused frequently during drawing and animating. */ protected var _flashPoint:Point; /** * Internal, reused frequently during drawing and animating. */ protected var _flashRect:Rectangle; /** * Internal, reused frequently during drawing and animating. */ protected var _flashRect2:Rectangle; /** * Internal, reused frequently during drawing and animating. Always contains (0,0). */ protected var _flashPointZero:Point; /** * Internal, helps with animation, caching and drawing. */ protected var _colorTransform:ColorTransform; /** * Internal, helps with animation, caching and drawing. */ protected var _matrix:Matrix; /** * Creates a white 8x8 square FlxSprite at the specified position. * Optionally can load a simple, one-frame graphic instead. * * @param X The initial X position of the sprite. * @param Y The initial Y position of the sprite. * @param SimpleGraphic The graphic you want to display (OPTIONAL - for simple stuff only, do NOT use for animated images!). */ public function FlxSprite(X:Number=0,Y:Number=0,SimpleGraphic:Class=null) { super(X,Y); _flashPoint = new Point(); _flashRect = new Rectangle(); _flashRect2 = new Rectangle(); _flashPointZero = new Point(); offset = new FlxPoint(); origin = new FlxPoint(); scale = new FlxPoint(1.0,1.0); _alpha = 1; _color = 0x00ffffff; blend = null; antialiasing = false; cameras = null; finished = false; _facing = RIGHT; _animations = new Array(); _flipped = 0; _curAnim = null; _curFrame = 0; _curIndex = 0; _frameTimer = 0; _matrix = new Matrix(); _callback = null; if(SimpleGraphic == null) SimpleGraphic = ImgDefault; loadGraphic(SimpleGraphic); } /** * Clean up memory. */ override public function destroy():void { if(_animations != null) { var a:FlxAnim; var i:uint = 0; var l:uint = _animations.length; while(i < l) { a = _animations[i++]; if(a != null) a.destroy(); } _animations = null; } _flashPoint = null; _flashRect = null; _flashRect2 = null; _flashPointZero = null; offset = null; origin = null; scale = null; _curAnim = null; _matrix = null; _callback = null; framePixels = null; } /** * Load an image from an embedded graphic file. * * @param Graphic The image you want to use. * @param Animated Whether the Graphic parameter is a single sprite or a row of sprites. * @param Reverse Whether you need this class to generate horizontally flipped versions of the animation frames. * @param Width Optional, specify the width of your sprite (helps FlxSprite figure out what to do with non-square sprites or sprite sheets). * @param Height Optional, specify the height of your sprite (helps FlxSprite figure out what to do with non-square sprites or sprite sheets). * @param Unique Optional, whether the graphic should be a unique instance in the graphics cache. Default is false. * * @return This FlxSprite instance (nice for chaining stuff together, if you're into that). */ public function loadGraphic(Graphic:Class,Animated:Boolean=false,Reverse:Boolean=false,Width:uint=0,Height:uint=0,Unique:Boolean=false):FlxSprite { _bakedRotation = 0; _pixels = FlxG.addBitmap(Graphic,Reverse,Unique); if(Reverse) _flipped = _pixels.width>>1; else _flipped = 0; if(Width == 0) { if(Animated) Width = _pixels.height; else if(_flipped > 0) Width = _pixels.width*0.5; else Width = _pixels.width; } width = frameWidth = Width; if(Height == 0) { if(Animated) Height = width; else Height = _pixels.height; } height = frameHeight = Height; resetHelpers(); return this; } /** * Create a pre-rotated sprite sheet from a simple sprite. * This can make a huge difference in graphical performance! * * @param Graphic The image you want to rotate and stamp. * @param Rotations The number of rotation frames the final sprite should have. For small sprites this can be quite a large number (360 even) without any problems. * @param Frame If the Graphic has a single row of square animation frames on it, you can specify which of the frames you want to use here. Default is -1, or "use whole graphic." * @param AntiAliasing Whether to use high quality rotations when creating the graphic. Default is false. * @param AutoBuffer Whether to automatically increase the image size to accomodate rotated corners. Default is false. Will create frames that are 150% larger on each axis than the original frame or graphic. * * @return This FlxSprite instance (nice for chaining stuff together, if you're into that). */ public function loadRotatedGraphic(Graphic:Class, Rotations:uint=16, Frame:int=-1, AntiAliasing:Boolean=false, AutoBuffer:Boolean=false):FlxSprite { //Create the brush and canvas var rows:uint = Math.sqrt(Rotations); var brush:BitmapData = FlxG.addBitmap(Graphic); if(Frame >= 0) { //Using just a segment of the graphic - find the right bit here var full:BitmapData = brush; brush = new BitmapData(full.height,full.height); var rx:uint = Frame*brush.width; var ry:uint = 0; var fw:uint = full.width; if(rx >= fw) { ry = uint(rx/fw)*brush.height; rx %= fw; } _flashRect.x = rx; _flashRect.y = ry; _flashRect.width = brush.width; _flashRect.height = brush.height; brush.copyPixels(full,_flashRect,_flashPointZero); } var max:uint = brush.width; if(brush.height > max) max = brush.height; if(AutoBuffer) max *= 1.5; var columns:uint = FlxU.ceil(Rotations/rows); width = max*columns; height = max*rows; var key:String = String(Graphic) + ":" + Frame + ":" + width + "x" + height; var skipGen:Boolean = FlxG.checkBitmapCache(key); _pixels = FlxG.createBitmap(width, height, 0, true, key); width = frameWidth = _pixels.width; height = frameHeight = _pixels.height; _bakedRotation = 360/Rotations; //Generate a new sheet if necessary, then fix up the width and height if(!skipGen) { var row:uint = 0; var column:uint; var bakedAngle:Number = 0; var halfBrushWidth:uint = brush.width*0.5; var halfBrushHeight:uint = brush.height*0.5; var midpointX:uint = max*0.5; var midpointY:uint = max*0.5; while(row < rows) { column = 0; while(column < columns) { _matrix.identity(); _matrix.translate(-halfBrushWidth,-halfBrushHeight); _matrix.rotate(bakedAngle*0.017453293); _matrix.translate(max*column+midpointX, midpointY); bakedAngle += _bakedRotation; _pixels.draw(brush,_matrix,null,null,null,AntiAliasing); column++; } midpointY += max; row++; } } frameWidth = frameHeight = width = height = max; resetHelpers(); if(AutoBuffer) { width = brush.width; height = brush.height; centerOffsets(); } return this; } /** * This function creates a flat colored square image dynamically. * * @param Width The width of the sprite you want to generate. * @param Height The height of the sprite you want to generate. * @param Color Specifies the color of the generated block. * @param Unique Whether the graphic should be a unique instance in the graphics cache. Default is false. * @param Key Optional parameter - specify a string key to identify this graphic in the cache. Trumps Unique flag. * * @return This FlxSprite instance (nice for chaining stuff together, if you're into that). */ public function makeGraphic(Width:uint,Height:uint,Color:uint=0xffffffff,Unique:Boolean=false,Key:String=null):FlxSprite { _bakedRotation = 0; _pixels = FlxG.createBitmap(Width,Height,Color,Unique,Key); width = frameWidth = _pixels.width; height = frameHeight = _pixels.height; resetHelpers(); return this; } /** * Resets some important variables for sprite optimization and rendering. */ protected function resetHelpers():void { _flashRect.x = 0; _flashRect.y = 0; _flashRect.width = frameWidth; _flashRect.height = frameHeight; _flashRect2.x = 0; _flashRect2.y = 0; _flashRect2.width = _pixels.width; _flashRect2.height = _pixels.height; if((framePixels == null) || (framePixels.width != width) || (framePixels.height != height)) framePixels = new BitmapData(width,height); origin.make(frameWidth*0.5,frameHeight*0.5); framePixels.copyPixels(_pixels,_flashRect,_flashPointZero); frames = (_flashRect2.width / _flashRect.width) * (_flashRect2.height / _flashRect.height); if(_colorTransform != null) framePixels.colorTransform(_flashRect,_colorTransform); _curIndex = 0; } /** * Automatically called after update() by the game loop, * this function just calls updateAnimation(). */ override public function postUpdate():void { super.postUpdate(); updateAnimation(); } /** * Called by game loop, updates then blits or renders current frame of animation to the screen */ override public function draw():void { if(_flickerTimer != 0) { _flicker = !_flicker; if(_flicker) return; } if(dirty) //rarely calcFrame(); if(cameras == null) cameras = FlxG.cameras; var camera:FlxCamera; var i:uint = 0; var l:uint = cameras.length; while(i < l) { camera = cameras[i++]; if(!onScreen(camera)) continue; _point.x = x - int(camera.scroll.x*scrollFactor.x) - offset.x; _point.y = y - int(camera.scroll.y*scrollFactor.y) - offset.y; _point.x += (_point.x > 0)?0.0000001:-0.0000001; _point.y += (_point.y > 0)?0.0000001:-0.0000001; if(((angle == 0) || (_bakedRotation > 0)) && (scale.x == 1) && (scale.y == 1) && (blend == null)) { //Simple render _flashPoint.x = _point.x; _flashPoint.y = _point.y; camera.buffer.copyPixels(framePixels,_flashRect,_flashPoint,null,null,true); } else { //Advanced render _matrix.identity(); _matrix.translate(-origin.x,-origin.y); _matrix.scale(scale.x,scale.y); if((angle != 0) && (_bakedRotation <= 0)) _matrix.rotate(angle * 0.017453293); _matrix.translate(_point.x+origin.x,_point.y+origin.y); camera.buffer.draw(framePixels,_matrix,null,blend,null,antialiasing); } _VISIBLECOUNT++; if(FlxG.visualDebug && !ignoreDrawDebug) drawDebug(camera); } } /** * This function draws or stamps one FlxSprite onto another. * This function is NOT intended to replace draw()! * * @param Brush The image you want to use as a brush or stamp or pen or whatever. * @param X The X coordinate of the brush's top left corner on this sprite. * @param Y They Y coordinate of the brush's top left corner on this sprite. */ public function stamp(Brush:FlxSprite,X:int=0,Y:int=0):void { Brush.drawFrame(); var bitmapData:BitmapData = Brush.framePixels; //Simple draw if(((Brush.angle == 0) || (Brush._bakedRotation > 0)) && (Brush.scale.x == 1) && (Brush.scale.y == 1) && (Brush.blend == null)) { _flashPoint.x = X; _flashPoint.y = Y; _flashRect2.width = bitmapData.width; _flashRect2.height = bitmapData.height; _pixels.copyPixels(bitmapData,_flashRect2,_flashPoint,null,null,true); _flashRect2.width = _pixels.width; _flashRect2.height = _pixels.height; calcFrame(); return; } //Advanced draw _matrix.identity(); _matrix.translate(-Brush.origin.x,-Brush.origin.y); _matrix.scale(Brush.scale.x,Brush.scale.y); if(Brush.angle != 0) _matrix.rotate(Brush.angle * 0.017453293); _matrix.translate(X+Brush.origin.x,Y+Brush.origin.y); _pixels.draw(bitmapData,_matrix,null,Brush.blend,null,Brush.antialiasing); calcFrame(); } /** * This function draws a line on this sprite from position X1,Y1 * to position X2,Y2 with the specified color. * * @param StartX X coordinate of the line's start point. * @param StartY Y coordinate of the line's start point. * @param EndX X coordinate of the line's end point. * @param EndY Y coordinate of the line's end point. * @param Color The line's color. * @param Thickness How thick the line is in pixels (default value is 1). */ public function drawLine(StartX:Number,StartY:Number,EndX:Number,EndY:Number,Color:uint,Thickness:uint=1):void { //Draw line var gfx:Graphics = FlxG.flashGfx; gfx.clear(); gfx.moveTo(StartX,StartY); var alphaComponent:Number = Number((Color >> 24) & 0xFF) / 255; if(alphaComponent <= 0) alphaComponent = 1; gfx.lineStyle(Thickness,Color,alphaComponent); gfx.lineTo(EndX,EndY); //Cache line to bitmap _pixels.draw(FlxG.flashGfxSprite); dirty = true; } /** * Fills this sprite's graphic with a specific color. * * @param Color The color with which to fill the graphic, format 0xAARRGGBB. */ public function fill(Color:uint):void { _pixels.fillRect(_flashRect2,Color); if(_pixels != framePixels) dirty = true; } /** * Internal function for updating the sprite's animation. * Useful for cases when you need to update this but are buried down in too many supers. * This function is called automatically by FlxSprite.postUpdate(). */ protected function updateAnimation():void { if(_bakedRotation > 0) { var oldIndex:uint = _curIndex; var angleHelper:int = angle%360; if(angleHelper < 0) angleHelper += 360; _curIndex = angleHelper/_bakedRotation + 0.5; if(oldIndex != _curIndex) dirty = true; } else if((_curAnim != null) && (_curAnim.delay > 0) && (_curAnim.looped || !finished)) { _frameTimer += FlxG.elapsed; while(_frameTimer > _curAnim.delay) { _frameTimer = _frameTimer - _curAnim.delay; if(_curFrame == _curAnim.frames.length-1) { if(_curAnim.looped) _curFrame = 0; finished = true; } else _curFrame++; _curIndex = _curAnim.frames[_curFrame]; dirty = true; } } if(dirty) calcFrame(); } /** * Request (or force) that the sprite update the frame before rendering. * Useful if you are doing procedural generation or other weirdness! * * @param Force Force the frame to redraw, even if its not flagged as necessary. */ public function drawFrame(Force:Boolean=false):void { if(Force || dirty) calcFrame(); } /** * Adds a new animation to the sprite. * * @param Name What this animation should be called (e.g. "run"). * @param Frames An array of numbers indicating what frames to play in what order (e.g. 1, 2, 3). * @param FrameRate The speed in frames per second that the animation should play at (e.g. 40 fps). * @param Looped Whether or not the animation is looped or just plays once. */ public function addAnimation(Name:String, Frames:Array, FrameRate:Number=0, Looped:Boolean=true):void { _animations.push(new FlxAnim(Name,Frames,FrameRate,Looped)); } /** * Pass in a function to be called whenever this sprite's animation changes. * * @param AnimationCallback A function that has 3 parameters: a string name, a uint frame number, and a uint frame index. */ public function addAnimationCallback(AnimationCallback:Function):void { _callback = AnimationCallback; } /** * Plays an existing animation (e.g. "run"). * If you call an animation that is already playing it will be ignored. * * @param AnimName The string name of the animation you want to play. * @param Force Whether to force the animation to restart. */ public function play(AnimName:String,Force:Boolean=false):void { if(!Force && (_curAnim != null) && (AnimName == _curAnim.name) && (!_curAnim.looped || !finished)) return; _curFrame = 0; _curIndex = 0; _frameTimer = 0; var i:uint = 0; var l:uint = _animations.length; while(i < l) { if(_animations[i].name == AnimName) { _curAnim = _animations[i]; if(_curAnim.delay <= 0) finished = true; else finished = false; _curIndex = _curAnim.frames[_curFrame]; dirty = true; return; } i++; } FlxG.log("WARNING: No animation called \""+AnimName+"\""); } /** * Tell the sprite to change to a random frame of animation * Useful for instantiating particles or other weird things. */ public function randomFrame():void { _curAnim = null; _curIndex = int(FlxG.random()*(_pixels.width/frameWidth)); dirty = true; } /** * Helper function that just sets origin to (0,0) */ public function setOriginToCorner():void { origin.x = origin.y = 0; } /** * Helper function that adjusts the offset automatically to center the bounding box within the graphic. * * @param AdjustPosition Adjusts the actual X and Y position just once to match the offset change. Default is false. */ public function centerOffsets(AdjustPosition:Boolean=false):void { offset.x = (frameWidth-width)*0.5; offset.y = (frameHeight-height)*0.5; if(AdjustPosition) { x += offset.x; y += offset.y; } } public function replaceColor(Color:uint,NewColor:uint,FetchPositions:Boolean=false):Array { var positions:Array = null; if(FetchPositions) positions = new Array(); var row:uint = 0; var column:uint; var rows:uint = _pixels.height; var columns:uint = _pixels.width; while(row < rows) { column = 0; while(column < columns) { if(_pixels.getPixel32(column,row) == Color) { _pixels.setPixel32(column,row,NewColor); if(FetchPositions) positions.push(new FlxPoint(column,row)); dirty = true; } column++; } row++; } return positions; } /** * Set pixels to any BitmapData object. * Automatically adjust graphic size and render helpers. */ public function get pixels():BitmapData { return _pixels; } /** * @private */ public function set pixels(Pixels:BitmapData):void { _pixels = Pixels; width = frameWidth = _pixels.width; height = frameHeight = _pixels.height; resetHelpers(); } /** * Set facing using FlxSprite.LEFT,RIGHT, * UP, and DOWN to take advantage of * flipped sprites and/or just track player orientation more easily. */ public function get facing():uint { return _facing; } /** * @private */ public function set facing(Direction:uint):void { if(_facing != Direction) dirty = true; _facing = Direction; } /** * Set alpha to a number between 0 and 1 to change the opacity of the sprite. */ public function get alpha():Number { return _alpha; } /** * @private */ public function set alpha(Alpha:Number):void { if(Alpha > 1) Alpha = 1; if(Alpha < 0) Alpha = 0; if(Alpha == _alpha) return; _alpha = Alpha; if((_alpha != 1) || (_color != 0x00ffffff)) _colorTransform = new ColorTransform((_color>>16)*0.00392,(_color>>8&0xff)*0.00392,(_color&0xff)*0.00392,_alpha); else _colorTransform = null; dirty = true; } /** * Set color to a number in this format: 0xRRGGBB. * color IGNORES ALPHA. To change the opacity use alpha. * Tints the whole sprite to be this color (similar to OpenGL vertex colors). */ public function get color():uint { return _color; } /** * @private */ public function set color(Color:uint):void { Color &= 0x00ffffff; if(_color == Color) return; _color = Color; if((_alpha != 1) || (_color != 0x00ffffff)) _colorTransform = new ColorTransform((_color>>16)*0.00392,(_color>>8&0xff)*0.00392,(_color&0xff)*0.00392,_alpha); else _colorTransform = null; dirty = true; } /** * Tell the sprite to change to a specific frame of animation. * * @param Frame The frame you want to display. */ public function get frame():uint { return _curIndex; } /** * @private */ public function set frame(Frame:uint):void { _curAnim = null; _curIndex = Frame; dirty = true; } /** * Check and see if this object is currently on screen. * Differs from FlxObject's implementation * in that it takes the actual graphic into account, * not just the hitbox or bounding box or whatever. * * @param Camera Specify which game camera you want. If null getScreenXY() will just grab the first global camera. * * @return Whether the object is on screen or not. */ override public function onScreen(Camera:FlxCamera=null):Boolean { if(Camera == null) Camera = FlxG.camera; getScreenXY(_point,Camera); _point.x = _point.x - offset.x; _point.y = _point.y - offset.y; if(((angle == 0) || (_bakedRotation > 0)) && (scale.x == 1) && (scale.y == 1)) return ((_point.x + frameWidth > 0) && (_point.x < Camera.width) && (_point.y + frameHeight > 0) && (_point.y < Camera.height)); var halfWidth:Number = frameWidth/2; var halfHeight:Number = frameHeight/2; var absScaleX:Number = (scale.x>0)?scale.x:-scale.x; var absScaleY:Number = (scale.y>0)?scale.y:-scale.y; var radius:Number = Math.sqrt(halfWidth*halfWidth+halfHeight*halfHeight)*((absScaleX >= absScaleY)?absScaleX:absScaleY); _point.x += halfWidth; _point.y += halfHeight; return ((_point.x + radius > 0) && (_point.x - radius < Camera.width) && (_point.y + radius > 0) && (_point.y - radius < Camera.height)); } /** * Checks to see if a point in 2D world space overlaps this FlxSprite object's current displayed pixels. * This check is ALWAYS made in screen space, and always takes scroll factors into account. * * @param Point The point in world space you want to check. * @param Mask Used in the pixel hit test to determine what counts as solid. * @param Camera Specify which game camera you want. If null getScreenXY() will just grab the first global camera. * * @return Whether or not the point overlaps this object. */ public function pixelsOverlapPoint(Point:FlxPoint,Mask:uint=0xFF,Camera:FlxCamera=null):Boolean { if(Camera == null) Camera = FlxG.camera; getScreenXY(_point,Camera); _point.x = _point.x - offset.x; _point.y = _point.y - offset.y; _flashPoint.x = (Point.x - Camera.scroll.x) - _point.x; _flashPoint.y = (Point.y - Camera.scroll.y) - _point.y; return framePixels.hitTest(_flashPointZero,Mask,_flashPoint); } /** * Internal function to update the current animation frame. */ protected function calcFrame():void { var indexX:uint = _curIndex*frameWidth; var indexY:uint = 0; //Handle sprite sheets var widthHelper:uint = _flipped?_flipped:_pixels.width; if(indexX >= widthHelper) { indexY = uint(indexX/widthHelper)*frameHeight; indexX %= widthHelper; } //handle reversed sprites if(_flipped && (_facing == LEFT)) indexX = (_flipped<<1)-indexX-frameWidth; //Update display bitmap _flashRect.x = indexX; _flashRect.y = indexY; framePixels.copyPixels(_pixels,_flashRect,_flashPointZero); _flashRect.x = _flashRect.y = 0; if(_colorTransform != null) framePixels.colorTransform(_flashRect,_colorTransform); if(_callback != null) _callback(((_curAnim != null)?(_curAnim.name):null),_curFrame,_curIndex); dirty = false; } } } ================================================ FILE: org/flixel/FlxState.as ================================================ package org.flixel { import org.flixel.system.FlxQuadTree; /** * This is the basic game "state" object - e.g. in a simple game * you might have a menu state and a play state. * It is for all intents and purpose a fancy FlxGroup. * And really, it's not even that fancy. * * @author Adam Atomic */ public class FlxState extends FlxGroup { /** * This function is called after the game engine successfully switches states. * Override this function, NOT the constructor, to initialize or set up your game state. * We do NOT recommend overriding the constructor, unless you want some crazy unpredictable things to happen! */ public function create():void { } } } ================================================ FILE: org/flixel/FlxText.as ================================================ package org.flixel { import flash.display.BitmapData; import flash.text.TextField; import flash.text.TextFormat; /** * Extends FlxSprite to support rendering text. * Can tint, fade, rotate and scale just like a sprite. * Doesn't really animate though, as far as I know. * Also does nice pixel-perfect centering on pixel fonts * as long as they are only one liners. * * @author Adam Atomic */ public class FlxText extends FlxSprite { /** * Internal reference to a Flash TextField object. */ protected var _textField:TextField; /** * Whether the actual text field needs to be regenerated and stamped again. * This is NOT the same thing as FlxSprite.dirty. */ protected var _regen:Boolean; /** * Internal tracker for the text shadow color, default is clear/transparent. */ protected var _shadow:uint; /** * Creates a new FlxText object at the specified position. * * @param X The X position of the text. * @param Y The Y position of the text. * @param Width The width of the text object (height is determined automatically). * @param Text The actual text you would like to display initially. * @param EmbeddedFont Whether this text field uses embedded fonts or nto */ public function FlxText(X:Number, Y:Number, Width:uint, Text:String=null, EmbeddedFont:Boolean=true) { super(X,Y); makeGraphic(Width,1,0); if(Text == null) Text = ""; _textField = new TextField(); _textField.width = Width; _textField.embedFonts = EmbeddedFont; _textField.selectable = false; _textField.sharpness = 100; _textField.multiline = true; _textField.wordWrap = true; _textField.text = Text; var format:TextFormat = new TextFormat("system",8,0xffffff); _textField.defaultTextFormat = format; _textField.setTextFormat(format); if(Text.length <= 0) _textField.height = 1; else _textField.height = 10; _regen = true; _shadow = 0; allowCollisions = NONE; calcFrame(); } /** * Clean up memory. */ override public function destroy():void { _textField = null; super.destroy(); } /** * You can use this if you have a lot of text parameters * to set instead of the individual properties. * * @param Font The name of the font face for the text display. * @param Size The size of the font (in pixels essentially). * @param Color The color of the text in traditional flash 0xRRGGBB format. * @param Alignment A string representing the desired alignment ("left,"right" or "center"). * @param ShadowColor A uint representing the desired text shadow color in flash 0xRRGGBB format. * * @return This FlxText instance (nice for chaining stuff together, if you're into that). */ public function setFormat(Font:String=null,Size:Number=8,Color:uint=0xffffff,Alignment:String=null,ShadowColor:uint=0):FlxText { if(Font == null) Font = ""; var format:TextFormat = dtfCopy(); format.font = Font; format.size = Size; format.color = Color; format.align = Alignment; _textField.defaultTextFormat = format; _textField.setTextFormat(format); _shadow = ShadowColor; _regen = true; calcFrame(); return this; } /** * The text being displayed. */ public function get text():String { return _textField.text; } /** * @private */ public function set text(Text:String):void { var ot:String = _textField.text; _textField.text = Text; if(_textField.text != ot) { _regen = true; calcFrame(); } } /** * The size of the text being displayed. */ public function get size():Number { return _textField.defaultTextFormat.size as Number; } /** * @private */ public function set size(Size:Number):void { var format:TextFormat = dtfCopy(); format.size = Size; _textField.defaultTextFormat = format; _textField.setTextFormat(format); _regen = true; calcFrame(); } /** * The color of the text being displayed. */ override public function get color():uint { return _textField.defaultTextFormat.color as uint; } /** * @private */ override public function set color(Color:uint):void { var format:TextFormat = dtfCopy(); format.color = Color; _textField.defaultTextFormat = format; _textField.setTextFormat(format); _regen = true; calcFrame(); } /** * The font used for this text. */ public function get font():String { return _textField.defaultTextFormat.font; } /** * @private */ public function set font(Font:String):void { var format:TextFormat = dtfCopy(); format.font = Font; _textField.defaultTextFormat = format; _textField.setTextFormat(format); _regen = true; calcFrame(); } /** * The alignment of the font ("left", "right", or "center"). */ public function get alignment():String { return _textField.defaultTextFormat.align; } /** * @private */ public function set alignment(Alignment:String):void { var format:TextFormat = dtfCopy(); format.align = Alignment; _textField.defaultTextFormat = format; _textField.setTextFormat(format); calcFrame(); } /** * The color of the text shadow in 0xAARRGGBB hex format. */ public function get shadow():uint { return _shadow; } /** * @private */ public function set shadow(Color:uint):void { _shadow = Color; calcFrame(); } /** * Internal function to update the current animation frame. */ override protected function calcFrame():void { if(_regen) { //Need to generate a new buffer to store the text graphic var i:uint = 0; var nl:uint = _textField.numLines; height = 0; while(i < nl) height += _textField.getLineMetrics(i++).height; height += 4; //account for 2px gutter on top and bottom _pixels = new BitmapData(width,height,true,0); frameHeight = height; _textField.height = height*1.2; _flashRect.x = 0; _flashRect.y = 0; _flashRect.width = width; _flashRect.height = height; _regen = false; } else //Else just clear the old buffer before redrawing the text _pixels.fillRect(_flashRect,0); if((_textField != null) && (_textField.text != null) && (_textField.text.length > 0)) { //Now that we've cleared a buffer, we need to actually render the text to it var format:TextFormat = _textField.defaultTextFormat; var formatAdjusted:TextFormat = format; _matrix.identity(); //If it's a single, centered line of text, we center it ourselves so it doesn't blur to hell if((format.align == "center") && (_textField.numLines == 1)) { formatAdjusted = new TextFormat(format.font,format.size,format.color,null,null,null,null,null,"left"); _textField.setTextFormat(formatAdjusted); _matrix.translate(Math.floor((width - _textField.getLineMetrics(0).width)/2),0); } //Render a single pixel shadow beneath the text if(_shadow > 0) { _textField.setTextFormat(new TextFormat(formatAdjusted.font,formatAdjusted.size,_shadow,null,null,null,null,null,formatAdjusted.align)); _matrix.translate(1,1); _pixels.draw(_textField,_matrix,_colorTransform); _matrix.translate(-1,-1); _textField.setTextFormat(new TextFormat(formatAdjusted.font,formatAdjusted.size,formatAdjusted.color,null,null,null,null,null,formatAdjusted.align)); } //Actually draw the text onto the buffer _pixels.draw(_textField,_matrix,_colorTransform); _textField.setTextFormat(new TextFormat(format.font,format.size,format.color,null,null,null,null,null,format.align)); } //Finally, update the visible pixels if((framePixels == null) || (framePixels.width != _pixels.width) || (framePixels.height != _pixels.height)) framePixels = new BitmapData(_pixels.width,_pixels.height,true,0); framePixels.copyPixels(_pixels,_flashRect,_flashPointZero); } /** * A helper function for updating the TextField that we use for rendering. * * @return A writable copy of TextField.defaultTextFormat. */ protected function dtfCopy():TextFormat { var defaultTextFormat:TextFormat = _textField.defaultTextFormat; return new TextFormat(defaultTextFormat.font,defaultTextFormat.size,defaultTextFormat.color,defaultTextFormat.bold,defaultTextFormat.italic,defaultTextFormat.underline,defaultTextFormat.url,defaultTextFormat.target,defaultTextFormat.align); } } } ================================================ FILE: org/flixel/FlxTileblock.as ================================================ package org.flixel { import flash.display.BitmapData; import flash.geom.Rectangle; /** * This is a basic "environment object" class, used to create simple walls and floors. * It can be filled with a random selection of tiles to quickly add detail. * * @author Adam Atomic */ public class FlxTileblock extends FlxSprite { /** * Creates a new FlxBlock object with the specified position and size. * * @param X The X position of the block. * @param Y The Y position of the block. * @param Width The width of the block. * @param Height The height of the block. */ public function FlxTileblock(X:int,Y:int,Width:uint,Height:uint) { super(X,Y); makeGraphic(Width,Height,0,true); active = false; immovable = true; } /** * Fills the block with a randomly arranged selection of graphics from the image provided. * * @param TileGraphic The graphic class that contains the tiles that should fill this block. * @param TileWidth The width of a single tile in the graphic. * @param TileHeight The height of a single tile in the graphic. * @param Empties The number of "empty" tiles to add to the auto-fill algorithm (e.g. 8 tiles + 4 empties = 1/3 of block will be open holes). */ public function loadTiles(TileGraphic:Class,TileWidth:uint=0,TileHeight:uint=0,Empties:uint=0):FlxTileblock { if(TileGraphic == null) return this; //First create a tile brush var sprite:FlxSprite = new FlxSprite().loadGraphic(TileGraphic,true,false,TileWidth,TileHeight); var spriteWidth:uint = sprite.width; var spriteHeight:uint = sprite.height; var total:uint = sprite.frames + Empties; //Then prep the "canvas" as it were (just doublechecking that the size is on tile boundaries) var regen:Boolean = false; if(width % sprite.width != 0) { width = uint(width/spriteWidth+1)*spriteWidth; regen = true; } if(height % sprite.height != 0) { height = uint(height/spriteHeight+1)*spriteHeight; regen = true; } if(regen) makeGraphic(width,height,0,true); else this.fill(0); //Stamp random tiles onto the canvas var row:uint = 0; var column:uint; var destinationX:uint; var destinationY:uint = 0; var widthInTiles:uint = width/spriteWidth; var heightInTiles:uint = height/spriteHeight; while(row < heightInTiles) { destinationX = 0; column = 0; while(column < widthInTiles) { if(FlxG.random()*total > Empties) { sprite.randomFrame(); sprite.drawFrame(); stamp(sprite,destinationX,destinationY); } destinationX += spriteWidth; column++; } destinationY += spriteHeight; row++; } return this; } } } ================================================ FILE: org/flixel/FlxTilemap.as ================================================ package org.flixel { import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.Graphics; import flash.geom.Matrix; import flash.geom.Point; import flash.geom.Rectangle; import org.flixel.system.FlxTile; import org.flixel.system.FlxTilemapBuffer; /** * This is a traditional tilemap display and collision class. * It takes a string of comma-separated numbers and then associates * those values with tiles from the sheet you pass in. * It also includes some handy static parsers that can convert * arrays or images into strings that can be loaded. * * @author Adam Atomic */ public class FlxTilemap extends FlxObject { [Embed(source="data/autotiles.png")] static public var ImgAuto:Class; [Embed(source="data/autotiles_alt.png")] static public var ImgAutoAlt:Class; /** * No auto-tiling. */ static public const OFF:uint = 0; /** * Good for levels with thin walls that don'tile need interior corner art. */ static public const AUTO:uint = 1; /** * Better for levels with thick walls that look better with interior corner art. */ static public const ALT:uint = 2; /** * Set this flag to use one of the 16-tile binary auto-tile algorithms (OFF, AUTO, or ALT). */ public var auto:uint; /** * Read-only variable, do NOT recommend changing after the map is loaded! */ public var widthInTiles:uint; /** * Read-only variable, do NOT recommend changing after the map is loaded! */ public var heightInTiles:uint; /** * Read-only variable, do NOT recommend changing after the map is loaded! */ public var totalTiles:uint; /** * Rendering helper, minimize new object instantiation on repetitive methods. */ protected var _flashPoint:Point; /** * Rendering helper, minimize new object instantiation on repetitive methods. */ protected var _flashRect:Rectangle; /** * Internal reference to the bitmap data object that stores the original tile graphics. */ protected var _tiles:BitmapData; /** * Internal list of buffers, one for each camera, used for drawing the tilemaps. */ protected var _buffers:Array; /** * Internal representation of the actual tile data, as a large 1D array of integers. */ protected var _data:Array; /** * Internal representation of rectangles, one for each tile in the entire tilemap, used to speed up drawing. */ protected var _rects:Array; /** * Internal, the width of a single tile. */ protected var _tileWidth:uint; /** * Internal, the height of a single tile. */ protected var _tileHeight:uint; /** * Internal collection of tile objects, one for each type of tile in the map (NOTE one for every single tile in the whole map). */ protected var _tileObjects:Array; /** * Internal, used for rendering the debug bounding box display. */ protected var _debugTileNotSolid:BitmapData; /** * Internal, used for rendering the debug bounding box display. */ protected var _debugTilePartial:BitmapData; /** * Internal, used for rendering the debug bounding box display. */ protected var _debugTileSolid:BitmapData; /** * Internal, used for rendering the debug bounding box display. */ protected var _debugRect:Rectangle; /** * Internal flag for checking to see if we need to refresh * the tilemap display to show or hide the bounding boxes. */ protected var _lastVisualDebug:Boolean; /** * Internal, used to sort of insert blank tiles in front of the tiles in the provided graphic. */ protected var _startingIndex:uint; /** * The tilemap constructor just initializes some basic variables. */ public function FlxTilemap() { super(); auto = OFF; widthInTiles = 0; heightInTiles = 0; totalTiles = 0; _buffers = new Array(); _flashPoint = new Point(); _flashRect = null; _data = null; _tileWidth = 0; _tileHeight = 0; _rects = null; _tiles = null; _tileObjects = null; immovable = true; cameras = null; _debugTileNotSolid = null; _debugTilePartial = null; _debugTileSolid = null; _debugRect = null; _lastVisualDebug = FlxG.visualDebug; _startingIndex = 0; } /** * Clean up memory. */ override public function destroy():void { _flashPoint = null; _flashRect = null; _tiles = null; var i:uint = 0; var l:uint = _tileObjects.length; while(i < l) (_tileObjects[i++] as FlxTile).destroy(); _tileObjects = null; i = 0; l = _buffers.length; while(i < l) (_buffers[i++] as FlxTilemapBuffer).destroy(); _buffers = null; _data = null; _rects = null; _debugTileNotSolid = null; _debugTilePartial = null; _debugTileSolid = null; _debugRect = null; super.destroy(); } /** * Load the tilemap with string data and a tile graphic. * * @param MapData A string of comma and line-return delineated indices indicating what order the tiles should go in. * @param TileGraphic All the tiles you want to use, arranged in a strip corresponding to the numbers in MapData. * @param TileWidth The width of your tiles (e.g. 8) - defaults to height of the tile graphic if unspecified. * @param TileHeight The height of your tiles (e.g. 8) - defaults to width if unspecified. * @param AutoTile Whether to load the map using an automatic tile placement algorithm. Setting this to either AUTO or ALT will override any values you put for StartingIndex, DrawIndex, or CollideIndex. * @param StartingIndex Used to sort of insert empty tiles in front of the provided graphic. Default is 0, usually safest ot leave it at that. Ignored if AutoTile is set. * @param DrawIndex Initializes all tile objects equal to and after this index as visible. Default value is 1. Ignored if AutoTile is set. * @param CollideIndex Initializes all tile objects equal to and after this index as allowCollisions = ANY. Default value is 1. Ignored if AutoTile is set. Can override and customize per-tile-type collision behavior using setTileProperties(). * * @return A pointer this instance of FlxTilemap, for chaining as usual :) */ public function loadMap(MapData:String, TileGraphic:Class, TileWidth:uint=0, TileHeight:uint=0, AutoTile:uint=OFF, StartingIndex:uint=0, DrawIndex:uint=1, CollideIndex:uint=1):FlxTilemap { auto = AutoTile; _startingIndex = StartingIndex; //Figure out the map dimensions based on the data string var columns:Array; var rows:Array = MapData.split("\n"); heightInTiles = rows.length; _data = new Array(); var row:uint = 0; var column:uint; while(row < heightInTiles) { columns = rows[row++].split(","); if(columns.length <= 1) { heightInTiles = heightInTiles - 1; continue; } if(widthInTiles == 0) widthInTiles = columns.length; column = 0; while(column < widthInTiles) _data.push(uint(columns[column++])); } //Pre-process the map data if it's auto-tiled var i:uint; totalTiles = widthInTiles*heightInTiles; if(auto > OFF) { _startingIndex = 1; DrawIndex = 1; CollideIndex = 1; i = 0; while(i < totalTiles) autoTile(i++); } //Figure out the size of the tiles _tiles = FlxG.addBitmap(TileGraphic); _tileWidth = TileWidth; if(_tileWidth == 0) _tileWidth = _tiles.height; _tileHeight = TileHeight; if(_tileHeight == 0) _tileHeight = _tileWidth; //create some tile objects that we'll use for overlap checks (one for each tile) i = 0; var l:uint = (_tiles.width/_tileWidth) * (_tiles.height/_tileHeight); if(auto > OFF) l++; _tileObjects = new Array(l); var ac:uint; while(i < l) { _tileObjects[i] = new FlxTile(this,i,_tileWidth,_tileHeight,(i >= DrawIndex),(i >= CollideIndex)?allowCollisions:NONE); i++; } //create debug tiles for rendering bounding boxes on demand _debugTileNotSolid = makeDebugTile(FlxG.BLUE); _debugTilePartial = makeDebugTile(FlxG.PINK); _debugTileSolid = makeDebugTile(FlxG.GREEN); _debugRect = new Rectangle(0,0,_tileWidth,_tileHeight); //Then go through and create the actual map width = widthInTiles*_tileWidth; height = heightInTiles*_tileHeight; _rects = new Array(totalTiles); i = 0; while(i < totalTiles) updateTile(i++); return this; } /** * Internal function to clean up the map loading code. * Just generates a wireframe box the size of a tile with the specified color. */ protected function makeDebugTile(Color:uint):BitmapData { var debugTile:BitmapData debugTile = new BitmapData(_tileWidth,_tileHeight,true,0); var gfx:Graphics = FlxG.flashGfx; gfx.clear(); gfx.moveTo(0,0); gfx.lineStyle(1,Color,0.5); gfx.lineTo(_tileWidth-1,0); gfx.lineTo(_tileWidth-1,_tileHeight-1); gfx.lineTo(0,_tileHeight-1); gfx.lineTo(0,0); debugTile.draw(FlxG.flashGfxSprite); return debugTile; } /** * Main logic loop for tilemap is pretty simple, * just checks to see if visual debug got turned on. * If it did, the tilemap is flagged as dirty so it * will be redrawn with debug info on the next draw call. */ override public function update():void { if(_lastVisualDebug != FlxG.visualDebug) { _lastVisualDebug = FlxG.visualDebug; setDirty(); } } /** * Internal function that actually renders the tilemap to the tilemap buffer. Called by draw(). * * @param Buffer The FlxTilemapBuffer you are rendering to. * @param Camera The related FlxCamera, mainly for scroll values. */ protected function drawTilemap(Buffer:FlxTilemapBuffer,Camera:FlxCamera):void { Buffer.fill(); //Copy tile images into the tile buffer _point.x = int(Camera.scroll.x*scrollFactor.x) - x; //modified from getScreenXY() _point.y = int(Camera.scroll.y*scrollFactor.y) - y; var screenXInTiles:int = (_point.x + ((_point.x > 0)?0.0000001:-0.0000001))/_tileWidth; var screenYInTiles:int = (_point.y + ((_point.y > 0)?0.0000001:-0.0000001))/_tileHeight; var screenRows:uint = Buffer.rows; var screenColumns:uint = Buffer.columns; //Bound the upper left corner if(screenXInTiles < 0) screenXInTiles = 0; if(screenXInTiles > widthInTiles-screenColumns) screenXInTiles = widthInTiles-screenColumns; if(screenYInTiles < 0) screenYInTiles = 0; if(screenYInTiles > heightInTiles-screenRows) screenYInTiles = heightInTiles-screenRows; var rowIndex:int = screenYInTiles*widthInTiles+screenXInTiles; _flashPoint.y = 0; var row:uint = 0; var column:uint; var columnIndex:uint; var tile:FlxTile; var debugTile:BitmapData; while(row < screenRows) { columnIndex = rowIndex; column = 0; _flashPoint.x = 0; while(column < screenColumns) { _flashRect = _rects[columnIndex] as Rectangle; if(_flashRect != null) { Buffer.pixels.copyPixels(_tiles,_flashRect,_flashPoint,null,null,true); if(FlxG.visualDebug && !ignoreDrawDebug) { tile = _tileObjects[_data[columnIndex]]; if(tile != null) { if(tile.allowCollisions <= NONE) debugTile = _debugTileNotSolid; //blue else if(tile.allowCollisions != ANY) debugTile = _debugTilePartial; //pink else debugTile = _debugTileSolid; //green Buffer.pixels.copyPixels(debugTile,_debugRect,_flashPoint,null,null,true); } } } _flashPoint.x += _tileWidth; column++; columnIndex++; } rowIndex += widthInTiles; _flashPoint.y += _tileHeight; row++; } Buffer.x = screenXInTiles*_tileWidth; Buffer.y = screenYInTiles*_tileHeight; } /** * Draws the tilemap buffers to the cameras and handles flickering. */ override public function draw():void { if(_flickerTimer != 0) { _flicker = !_flicker; if(_flicker) return; } if(cameras == null) cameras = FlxG.cameras; var camera:FlxCamera; var buffer:FlxTilemapBuffer; var i:uint = 0; var l:uint = cameras.length; while(i < l) { camera = cameras[i]; if(_buffers[i] == null) _buffers[i] = new FlxTilemapBuffer(_tileWidth,_tileHeight,widthInTiles,heightInTiles,camera); buffer = _buffers[i++] as FlxTilemapBuffer; if(!buffer.dirty) { _point.x = x - int(camera.scroll.x*scrollFactor.x) + buffer.x; //copied from getScreenXY() _point.y = y - int(camera.scroll.y*scrollFactor.y) + buffer.y; buffer.dirty = (_point.x > 0) || (_point.y > 0) || (_point.x + buffer.width < camera.width) || (_point.y + buffer.height < camera.height); } if(buffer.dirty) { drawTilemap(buffer,camera); buffer.dirty = false; } _flashPoint.x = x - int(camera.scroll.x*scrollFactor.x) + buffer.x; //copied from getScreenXY() _flashPoint.y = y - int(camera.scroll.y*scrollFactor.y) + buffer.y; _flashPoint.x += (_flashPoint.x > 0)?0.0000001:-0.0000001; _flashPoint.y += (_flashPoint.y > 0)?0.0000001:-0.0000001; buffer.draw(camera,_flashPoint); _VISIBLECOUNT++; } } /** * Fetches the tilemap data array. * * @param Simple If true, returns the data as copy, as a series of 1s and 0s (useful for auto-tiling stuff). Default value is false, meaning it will return the actual data array (NOT a copy). * * @return An array the size of the tilemap full of integers indicating tile placement. */ public function getData(Simple:Boolean=false):Array { if(!Simple) return _data; var i:uint = 0; var l:uint = _data.length; var data:Array = new Array(l); while(i < l) { data[i] = ((_tileObjects[_data[i]] as FlxTile).allowCollisions > 0)?1:0; i++; } return data; } /** * Set the dirty flag on all the tilemap buffers. * Basically forces a reset of the drawn tilemaps, even if it wasn'tile necessary. * * @param Dirty Whether to flag the tilemap buffers as dirty or not. */ public function setDirty(Dirty:Boolean=true):void { var i:uint = 0; var l:uint = _buffers.length; while(i < l) (_buffers[i++] as FlxTilemapBuffer).dirty = Dirty; } /** * Find a path through the tilemap. Any tile with any collision flags set is treated as impassable. * If no path is discovered then a null reference is returned. * * @param Start The start point in world coordinates. * @param End The end point in world coordinates. * @param Simplify Whether to run a basic simplification algorithm over the path data, removing extra points that are on the same line. Default value is true. * @param RaySimplify Whether to run an extra raycasting simplification algorithm over the remaining path data. This can result in some close corners being cut, and should be used with care if at all (yet). Default value is false. * * @return A FlxPath from the start to the end. If no path could be found, then a null reference is returned. */ public function findPath(Start:FlxPoint,End:FlxPoint,Simplify:Boolean=true,RaySimplify:Boolean=false):FlxPath { //figure out what tile we are starting and ending on. var startIndex:uint = int((Start.y-y)/_tileHeight) * widthInTiles + int((Start.x-x)/_tileWidth); var endIndex:uint = int((End.y-y)/_tileHeight) * widthInTiles + int((End.x-x)/_tileWidth); //check that the start and end are clear. if( ((_tileObjects[_data[startIndex]] as FlxTile).allowCollisions > 0) || ((_tileObjects[_data[endIndex]] as FlxTile).allowCollisions > 0) ) return null; //figure out how far each of the tiles is from the starting tile var distances:Array = computePathDistance(startIndex,endIndex); if(distances == null) return null; //then count backward to find the shortest path. var points:Array = new Array(); walkPath(distances,endIndex,points); //reset the start and end points to be exact var node:FlxPoint; node = points[points.length-1] as FlxPoint; node.x = Start.x; node.y = Start.y; node = points[0] as FlxPoint; node.x = End.x; node.y = End.y; //some simple path cleanup options if(Simplify) simplifyPath(points); if(RaySimplify) raySimplifyPath(points); //finally load the remaining points into a new path object and return it var path:FlxPath = new FlxPath(); var i:int = points.length - 1; while(i >= 0) { node = points[i--] as FlxPoint; if(node != null) path.addPoint(node,true); } return path; } /** * Pathfinding helper function, strips out extra points on the same line. * * @param Points An array of FlxPoint nodes. */ protected function simplifyPath(Points:Array):void { var deltaPrevious:Number; var deltaNext:Number; var last:FlxPoint = Points[0]; var node:FlxPoint; var i:uint = 1; var l:uint = Points.length-1; while(i < l) { node = Points[i]; deltaPrevious = (node.x - last.x)/(node.y - last.y); deltaNext = (node.x - Points[i+1].x)/(node.y - Points[i+1].y); if((last.x == Points[i+1].x) || (last.y == Points[i+1].y) || (deltaPrevious == deltaNext)) Points[i] = null; else last = node; i++; } } /** * Pathfinding helper function, strips out even more points by raycasting from one point to the next and dropping unnecessary points. * * @param Points An array of FlxPoint nodes. */ protected function raySimplifyPath(Points:Array):void { var source:FlxPoint = Points[0]; var lastIndex:int = -1; var node:FlxPoint; var i:uint = 1; var l:uint = Points.length; while(i < l) { node = Points[i++]; if(node == null) continue; if(ray(source,node,_point)) { if(lastIndex >= 0) Points[lastIndex] = null; } else source = Points[lastIndex]; lastIndex = i-1; } } /** * Pathfinding helper function, floods a grid with distance information until it finds the end point. * NOTE: Currently this process does NOT use any kind of fancy heuristic! It's pretty brute. * * @param StartIndex The starting tile's map index. * @param EndIndex The ending tile's map index. * * @return A Flash Array of FlxPoint nodes. If the end tile could not be found, then a null Array is returned instead. */ protected function computePathDistance(StartIndex:uint, EndIndex:uint):Array { //Create a distance-based representation of the tilemap. //All walls are flagged as -2, all open areas as -1. var mapSize:uint = widthInTiles*heightInTiles; var distances:Array = new Array(mapSize); var i:int = 0; while(i < mapSize) { if((_tileObjects[_data[i]] as FlxTile).allowCollisions) distances[i] = -2; else distances[i] = -1; i++; } distances[StartIndex] = 0; var distance:uint = 1; var neighbors:Array = [StartIndex]; var current:Array; var currentIndex:uint; var left:Boolean; var right:Boolean; var up:Boolean; var down:Boolean; var currentLength:uint; var foundEnd:Boolean = false; while(neighbors.length > 0) { current = neighbors; neighbors = new Array(); i = 0; currentLength = current.length; while(i < currentLength) { currentIndex = current[i++]; if(currentIndex == EndIndex) { foundEnd = true; neighbors.length = 0; break; } //basic map bounds left = currentIndex%widthInTiles > 0; right = currentIndex%widthInTiles < widthInTiles-1; up = currentIndex/widthInTiles > 0; down = currentIndex/widthInTiles < heightInTiles-1; var index:uint; if(up) { index = currentIndex - widthInTiles; if(distances[index] == -1) { distances[index] = distance; neighbors.push(index); } } if(right) { index = currentIndex + 1; if(distances[index] == -1) { distances[index] = distance; neighbors.push(index); } } if(down) { index = currentIndex + widthInTiles; if(distances[index] == -1) { distances[index] = distance; neighbors.push(index); } } if(left) { index = currentIndex - 1; if(distances[index] == -1) { distances[index] = distance; neighbors.push(index); } } if(up && right) { index = currentIndex - widthInTiles + 1; if((distances[index] == -1) && (distances[currentIndex-widthInTiles] >= -1) && (distances[currentIndex+1] >= -1)) { distances[index] = distance; neighbors.push(index); } } if(right && down) { index = currentIndex + widthInTiles + 1; if((distances[index] == -1) && (distances[currentIndex+widthInTiles] >= -1) && (distances[currentIndex+1] >= -1)) { distances[index] = distance; neighbors.push(index); } } if(left && down) { index = currentIndex + widthInTiles - 1; if((distances[index] == -1) && (distances[currentIndex+widthInTiles] >= -1) && (distances[currentIndex-1] >= -1)) { distances[index] = distance; neighbors.push(index); } } if(up && left) { index = currentIndex - widthInTiles - 1; if((distances[index] == -1) && (distances[currentIndex-widthInTiles] >= -1) && (distances[currentIndex-1] >= -1)) { distances[index] = distance; neighbors.push(index); } } } distance++; } if(!foundEnd) distances = null; return distances; } /** * Pathfinding helper function, recursively walks the grid and finds a shortest path back to the start. * * @param Data A Flash Array of distance information. * @param Start The tile we're on in our walk backward. * @param Points A Flash Array of FlxPoint nodes composing the path from the start to the end, compiled in reverse order. */ protected function walkPath(Data:Array,Start:uint,Points:Array):void { Points.push(new FlxPoint(x + uint(Start%widthInTiles)*_tileWidth + _tileWidth*0.5, y + uint(Start/widthInTiles)*_tileHeight + _tileHeight*0.5)); if(Data[Start] == 0) return; //basic map bounds var left:Boolean = Start%widthInTiles > 0; var right:Boolean = Start%widthInTiles < widthInTiles-1; var up:Boolean = Start/widthInTiles > 0; var down:Boolean = Start/widthInTiles < heightInTiles-1; var current:uint = Data[Start]; var i:uint; if(up) { i = Start - widthInTiles; if((Data[i] >= 0) && (Data[i] < current)) { walkPath(Data,i,Points); return; } } if(right) { i = Start + 1; if((Data[i] >= 0) && (Data[i] < current)) { walkPath(Data,i,Points); return; } } if(down) { i = Start + widthInTiles; if((Data[i] >= 0) && (Data[i] < current)) { walkPath(Data,i,Points); return; } } if(left) { i = Start - 1; if((Data[i] >= 0) && (Data[i] < current)) { walkPath(Data,i,Points); return; } } if(up && right) { i = Start - widthInTiles + 1; if((Data[i] >= 0) && (Data[i] < current)) { walkPath(Data,i,Points); return; } } if(right && down) { i = Start + widthInTiles + 1; if((Data[i] >= 0) && (Data[i] < current)) { walkPath(Data,i,Points); return; } } if(left && down) { i = Start + widthInTiles - 1; if((Data[i] >= 0) && (Data[i] < current)) { walkPath(Data,i,Points); return; } } if(up && left) { i = Start - widthInTiles - 1; if((Data[i] >= 0) && (Data[i] < current)) { walkPath(Data,i,Points); return; } } } /** * Checks to see if some FlxObject overlaps this FlxObject object in world space. * If the group has a LOT of things in it, it might be faster to use FlxG.overlaps(). * WARNING: Currently tilemaps do NOT support screen space overlap checks! * * @param Object The object being tested. * @param InScreenSpace Whether to take scroll factors into account when checking for overlap. * @param Camera Specify which game camera you want. If null getScreenXY() will just grab the first global camera. * * @return Whether or not the two objects overlap. */ override public function overlaps(ObjectOrGroup:FlxBasic,InScreenSpace:Boolean=false,Camera:FlxCamera=null):Boolean { if(ObjectOrGroup is FlxGroup) { var results:Boolean = false; var basic:FlxBasic; var i:uint = 0; var members:Array = (ObjectOrGroup as FlxGroup).members; while(i < length) { basic = members[i++] as FlxBasic; if(basic is FlxObject) { if(overlapsWithCallback(basic as FlxObject)) results = true; } else { if(overlaps(basic,InScreenSpace,Camera)) results = true; } } return results; } else if(ObjectOrGroup is FlxObject) return overlapsWithCallback(ObjectOrGroup as FlxObject); return false; } /** * Checks to see if this FlxObject were located at the given position, would it overlap the FlxObject or FlxGroup? * This is distinct from overlapsPoint(), which just checks that point, rather than taking the object's size into account. * WARNING: Currently tilemaps do NOT support screen space overlap checks! * * @param X The X position you want to check. Pretends this object (the caller, not the parameter) is located here. * @param Y The Y position you want to check. Pretends this object (the caller, not the parameter) is located here. * @param ObjectOrGroup The object or group being tested. * @param InScreenSpace Whether to take scroll factors into account when checking for overlap. Default is false, or "only compare in world space." * @param Camera Specify which game camera you want. If null getScreenXY() will just grab the first global camera. * * @return Whether or not the two objects overlap. */ override public function overlapsAt(X:Number,Y:Number,ObjectOrGroup:FlxBasic,InScreenSpace:Boolean=false,Camera:FlxCamera=null):Boolean { if(ObjectOrGroup is FlxGroup) { var results:Boolean = false; var basic:FlxBasic; var i:uint = 0; var members:Array = (ObjectOrGroup as FlxGroup).members; while(i < length) { basic = members[i++] as FlxBasic; if(basic is FlxObject) { _point.x = X; _point.y = Y; if(overlapsWithCallback(basic as FlxObject,null,false,_point)) results = true; } else { if(overlapsAt(X,Y,basic,InScreenSpace,Camera)) results = true; } } return results; } else if(ObjectOrGroup is FlxObject) { _point.x = X; _point.y = Y; return overlapsWithCallback(ObjectOrGroup as FlxObject,null,false,_point); } return false; } /** * Checks if the Object overlaps any tiles with any collision flags set, * and calls the specified callback function (if there is one). * Also calls the tile's registered callback if the filter matches. * * @param Object The FlxObject you are checking for overlaps against. * @param Callback An optional function that takes the form "myCallback(Object1:FlxObject,Object2:FlxObject)", where Object1 is a FlxTile object, and Object2 is the object passed in in the first parameter of this method. * @param FlipCallbackParams Used to preserve A-B list ordering from FlxObject.separate() - returns the FlxTile object as the second parameter instead. * @param Position Optional, specify a custom position for the tilemap (useful for overlapsAt()-type funcitonality). * * @return Whether there were overlaps, or if a callback was specified, whatever the return value of the callback was. */ public function overlapsWithCallback(Object:FlxObject,Callback:Function=null,FlipCallbackParams:Boolean=false,Position:FlxPoint=null):Boolean { var results:Boolean = false; var X:Number = x; var Y:Number = y; if(Position != null) { X = Position.x; Y = Position.y; } //Figure out what tiles we need to check against var selectionX:int = FlxU.floor((Object.x - X)/_tileWidth); var selectionY:int = FlxU.floor((Object.y - Y)/_tileHeight); var selectionWidth:uint = selectionX + (FlxU.ceil(Object.width/_tileWidth)) + 1; var selectionHeight:uint = selectionY + FlxU.ceil(Object.height/_tileHeight) + 1; //Then bound these coordinates by the map edges if(selectionX < 0) selectionX = 0; if(selectionY < 0) selectionY = 0; if(selectionWidth > widthInTiles) selectionWidth = widthInTiles; if(selectionHeight > heightInTiles) selectionHeight = heightInTiles; //Then loop through this selection of tiles and call FlxObject.separate() accordingly var rowStart:uint = selectionY*widthInTiles; var row:uint = selectionY; var column:uint; var tile:FlxTile; var overlapFound:Boolean; var deltaX:Number = X - last.x; var deltaY:Number = Y - last.y; while(row < selectionHeight) { column = selectionX; while(column < selectionWidth) { overlapFound = false; tile = _tileObjects[_data[rowStart+column]] as FlxTile; if(tile.allowCollisions) { tile.x = X+column*_tileWidth; tile.y = Y+row*_tileHeight; tile.last.x = tile.x - deltaX; tile.last.y = tile.y - deltaY; if(Callback != null) { if(FlipCallbackParams) overlapFound = Callback(Object,tile); else overlapFound = Callback(tile,Object); } else overlapFound = (Object.x + Object.width > tile.x) && (Object.x < tile.x + tile.width) && (Object.y + Object.height > tile.y) && (Object.y < tile.y + tile.height); if(overlapFound) { if((tile.callback != null) && ((tile.filter == null) || (Object is tile.filter))) { tile.mapIndex = rowStart+column; tile.callback(tile,Object); } results = true; } } else if((tile.callback != null) && ((tile.filter == null) || (Object is tile.filter))) { tile.mapIndex = rowStart+column; tile.callback(tile,Object); } column++; } rowStart += widthInTiles; row++; } return results; } /** * Checks to see if a point in 2D world space overlaps this FlxObject object. * * @param Point The point in world space you want to check. * @param InScreenSpace Whether to take scroll factors into account when checking for overlap. * @param Camera Specify which game camera you want. If null getScreenXY() will just grab the first global camera. * * @return Whether or not the point overlaps this object. */ override public function overlapsPoint(Point:FlxPoint,InScreenSpace:Boolean=false,Camera:FlxCamera=null):Boolean { if(!InScreenSpace) return (_tileObjects[_data[uint(uint((Point.y-y)/_tileHeight)*widthInTiles + (Point.x-x)/_tileWidth)]] as FlxTile).allowCollisions > 0; if(Camera == null) Camera = FlxG.camera; Point.x = Point.x - Camera.scroll.x; Point.y = Point.y - Camera.scroll.y; getScreenXY(_point,Camera); return (_tileObjects[_data[uint(uint((Point.y-_point.y)/_tileHeight)*widthInTiles + (Point.x-_point.x)/_tileWidth)]] as FlxTile).allowCollisions > 0; } /** * Check the value of a particular tile. * * @param X The X coordinate of the tile (in tiles, not pixels). * @param Y The Y coordinate of the tile (in tiles, not pixels). * * @return A uint containing the value of the tile at this spot in the array. */ public function getTile(X:uint,Y:uint):uint { return _data[Y * widthInTiles + X] as uint; } /** * Get the value of a tile in the tilemap by index. * * @param Index The slot in the data array (Y * widthInTiles + X) where this tile is stored. * * @return A uint containing the value of the tile at this spot in the array. */ public function getTileByIndex(Index:uint):uint { return _data[Index] as uint; } /** * Returns a new Flash Array full of every map index of the requested tile type. * * @param Index The requested tile type. * * @return An Array with a list of all map indices of that tile type. */ public function getTileInstances(Index:uint):Array { var array:Array = null; var i:uint = 0; var l:uint = widthInTiles * heightInTiles; while(i < l) { if(_data[i] == Index) { if(array == null) array = new Array(); array.push(i); } i++; } return array; } /** * Returns a new Flash Array full of every coordinate of the requested tile type. * * @param Index The requested tile type. * @param Midpoint Whether to return the coordinates of the tile midpoint, or upper left corner. Default is true, return midpoint. * * @return An Array with a list of all the coordinates of that tile type. */ public function getTileCoords(Index:uint,Midpoint:Boolean=true):Array { var array:Array = null; var point:FlxPoint; var i:uint = 0; var l:uint = widthInTiles * heightInTiles; while(i < l) { if(_data[i] == Index) { point = new FlxPoint(x + uint(i%widthInTiles)*_tileWidth,y + uint(i/widthInTiles)*_tileHeight); if(Midpoint) { point.x += _tileWidth*0.5; point.y += _tileHeight*0.5; } if(array == null) array = new Array(); array.push(point); } i++; } return array; } /** * Change the data and graphic of a tile in the tilemap. * * @param X The X coordinate of the tile (in tiles, not pixels). * @param Y The Y coordinate of the tile (in tiles, not pixels). * @param Tile The new integer data you wish to inject. * @param UpdateGraphics Whether the graphical representation of this tile should change. * * @return Whether or not the tile was actually changed. */ public function setTile(X:uint,Y:uint,Tile:uint,UpdateGraphics:Boolean=true):Boolean { if((X >= widthInTiles) || (Y >= heightInTiles)) return false; return setTileByIndex(Y * widthInTiles + X,Tile,UpdateGraphics); } /** * Change the data and graphic of a tile in the tilemap. * * @param Index The slot in the data array (Y * widthInTiles + X) where this tile is stored. * @param Tile The new integer data you wish to inject. * @param UpdateGraphics Whether the graphical representation of this tile should change. * * @return Whether or not the tile was actually changed. */ public function setTileByIndex(Index:uint,Tile:uint,UpdateGraphics:Boolean=true):Boolean { if(Index >= _data.length) return false; var ok:Boolean = true; _data[Index] = Tile; if(!UpdateGraphics) return ok; setDirty(); if(auto == OFF) { updateTile(Index); return ok; } //If this map is autotiled and it changes, locally update the arrangement var i:uint; var row:int = int(Index/widthInTiles) - 1; var rowLength:int = row + 3; var column:int = Index%widthInTiles - 1; var columnHeight:int = column + 3; while(row < rowLength) { column = columnHeight - 3; while(column < columnHeight) { if((row >= 0) && (row < heightInTiles) && (column >= 0) && (column < widthInTiles)) { i = row*widthInTiles+column; autoTile(i); updateTile(i); } column++; } row++; } return ok; } /** * Adjust collision settings and/or bind a callback function to a range of tiles. * This callback function, if present, is triggered by calls to overlap() or overlapsWithCallback(). * * @param Tile The tile or tiles you want to adjust. * @param AllowCollisions Modify the tile or tiles to only allow collisions from certain directions, use FlxObject constants NONE, ANY, LEFT, RIGHT, etc. Default is "ANY". * @param Callback The function to trigger, e.g. lavaCallback(Tile:FlxTile, Object:FlxObject). * @param CallbackFilter If you only want the callback to go off for certain classes or objects based on a certain class, set that class here. * @param Range If you want this callback to work for a bunch of different tiles, input the range here. Default value is 1. */ public function setTileProperties(Tile:uint,AllowCollisions:uint=0x1111,Callback:Function=null,CallbackFilter:Class=null,Range:uint=1):void { if(Range <= 0) Range = 1; var tile:FlxTile; var i:uint = Tile; var l:uint = Tile+Range; while(i < l) { tile = _tileObjects[i++] as FlxTile; tile.allowCollisions = AllowCollisions; tile.callback = Callback; tile.filter = CallbackFilter; } } /** * Call this function to lock the automatic camera to the map's edges. * * @param Camera Specify which game camera you want. If null getScreenXY() will just grab the first global camera. * @param Border Adjusts the camera follow boundary by whatever number of tiles you specify here. Handy for blocking off deadends that are offscreen, etc. Use a negative number to add padding instead of hiding the edges. * @param UpdateWorld Whether to update the collision system's world size, default value is true. */ public function follow(Camera:FlxCamera=null,Border:int=0,UpdateWorld:Boolean=true):void { if(Camera == null) Camera = FlxG.camera; Camera.setBounds(x+Border*_tileWidth,y+Border*_tileHeight,width-Border*_tileWidth*2,height-Border*_tileHeight*2,UpdateWorld); } /** * Get the world coordinates and size of the entire tilemap as a FlxRect. * * @param Bounds Optional, pass in a pre-existing FlxRect to prevent instantiation of a new object. * * @return A FlxRect containing the world coordinates and size of the entire tilemap. */ public function getBounds(Bounds:FlxRect=null):FlxRect { if(Bounds == null) Bounds = new FlxRect(); return Bounds.make(x,y,width,height); } /** * Shoots a ray from the start point to the end point. * If/when it passes through a tile, it stores that point and returns false. * * @param Start The world coordinates of the start of the ray. * @param End The world coordinates of the end of the ray. * @param Result A Point object containing the first wall impact. * @param Resolution Defaults to 1, meaning check every tile or so. Higher means more checks! * @return Returns true if the ray made it from Start to End without hitting anything. Returns false and fills Result if a tile was hit. */ public function ray(Start:FlxPoint, End:FlxPoint, Result:FlxPoint=null, Resolution:Number=1):Boolean { var step:Number = _tileWidth; if(_tileHeight < _tileWidth) step = _tileHeight; step /= Resolution; var deltaX:Number = End.x - Start.x; var deltaY:Number = End.y - Start.y; var distance:Number = Math.sqrt(deltaX*deltaX + deltaY*deltaY); var steps:uint = Math.ceil(distance/step); var stepX:Number = deltaX/steps; var stepY:Number = deltaY/steps; var curX:Number = Start.x - stepX - x; var curY:Number = Start.y - stepY - y; var tileX:uint; var tileY:uint; var i:uint = 0; while(i < steps) { curX += stepX; curY += stepY; if((curX < 0) || (curX > width) || (curY < 0) || (curY > height)) { i++; continue; } tileX = curX/_tileWidth; tileY = curY/_tileHeight; if((_tileObjects[_data[tileY*widthInTiles+tileX]] as FlxTile).allowCollisions) { //Some basic helper stuff tileX *= _tileWidth; tileY *= _tileHeight; var rx:Number = 0; var ry:Number = 0; var q:Number; var lx:Number = curX-stepX; var ly:Number = curY-stepY; //Figure out if it crosses the X boundary q = tileX; if(deltaX < 0) q += _tileWidth; rx = q; ry = ly + stepY*((q-lx)/stepX); if((ry > tileY) && (ry < tileY + _tileHeight)) { if(Result == null) Result = new FlxPoint(); Result.x = rx; Result.y = ry; return false; } //Else, figure out if it crosses the Y boundary q = tileY; if(deltaY < 0) q += _tileHeight; rx = lx + stepX*((q-ly)/stepY); ry = q; if((rx > tileX) && (rx < tileX + _tileWidth)) { if(Result == null) Result = new FlxPoint(); Result.x = rx; Result.y = ry; return false; } return true; } i++; } return true; } /** * Converts a one-dimensional array of tile data to a comma-separated string. * * @param Data An array full of integer tile references. * @param Width The number of tiles in each row. * @param Invert Recommended only for 1-bit arrays - changes 0s to 1s and vice versa. * * @return A comma-separated string containing the level data in a FlxTilemap-friendly format. */ static public function arrayToCSV(Data:Array,Width:int,Invert:Boolean=false):String { var row:uint = 0; var column:uint; var csv:String; var Height:int = Data.length / Width; var index:int; while(row < Height) { column = 0; while(column < Width) { index = Data[row*Width+column]; if(Invert) { if(index == 0) index = 1; else if(index == 1) index = 0; } if(column == 0) { if(row == 0) csv += index; else csv += "\n"+index; } else csv += ", "+index; column++; } row++; } return csv; } /** * Converts a BitmapData object to a comma-separated string. * Black pixels are flagged as 'solid' by default, * non-black pixels are set as non-colliding. * Black pixels must be PURE BLACK. * * @param bitmapData A Flash BitmapData object, preferably black and white. * @param Invert Load white pixels as solid instead. * @param Scale Default is 1. Scale of 2 means each pixel forms a 2x2 block of tiles, and so on. * @param ColorMap An array of color values (uint 0xAARRGGBB) in the order they're intended to be assigned as indices * * @return A comma-separated string containing the level data in a FlxTilemap-friendly format. */ static public function bitmapToCSV(bitmapData:BitmapData,Invert:Boolean=false,Scale:uint=1,ColorMap:Array=null):String { //Import and scale image if necessary if(Scale > 1) { var bd:BitmapData = bitmapData; bitmapData = new BitmapData(bitmapData.width*Scale,bitmapData.height*Scale); var mtx:Matrix = new Matrix(); mtx.scale(Scale,Scale); bitmapData.draw(bd,mtx); } //Walk image and export pixel values var row:uint = 0; var column:uint; var pixel:uint; var csv:String = ""; var bitmapWidth:uint = bitmapData.width; var bitmapHeight:uint = bitmapData.height; while(row < bitmapHeight) { column = 0; while(column < bitmapWidth) { //Decide if this pixel/tile is solid (1) or not (0) pixel = bitmapData.getPixel(column,row); if(ColorMap != null) pixel = ColorMap.indexOf(pixel); else if((Invert && (pixel > 0)) || (!Invert && (pixel == 0))) pixel = 1; else pixel = 0; //Write the result to the string if(column == 0) { if(row == 0) csv += pixel; else csv += "\n"+pixel; } else csv += ", "+pixel; column++; } row++; } return csv; } /** * Converts a resource image file to a comma-separated string. * Black pixels are flagged as 'solid' by default, * non-black pixels are set as non-colliding. * Black pixels must be PURE BLACK. * * @param ImageFile An embedded graphic, preferably black and white. * @param Invert Load white pixels as solid instead. * @param Scale Default is 1. Scale of 2 means each pixel forms a 2x2 block of tiles, and so on. * * @return A comma-separated string containing the level data in a FlxTilemap-friendly format. */ static public function imageToCSV(ImageFile:Class,Invert:Boolean=false,Scale:uint=1):String { return bitmapToCSV((new ImageFile).bitmapData,Invert,Scale); } /** * An internal function used by the binary auto-tilers. * * @param Index The index of the tile you want to analyze. */ protected function autoTile(Index:uint):void { if(_data[Index] == 0) return; _data[Index] = 0; if((Index-widthInTiles < 0) || (_data[Index-widthInTiles] > 0)) //UP _data[Index] += 1; if((Index%widthInTiles >= widthInTiles-1) || (_data[Index+1] > 0)) //RIGHT _data[Index] += 2; if((Index+widthInTiles >= totalTiles) || (_data[Index+widthInTiles] > 0)) //DOWN _data[Index] += 4; if((Index%widthInTiles <= 0) || (_data[Index-1] > 0)) //LEFT _data[Index] += 8; if((auto == ALT) && (_data[Index] == 15)) //The alternate algo checks for interior corners { if((Index%widthInTiles > 0) && (Index+widthInTiles < totalTiles) && (_data[Index+widthInTiles-1] <= 0)) _data[Index] = 1; //BOTTOM LEFT OPEN if((Index%widthInTiles > 0) && (Index-widthInTiles >= 0) && (_data[Index-widthInTiles-1] <= 0)) _data[Index] = 2; //TOP LEFT OPEN if((Index%widthInTiles < widthInTiles-1) && (Index-widthInTiles >= 0) && (_data[Index-widthInTiles+1] <= 0)) _data[Index] = 4; //TOP RIGHT OPEN if((Index%widthInTiles < widthInTiles-1) && (Index+widthInTiles < totalTiles) && (_data[Index+widthInTiles+1] <= 0)) _data[Index] = 8; //BOTTOM RIGHT OPEN } _data[Index] += 1; } /** * Internal function used in setTileByIndex() and the constructor to update the map. * * @param Index The index of the tile you want to update. */ protected function updateTile(Index:uint):void { var tile:FlxTile = _tileObjects[_data[Index]] as FlxTile; if((tile == null) || !tile.visible) { _rects[Index] = null; return; } var rx:uint = (_data[Index]-_startingIndex)*_tileWidth; var ry:uint = 0; if(rx >= _tiles.width) { ry = uint(rx/_tiles.width)*_tileHeight; rx %= _tiles.width; } _rects[Index] = (new Rectangle(rx,ry,_tileWidth,_tileHeight)); } } } ================================================ FILE: org/flixel/FlxTimer.as ================================================ package org.flixel { import org.flixel.plugin.TimerManager; /** * A simple timer class, leveraging the new plugins system. * Can be used with callbacks or by polling the finished flag. * Not intended to be added to a game state or group; the timer manager * is responsible for actually calling update(), not the user. * * @author Adam Atomic */ public class FlxTimer { /** * How much time the timer was set for. */ public var time:Number; /** * How many loops the timer was set for. */ public var loops:uint; /** * Pauses or checks the pause state of the timer. */ public var paused:Boolean; /** * Check to see if the timer is finished. */ public var finished:Boolean; /** * Internal tracker for the time's-up callback function. * Callback should be formed "onTimer(Timer:FlxTimer);" */ protected var _callback:Function; /** * Internal tracker for the actual timer counting up. */ protected var _timeCounter:Number; /** * Internal tracker for the loops counting up. */ protected var _loopsCounter:uint; /** * Instantiate the timer. Does not set or start the timer. */ public function FlxTimer() { time = 0; loops = 0; _callback = null; _timeCounter = 0; _loopsCounter = 0; paused = false; finished = false; } /** * Clean up memory. */ public function destroy():void { stop(); _callback = null; } /** * Called by the timer manager plugin to update the timer. * If time runs out, the loop counter is advanced, the timer reset, and the callback called if it exists. * If the timer runs out of loops, then the timer calls stop(). * However, callbacks are called AFTER stop() is called. */ public function update():void { _timeCounter += FlxG.elapsed; while((_timeCounter >= time) && !paused && !finished) { _timeCounter -= time; _loopsCounter++; if((loops > 0) && (_loopsCounter >= loops)) stop(); if(_callback != null) _callback(this); } } /** * Starts or resumes the timer. If this timer was paused, * then all the parameters are ignored, and the timer is resumed. * Adds the timer to the timer manager. * * @param Time How many seconds it takes for the timer to go off. * @param Loops How many times the timer should go off. Default is 1, or "just count down once." * @param Callback Optional, triggered whenever the time runs out, once for each loop. Callback should be formed "onTimer(Timer:FlxTimer);" * * @return A reference to itself (handy for chaining or whatever). */ public function start(Time:Number=1,Loops:uint=1,Callback:Function=null):FlxTimer { var timerManager:TimerManager = manager; if(timerManager != null) timerManager.add(this); if(paused) { paused = false; return this; } paused = false; finished = false; time = Time; loops = Loops; _callback = Callback; _timeCounter = 0; _loopsCounter = 0; return this; } /** * Stops the timer and removes it from the timer manager. */ public function stop():void { finished = true; var timerManager:TimerManager = manager; if(timerManager != null) timerManager.remove(this); } /** * Read-only: check how much time is left on the timer. */ public function get timeLeft():Number { return time-_timeCounter; } /** * Read-only: check how many loops are left on the timer. */ public function get loopsLeft():int { return loops-_loopsCounter; } /** * Read-only: how far along the timer is, on a scale of 0.0 to 1.0. */ public function get progress():Number { if(time > 0) return _timeCounter/time; else return 0; } static public function get manager():TimerManager { return FlxG.getPlugin(TimerManager) as TimerManager; } } } ================================================ FILE: org/flixel/FlxU.as ================================================ package org.flixel { import flash.net.URLRequest; import flash.net.navigateToURL; import flash.utils.getDefinitionByName; import flash.utils.getQualifiedClassName; import flash.utils.getTimer; public class FlxU { /** * Opens a web page in a new tab or window. * MUST be called from the UI thread or else badness. * * @param URL The address of the web page. */ static public function openURL(URL:String):void { navigateToURL(new URLRequest(URL), "_blank"); } /** * Calculate the absolute value of a number. * * @param Value Any number. * * @return The absolute value of that number. */ static public function abs(Value:Number):Number { return (Value>0)?Value:-Value; } /** * Round down to the next whole number. E.g. floor(1.7) == 1, and floor(-2.7) == -2. * * @param Value Any number. * * @return The rounded value of that number. */ static public function floor(Value:Number):Number { var number:Number = int(Value); return (Value>0)?(number):((number!=Value)?(number-1):(number)); } /** * Round up to the next whole number. E.g. ceil(1.3) == 2, and ceil(-2.3) == -3. * * @param Value Any number. * * @return The rounded value of that number. */ static public function ceil(Value:Number):Number { var number:Number = int(Value); return (Value>0)?((number!=Value)?(number+1):(number)):(number); } /** * Round to the closest whole number. E.g. round(1.7) == 2, and round(-2.3) == -2. * * @param Value Any number. * * @return The rounded value of that number. */ static public function round(Value:Number):Number { var number:Number = int(Value+0.5); return (Value>0)?(number):((number!=Value)?(number-1):(number)); } /** * Figure out which number is smaller. * * @param Number1 Any number. * @param Number2 Any number. * * @return The smaller of the two numbers. */ static public function min(Number1:Number,Number2:Number):Number { return (Number1 <= Number2)?Number1:Number2; } /** * Figure out which number is larger. * * @param Number1 Any number. * @param Number2 Any number. * * @return The larger of the two numbers. */ static public function max(Number1:Number,Number2:Number):Number { return (Number1 >= Number2)?Number1:Number2; } /** * Bound a number by a minimum and maximum. * Ensures that this number is no smaller than the minimum, * and no larger than the maximum. * * @param Value Any number. * @param Min Any number. * @param Max Any number. * * @return The bounded value of the number. */ static public function bound(Value:Number,Min:Number,Max:Number):Number { var lowerBound:Number = (ValueMax)?Max:lowerBound; } /** * Generates a random number based on the seed provided. * * @param Seed A number between 0 and 1, used to generate a predictable random number (very optional). * * @return A Number between 0 and 1. */ static public function srand(Seed:Number):Number { return ((69621 * int(Seed * 0x7FFFFFFF)) % 0x7FFFFFFF) / 0x7FFFFFFF; } /** * Shuffles the entries in an array into a new random order. * FlxG.shuffle() is deterministic and safe for use with replays/recordings. * HOWEVER, FlxU.shuffle() is NOT deterministic and unsafe for use with replays/recordings. * * @param A A Flash Array object containing...stuff. * @param HowManyTimes How many swaps to perform during the shuffle operation. Good rule of thumb is 2-4 times as many objects are in the list. * * @return The same Flash Array object that you passed in in the first place. */ static public function shuffle(Objects:Array,HowManyTimes:uint):Array { var i:uint = 0; var index1:uint; var index2:uint; var object:Object; while(i < HowManyTimes) { index1 = Math.random()*Objects.length; index2 = Math.random()*Objects.length; object = Objects[index2]; Objects[index2] = Objects[index1]; Objects[index1] = object; i++; } return Objects; } /** * Fetch a random entry from the given array. * Will return null if random selection is missing, or array has no entries. * FlxG.getRandom() is deterministic and safe for use with replays/recordings. * HOWEVER, FlxU.getRandom() is NOT deterministic and unsafe for use with replays/recordings. * * @param Objects A Flash array of objects. * @param StartIndex Optional offset off the front of the array. Default value is 0, or the beginning of the array. * @param Length Optional restriction on the number of values you want to randomly select from. * * @return The random object that was selected. */ static public function getRandom(Objects:Array,StartIndex:uint=0,Length:uint=0):Object { if(Objects != null) { var l:uint = Length; if((l == 0) || (l > Objects.length - StartIndex)) l = Objects.length - StartIndex; if(l > 0) return Objects[StartIndex + uint(Math.random()*l)]; } return null; } /** * Just grabs the current "ticks" or time in milliseconds that has passed since Flash Player started up. * Useful for finding out how long it takes to execute specific blocks of code. * * @return A uint to be passed to FlxU.endProfile(). */ static public function getTicks():uint { return getTimer(); } /** * Takes two "ticks" timestamps and formats them into the number of seconds that passed as a String. * Useful for logging, debugging, the watch window, or whatever else. * * @param StartTicks The first timestamp from the system. * @param EndTicks The second timestamp from the system. * * @return A String containing the formatted time elapsed information. */ static public function formatTicks(StartTicks:uint,EndTicks:uint):String { return ((EndTicks-StartTicks)/1000)+"s" } /** * Generate a Flash uint color from RGBA components. * * @param Red The red component, between 0 and 255. * @param Green The green component, between 0 and 255. * @param Blue The blue component, between 0 and 255. * @param Alpha How opaque the color should be, either between 0 and 1 or 0 and 255. * * @return The color as a uint. */ static public function makeColor(Red:uint, Green:uint, Blue:uint, Alpha:Number=1.0):uint { return (((Alpha>1)?Alpha:(Alpha * 255)) & 0xFF) << 24 | (Red & 0xFF) << 16 | (Green & 0xFF) << 8 | (Blue & 0xFF); } /** * Generate a Flash uint color from HSB components. * * @param Hue A number between 0 and 360, indicating position on a color strip or wheel. * @param Saturation A number between 0 and 1, indicating how colorful or gray the color should be. 0 is gray, 1 is vibrant. * @param Brightness A number between 0 and 1, indicating how bright the color should be. 0 is black, 1 is full bright. * @param Alpha How opaque the color should be, either between 0 and 1 or 0 and 255. * * @return The color as a uint. */ static public function makeColorFromHSB(Hue:Number,Saturation:Number,Brightness:Number,Alpha:Number=1.0):uint { var red:Number; var green:Number; var blue:Number; if(Saturation == 0.0) { red = Brightness; green = Brightness; blue = Brightness; } else { if(Hue == 360) Hue = 0; var slice:int = Hue/60; var hf:Number = Hue/60 - slice; var aa:Number = Brightness*(1 - Saturation); var bb:Number = Brightness*(1 - Saturation*hf); var cc:Number = Brightness*(1 - Saturation*(1.0 - hf)); switch (slice) { case 0: red = Brightness; green = cc; blue = aa; break; case 1: red = bb; green = Brightness; blue = aa; break; case 2: red = aa; green = Brightness; blue = cc; break; case 3: red = aa; green = bb; blue = Brightness; break; case 4: red = cc; green = aa; blue = Brightness; break; case 5: red = Brightness; green = aa; blue = bb; break; default: red = 0; green = 0; blue = 0; break; } } return (((Alpha>1)?Alpha:(Alpha * 255)) & 0xFF) << 24 | uint(red*255) << 16 | uint(green*255) << 8 | uint(blue*255); } /** * Loads an array with the RGBA values of a Flash uint color. * RGB values are stored 0-255. Alpha is stored as a floating point number between 0 and 1. * * @param Color The color you want to break into components. * @param Results An optional parameter, allows you to use an array that already exists in memory to store the result. * * @return An Array object containing the Red, Green, Blue and Alpha values of the given color. */ static public function getRGBA(Color:uint,Results:Array=null):Array { if(Results == null) Results = new Array(); Results[0] = (Color >> 16) & 0xFF; Results[1] = (Color >> 8) & 0xFF; Results[2] = Color & 0xFF; Results[3] = Number((Color >> 24) & 0xFF) / 255; return Results; } /** * Loads an array with the HSB values of a Flash uint color. * Hue is a value between 0 and 360. Saturation, Brightness and Alpha * are as floating point numbers between 0 and 1. * * @param Color The color you want to break into components. * @param Results An optional parameter, allows you to use an array that already exists in memory to store the result. * * @return An Array object containing the Red, Green, Blue and Alpha values of the given color. */ static public function getHSB(Color:uint,Results:Array=null):Array { if(Results == null) Results = new Array(); var red:Number = Number((Color >> 16) & 0xFF) / 255; var green:Number = Number((Color >> 8) & 0xFF) / 255; var blue:Number = Number((Color) & 0xFF) / 255; var m:Number = (red>green)?red:green; var dmax:Number = (m>blue)?m:blue; m = (red>green)?green:red; var dmin:Number = (m>blue)?blue:m; var range:Number = dmax - dmin; Results[2] = dmax; Results[1] = 0; Results[0] = 0; if(dmax != 0) Results[1] = range / dmax; if(Results[1] != 0) { if (red == dmax) Results[0] = (green - blue) / range; else if (green == dmax) Results[0] = 2 + (blue - red) / range; else if (blue == dmax) Results[0] = 4 + (red - green) / range; Results[0] *= 60; if(Results[0] < 0) Results[0] += 360; } Results[3] = Number((Color >> 24) & 0xFF) / 255; return Results; } /** * Format seconds as minutes with a colon, an optionally with milliseconds too. * * @param Seconds The number of seconds (for example, time remaining, time spent, etc). * @param ShowMS Whether to show milliseconds after a "." as well. Default value is false. * * @return A nicely formatted String, like "1:03". */ static public function formatTime(Seconds:Number,ShowMS:Boolean=false):String { var timeString:String = int(Seconds/60) + ":"; var timeStringHelper:int = int(Seconds)%60; if(timeStringHelper < 10) timeString += "0"; timeString += timeStringHelper; if(ShowMS) { timeString += "."; timeStringHelper = (Seconds-int(Seconds))*100; if(timeStringHelper < 10) timeString += "0"; timeString += timeStringHelper; } return timeString; } /** * Generate a comma-separated string from an array. * Especially useful for tracing or other debug output. * * @param AnyArray Any Array object. * * @return A comma-separated String containing the .toString() output of each element in the array. */ static public function formatArray(AnyArray:Array):String { if((AnyArray == null) || (AnyArray.length <= 0)) return ""; var string:String = AnyArray[0].toString(); var i:uint = 1; var l:uint = AnyArray.length; while(i < l) string += ", " + AnyArray[i++].toString(); return string; } /** * Automatically commas and decimals in the right places for displaying money amounts. * Does not include a dollar sign or anything, so doesn't really do much * if you call say var results:String = FlxU.formatMoney(10,false); * However, very handy for displaying large sums or decimal money values. * * @param Amount How much moneys (in dollars, or the equivalent "main" currency - i.e. not cents). * @param ShowDecimal Whether to show the decimals/cents component. Default value is true. * @param EnglishStyle Major quantities (thousands, millions, etc) separated by commas, and decimal by a period. Default value is true. * * @return A nicely formatted String. Does not include a dollar sign or anything! */ static public function formatMoney(Amount:Number,ShowDecimal:Boolean=true,EnglishStyle:Boolean=true):String { var helper:int; var amount:int = Amount; var string:String = ""; var comma:String = ""; var zeroes:String = ""; while(amount > 0) { if((string.length > 0) && comma.length <= 0) { if(EnglishStyle) comma = ","; else comma = "."; } zeroes = ""; helper = amount - int(amount/1000)*1000; amount /= 1000; if(amount > 0) { if(helper < 100) zeroes += "0"; if(helper < 10) zeroes += "0"; } string = zeroes + helper + comma + string; } if(ShowDecimal) { amount = int(Amount*100)-(int(Amount)*100); string += (EnglishStyle?".":",") + amount; if(amount < 10) string += "0"; } return string; } /** * Get the String name of any Object. * * @param Obj The Object object in question. * @param Simple Returns only the class name, not the package or packages. * * @return The name of the Class as a String object. */ static public function getClassName(Obj:Object,Simple:Boolean=false):String { var string:String = getQualifiedClassName(Obj); string = string.replace("::","."); if(Simple) string = string.substr(string.lastIndexOf(".")+1); return string; } /** * Check to see if two objects have the same class name. * * @param Object1 The first object you want to check. * @param Object2 The second object you want to check. * * @return Whether they have the same class name or not. */ static public function compareClassNames(Object1:Object,Object2:Object):Boolean { return getQualifiedClassName(Object1) == getQualifiedClassName(Object2); } /** * Look up a Class object by its string name. * * @param Name The String name of the Class you are interested in. * * @return A Class object. */ static public function getClass(Name:String):Class { return getDefinitionByName(Name) as Class; } /** * A tween-like function that takes a starting velocity * and some other factors and returns an altered velocity. * * @param Velocity Any component of velocity (e.g. 20). * @param Acceleration Rate at which the velocity is changing. * @param Drag Really kind of a deceleration, this is how much the velocity changes if Acceleration is not set. * @param Max An absolute value cap for the velocity. * * @return The altered Velocity value. */ static public function computeVelocity(Velocity:Number, Acceleration:Number=0, Drag:Number=0, Max:Number=10000):Number { if(Acceleration != 0) Velocity += Acceleration*FlxG.elapsed; else if(Drag != 0) { var drag:Number = Drag*FlxG.elapsed; if(Velocity - drag > 0) Velocity = Velocity - drag; else if(Velocity + drag < 0) Velocity += drag; else Velocity = 0; } if((Velocity != 0) && (Max != 10000)) { if(Velocity > Max) Velocity = Max; else if(Velocity < -Max) Velocity = -Max; } return Velocity; } //*** NOTE: THESE LAST THREE FUNCTIONS REQUIRE FLXPOINT ***// /** * Rotates a point in 2D space around another point by the given angle. * * @param X The X coordinate of the point you want to rotate. * @param Y The Y coordinate of the point you want to rotate. * @param PivotX The X coordinate of the point you want to rotate around. * @param PivotY The Y coordinate of the point you want to rotate around. * @param Angle Rotate the point by this many degrees. * @param Point Optional FlxPoint to store the results in. * * @return A FlxPoint containing the coordinates of the rotated point. */ static public function rotatePoint(X:Number, Y:Number, PivotX:Number, PivotY:Number, Angle:Number,Point:FlxPoint=null):FlxPoint { var sin:Number = 0; var cos:Number = 0; var radians:Number = Angle * -0.017453293; while (radians < -3.14159265) radians += 6.28318531; while (radians > 3.14159265) radians = radians - 6.28318531; if (radians < 0) { sin = 1.27323954 * radians + .405284735 * radians * radians; if (sin < 0) sin = .225 * (sin *-sin - sin) + sin; else sin = .225 * (sin * sin - sin) + sin; } else { sin = 1.27323954 * radians - 0.405284735 * radians * radians; if (sin < 0) sin = .225 * (sin *-sin - sin) + sin; else sin = .225 * (sin * sin - sin) + sin; } radians += 1.57079632; if (radians > 3.14159265) radians = radians - 6.28318531; if (radians < 0) { cos = 1.27323954 * radians + 0.405284735 * radians * radians; if (cos < 0) cos = .225 * (cos *-cos - cos) + cos; else cos = .225 * (cos * cos - cos) + cos; } else { cos = 1.27323954 * radians - 0.405284735 * radians * radians; if (cos < 0) cos = .225 * (cos *-cos - cos) + cos; else cos = .225 * (cos * cos - cos) + cos; } var dx:Number = X-PivotX; var dy:Number = PivotY+Y; //Y axis is inverted in flash, normally this would be a subtract operation if(Point == null) Point = new FlxPoint(); Point.x = PivotX + cos*dx - sin*dy; Point.y = PivotY - sin*dx - cos*dy; return Point; }; /** * Calculates the angle between two points. 0 degrees points straight up. * * @param Point1 The X coordinate of the point. * @param Point2 The Y coordinate of the point. * * @return The angle in degrees, between -180 and 180. */ static public function getAngle(Point1:FlxPoint, Point2:FlxPoint):Number { var x:Number = Point2.x - Point1.x; var y:Number = Point2.y - Point1.y; if((x == 0) && (y == 0)) return 0; var c1:Number = 3.14159265 * 0.25; var c2:Number = 3 * c1; var ay:Number = (y < 0)?-y:y; var angle:Number = 0; if (x >= 0) angle = c1 - c1 * ((x - ay) / (x + ay)); else angle = c2 - c1 * ((x + ay) / (ay - x)); angle = ((y < 0)?-angle:angle)*57.2957796; if(angle > 90) angle = angle - 270; else angle += 90; return angle; }; /** * Calculate the distance between two points. * * @param Point1 A FlxPoint object referring to the first location. * @param Point2 A FlxPoint object referring to the second location. * * @return The distance between the two points as a floating point Number object. */ static public function getDistance(Point1:FlxPoint,Point2:FlxPoint):Number { var dx:Number = Point1.x - Point2.x; var dy:Number = Point1.y - Point2.y; return Math.sqrt(dx * dx + dy * dy); } } } ================================================ FILE: org/flixel/plugin/DebugPathDisplay.as ================================================ package org.flixel.plugin { import org.flixel.*; /** * A simple manager for tracking and drawing FlxPath debug data to the screen. * * @author Adam Atomic */ public class DebugPathDisplay extends FlxBasic { protected var _paths:Array; /** * Instantiates a new debug path display manager. */ public function DebugPathDisplay() { _paths = new Array(); active = false; //don't call update on this plugin } /** * Clean up memory. */ override public function destroy():void { super.destroy(); clear(); _paths = null; } /** * Called by FlxG.drawPlugins() after the game state has been drawn. * Cycles through cameras and calls drawDebug() on each one. */ override public function draw():void { if(!FlxG.visualDebug || ignoreDrawDebug) return; if(cameras == null) cameras = FlxG.cameras; var i:uint = 0; var l:uint = cameras.length; while(i < l) drawDebug(cameras[i++]); } /** * Similar to FlxObject's drawDebug() functionality, * this function calls drawDebug() on each FlxPath for the specified camera. * Very helpful for debugging! * * @param Camera Which FlxCamera object to draw the debug data to. */ override public function drawDebug(Camera:FlxCamera=null):void { if(Camera == null) Camera = FlxG.camera; var i:int = _paths.length-1; var path:FlxPath; while(i >= 0) { path = _paths[i--] as FlxPath; if((path != null) && !path.ignoreDrawDebug) path.drawDebug(Camera); } } /** * Add a path to the path debug display manager. * Usually called automatically by FlxPath's constructor. * * @param Path The FlxPath you want to add to the manager. */ public function add(Path:FlxPath):void { _paths.push(Path); } /** * Remove a path from the path debug display manager. * Usually called automatically by FlxPath's destroy() function. * * @param Path The FlxPath you want to remove from the manager. */ public function remove(Path:FlxPath):void { var index:int = _paths.indexOf(Path); if(index >= 0) _paths.splice(index,1); } /** * Removes all the paths from the path debug display manager. */ public function clear():void { var i:int = _paths.length-1; var path:FlxPath; while(i >= 0) { path = _paths[i--] as FlxPath; if(path != null) path.destroy(); } _paths.length = 0; } } } ================================================ FILE: org/flixel/plugin/TimerManager.as ================================================ package org.flixel.plugin { import org.flixel.*; /** * A simple manager for tracking and updating game timer objects. * * @author Adam Atomic */ public class TimerManager extends FlxBasic { protected var _timers:Array; /** * Instantiates a new timer manager. */ public function TimerManager() { _timers = new Array(); visible = false; //don't call draw on this plugin } /** * Clean up memory. */ override public function destroy():void { clear(); _timers = null; } /** * Called by FlxG.updatePlugins() before the game state has been updated. * Cycles through timers and calls update() on each one. */ override public function update():void { var i:int = _timers.length-1; var timer:FlxTimer; while(i >= 0) { timer = _timers[i--] as FlxTimer; if((timer != null) && !timer.paused && !timer.finished && (timer.time > 0)) timer.update(); } } /** * Add a new timer to the timer manager. * Usually called automatically by FlxTimer's constructor. * * @param Timer The FlxTimer you want to add to the manager. */ public function add(Timer:FlxTimer):void { _timers.push(Timer); } /** * Remove a timer from the timer manager. * Usually called automatically by FlxTimer's stop() function. * * @param Timer The FlxTimer you want to remove from the manager. */ public function remove(Timer:FlxTimer):void { var index:int = _timers.indexOf(Timer); if(index >= 0) _timers.splice(index,1); } /** * Removes all the timers from the timer manager. */ public function clear():void { var i:int = _timers.length-1; var timer:FlxTimer; while(i >= 0) { timer = _timers[i--] as FlxTimer; if(timer != null) timer.destroy(); } _timers.length = 0; } } } ================================================ FILE: org/flixel/system/FlxAnim.as ================================================ package org.flixel.system { /** * Just a helper structure for the FlxSprite animation system. * * @author Adam Atomic */ public class FlxAnim { /** * String name of the animation (e.g. "walk") */ public var name:String; /** * Seconds between frames (basically the framerate) */ public var delay:Number; /** * A list of frames stored as uint objects */ public var frames:Array; /** * Whether or not the animation is looped */ public var looped:Boolean; /** * Constructor * * @param Name What this animation should be called (e.g. "run") * @param Frames An array of numbers indicating what frames to play in what order (e.g. 1, 2, 3) * @param FrameRate The speed in frames per second that the animation should play at (e.g. 40) * @param Looped Whether or not the animation is looped or just plays once */ public function FlxAnim(Name:String, Frames:Array, FrameRate:Number=0, Looped:Boolean=true) { name = Name; delay = 0; if(FrameRate > 0) delay = 1.0/FrameRate; frames = Frames; looped = Looped; } /** * Clean up memory. */ public function destroy():void { frames = null; } } } ================================================ FILE: org/flixel/system/FlxDebugger.as ================================================ package org.flixel.system { import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.Sprite; import flash.events.MouseEvent; import flash.geom.Point; import flash.geom.Rectangle; import flash.text.TextField; import flash.text.TextFormat; import org.flixel.FlxG; import org.flixel.system.debug.Log; import org.flixel.system.debug.Perf; import org.flixel.system.debug.VCR; import org.flixel.system.debug.Vis; import org.flixel.system.debug.Watch; /** * Container for the new debugger overlay. * Most of the functionality is in the debug folder widgets, * but this class instantiates the widgets and handles their basic formatting and arrangement. */ public class FlxDebugger extends Sprite { /** * Container for the performance monitor widget. */ public var perf:Perf; /** * Container for the trace output widget. */ public var log:Log; /** * Container for the watch window widget. */ public var watch:Watch; /** * Container for the record, stop and play buttons. */ public var vcr:VCR; /** * Container for the visual debug mode toggle. */ public var vis:Vis; /** * Whether the mouse is currently over one of the debugger windows or not. */ public var hasMouse:Boolean; /** * Internal, tracks what debugger window layout user has currently selected. */ protected var _layout:uint; /** * Internal, stores width and height of the Flash Player window. */ protected var _screen:Point; /** * Internal, used to space out windows from the edges. */ protected var _gutter:uint; /** * Instantiates the debugger overlay. * * @param Width The width of the screen. * @param Height The height of the screen. */ public function FlxDebugger(Width:Number,Height:Number) { super(); visible = false; hasMouse = false; _screen = new Point(Width,Height); addChild(new Bitmap(new BitmapData(Width,15,true,0x7f000000))); var txt:TextField = new TextField(); txt.x = 2; txt.width = 160; txt.height = 16; txt.selectable = false; txt.multiline = false; txt.defaultTextFormat = new TextFormat("Courier",12,0xffffff); var str:String = FlxG.getLibraryName(); if(FlxG.debug) str += " [debug]"; else str += " [release]"; txt.text = str; addChild(txt); _gutter = 8; var screenBounds:Rectangle = new Rectangle(_gutter,15+_gutter/2,_screen.x-_gutter*2,_screen.y-_gutter*1.5-15); log = new Log("log",0,0,true,screenBounds); addChild(log); watch = new Watch("watch",0,0,true,screenBounds); addChild(watch); perf = new Perf("stats",0,0,false,screenBounds); addChild(perf); vcr = new VCR(); vcr.x = (Width - vcr.width/2)/2; vcr.y = 2; addChild(vcr); vis = new Vis(); vis.x = Width-vis.width - 4; vis.y = 2; addChild(vis); setLayout(FlxG.DEBUGGER_STANDARD); //Should help with fake mouse focus type behavior addEventListener(MouseEvent.MOUSE_OVER,handleMouseOver); addEventListener(MouseEvent.MOUSE_OUT,handleMouseOut); } /** * Clean up memory. */ public function destroy():void { _screen = null; removeChild(log); log.destroy(); log = null; removeChild(watch); watch.destroy(); watch = null; removeChild(perf); perf.destroy(); perf = null; removeChild(vcr); vcr.destroy(); vcr = null; removeChild(vis); vis.destroy(); vis = null; removeEventListener(MouseEvent.MOUSE_OVER,handleMouseOver); removeEventListener(MouseEvent.MOUSE_OUT,handleMouseOut); } /** * Mouse handler that helps with fake "mouse focus" type behavior. * * @param E Flash mouse event. */ protected function handleMouseOver(E:MouseEvent=null):void { hasMouse = true; } /** * Mouse handler that helps with fake "mouse focus" type behavior. * * @param E Flash mouse event. */ protected function handleMouseOut(E:MouseEvent=null):void { hasMouse = false; } /** * Rearrange the debugger windows using one of the constants specified in FlxG. * * @param Layout The layout style for the debugger windows, e.g. FlxG.DEBUGGER_MICRO. */ public function setLayout(Layout:uint):void { _layout = Layout; resetLayout(); } /** * Forces the debugger windows to reset to the last specified layout. * The default layout is FlxG.DEBUGGER_STANDARD. */ public function resetLayout():void { switch(_layout) { case FlxG.DEBUGGER_MICRO: log.resize(_screen.x/4,68); log.reposition(0,_screen.y); watch.resize(_screen.x/4,68); watch.reposition(_screen.x,_screen.y); perf.reposition(_screen.x,0); break; case FlxG.DEBUGGER_BIG: log.resize((_screen.x-_gutter*3)/2,_screen.y/2); log.reposition(0,_screen.y); watch.resize((_screen.x-_gutter*3)/2,_screen.y/2); watch.reposition(_screen.x,_screen.y); perf.reposition(_screen.x,0); break; case FlxG.DEBUGGER_TOP: log.resize((_screen.x-_gutter*3)/2,_screen.y/4); log.reposition(0,0); watch.resize((_screen.x-_gutter*3)/2,_screen.y/4); watch.reposition(_screen.x,0); perf.reposition(_screen.x,_screen.y); break; case FlxG.DEBUGGER_LEFT: log.resize(_screen.x/3,(_screen.y-15-_gutter*2.5)/2); log.reposition(0,0); watch.resize(_screen.x/3,(_screen.y-15-_gutter*2.5)/2); watch.reposition(0,_screen.y); perf.reposition(_screen.x,0); break; case FlxG.DEBUGGER_RIGHT: log.resize(_screen.x/3,(_screen.y-15-_gutter*2.5)/2); log.reposition(_screen.x,0); watch.resize(_screen.x/3,(_screen.y-15-_gutter*2.5)/2); watch.reposition(_screen.x,_screen.y); perf.reposition(0,0); break; case FlxG.DEBUGGER_STANDARD: default: log.resize((_screen.x-_gutter*3)/2,_screen.y/4); log.reposition(0,_screen.y); watch.resize((_screen.x-_gutter*3)/2,_screen.y/4); watch.reposition(_screen.x,_screen.y); perf.reposition(_screen.x,0); break; } } } } ================================================ FILE: org/flixel/system/FlxList.as ================================================ package org.flixel.system { import org.flixel.FlxObject; /** * A miniature linked list class. * Useful for optimizing time-critical or highly repetitive tasks! * See FlxQuadTree for how to use it, IF YOU DARE. */ public class FlxList { /** * Stores a reference to a FlxObject. */ public var object:FlxObject; /** * Stores a reference to the next link in the list. */ public var next:FlxList; /** * Creates a new link, and sets object and next to null. */ public function FlxList() { object = null; next = null; } /** * Clean up memory. */ public function destroy():void { object = null; if(next != null) next.destroy(); next = null; } } } ================================================ FILE: org/flixel/system/FlxPreloader.as ================================================ package org.flixel.system { import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.DisplayObject; 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.net.URLRequest; import flash.net.navigateToURL; import flash.text.TextField; import flash.text.TextFormat; import flash.utils.getDefinitionByName; import flash.utils.getTimer; import org.flixel.FlxG; /** * This class handles the 8-bit style preloader. */ public class FlxPreloader extends MovieClip { [Embed(source="../data/logo.png")] protected var ImgLogo:Class; [Embed(source="../data/logo_corners.png")] protected var ImgLogoCorners:Class; [Embed(source="../data/logo_light.png")] protected var ImgLogoLight:Class; /** * @private */ protected var _init:Boolean; /** * @private */ protected var _buffer:Sprite; /** * @private */ protected var _bmpBar:Bitmap; /** * @private */ protected var _text:TextField; /** * Useful for storing "real" stage width if you're scaling your preloader graphics. */ protected var _width:uint; /** * Useful for storing "real" stage height if you're scaling your preloader graphics. */ protected var _height:uint; /** * @private */ protected var _logo:Bitmap; /** * @private */ protected var _logoGlow:Bitmap; /** * @private */ protected var _min:uint; /** * This should always be the name of your main project/document class (e.g. GravityHook). */ public var className:String; /** * Set this to your game's URL to use built-in site-locking. */ public var myURL:String; /** * Change this if you want the flixel logo to show for more or less time. Default value is 0 seconds. */ public var minDisplayTime:Number; /** * Constructor */ public function FlxPreloader() { minDisplayTime = 0; stop(); stage.scaleMode = StageScaleMode.NO_SCALE; stage.align = StageAlign.TOP_LEFT; //Check if we are on debug or release mode and set _DEBUG accordingly try { throw new Error("Setting global debug flag..."); } catch(E:Error) { var re:RegExp = /\[.*:[0-9]+\]/; FlxG.debug = re.test(E.getStackTrace()); } var tmp:Bitmap; if(!FlxG.debug && (myURL != null) && (root.loaderInfo.url.indexOf(myURL) < 0)) { tmp = new Bitmap(new BitmapData(stage.stageWidth,stage.stageHeight,true,0xFFFFFFFF)); addChild(tmp); var format:TextFormat = new TextFormat(); format.color = 0x000000; format.size = 16; format.align = "center"; format.bold = true; format.font = "system"; var textField:TextField = new TextField(); textField.width = tmp.width-16; textField.height = tmp.height-16; textField.y = 8; textField.multiline = true; textField.wordWrap = true; textField.embedFonts = true; textField.defaultTextFormat = format; textField.text = "Hi there! It looks like somebody copied this game without my permission. Just click anywhere, or copy-paste this URL into your browser.\n\n"+myURL+"\n\nto play the game at my site. Thanks, and have fun!"; addChild(textField); textField.addEventListener(MouseEvent.CLICK,goToMyURL); tmp.addEventListener(MouseEvent.CLICK,goToMyURL); return; } this._init = false; addEventListener(Event.ENTER_FRAME, onEnterFrame); } private function goToMyURL(event:MouseEvent=null):void { var prefix:String = myURL.match(/^https?:/) ? "" : "http://"; navigateToURL(new URLRequest(prefix+myURL)); } private function onEnterFrame(event:Event):void { if(!this._init) { if((stage.stageWidth <= 0) || (stage.stageHeight <= 0)) return; create(); this._init = true; } graphics.clear(); var time:uint = getTimer(); if((framesLoaded >= totalFrames) && (time > _min)) { removeEventListener(Event.ENTER_FRAME, onEnterFrame); nextFrame(); var mainClass:Class = Class(getDefinitionByName(className)); if(mainClass) { var app:Object = new mainClass(); addChild(app as DisplayObject); } destroy(); } else { var percent:Number = root.loaderInfo.bytesLoaded/root.loaderInfo.bytesTotal; if((_min > 0) && (percent > time/_min)) percent = time/_min; update(percent); } } /** * Override this to create your own preloader objects. * Highly recommended you also override update()! */ protected function create():void { _min = 0; if(!FlxG.debug) _min = minDisplayTime*1000; _buffer = new Sprite(); _buffer.scaleX = 2; _buffer.scaleY = 2; addChild(_buffer); _width = stage.stageWidth/_buffer.scaleX; _height = stage.stageHeight/_buffer.scaleY; _buffer.addChild(new Bitmap(new BitmapData(_width,_height,false,0x00345e))); var bitmap:Bitmap = new ImgLogoLight(); bitmap.smoothing = true; bitmap.width = bitmap.height = _height; bitmap.x = (_width-bitmap.width)/2; _buffer.addChild(bitmap); _bmpBar = new Bitmap(new BitmapData(1,7,false,0x5f6aff)); _bmpBar.x = 4; _bmpBar.y = _height-11; _buffer.addChild(_bmpBar); _text = new TextField(); _text.defaultTextFormat = new TextFormat("system",8,0x5f6aff); _text.embedFonts = true; _text.selectable = false; _text.multiline = false; _text.x = 2; _text.y = _bmpBar.y - 11; _text.width = 80; _buffer.addChild(_text); _logo = new ImgLogo(); _logo.scaleX = _logo.scaleY = _height/8; _logo.x = (_width-_logo.width)/2; _logo.y = (_height-_logo.height)/2; _buffer.addChild(_logo); _logoGlow = new ImgLogo(); _logoGlow.smoothing = true; _logoGlow.blendMode = "screen"; _logoGlow.scaleX = _logoGlow.scaleY = _height/8; _logoGlow.x = (_width-_logoGlow.width)/2; _logoGlow.y = (_height-_logoGlow.height)/2; _buffer.addChild(_logoGlow); bitmap = new ImgLogoCorners(); bitmap.smoothing = true; bitmap.width = _width; bitmap.height = _height; _buffer.addChild(bitmap); bitmap = new Bitmap(new BitmapData(_width,_height,false,0xffffff)); var i:uint = 0; var j:uint = 0; while(i < _height) { j = 0; while(j < _width) bitmap.bitmapData.setPixel(j++,i,0); i+=2; } bitmap.blendMode = "overlay"; bitmap.alpha = 0.25; _buffer.addChild(bitmap); } protected function destroy():void { removeChild(_buffer); _buffer = null; _bmpBar = null; _text = null; _logo = null; _logoGlow = null; } /** * Override this function to manually update the preloader. * * @param Percent How much of the program has loaded. */ protected function update(Percent:Number):void { _bmpBar.scaleX = Percent*(_width-8); _text.text = "FLX v"+FlxG.LIBRARY_MAJOR_VERSION+"."+FlxG.LIBRARY_MINOR_VERSION+" "+Math.floor(Percent*100)+"%"; _text.setTextFormat(_text.defaultTextFormat); if(Percent < 0.1) { _logoGlow.alpha = 0; _logo.alpha = 0; } else if(Percent < 0.15) { _logoGlow.alpha = Math.random(); _logo.alpha = 0; } else if(Percent < 0.2) { _logoGlow.alpha = 0; _logo.alpha = 0; } else if(Percent < 0.25) { _logoGlow.alpha = 0; _logo.alpha = Math.random(); } else if(Percent < 0.7) { _logoGlow.alpha = (Percent-0.45)/0.45; _logo.alpha = 1; } else if((Percent > 0.8) && (Percent < 0.9)) { _logoGlow.alpha = 1-(Percent-0.8)/0.1; _logo.alpha = 0; } else if(Percent > 0.9) { _buffer.alpha = 1-(Percent-0.9)/0.1; } } } } ================================================ FILE: org/flixel/system/FlxQuadTree.as ================================================ package org.flixel.system { import org.flixel.FlxBasic; import org.flixel.FlxGroup; import org.flixel.FlxObject; import org.flixel.FlxRect; /** * A fairly generic quad tree structure for rapid overlap checks. * FlxQuadTree is also configured for single or dual list operation. * You can add items either to its A list or its B list. * When you do an overlap check, you can compare the A list to itself, * or the A list against the B list. Handy for different things! */ public class FlxQuadTree extends FlxRect { /** * Flag for specifying that you want to add an object to the A list. */ static public const A_LIST:uint = 0; /** * Flag for specifying that you want to add an object to the B list. */ static public const B_LIST:uint = 1; /** * Controls the granularity of the quad tree. Default is 6 (decent performance on large and small worlds). */ static public var divisions:uint; /** * Whether this branch of the tree can be subdivided or not. */ protected var _canSubdivide:Boolean; /** * Refers to the internal A and B linked lists, * which are used to store objects in the leaves. */ protected var _headA:FlxList; /** * Refers to the internal A and B linked lists, * which are used to store objects in the leaves. */ protected var _tailA:FlxList; /** * Refers to the internal A and B linked lists, * which are used to store objects in the leaves. */ protected var _headB:FlxList; /** * Refers to the internal A and B linked lists, * which are used to store objects in the leaves. */ protected var _tailB:FlxList; /** * Internal, governs and assists with the formation of the tree. */ static protected var _min:uint; /** * Internal, governs and assists with the formation of the tree. */ protected var _northWestTree:FlxQuadTree; /** * Internal, governs and assists with the formation of the tree. */ protected var _northEastTree:FlxQuadTree; /** * Internal, governs and assists with the formation of the tree. */ protected var _southEastTree:FlxQuadTree; /** * Internal, governs and assists with the formation of the tree. */ protected var _southWestTree:FlxQuadTree; /** * Internal, governs and assists with the formation of the tree. */ protected var _leftEdge:Number; /** * Internal, governs and assists with the formation of the tree. */ protected var _rightEdge:Number; /** * Internal, governs and assists with the formation of the tree. */ protected var _topEdge:Number; /** * Internal, governs and assists with the formation of the tree. */ protected var _bottomEdge:Number; /** * Internal, governs and assists with the formation of the tree. */ protected var _halfWidth:Number; /** * Internal, governs and assists with the formation of the tree. */ protected var _halfHeight:Number; /** * Internal, governs and assists with the formation of the tree. */ protected var _midpointX:Number; /** * Internal, governs and assists with the formation of the tree. */ protected var _midpointY:Number; /** * Internal, used to reduce recursive method parameters during object placement and tree formation. */ static protected var _object:FlxObject; /** * Internal, used to reduce recursive method parameters during object placement and tree formation. */ static protected var _objectLeftEdge:Number; /** * Internal, used to reduce recursive method parameters during object placement and tree formation. */ static protected var _objectTopEdge:Number; /** * Internal, used to reduce recursive method parameters during object placement and tree formation. */ static protected var _objectRightEdge:Number; /** * Internal, used to reduce recursive method parameters during object placement and tree formation. */ static protected var _objectBottomEdge:Number; /** * Internal, used during tree processing and overlap checks. */ static protected var _list:uint; /** * Internal, used during tree processing and overlap checks. */ static protected var _useBothLists:Boolean; /** * Internal, used during tree processing and overlap checks. */ static protected var _processingCallback:Function; /** * Internal, used during tree processing and overlap checks. */ static protected var _notifyCallback:Function; /** * Internal, used during tree processing and overlap checks. */ static protected var _iterator:FlxList; /** * Internal, helpers for comparing actual object-to-object overlap - see overlapNode(). */ static protected var _objectHullX:Number; /** * Internal, helpers for comparing actual object-to-object overlap - see overlapNode(). */ static protected var _objectHullY:Number; /** * Internal, helpers for comparing actual object-to-object overlap - see overlapNode(). */ static protected var _objectHullWidth:Number; /** * Internal, helpers for comparing actual object-to-object overlap - see overlapNode(). */ static protected var _objectHullHeight:Number; /** * Internal, helpers for comparing actual object-to-object overlap - see overlapNode(). */ static protected var _checkObjectHullX:Number; /** * Internal, helpers for comparing actual object-to-object overlap - see overlapNode(). */ static protected var _checkObjectHullY:Number; /** * Internal, helpers for comparing actual object-to-object overlap - see overlapNode(). */ static protected var _checkObjectHullWidth:Number; /** * Internal, helpers for comparing actual object-to-object overlap - see overlapNode(). */ static protected var _checkObjectHullHeight:Number; /** * Instantiate a new Quad Tree node. * * @param X The X-coordinate of the point in space. * @param Y The Y-coordinate of the point in space. * @param Width Desired width of this node. * @param Height Desired height of this node. * @param Parent The parent branch or node. Pass null to create a root. */ public function FlxQuadTree(X:Number, Y:Number, Width:Number, Height:Number, Parent:FlxQuadTree=null) { super(X,Y,Width,Height); _headA = _tailA = new FlxList(); _headB = _tailB = new FlxList(); //Copy the parent's children (if there are any) if(Parent != null) { var iterator:FlxList; var ot:FlxList; if(Parent._headA.object != null) { iterator = Parent._headA; while(iterator != null) { if(_tailA.object != null) { ot = _tailA; _tailA = new FlxList(); ot.next = _tailA; } _tailA.object = iterator.object; iterator = iterator.next; } } if(Parent._headB.object != null) { iterator = Parent._headB; while(iterator != null) { if(_tailB.object != null) { ot = _tailB; _tailB = new FlxList(); ot.next = _tailB; } _tailB.object = iterator.object; iterator = iterator.next; } } } else _min = (width + height)/(2*divisions); _canSubdivide = (width > _min) || (height > _min); //Set up comparison/sort helpers _northWestTree = null; _northEastTree = null; _southEastTree = null; _southWestTree = null; _leftEdge = x; _rightEdge = x+width; _halfWidth = width/2; _midpointX = _leftEdge+_halfWidth; _topEdge = y; _bottomEdge = y+height; _halfHeight = height/2; _midpointY = _topEdge+_halfHeight; } /** * Clean up memory. */ public function destroy():void { _headA.destroy(); _headA = null; _tailA.destroy(); _tailA = null; _headB.destroy(); _headB = null; _tailB.destroy(); _tailB = null; if(_northWestTree != null) _northWestTree.destroy(); _northWestTree = null; if(_northEastTree != null) _northEastTree.destroy(); _northEastTree = null; if(_southEastTree != null) _southEastTree.destroy(); _southEastTree = null; if(_southWestTree != null) _southWestTree.destroy(); _southWestTree = null; _object = null; _processingCallback = null; _notifyCallback = null; } /** * Load objects and/or groups into the quad tree, and register notify and processing callbacks. * * @param ObjectOrGroup1 Any object that is or extends FlxObject or FlxGroup. * @param ObjectOrGroup2 Any object that is or extends FlxObject or FlxGroup. If null, the first parameter will be checked against itself. * @param NotifyCallback A function with the form myFunction(Object1:FlxObject,Object2:FlxObject):void that is called whenever two objects are found to overlap in world space, and either no ProcessCallback is specified, or the ProcessCallback returns true. * @param ProcessCallback A function with the form myFunction(Object1:FlxObject,Object2:FlxObject):Boolean that is called whenever two objects are found to overlap in world space. The NotifyCallback is only called if this function returns true. See FlxObject.separate(). */ public function load(ObjectOrGroup1:FlxBasic, ObjectOrGroup2:FlxBasic=null, NotifyCallback:Function=null, ProcessCallback:Function=null):void { add(ObjectOrGroup1, A_LIST); if(ObjectOrGroup2 != null) { add(ObjectOrGroup2, B_LIST); _useBothLists = true; } else _useBothLists = false; _notifyCallback = NotifyCallback; _processingCallback = ProcessCallback; } /** * Call this function to add an object to the root of the tree. * This function will recursively add all group members, but * not the groups themselves. * * @param ObjectOrGroup FlxObjects are just added, FlxGroups are recursed and their applicable members added accordingly. * @param List A uint flag indicating the list to which you want to add the objects. Options are A_LIST and B_LIST. */ public function add(ObjectOrGroup:FlxBasic, List:uint):void { _list = List; if(ObjectOrGroup is FlxGroup) { var i:uint = 0; var basic:FlxBasic; var members:Array = (ObjectOrGroup as FlxGroup).members; var l:uint = (ObjectOrGroup as FlxGroup).length; while(i < l) { basic = members[i++] as FlxBasic; if((basic != null) && basic.exists) { if(basic is FlxGroup) add(basic,List); else if(basic is FlxObject) { _object = basic as FlxObject; if(_object.exists && _object.allowCollisions) { _objectLeftEdge = _object.x; _objectTopEdge = _object.y; _objectRightEdge = _object.x + _object.width; _objectBottomEdge = _object.y + _object.height; addObject(); } } } } } else { _object = ObjectOrGroup as FlxObject; if(_object.exists && _object.allowCollisions) { _objectLeftEdge = _object.x; _objectTopEdge = _object.y; _objectRightEdge = _object.x + _object.width; _objectBottomEdge = _object.y + _object.height; addObject(); } } } /** * Internal function for recursively navigating and creating the tree * while adding objects to the appropriate nodes. */ protected function addObject():void { //If this quad (not its children) lies entirely inside this object, add it here if(!_canSubdivide || ((_leftEdge >= _objectLeftEdge) && (_rightEdge <= _objectRightEdge) && (_topEdge >= _objectTopEdge) && (_bottomEdge <= _objectBottomEdge))) { addToList(); return; } //See if the selected object fits completely inside any of the quadrants if((_objectLeftEdge > _leftEdge) && (_objectRightEdge < _midpointX)) { if((_objectTopEdge > _topEdge) && (_objectBottomEdge < _midpointY)) { if(_northWestTree == null) _northWestTree = new FlxQuadTree(_leftEdge,_topEdge,_halfWidth,_halfHeight,this); _northWestTree.addObject(); return; } if((_objectTopEdge > _midpointY) && (_objectBottomEdge < _bottomEdge)) { if(_southWestTree == null) _southWestTree = new FlxQuadTree(_leftEdge,_midpointY,_halfWidth,_halfHeight,this); _southWestTree.addObject(); return; } } if((_objectLeftEdge > _midpointX) && (_objectRightEdge < _rightEdge)) { if((_objectTopEdge > _topEdge) && (_objectBottomEdge < _midpointY)) { if(_northEastTree == null) _northEastTree = new FlxQuadTree(_midpointX,_topEdge,_halfWidth,_halfHeight,this); _northEastTree.addObject(); return; } if((_objectTopEdge > _midpointY) && (_objectBottomEdge < _bottomEdge)) { if(_southEastTree == null) _southEastTree = new FlxQuadTree(_midpointX,_midpointY,_halfWidth,_halfHeight,this); _southEastTree.addObject(); return; } } //If it wasn't completely contained we have to check out the partial overlaps if((_objectRightEdge > _leftEdge) && (_objectLeftEdge < _midpointX) && (_objectBottomEdge > _topEdge) && (_objectTopEdge < _midpointY)) { if(_northWestTree == null) _northWestTree = new FlxQuadTree(_leftEdge,_topEdge,_halfWidth,_halfHeight,this); _northWestTree.addObject(); } if((_objectRightEdge > _midpointX) && (_objectLeftEdge < _rightEdge) && (_objectBottomEdge > _topEdge) && (_objectTopEdge < _midpointY)) { if(_northEastTree == null) _northEastTree = new FlxQuadTree(_midpointX,_topEdge,_halfWidth,_halfHeight,this); _northEastTree.addObject(); } if((_objectRightEdge > _midpointX) && (_objectLeftEdge < _rightEdge) && (_objectBottomEdge > _midpointY) && (_objectTopEdge < _bottomEdge)) { if(_southEastTree == null) _southEastTree = new FlxQuadTree(_midpointX,_midpointY,_halfWidth,_halfHeight,this); _southEastTree.addObject(); } if((_objectRightEdge > _leftEdge) && (_objectLeftEdge < _midpointX) && (_objectBottomEdge > _midpointY) && (_objectTopEdge < _bottomEdge)) { if(_southWestTree == null) _southWestTree = new FlxQuadTree(_leftEdge,_midpointY,_halfWidth,_halfHeight,this); _southWestTree.addObject(); } } /** * Internal function for recursively adding objects to leaf lists. */ protected function addToList():void { var ot:FlxList; if(_list == A_LIST) { if(_tailA.object != null) { ot = _tailA; _tailA = new FlxList(); ot.next = _tailA; } _tailA.object = _object; } else { if(_tailB.object != null) { ot = _tailB; _tailB = new FlxList(); ot.next = _tailB; } _tailB.object = _object; } if(!_canSubdivide) return; if(_northWestTree != null) _northWestTree.addToList(); if(_northEastTree != null) _northEastTree.addToList(); if(_southEastTree != null) _southEastTree.addToList(); if(_southWestTree != null) _southWestTree.addToList(); } /** * FlxQuadTree's other main function. Call this after adding objects * using FlxQuadTree.load() to compare the objects that you loaded. * * @return Whether or not any overlaps were found. */ public function execute():Boolean { var overlapProcessed:Boolean = false; var iterator:FlxList; if(_headA.object != null) { iterator = _headA; while(iterator != null) { _object = iterator.object; if(_useBothLists) _iterator = _headB; else _iterator = iterator.next; if( _object.exists && (_object.allowCollisions > 0) && (_iterator != null) && (_iterator.object != null) && _iterator.object.exists &&overlapNode()) { overlapProcessed = true; } iterator = iterator.next; } } //Advance through the tree by calling overlap on each child if((_northWestTree != null) && _northWestTree.execute()) overlapProcessed = true; if((_northEastTree != null) && _northEastTree.execute()) overlapProcessed = true; if((_southEastTree != null) && _southEastTree.execute()) overlapProcessed = true; if((_southWestTree != null) && _southWestTree.execute()) overlapProcessed = true; return overlapProcessed; } /** * An internal function for comparing an object against the contents of a node. * * @return Whether or not any overlaps were found. */ protected function overlapNode():Boolean { //Walk the list and check for overlaps var overlapProcessed:Boolean = false; var checkObject:FlxObject; while(_iterator != null) { if(!_object.exists || (_object.allowCollisions <= 0)) break; checkObject = _iterator.object; if((_object === checkObject) || !checkObject.exists || (checkObject.allowCollisions <= 0)) { _iterator = _iterator.next; continue; } //calculate bulk hull for _object _objectHullX = (_object.x < _object.last.x)?_object.x:_object.last.x; _objectHullY = (_object.y < _object.last.y)?_object.y:_object.last.y; _objectHullWidth = _object.x - _object.last.x; _objectHullWidth = _object.width + ((_objectHullWidth>0)?_objectHullWidth:-_objectHullWidth); _objectHullHeight = _object.y - _object.last.y; _objectHullHeight = _object.height + ((_objectHullHeight>0)?_objectHullHeight:-_objectHullHeight); //calculate bulk hull for checkObject _checkObjectHullX = (checkObject.x < checkObject.last.x)?checkObject.x:checkObject.last.x; _checkObjectHullY = (checkObject.y < checkObject.last.y)?checkObject.y:checkObject.last.y; _checkObjectHullWidth = checkObject.x - checkObject.last.x; _checkObjectHullWidth = checkObject.width + ((_checkObjectHullWidth>0)?_checkObjectHullWidth:-_checkObjectHullWidth); _checkObjectHullHeight = checkObject.y - checkObject.last.y; _checkObjectHullHeight = checkObject.height + ((_checkObjectHullHeight>0)?_checkObjectHullHeight:-_checkObjectHullHeight); //check for intersection of the two hulls if( (_objectHullX + _objectHullWidth > _checkObjectHullX) && (_objectHullX < _checkObjectHullX + _checkObjectHullWidth) && (_objectHullY + _objectHullHeight > _checkObjectHullY) && (_objectHullY < _checkObjectHullY + _checkObjectHullHeight) ) { //Execute callback functions if they exist if((_processingCallback == null) || _processingCallback(_object,checkObject)) overlapProcessed = true; if(overlapProcessed && (_notifyCallback != null)) _notifyCallback(_object,checkObject); } _iterator = _iterator.next; } return overlapProcessed; } } } ================================================ FILE: org/flixel/system/FlxReplay.as ================================================ package org.flixel.system { import org.flixel.FlxG; import org.flixel.system.replay.FrameRecord; import org.flixel.system.replay.MouseRecord; /** * The replay object both records and replays game recordings, * as well as handle saving and loading replays to and from files. * Gameplay recordings are essentially a list of keyboard and mouse inputs, * but since Flixel is fairly deterministic, we can use these to play back * recordings of gameplay with a decent amount of fidelity. * * @author Adam Atomic */ public class FlxReplay { /** * The random number generator seed value for this recording. */ public var seed:Number; /** * The current frame for this recording. */ public var frame:int; /** * The number of frames in this recording. */ public var frameCount:int; /** * Whether the replay has finished playing or not. */ public var finished:Boolean; /** * Internal container for all the frames in this replay. */ protected var _frames:Array; /** * Internal tracker for max number of frames we can fit before growing the _frames again. */ protected var _capacity:int; /** * Internal helper variable for keeping track of where we are in _frames during recording or replay. */ protected var _marker:int; /** * Instantiate a new replay object. Doesn't actually do much until you call create() or load(). */ public function FlxReplay() { seed = 0; frame = 0; frameCount = 0; finished = false; _frames = null; _capacity = 0; _marker = 0; } /** * Clean up memory. */ public function destroy():void { if(_frames == null) return; var i:int = frameCount-1; while(i >= 0) (_frames[i--] as FrameRecord).destroy(); _frames = null; } /** * Create a new gameplay recording. Requires the current random number generator seed. * * @param Seed The current seed from the random number generator. */ public function create(Seed:Number):void { destroy(); init(); seed = Seed; rewind(); } /** * Load replay data from a String object. * Strings can come from embedded assets or external * files loaded through the debugger overlay. * * @param FileContents A String object containing a gameplay recording. */ public function load(FileContents:String):void { init(); var lines:Array = FileContents.split("\n"); seed = Number(lines[0]); var line:String; var i:uint = 1; var l:uint = lines.length; while(i < l) { line = lines[i++] as String; if(line.length > 3) { _frames[frameCount++] = new FrameRecord().load(line); if(frameCount >= _capacity) { _capacity *= 2; _frames.length = _capacity; } } } rewind(); } /** * Common initialization terms used by both create() and load() to set up the replay object. */ protected function init():void { _capacity = 100; _frames = new Array(_capacity); frameCount = 0; } /** * Save the current recording data off to a String object. * Basically goes through and calls FrameRecord.save() on each frame in the replay. * * return The gameplay recording in simple ASCII format. */ public function save():String { if(frameCount <= 0) return null; var output:String = seed+"\n"; var i:uint = 0; while(i < frameCount) output += _frames[i++].save() + "\n"; return output; } /** * Get the current input data from the input managers and store it in a new frame record. */ public function recordFrame():void { var keysRecord:Array = FlxG.keys.record(); var mouseRecord:MouseRecord = FlxG.mouse.record(); if((keysRecord == null) && (mouseRecord == null)) { frame++; return; } _frames[frameCount++] = new FrameRecord().create(frame++,keysRecord,mouseRecord); if(frameCount >= _capacity) { _capacity *= 2; _frames.length = _capacity; } } /** * Get the current frame record data and load it into the input managers. */ public function playNextFrame():void { FlxG.resetInput(); if(_marker >= frameCount) { finished = true; return; } if((_frames[_marker] as FrameRecord).frame != frame++) return; var fr:FrameRecord = _frames[_marker++]; if(fr.keys != null) FlxG.keys.playback(fr.keys); if(fr.mouse != null) FlxG.mouse.playback(fr.mouse); } /** * Reset the replay back to the first frame. */ public function rewind():void { _marker = 0; frame = 0; finished = false; } } } ================================================ FILE: org/flixel/system/FlxTile.as ================================================ package org.flixel.system { import org.flixel.FlxObject; import org.flixel.FlxTilemap; /** * A simple helper object for FlxTilemap that helps expand collision opportunities and control. * You can use FlxTilemap.setTileProperties() to alter the collision properties and * callback functions and filters for this object to do things like one-way tiles or whatever. * * @author Adam Atomic */ public class FlxTile extends FlxObject { /** * This function is called whenever an object hits a tile of this type. * This function should take the form myFunction(Tile:FlxTile,Object:FlxObject):void. * Defaults to null, set through FlxTilemap.setTileProperties(). */ public var callback:Function; /** * Each tile can store its own filter class for their callback functions. * That is, the callback will only be triggered if an object with a class * type matching the filter touched it. * Defaults to null, set through FlxTilemap.setTileProperties(). */ public var filter:Class; /** * A reference to the tilemap this tile object belongs to. */ public var tilemap:FlxTilemap; /** * The index of this tile type in the core map data. * For example, if your map only has 16 kinds of tiles in it, * this number is usually between 0 and 15. */ public var index:uint; /** * The current map index of this tile object at this moment. * You can think of tile objects as moving around the tilemap helping with collisions. * This value is only reliable and useful if used from the callback function. */ public var mapIndex:uint; /** * Instantiate this new tile object. This is usually called from FlxTilemap.loadMap(). * * @param Tilemap A reference to the tilemap object creating the tile. * @param Index The actual core map data index for this tile type. * @param Width The width of the tile. * @param Height The height of the tile. * @param Visible Whether the tile is visible or not. * @param AllowCollisions The collision flags for the object. By default this value is ANY or NONE depending on the parameters sent to loadMap(). */ public function FlxTile(Tilemap:FlxTilemap, Index:uint, Width:Number, Height:Number, Visible:Boolean, AllowCollisions:uint) { super(0, 0, Width, Height); immovable = true; moves = false; callback = null; filter = null; tilemap = Tilemap; index = Index; visible = Visible; allowCollisions = AllowCollisions; mapIndex = 0; } /** * Clean up memory. */ override public function destroy():void { super.destroy(); callback = null; tilemap = null; } } } ================================================ FILE: org/flixel/system/FlxTilemapBuffer.as ================================================ package org.flixel.system { import flash.display.BitmapData; import flash.geom.Point; import flash.geom.Rectangle; import org.flixel.FlxCamera; import org.flixel.FlxG; import org.flixel.FlxU; /** * A helper object to keep tilemap drawing performance decent across the new multi-camera system. * Pretty much don't even have to think about this class unless you are doing some crazy hacking. * * @author Adam Atomic */ public class FlxTilemapBuffer { /** * The current X position of the buffer. */ public var x:Number; /** * The current Y position of the buffer. */ public var y:Number; /** * The width of the buffer (usually just a few tiles wider than the camera). */ public var width:Number; /** * The height of the buffer (usually just a few tiles taller than the camera). */ public var height:Number; /** * Whether the buffer needs to be redrawn. */ public var dirty:Boolean; /** * How many rows of tiles fit in this buffer. */ public var rows:uint; /** * How many columns of tiles fit in this buffer. */ public var columns:uint; protected var _pixels:BitmapData; protected var _flashRect:Rectangle; /** * Instantiates a new camera-specific buffer for storing the visual tilemap data. * * @param TileWidth The width of the tiles in this tilemap. * @param TileHeight The height of the tiles in this tilemap. * @param WidthInTiles How many tiles wide the tilemap is. * @param HeightInTiles How many tiles tall the tilemap is. * @param Camera Which camera this buffer relates to. */ public function FlxTilemapBuffer(TileWidth:Number,TileHeight:Number,WidthInTiles:uint,HeightInTiles:uint,Camera:FlxCamera=null) { if(Camera == null) Camera = FlxG.camera; columns = FlxU.ceil(Camera.width/TileWidth)+1; if(columns > WidthInTiles) columns = WidthInTiles; rows = FlxU.ceil(Camera.height/TileHeight)+1; if(rows > HeightInTiles) rows = HeightInTiles; _pixels = new BitmapData(columns*TileWidth,rows*TileHeight,true,0); width = _pixels.width; height = _pixels.height; _flashRect = new Rectangle(0,0,width,height); dirty = true; } /** * Clean up memory. */ public function destroy():void { _pixels = null; } /** * Fill the buffer with the specified color. * Default value is transparent. * * @param Color What color to fill with, in 0xAARRGGBB hex format. */ public function fill(Color:uint=0):void { _pixels.fillRect(_flashRect,Color); } /** * Read-only, nab the actual buffer BitmapData object. * * @return The buffer bitmap data. */ public function get pixels():BitmapData { return _pixels; } /** * Just stamps this buffer onto the specified camera at the specified location. * * @param Camera Which camera to draw the buffer onto. * @param FlashPoint Where to draw the buffer at in camera coordinates. */ public function draw(Camera:FlxCamera,FlashPoint:Point):void { Camera.buffer.copyPixels(_pixels,_flashRect,FlashPoint,null,null,true); } } } ================================================ FILE: org/flixel/system/FlxWindow.as ================================================ package org.flixel.system { import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.Sprite; import flash.events.Event; import flash.events.MouseEvent; import flash.geom.Point; import flash.geom.Rectangle; import flash.text.TextField; import flash.text.TextFormat; import org.flixel.FlxU; /** * A generic, Flash-based window class, created for use in FlxDebugger. * * @author Adam Atomic */ public class FlxWindow extends Sprite { [Embed(source="../data/handle.png")] protected var ImgHandle:Class; /** * Minimum allowed X and Y dimensions for this window. */ public var minSize:Point; /** * Maximum allowed X and Y dimensions for this window. */ public var maxSize:Point; /** * Width of the window. Using Sprite.width is super unreliable for some reason! */ protected var _width:uint; /** * Height of the window. Using Sprite.height is super unreliable for some reason! */ protected var _height:uint; /** * Controls where the window is allowed to be positioned. */ protected var _bounds:Rectangle; /** * Window display element. */ protected var _background:Bitmap; /** * Window display element. */ protected var _header:Bitmap; /** * Window display element. */ protected var _shadow:Bitmap; /** * Window display element. */ protected var _title:TextField; /** * Window display element. */ protected var _handle:Bitmap; /** * Helper for interaction. */ protected var _overHeader:Boolean; /** * Helper for interaction. */ protected var _overHandle:Boolean; /** * Helper for interaction. */ protected var _drag:Point; /** * Helper for interaction. */ protected var _dragging:Boolean; /** * Helper for interaction. */ protected var _resizing:Boolean; /** * Helper for interaction. */ protected var _resizable:Boolean; /** * Creates a new window object. This Flash-based class is mainly (only?) used by FlxDebugger. * * @param Title The name of the window, displayed in the header bar. * @param Width The initial width of the window. * @param Height The initial height of the window. * @param Resizable Whether you can change the size of the window with a drag handle. * @param Bounds A rectangle indicating the valid screen area for the window. * @param BGColor What color the window background should be, default is gray and transparent. * @param TopColor What color the window header bar should be, default is black and transparent. */ public function FlxWindow(Title:String,Width:Number,Height:Number,Resizable:Boolean=true,Bounds:Rectangle=null,BGColor:uint=0x7f7f7f7f, TopColor:uint=0x7f000000) { super(); _width = Width; _height = Height; _bounds = Bounds; minSize = new Point(50,30); if(_bounds != null) maxSize = new Point(_bounds.width,_bounds.height); else maxSize = new Point(Number.MAX_VALUE,Number.MAX_VALUE); _drag = new Point(); _resizable = Resizable; _shadow = new Bitmap(new BitmapData(1,2,true,0xff000000)); addChild(_shadow); _background = new Bitmap(new BitmapData(1,1,true,BGColor)); _background.y = 15; addChild(_background); _header = new Bitmap(new BitmapData(1,15,true,TopColor)); addChild(_header); _title = new TextField(); _title.x = 2; _title.height = 16; _title.selectable = false; _title.multiline = false; _title.defaultTextFormat = new TextFormat("Courier",12,0xffffff); _title.text = Title; addChild(_title); if(_resizable) { _handle = new ImgHandle(); addChild(_handle); } if((_width != 0) || (_height != 0)) updateSize(); bound(); addEventListener(Event.ENTER_FRAME,init); } /** * Clean up memory. */ public function destroy():void { minSize = null; maxSize = null; _bounds = null; removeChild(_shadow); _shadow = null; removeChild(_background); _background = null; removeChild(_header); _header = null; removeChild(_title); _title = null; if(_handle != null) removeChild(_handle); _handle = null; _drag = null; } /** * Resize the window. Subject to pre-specified minimums, maximums, and bounding rectangles. * * @param Width How wide to make the window. * @param Height How tall to make the window. */ public function resize(Width:Number,Height:Number):void { _width = Width; _height = Height; updateSize(); } /** * Change the position of the window. Subject to pre-specified bounding rectangles. * * @param X Desired X position of top left corner of the window. * @param Y Desired Y position of top left corner of the window. */ public function reposition(X:Number,Y:Number):void { x = X; y = Y; bound(); } //***EVENT HANDLERS***// /** * Used to set up basic mouse listeners. * * @param E Flash event. */ protected function init(E:Event=null):void { if(root == null) return; removeEventListener(Event.ENTER_FRAME,init); stage.addEventListener(MouseEvent.MOUSE_MOVE,handleMouseMove); stage.addEventListener(MouseEvent.MOUSE_DOWN,handleMouseDown); stage.addEventListener(MouseEvent.MOUSE_UP,handleMouseUp); } /** * Mouse movement handler. Figures out if mouse is over handle or header bar or what. * * @param E Flash mouse event. */ protected function handleMouseMove(E:MouseEvent=null):void { if(_dragging) //user is moving the window around { _overHeader = true; reposition(parent.mouseX - _drag.x, parent.mouseY - _drag.y); } else if(_resizing) { _overHandle = true; resize(mouseX - _drag.x, mouseY - _drag.y); } else if((mouseX >= 0) && (mouseX <= _width) && (mouseY >= 0) && (mouseY <= _height)) { //not dragging, mouse is over the window _overHeader = (mouseX <= _header.width) && (mouseY <= _header.height); if(_resizable) _overHandle = (mouseX >= _width - _handle.width) && (mouseY >= _height - _handle.height); } else { //not dragging, mouse is NOT over window _overHandle = _overHeader = false; } updateGUI(); } /** * Figure out if window is being repositioned (clicked on header) or resized (clicked on handle). * * @param E Flash mouse event. */ protected function handleMouseDown(E:MouseEvent=null):void { if(_overHeader) { _dragging = true; _drag.x = mouseX; _drag.y = mouseY; } else if(_overHandle) { _resizing = true; _drag.x = _width-mouseX; _drag.y = _height-mouseY; } } /** * User let go of header bar or handler (or nothing), so turn off drag and resize behaviors. * * @param E Flash mouse event. */ protected function handleMouseUp(E:MouseEvent=null):void { _dragging = false; _resizing = false; } //***MISC GUI MGMT STUFF***// /** * Keep the window within the pre-specified bounding rectangle. */ protected function bound():void { if(_bounds != null) { x = FlxU.bound(x,_bounds.left,_bounds.right-_width); y = FlxU.bound(y,_bounds.top,_bounds.bottom-_height); } } /** * Update the Flash shapes to match the new size, and reposition the header, shadow, and handle accordingly. */ protected function updateSize():void { _width = FlxU.bound(_width,minSize.x,maxSize.x); _height = FlxU.bound(_height,minSize.y,maxSize.y); _header.scaleX = _width; _background.scaleX = _width; _background.scaleY = _height-15; _shadow.scaleX = _width; _shadow.y = _height; _title.width = _width-4; if(_resizable) { _handle.x = _width-_handle.width; _handle.y = _height-_handle.height; } } /** * Figure out if the header or handle are highlighted. */ protected function updateGUI():void { if(_overHeader || _overHandle) { if(_title.alpha != 1.0) _title.alpha = 1.0; } else { if(_title.alpha != 0.65) _title.alpha = 0.65; } } } } ================================================ FILE: org/flixel/system/debug/Log.as ================================================ package org.flixel.system.debug { import flash.geom.Rectangle; import flash.text.TextField; import flash.text.TextFormat; import org.flixel.system.FlxWindow; /** * A simple trace output window for use in the debugger overlay. * * @author Adam Atomic */ public class Log extends FlxWindow { static protected const MAX_LOG_LINES:uint = 200; protected var _text:TextField; protected var _lines:Array; /** * Creates a new window object. This Flash-based class is mainly (only?) used by FlxDebugger. * * @param Title The name of the window, displayed in the header bar. * @param Width The initial width of the window. * @param Height The initial height of the window. * @param Resizable Whether you can change the size of the window with a drag handle. * @param Bounds A rectangle indicating the valid screen area for the window. * @param BGColor What color the window background should be, default is gray and transparent. * @param TopColor What color the window header bar should be, default is black and transparent. */ public function Log(Title:String, Width:Number, Height:Number, Resizable:Boolean=true, Bounds:Rectangle=null, BGColor:uint=0x7f7f7f7f, TopColor:uint=0x7f000000) { super(Title, Width, Height, Resizable, Bounds, BGColor, TopColor); _text = new TextField(); _text.x = 2; _text.y = 15; _text.multiline = true; _text.wordWrap = true; _text.selectable = true; _text.defaultTextFormat = new TextFormat("Courier",12,0xffffff); addChild(_text); _lines = new Array(); } /** * Clean up memory. */ override public function destroy():void { removeChild(_text); _text = null; _lines = null; super.destroy(); } /** * Adds a new line to the log window. * * @param Text The line you want to add to the log window. */ public function add(Text:String):void { if(_lines.length <= 0) _text.text = ""; _lines.push(Text); if(_lines.length > MAX_LOG_LINES) { _lines.shift(); var newText:String = ""; for(var i:uint = 0; i < _lines.length; i++) newText += _lines[i]+"\n"; _text.text = newText; } else _text.appendText(Text+"\n"); _text.scrollV = _text.height; } /** * Adjusts the width and height of the text field accordingly. */ override protected function updateSize():void { super.updateSize(); _text.width = _width-10; _text.height = _height-15; } } } ================================================ FILE: org/flixel/system/debug/Perf.as ================================================ package org.flixel.system.debug { import flash.geom.Rectangle; import flash.system.System; import flash.text.TextField; import flash.text.TextFormat; import flash.utils.getTimer; import org.flixel.FlxG; import org.flixel.system.FlxWindow; /** * A simple performance monitor widget, for use in the debugger overlay. * * @author Adam Atomic */ public class Perf extends FlxWindow { protected var _text:TextField; protected var _lastTime:int; protected var _updateTimer:int; protected var _flixelUpdate:Array; protected var _flixelUpdateMarker:uint; protected var _flixelDraw:Array; protected var _flixelDrawMarker:uint; protected var _flash:Array; protected var _flashMarker:uint; protected var _activeObject:Array; protected var _objectMarker:uint; protected var _visibleObject:Array; protected var _visibleObjectMarker:uint; /** * Creates flashPlayerFramerate new window object. This Flash-based class is mainly (only?) used by FlxDebugger. * * @param Title The name of the window, displayed in the header bar. * @param Width The initial width of the window. * @param Height The initial height of the window. * @param Resizable Whether you can change the size of the window with flashPlayerFramerate drag handle. * @param Bounds A rectangle indicating the valid screen area for the window. * @param BGColor What color the window background should be, default is gray and transparent. * @param TopColor What color the window header bar should be, default is black and transparent. */ public function Perf(Title:String, Width:Number, Height:Number, Resizable:Boolean=true, Bounds:Rectangle=null, BGColor:uint=0x7f7f7f7f, TopColor:uint=0x7f000000) { super(Title, Width, Height, Resizable, Bounds, BGColor, TopColor); resize(90,66); _lastTime = 0; _updateTimer = 0; _text = new TextField(); _text.width = _width; _text.x = 2; _text.y = 15; _text.multiline = true; _text.wordWrap = true; _text.selectable = true; _text.defaultTextFormat = new TextFormat("Courier",12,0xffffff); addChild(_text); _flixelUpdate = new Array(32); _flixelUpdateMarker = 0; _flixelDraw = new Array(32); _flixelDrawMarker = 0; _flash = new Array(32); _flashMarker = 0; _activeObject = new Array(32); _objectMarker = 0; _visibleObject = new Array(32); _visibleObjectMarker = 0; } /** * Clean up memory. */ override public function destroy():void { removeChild(_text); _text = null; _flixelUpdate = null; _flixelDraw = null; _flash = null; _activeObject = null; _visibleObject = null; super.destroy(); } /** * Called each frame, but really only updates once every second or so, to save on performance. * Takes all the data in the accumulators and parses it into useful performance data. */ public function update():void { var time:int = getTimer(); var elapsed:int = time - _lastTime; var updateEvery:uint = 500; if(elapsed > updateEvery) elapsed = updateEvery; _lastTime = time; _updateTimer += elapsed; if(_updateTimer > updateEvery) { var i:uint; var output:String = ""; var flashPlayerFramerate:Number = 0; i = 0; while (i < _flashMarker) flashPlayerFramerate += _flash[i++]; flashPlayerFramerate /= _flashMarker; output += uint(1/(flashPlayerFramerate/1000)) + "/" + FlxG.flashFramerate + "fps\n"; output += Number( ( System.totalMemory * 0.000000954 ).toFixed(2) ) + "MB\n"; var updateTime:uint = 0; i = 0; while(i < _flixelUpdateMarker) updateTime += _flixelUpdate[i++]; var activeCount:uint = 0; var te:uint = 0; i = 0; while(i < _objectMarker) { activeCount += _activeObject[i]; visibleCount += _visibleObject[i++]; } activeCount /= _objectMarker; output += "U:" + activeCount + " " + uint(updateTime/_flixelDrawMarker) + "ms\n"; var drawTime:uint = 0; i = 0; while(i < _flixelDrawMarker) drawTime += _flixelDraw[i++]; var visibleCount:uint = 0; i = 0; while(i < _visibleObjectMarker) visibleCount += _visibleObject[i++]; visibleCount /= _visibleObjectMarker; output += "D:" + visibleCount + " " + uint(drawTime/_flixelDrawMarker) + "ms"; _text.text = output; _flixelUpdateMarker = 0; _flixelDrawMarker = 0; _flashMarker = 0; _objectMarker = 0; _visibleObjectMarker = 0; _updateTimer -= updateEvery; } } /** * Keep track of how long updates take. * * @param Time How long this update took. */ public function flixelUpdate(Time:int):void { _flixelUpdate[_flixelUpdateMarker++] = Time; } /** * Keep track of how long renders take. * * @param Time How long this render took. */ public function flixelDraw(Time:int):void { _flixelDraw[_flixelDrawMarker++] = Time; } /** * Keep track of how long the Flash player and browser take. * * @param Time How long Flash/browser took. */ public function flash(Time:int):void { _flash[_flashMarker++] = Time; } /** * Keep track of how many objects were updated. * * @param Count How many objects were updated. */ public function activeObjects(Count:int):void { _activeObject[_objectMarker++] = Count; } /** * Keep track of how many objects were updated. * * @param Count How many objects were updated. */ public function visibleObjects(Count:int):void { _visibleObject[_visibleObjectMarker++] = Count; } } } ================================================ FILE: org/flixel/system/debug/VCR.as ================================================ package org.flixel.system.debug { import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.Sprite; import flash.events.Event; import flash.events.IOErrorEvent; import flash.events.MouseEvent; import flash.net.FileFilter; import flash.net.FileReference; import flash.text.TextField; import flash.text.TextFormat; import flash.utils.ByteArray; import org.flixel.FlxG; import org.flixel.FlxU; import org.flixel.system.FlxReplay; import org.flixel.system.replay.FrameRecord; import org.flixel.system.replay.MouseRecord; /** * This class contains the record, stop, play, and step 1 frame buttons seen on the top edge of the debugger overlay. * * @author Adam Atomic */ public class VCR extends Sprite { [Embed(source="../../data/vcr/open.png")] protected var ImgOpen:Class; [Embed(source="../../data/vcr/record_off.png")] protected var ImgRecordOff:Class; [Embed(source="../../data/vcr/record_on.png")] protected var ImgRecordOn:Class; [Embed(source="../../data/vcr/stop.png")] protected var ImgStop:Class; [Embed(source="../../data/vcr/flixel.png")] protected var ImgFlixel:Class; [Embed(source="../../data/vcr/restart.png")] protected var ImgRestart:Class; [Embed(source="../../data/vcr/pause.png")] protected var ImgPause:Class; [Embed(source="../../data/vcr/play.png")] protected var ImgPlay:Class; [Embed(source="../../data/vcr/step.png")] protected var ImgStep:Class; static protected const FILE_TYPES:Array = [new FileFilter("Flixel Game Recording", "*.fgr")]; static protected const DEFAULT_FILE_NAME:String = "replay.fgr"; /** * Whether the debugger has been paused. */ public var paused:Boolean; /** * Whether a "1 frame step forward" was requested. */ public var stepRequested:Boolean; protected var _open:Bitmap; protected var _recordOff:Bitmap; protected var _recordOn:Bitmap; protected var _stop:Bitmap; protected var _flixel:Bitmap; protected var _restart:Bitmap; protected var _pause:Bitmap; protected var _play:Bitmap; protected var _step:Bitmap; protected var _overOpen:Boolean; protected var _overRecord:Boolean; protected var _overRestart:Boolean; protected var _overPause:Boolean; protected var _overStep:Boolean; protected var _pressingOpen:Boolean; protected var _pressingRecord:Boolean; protected var _pressingRestart:Boolean; protected var _pressingPause:Boolean; protected var _pressingStep:Boolean; protected var _file:FileReference; protected var _runtimeDisplay:TextField; protected var _runtime:uint; /** * Creates the "VCR" control panel for debugger pausing, stepping, and recording. */ public function VCR() { super(); var spacing:uint = 7; _open = new ImgOpen(); addChild(_open); _recordOff = new ImgRecordOff(); _recordOff.x = _open.x + _open.width + spacing; addChild(_recordOff); _recordOn = new ImgRecordOn(); _recordOn.x = _recordOff.x; _recordOn.visible = false; addChild(_recordOn); _stop = new ImgStop(); _stop.x = _recordOff.x; _stop.visible = false; addChild(_stop); _flixel = new ImgFlixel(); _flixel.x = _recordOff.x + _recordOff.width + spacing; addChild(_flixel); _restart = new ImgRestart(); _restart.x = _flixel.x + _flixel.width + spacing; addChild(_restart); _pause = new ImgPause(); _pause.x = _restart.x + _restart.width + spacing; addChild(_pause); _play = new ImgPlay(); _play.x = _pause.x; _play.visible = false; addChild(_play); _step = new ImgStep(); _step.x = _pause.x + _pause.width + spacing; addChild(_step); _runtimeDisplay = new TextField(); _runtimeDisplay.width = width; _runtimeDisplay.x = width; _runtimeDisplay.y = -2; _runtimeDisplay.multiline = false; _runtimeDisplay.wordWrap = false; _runtimeDisplay.selectable = false; _runtimeDisplay.defaultTextFormat = new TextFormat("Courier",12,0xffffff,null,null,null,null,null,"center"); _runtimeDisplay.visible = false; addChild(_runtimeDisplay); _runtime = 0; stepRequested = false; _file = null; unpress(); checkOver(); updateGUI(); addEventListener(Event.ENTER_FRAME,init); } /** * Clean up memory. */ public function destroy():void { _file = null; removeChild(_open); _open = null; removeChild(_recordOff); _recordOff = null; removeChild(_recordOn); _recordOn = null; removeChild(_stop); _stop = null; removeChild(_flixel); _flixel = null; removeChild(_restart); _restart = null; removeChild(_pause); _pause = null; removeChild(_play); _play = null; removeChild(_step); _step = null; parent.removeEventListener(MouseEvent.MOUSE_MOVE,handleMouseMove); parent.removeEventListener(MouseEvent.MOUSE_DOWN,handleMouseDown); parent.removeEventListener(MouseEvent.MOUSE_UP,handleMouseUp); } /** * Usually called by FlxGame when a requested recording has begun. * Just updates the VCR GUI so the buttons are in the right state. */ public function recording():void { _stop.visible = false; _recordOff.visible = false; _recordOn.visible = true; } /** * Usually called by FlxGame when a replay has been stopped. * Just updates the VCR GUI so the buttons are in the right state. */ public function stopped():void { _stop.visible = false; _recordOn.visible = false; _recordOff.visible = true; } /** * Usually called by FlxGame when a requested replay has begun. * Just updates the VCR GUI so the buttons are in the right state. */ public function playing():void { _recordOff.visible = false; _recordOn.visible = false; _stop.visible = true; } /** * Just updates the VCR GUI so the runtime displays roughly the right thing. */ public function updateRuntime(Time:uint):void { _runtime += Time; _runtimeDisplay.text = FlxU.formatTime(_runtime/1000,true); if(!_runtimeDisplay.visible) _runtimeDisplay.visible = true; } //*** ACTUAL BUTTON BEHAVIORS ***// /** * Called when the "open file" button is pressed. * Opens the file dialog and registers event handlers for the file dialog. */ public function onOpen():void { _file = new FileReference(); _file.addEventListener(Event.SELECT, onOpenSelect); _file.addEventListener(Event.CANCEL, onOpenCancel); _file.browse(FILE_TYPES); } /** * Called when a file is picked from the file dialog. * Attempts to load the file and registers file loading event handlers. * * @param E Flash event. */ protected function onOpenSelect(E:Event=null):void { _file.removeEventListener(Event.SELECT, onOpenSelect); _file.removeEventListener(Event.CANCEL, onOpenCancel); _file.addEventListener(Event.COMPLETE, onOpenComplete); _file.addEventListener(IOErrorEvent.IO_ERROR, onOpenError); _file.load(); } /** * Called when a file is opened successfully. * If there's stuff inside, then the contents are loaded into a new replay. * * @param E Flash Event. */ protected function onOpenComplete(E:Event=null):void { _file.removeEventListener(Event.COMPLETE, onOpenComplete); _file.removeEventListener(IOErrorEvent.IO_ERROR, onOpenError); //Turn the file into a giant string var fileContents:String = null; var data:ByteArray = _file.data; if(data != null) fileContents = data.readUTFBytes(data.bytesAvailable); _file = null; if((fileContents == null) || (fileContents.length <= 0)) { FlxG.log("ERROR: Empty flixel gameplay record."); return; } FlxG.loadReplay(fileContents); } /** * Called if the open file dialog is canceled. * * @param E Flash Event. */ protected function onOpenCancel(E:Event=null):void { _file.removeEventListener(Event.SELECT, onOpenSelect); _file.removeEventListener(Event.CANCEL, onOpenCancel); _file = null; } /** * Called if there is a file open error. * * @param E Flash Event. */ protected function onOpenError(E:Event=null):void { _file.removeEventListener(Event.COMPLETE, onOpenComplete); _file.removeEventListener(IOErrorEvent.IO_ERROR, onOpenError); _file = null; FlxG.log("ERROR: Unable to open flixel gameplay record."); } /** * Called when the user presses the white record button. * If Alt is pressed, the current state is reset, and a new recording is requested. * If Alt is NOT pressed, the game is reset, and a new recording is requested. * * @param StandardMode Whether to reset the whole game, or just this FlxState. StandardMode == false is useful for recording demos or attract modes. */ public function onRecord(StandardMode:Boolean=false):void { if(_play.visible) onPlay(); FlxG.recordReplay(StandardMode); } /** * Called when the user presses the red record button. * Stops the current recording, opens the save file dialog, and registers event handlers. */ public function stopRecording():void { var data:String = FlxG.stopRecording(); if((data != null) && (data.length > 0)) { _file = new FileReference(); _file.addEventListener(Event.COMPLETE, onSaveComplete); _file.addEventListener(Event.CANCEL,onSaveCancel); _file.addEventListener(IOErrorEvent.IO_ERROR, onSaveError); _file.save(data, DEFAULT_FILE_NAME); } } /** * Called when the file is saved successfully. * * @param E Flash Event. */ protected function onSaveComplete(E:Event=null):void { _file.removeEventListener(Event.COMPLETE, onSaveComplete); _file.removeEventListener(Event.CANCEL,onSaveCancel); _file.removeEventListener(IOErrorEvent.IO_ERROR, onSaveError); _file = null; FlxG.log("FLIXEL: successfully saved flixel gameplay record."); } /** * Called when the save file dialog is cancelled. * * @param E Flash Event. */ protected function onSaveCancel(E:Event=null):void { _file.removeEventListener(Event.COMPLETE, onSaveComplete); _file.removeEventListener(Event.CANCEL,onSaveCancel); _file.removeEventListener(IOErrorEvent.IO_ERROR, onSaveError); _file = null; } /** * Called if there is an error while saving the gameplay recording. * * @param E Flash Event. */ protected function onSaveError(E:Event=null):void { _file.removeEventListener(Event.COMPLETE, onSaveComplete); _file.removeEventListener(Event.CANCEL,onSaveCancel); _file.removeEventListener(IOErrorEvent.IO_ERROR, onSaveError); _file = null; FlxG.log("ERROR: problem saving flixel gameplay record."); } /** * Called when the user presses the stop button. * Stops the current replay. */ public function onStop():void { FlxG.stopReplay(); } /** * Called when the user presses the Rewind-looking button. * If Alt is pressed, the entire game is reset. * If Alt is NOT pressed, only the current state is reset. * The GUI is updated accordingly. * * @param StandardMode Whether to reset the current game (== true), or just the current state. Just resetting the current state can be very handy for debugging. */ public function onRestart(StandardMode:Boolean=false):void { if(FlxG.reloadReplay(StandardMode)) { _recordOff.visible = false; _recordOn.visible = false; _stop.visible = true; } } /** * Called when the user presses the Pause button. * This is different from user-defined pause behavior, or focus lost behavior. * Does NOT pause music playback!! */ public function onPause():void { paused = true; _pause.visible = false; _play.visible = true; } /** * Called when the user presses the Play button. * This is different from user-defined unpause behavior, or focus gained behavior. */ public function onPlay():void { paused = false; _play.visible = false; _pause.visible = true; } /** * Called when the user presses the fast-forward-looking button. * Requests a 1-frame step forward in the game loop. */ public function onStep():void { if(!paused) onPause(); stepRequested = true; } //***EVENT HANDLERS***// /** * Just sets up basic mouse listeners, a la FlxWindow. * * @param E Flash event. */ protected function init(E:Event=null):void { if(root == null) return; removeEventListener(Event.ENTER_FRAME,init); parent.addEventListener(MouseEvent.MOUSE_MOVE,handleMouseMove); parent.addEventListener(MouseEvent.MOUSE_DOWN,handleMouseDown); parent.addEventListener(MouseEvent.MOUSE_UP,handleMouseUp); } /** * If the mouse moves, check to see if any buttons should be highlighted. * * @param E Flash mouse event. */ protected function handleMouseMove(E:MouseEvent=null):void { if(!checkOver()) unpress(); updateGUI(); } /** * If the mouse is pressed down, check to see if the user started pressing down a specific button. * * @param E Flash mouse event. */ protected function handleMouseDown(E:MouseEvent=null):void { unpress(); if(_overOpen) _pressingOpen = true; if(_overRecord) _pressingRecord = true; if(_overRestart) _pressingRestart = true; if(_overPause) _pressingPause = true; if(_overStep) _pressingStep = true; } /** * If the mouse is released, check to see if it was released over a button that was pressed. * If it was, take the appropriate action based on button state and visibility. * * @param E Flash mouse event. */ protected function handleMouseUp(E:MouseEvent=null):void { if(_overOpen && _pressingOpen) onOpen(); else if(_overRecord && _pressingRecord) { if(_stop.visible) onStop(); else if(_recordOn.visible) stopRecording(); else onRecord(!E.altKey); } else if(_overRestart && _pressingRestart) onRestart(!E.altKey); else if(_overPause && _pressingPause) { if(_play.visible) onPlay(); else onPause(); } else if(_overStep && _pressingStep) onStep(); unpress(); checkOver(); updateGUI(); } //***MISC GUI MGMT STUFF***// /** * This function checks to see what button the mouse is currently over. * Has some special behavior based on whether a recording is happening or not. * * @return Whether the mouse was over any buttons or not. */ protected function checkOver():Boolean { _overOpen = _overRecord = _overRestart = _overPause = _overStep = false; if((mouseX < 0) || (mouseX > width) || (mouseY < 0) || (mouseY > 15)) return false; if((mouseX >= _recordOff.x) && (mouseX <= _recordOff.x + _recordOff.width)) _overRecord = true; if(!_recordOn.visible && !_overRecord) { if((mouseX >= _open.x) && (mouseX <= _open.x + _open.width)) _overOpen = true; else if((mouseX >= _restart.x) && (mouseX <= _restart.x + _restart.width)) _overRestart = true; else if((mouseX >= _pause.x) && (mouseX <= _pause.x + _pause.width)) _overPause = true; else if((mouseX >= _step.x) && (mouseX <= _step.x + _step.width)) _overStep = true; } return true; } /** * Sets all the pressed state variables for the buttons to false. */ protected function unpress():void { _pressingOpen = false; _pressingRecord = false; _pressingRestart = false; _pressingPause = false; _pressingStep = false; } /** * Figures out what buttons to highlight based on the _overWhatever and _pressingWhatever variables. */ protected function updateGUI():void { if(_recordOn.visible) { _open.alpha = _restart.alpha = _pause.alpha = _step.alpha = 0.35; _recordOn.alpha = 1.0; return; } if(_overOpen && (_open.alpha != 1.0)) _open.alpha = 1.0; else if(!_overOpen && (_open.alpha != 0.8)) _open.alpha = 0.8; if(_overRecord && (_recordOff.alpha != 1.0)) _recordOff.alpha = _recordOn.alpha = _stop.alpha = 1.0; else if(!_overRecord && (_recordOff.alpha != 0.8)) _recordOff.alpha = _recordOn.alpha = _stop.alpha = 0.8; if(_overRestart && (_restart.alpha != 1.0)) _restart.alpha = 1.0; else if(!_overRestart && (_restart.alpha != 0.8)) _restart.alpha = 0.8; if(_overPause && (_pause.alpha != 1.0)) _pause.alpha = _play.alpha = 1.0; else if(!_overPause && (_pause.alpha != 0.8)) _pause.alpha = _play.alpha = 0.8; if(_overStep && (_step.alpha != 1.0)) _step.alpha = 1.0; else if(!_overStep && (_step.alpha != 0.8)) _step.alpha = 0.8; } } } ================================================ FILE: org/flixel/system/debug/Vis.as ================================================ package org.flixel.system.debug { import flash.display.Bitmap; import flash.display.Sprite; import flash.events.Event; import flash.events.MouseEvent; import org.flixel.FlxG; /** * This control panel has all the visual debugger toggles in it, in the debugger overlay. * Currently there is only one toggle that flips on all the visual debug settings. * This panel is heavily based on the VCR panel. * * @author Adam Atomic */ public class Vis extends Sprite { [Embed(source="../../data/vis/bounds.png")] protected var ImgBounds:Class; protected var _bounds:Bitmap; protected var _overBounds:Boolean; protected var _pressingBounds:Boolean; /** * Instantiate the visual debugger panel. */ public function Vis() { super(); var spacing:uint = 7; _bounds = new ImgBounds(); addChild(_bounds); unpress(); checkOver(); updateGUI(); addEventListener(Event.ENTER_FRAME,init); } /** * Clean up memory. */ public function destroy():void { removeChild(_bounds); _bounds = null; parent.removeEventListener(MouseEvent.MOUSE_MOVE,handleMouseMove); parent.removeEventListener(MouseEvent.MOUSE_DOWN,handleMouseDown); parent.removeEventListener(MouseEvent.MOUSE_UP,handleMouseUp); } //***ACTUAL BUTTON BEHAVIORS***// /** * Called when the bounding box toggle is pressed. */ public function onBounds():void { FlxG.visualDebug = !FlxG.visualDebug; } //***EVENT HANDLERS***// /** * Just sets up basic mouse listeners, a la FlxWindow. * * @param E Flash event. */ protected function init(E:Event=null):void { if(root == null) return; removeEventListener(Event.ENTER_FRAME,init); parent.addEventListener(MouseEvent.MOUSE_MOVE,handleMouseMove); parent.addEventListener(MouseEvent.MOUSE_DOWN,handleMouseDown); parent.addEventListener(MouseEvent.MOUSE_UP,handleMouseUp); } /** * If the mouse moves, check to see if any buttons should be highlighted. * * @param E Flash mouse event. */ protected function handleMouseMove(E:MouseEvent=null):void { if(!checkOver()) unpress(); updateGUI(); } /** * If the mouse is pressed down, check to see if the user started pressing down a specific button. * * @param E Flash mouse event. */ protected function handleMouseDown(E:MouseEvent=null):void { unpress(); if(_overBounds) _pressingBounds = true; } /** * If the mouse is released, check to see if it was released over a button that was pressed. * If it was, take the appropriate action based on button state and visibility. * * @param E Flash mouse event. */ protected function handleMouseUp(E:MouseEvent=null):void { if(_overBounds && _pressingBounds) onBounds(); unpress(); checkOver(); updateGUI(); } //***MISC GUI MGMT STUFF***// /** * This function checks to see what button the mouse is currently over. * Has some special behavior based on whether a recording is happening or not. * * @return Whether the mouse was over any buttons or not. */ protected function checkOver():Boolean { _overBounds = false; if((mouseX < 0) || (mouseX > width) || (mouseY < 0) || (mouseY > height)) return false; if((mouseX > _bounds.x) || (mouseX < _bounds.x + _bounds.width)) _overBounds = true; return true; } /** * Sets all the pressed state variables for the buttons to false. */ protected function unpress():void { _pressingBounds = false; } /** * Figures out what buttons to highlight based on the _overWhatever and _pressingWhatever variables. */ protected function updateGUI():void { if(FlxG.visualDebug) { if(_overBounds && (_bounds.alpha != 1.0)) _bounds.alpha = 1.0; else if(!_overBounds && (_bounds.alpha != 0.9)) _bounds.alpha = 0.9; } else { if(_overBounds && (_bounds.alpha != 0.6)) _bounds.alpha = 0.6; else if(!_overBounds && (_bounds.alpha != 0.5)) _bounds.alpha = 0.5; } } } } ================================================ FILE: org/flixel/system/debug/Watch.as ================================================ package org.flixel.system.debug { import flash.display.Sprite; import flash.geom.Rectangle; import flash.text.TextField; import flash.text.TextFormat; import org.flixel.FlxU; import org.flixel.system.FlxWindow; /** * A Visual Studio-style "watch" window, for use in the debugger overlay. * Track the values of any public variable in real-time, and/or edit their values on the fly. * * @author Adam Atomic */ public class Watch extends FlxWindow { static protected const MAX_LOG_LINES:uint = 1024; static protected const LINE_HEIGHT:uint = 15; /** * Whether a watch entry is currently being edited or not. */ public var editing:Boolean; protected var _names:Sprite; protected var _values:Sprite; protected var _watching:Array; /** * Creates a new window object. This Flash-based class is mainly (only?) used by FlxDebugger. * * @param Title The name of the window, displayed in the header bar. * @param Width The initial width of the window. * @param Height The initial height of the window. * @param Resizable Whether you can change the size of the window with a drag handle. * @param Bounds A rectangle indicating the valid screen area for the window. * @param BGColor What color the window background should be, default is gray and transparent. * @param TopColor What color the window header bar should be, default is black and transparent. */ public function Watch(Title:String, Width:Number, Height:Number, Resizable:Boolean=true, Bounds:Rectangle=null, BGColor:uint=0x7f7f7f7f, TopColor:uint=0x7f000000) { super(Title, Width, Height, Resizable, Bounds, BGColor, TopColor); _names = new Sprite(); _names.x = 2; _names.y = 15; addChild(_names); _values = new Sprite(); _values.x = 2; _values.y = 15; addChild(_values); _watching = new Array(); editing = false; removeAll(); } /** * Clean up memory. */ override public function destroy():void { removeChild(_names); _names = null; removeChild(_values); _values = null; var i:int = 0; var l:uint = _watching.length; while(i < l) (_watching[i++] as WatchEntry).destroy(); _watching = null; super.destroy(); } /** * Add a new variable to the watch window. * Has some simple code in place to prevent * accidentally watching the same variable twice. * * @param AnyObject The Object containing the variable you want to track, e.g. this or Player.velocity. * @param VariableName The String name of the variable you want to track, e.g. "width" or "x". * @param DisplayName Optional String that can be displayed in the watch window instead of the basic class-name information. */ public function add(AnyObject:Object,VariableName:String,DisplayName:String=null):void { //Don't add repeats var watchEntry:WatchEntry; var i:int = 0; var l:uint = _watching.length; while(i < l) { watchEntry = _watching[i++] as WatchEntry; if((watchEntry.object == AnyObject) && (watchEntry.field == VariableName)) return; } //Good, no repeats, add away! watchEntry = new WatchEntry(_watching.length*LINE_HEIGHT,_width/2,_width/2-10,AnyObject,VariableName,DisplayName); _names.addChild(watchEntry.nameDisplay); _values.addChild(watchEntry.valueDisplay); _watching.push(watchEntry); } /** * Remove a variable from the watch window. * * @param AnyObject The Object containing the variable you want to remove, e.g. this or Player.velocity. * @param VariableName The String name of the variable you want to remove, e.g. "width" or "x". If left null, this will remove all variables of that object. */ public function remove(AnyObject:Object,VariableName:String=null):void { //splice out the requested object var watchEntry:WatchEntry; var i:int = _watching.length-1; while(i >= 0) { watchEntry = _watching[i]; if((watchEntry.object == AnyObject) && ((VariableName == null) || (watchEntry.field == VariableName))) { _watching.splice(i,1); _names.removeChild(watchEntry.nameDisplay); _values.removeChild(watchEntry.valueDisplay); watchEntry.destroy(); } i--; } watchEntry = null; //reset the display heights of the remaining objects i = 0; var l:uint = _watching.length; while(i < l) { (_watching[i] as WatchEntry).setY(i*LINE_HEIGHT); i++; } } /** * Remove everything from the watch window. */ public function removeAll():void { var watchEntry:WatchEntry; var i:int = 0; var l:uint = _watching.length; while(i < l) { watchEntry = _watching.pop(); _names.removeChild(watchEntry.nameDisplay); _values.removeChild(watchEntry.valueDisplay); watchEntry.destroy(); i++ } _watching.length = 0; } /** * Update all the entries in the watch window. */ public function update():void { editing = false; var i:uint = 0; var l:uint = _watching.length; while(i < l) { if(!(_watching[i++] as WatchEntry).updateValue()) editing = true; } } /** * Force any watch entries currently being edited to submit their changes. */ public function submit():void { var i:uint = 0; var l:uint = _watching.length; var watchEntry:WatchEntry; while(i < l) { watchEntry = _watching[i++] as WatchEntry; if(watchEntry.editing) watchEntry.submit(); } editing = false; } /** * Update the Flash shapes to match the new size, and reposition the header, shadow, and handle accordingly. * Also adjusts the width of the entries and stuff, and makes sure there is room for all the entries. */ override protected function updateSize():void { if(_height < _watching.length*LINE_HEIGHT + 17) _height = _watching.length*LINE_HEIGHT + 17; super.updateSize(); _values.x = _width/2 + 2; var i:int = 0; var l:uint = _watching.length; while(i < l) (_watching[i++] as WatchEntry).updateWidth(_width/2,_width/2-10); } } } ================================================ FILE: org/flixel/system/debug/WatchEntry.as ================================================ package org.flixel.system.debug { import flash.events.KeyboardEvent; import flash.events.MouseEvent; import flash.text.TextField; import flash.text.TextFieldType; import flash.text.TextFormat; import org.flixel.FlxU; /** * Helper class for the debugger overlay's Watch window. * Handles the display and modification of game variables on the fly. * * @author Adam Atomic */ public class WatchEntry { /** * The Object being watched. */ public var object:Object; /** * The member variable of that object. */ public var field:String; /** * A custom display name for this object, if there is any. */ public var custom:String; /** * The Flash TextField object used to display this entry's name. */ public var nameDisplay:TextField; /** * The Flash TextField object used to display and edit this entry's value. */ public var valueDisplay:TextField; /** * Whether the entry is currently being edited or not. */ public var editing:Boolean; /** * The value of the field before it was edited. */ public var oldValue:Object; protected var _whiteText:TextFormat; protected var _blackText:TextFormat; /** * Creates a new watch entry in the watch window. * * @param Y The initial height in the Watch window. * @param NameWidth The initial width of the name field. * @param ValueWidth The initial width of the value field. * @param Obj The Object containing the variable we want to watch. * @param Field The variable name we want to watch. * @param Custom A custom display name (optional). */ public function WatchEntry(Y:Number,NameWidth:Number,ValueWidth:Number,Obj:Object,Field:String,Custom:String=null) { editing = false; object = Obj; field = Field; custom = Custom; _whiteText = new TextFormat("Courier",12,0xffffff); _blackText = new TextFormat("Courier",12,0); nameDisplay = new TextField(); nameDisplay.y = Y; nameDisplay.multiline = false; nameDisplay.selectable = true; nameDisplay.defaultTextFormat = _whiteText; valueDisplay = new TextField(); valueDisplay.y = Y; valueDisplay.height = 15; valueDisplay.multiline = false; valueDisplay.selectable = true; valueDisplay.doubleClickEnabled = true; valueDisplay.addEventListener(KeyboardEvent.KEY_UP,handleKeyUp); valueDisplay.addEventListener(MouseEvent.MOUSE_UP,handleMouseUp); valueDisplay.background = false; valueDisplay.backgroundColor = 0xffffff; valueDisplay.defaultTextFormat = _whiteText; updateWidth(NameWidth,ValueWidth); } /** * Clean up memory. */ public function destroy():void { object = null; oldValue = null; nameDisplay = null; field = null; custom = null; valueDisplay.removeEventListener(MouseEvent.MOUSE_UP,handleMouseUp); valueDisplay.removeEventListener(KeyboardEvent.KEY_UP,handleKeyUp); valueDisplay = null; } /** * Set the watch window Y height of the Flash TextField objects. */ public function setY(Y:Number):void { nameDisplay.y = Y; valueDisplay.y = Y; } /** * Adjust the width of the Flash TextField objects. */ public function updateWidth(NameWidth:Number,ValueWidth:Number):void { nameDisplay.width = NameWidth; valueDisplay.width = ValueWidth; if(custom != null) nameDisplay.text = custom; else { nameDisplay.text = ""; if(NameWidth > 120) nameDisplay.appendText(FlxU.getClassName(object,(NameWidth < 240)) + "."); nameDisplay.appendText(field); } } /** * Update the variable value on display with the current in-game value. */ public function updateValue():Boolean { if(editing) return false; valueDisplay.text = object[field].toString(); return true; } /** * A watch entry was clicked, so flip into edit mode for that entry. * * @param FlashEvent Flash mouse event. */ public function handleMouseUp(FlashEvent:MouseEvent):void { editing = true; oldValue = object[field]; valueDisplay.type = TextFieldType.INPUT; valueDisplay.setTextFormat(_blackText); valueDisplay.background = true; } /** * Check to see if Enter, Tab or Escape were just released. * Enter or Tab submit the change, and Escape cancels it. * * @param FlashEvent Flash keyboard event. */ public function handleKeyUp(FlashEvent:KeyboardEvent):void { if((FlashEvent.keyCode == 13) || (FlashEvent.keyCode == 9) || (FlashEvent.keyCode == 27)) //enter or tab or escape { if(FlashEvent.keyCode == 27) cancel(); else submit(); } } /** * Cancel the current edits and stop editing. */ public function cancel():void { valueDisplay.text = oldValue.toString(); doneEditing(); } /** * Submit the current edits and stop editing. */ public function submit():void { object[field] = valueDisplay.text; doneEditing(); } /** * Helper function, switches the text field back to display mode. */ protected function doneEditing():void { valueDisplay.type = TextFieldType.DYNAMIC; valueDisplay.setTextFormat(_whiteText); valueDisplay.defaultTextFormat = _whiteText; valueDisplay.background = false; editing = false; } } } ================================================ FILE: org/flixel/system/input/Input.as ================================================ package org.flixel.system.input { /** * Basic input class that manages the fast-access Booleans and detailed key-state tracking. * Keyboard extends this with actual specific key data. * * @author Adam Atomic */ public class Input { /** * @private */ internal var _lookup:Object; /** * @private */ internal var _map:Array; /** * @private */ internal const _total:uint = 256; /** * Constructor */ public function Input() { _lookup = new Object(); _map = new Array(_total); } /** * Updates the key states (for tracking just pressed, just released, etc). */ public function update():void { var i:uint = 0; while(i < _total) { var o:Object = _map[i++]; if(o == null) continue; if((o.last == -1) && (o.current == -1)) o.current = 0; else if((o.last == 2) && (o.current == 2)) o.current = 1; o.last = o.current; } } /** * Resets all the keys. */ public function reset():void { var i:uint = 0; while(i < _total) { var o:Object = _map[i++]; if(o == null) continue; this[o.name] = false; o.current = 0; o.last = 0; } } /** * Check to see if this key is pressed. * * @param Key One of the key constants listed above (e.g. "LEFT" or "A"). * * @return Whether the key is pressed */ public function pressed(Key:String):Boolean { return this[Key]; } /** * Check to see if this key was just pressed. * * @param Key One of the key constants listed above (e.g. "LEFT" or "A"). * * @return Whether the key was just pressed */ public function justPressed(Key:String):Boolean { return _map[_lookup[Key]].current == 2; } /** * Check to see if this key is just released. * * @param Key One of the key constants listed above (e.g. "LEFT" or "A"). * * @return Whether the key is just released. */ public function justReleased(Key:String):Boolean { return _map[_lookup[Key]].current == -1; } /** * If any keys are not "released" (0), * this function will return an array indicating * which keys are pressed and what state they are in. * * @return An array of key state data. Null if there is no data. */ public function record():Array { var data:Array = null; var i:uint = 0; while(i < _total) { var o:Object = _map[i++]; if((o == null) || (o.current == 0)) continue; if(data == null) data = new Array(); data.push({code:i-1,value:o.current}); } return data; } /** * Part of the keystroke recording system. * Takes data about key presses and sets it into array. * * @param Record Array of data about key states. */ public function playback(Record:Array):void { var i:uint = 0; var l:uint = Record.length; var o:Object; var o2:Object; while(i < l) { o = Record[i++]; o2 = _map[o.code]; o2.current = o.value; if(o.value > 0) this[o2.name] = true; } } /** * Look up the key code for any given string name of the key or button. * * @param KeyName The String name of the key. * * @return The key code for that key. */ public function getKeyCode(KeyName:String):int { return _lookup[KeyName]; } /** * Check to see if any keys are pressed right now. * * @return Whether any keys are currently pressed. */ public function any():Boolean { var i:uint = 0; while(i < _total) { var o:Object = _map[i++]; if((o != null) && (o.current > 0)) return true; } return false; } /** * An internal helper function used to build the key array. * * @param KeyName String name of the key (e.g. "LEFT" or "A") * @param KeyCode The numeric Flash code for this key. */ protected function addKey(KeyName:String,KeyCode:uint):void { _lookup[KeyName] = KeyCode; _map[KeyCode] = { name: KeyName, current: 0, last: 0 }; } /** * Clean up memory. */ public function destroy():void { _lookup = null; _map = null; } } } ================================================ FILE: org/flixel/system/input/Keyboard.as ================================================ package org.flixel.system.input { import flash.events.KeyboardEvent; /** * Keeps track of what keys are pressed and how with handy booleans or strings. * * @author Adam Atomic */ public class Keyboard extends Input { public var ESCAPE:Boolean; public var F1:Boolean; public var F2:Boolean; public var F3:Boolean; public var F4:Boolean; public var F5:Boolean; public var F6:Boolean; public var F7:Boolean; public var F8:Boolean; public var F9:Boolean; public var F10:Boolean; public var F11:Boolean; public var F12:Boolean; public var ONE:Boolean; public var TWO:Boolean; public var THREE:Boolean; public var FOUR:Boolean; public var FIVE:Boolean; public var SIX:Boolean; public var SEVEN:Boolean; public var EIGHT:Boolean; public var NINE:Boolean; public var ZERO:Boolean; public var NUMPADONE:Boolean; public var NUMPADTWO:Boolean; public var NUMPADTHREE:Boolean; public var NUMPADFOUR:Boolean; public var NUMPADFIVE:Boolean; public var NUMPADSIX:Boolean; public var NUMPADSEVEN:Boolean; public var NUMPADEIGHT:Boolean; public var NUMPADNINE:Boolean; public var NUMPADZERO:Boolean; public var PAGEUP:Boolean; public var PAGEDOWN:Boolean; public var HOME:Boolean; public var END:Boolean; public var INSERT:Boolean; public var MINUS:Boolean; public var NUMPADMINUS:Boolean; public var PLUS:Boolean; public var NUMPADPLUS:Boolean; public var DELETE:Boolean; public var BACKSPACE:Boolean; public var TAB:Boolean; public var Q:Boolean; public var W:Boolean; public var E:Boolean; public var R:Boolean; public var T:Boolean; public var Y:Boolean; public var U:Boolean; public var I:Boolean; public var O:Boolean; public var P:Boolean; public var LBRACKET:Boolean; public var RBRACKET:Boolean; public var BACKSLASH:Boolean; public var CAPSLOCK:Boolean; public var A:Boolean; public var S:Boolean; public var D:Boolean; public var F:Boolean; public var G:Boolean; public var H:Boolean; public var J:Boolean; public var K:Boolean; public var L:Boolean; public var SEMICOLON:Boolean; public var QUOTE:Boolean; public var ENTER:Boolean; public var SHIFT:Boolean; public var Z:Boolean; public var X:Boolean; public var C:Boolean; public var V:Boolean; public var B:Boolean; public var N:Boolean; public var M:Boolean; public var COMMA:Boolean; public var PERIOD:Boolean; public var NUMPADPERIOD:Boolean; public var SLASH:Boolean; public var NUMPADSLASH:Boolean; public var CONTROL:Boolean; public var ALT:Boolean; public var SPACE:Boolean; public var UP:Boolean; public var DOWN:Boolean; public var LEFT:Boolean; public var RIGHT:Boolean; public function Keyboard() { var i:uint; //LETTERS i = 65; while(i <= 90) addKey(String.fromCharCode(i),i++); //NUMBERS i = 48; addKey("ZERO",i++); addKey("ONE",i++); addKey("TWO",i++); addKey("THREE",i++); addKey("FOUR",i++); addKey("FIVE",i++); addKey("SIX",i++); addKey("SEVEN",i++); addKey("EIGHT",i++); addKey("NINE",i++); i = 96; addKey("NUMPADZERO",i++); addKey("NUMPADONE",i++); addKey("NUMPADTWO",i++); addKey("NUMPADTHREE",i++); addKey("NUMPADFOUR",i++); addKey("NUMPADFIVE",i++); addKey("NUMPADSIX",i++); addKey("NUMPADSEVEN",i++); addKey("NUMPADEIGHT",i++); addKey("NUMPADNINE",i++); addKey("PAGEUP", 33); addKey("PAGEDOWN", 34); addKey("HOME", 36); addKey("END", 35); addKey("INSERT", 45); //FUNCTION KEYS i = 1; while(i <= 12) addKey("F"+i,111+(i++)); //SPECIAL KEYS + PUNCTUATION addKey("ESCAPE",27); addKey("MINUS",189); addKey("NUMPADMINUS",109); addKey("PLUS",187); addKey("NUMPADPLUS",107); addKey("DELETE",46); addKey("BACKSPACE",8); addKey("LBRACKET",219); addKey("RBRACKET",221); addKey("BACKSLASH",220); addKey("CAPSLOCK",20); addKey("SEMICOLON",186); addKey("QUOTE",222); addKey("ENTER",13); addKey("SHIFT",16); addKey("COMMA",188); addKey("PERIOD",190); addKey("NUMPADPERIOD",110); addKey("SLASH",191); addKey("NUMPADSLASH",191); addKey("CONTROL",17); addKey("ALT",18); addKey("SPACE",32); addKey("UP",38); addKey("DOWN",40); addKey("LEFT",37); addKey("RIGHT",39); addKey("TAB",9); } /** * Event handler so FlxGame can toggle keys. * * @param FlashEvent A KeyboardEvent object. */ public function handleKeyDown(FlashEvent:KeyboardEvent):void { var object:Object = _map[FlashEvent.keyCode]; if(object == null) return; if(object.current > 0) object.current = 1; else object.current = 2; this[object.name] = true; } /** * Event handler so FlxGame can toggle keys. * * @param FlashEvent A KeyboardEvent object. */ public function handleKeyUp(FlashEvent:KeyboardEvent):void { var object:Object = _map[FlashEvent.keyCode]; if(object == null) return; if(object.current > 0) object.current = -1; else object.current = 0; this[object.name] = false; } } } ================================================ FILE: org/flixel/system/input/Mouse.as ================================================ package org.flixel.system.input { import flash.display.Bitmap; import flash.display.Sprite; import flash.events.MouseEvent; import org.flixel.FlxCamera; import org.flixel.FlxG; import org.flixel.FlxPoint; import org.flixel.FlxSprite; import org.flixel.FlxU; import org.flixel.system.replay.MouseRecord; /** * This class helps contain and track the mouse pointer in your game. * Automatically accounts for parallax scrolling, etc. * * @author Adam Atomic */ public class Mouse extends FlxPoint { [Embed(source="../../data/cursor.png")] protected var ImgDefaultCursor:Class; /** * Current "delta" value of mouse wheel. If the wheel was just scrolled up, it will have a positive value. If it was just scrolled down, it will have a negative value. If it wasn't just scroll this frame, it will be 0. */ public var wheel:int; /** * Current X position of the mouse pointer on the screen. */ public var screenX:int; /** * Current Y position of the mouse pointer on the screen. */ public var screenY:int; /** * Helper variable for tracking whether the mouse was just pressed or just released. */ protected var _current:int; /** * Helper variable for tracking whether the mouse was just pressed or just released. */ protected var _last:int; /** * A display container for the mouse cursor. * This container is a child of FlxGame and sits at the right "height". */ protected var _cursorContainer:Sprite; /** * This is just a reference to the current cursor image, if there is one. */ protected var _cursor:Bitmap; /** * Helper variables for recording purposes. */ protected var _lastX:int; protected var _lastY:int; protected var _lastWheel:int; protected var _point:FlxPoint; protected var _globalScreenPosition:FlxPoint; /** * Constructor. */ public function Mouse(CursorContainer:Sprite) { super(); _cursorContainer = CursorContainer; _lastX = screenX = 0; _lastY = screenY = 0; _lastWheel = wheel = 0; _current = 0; _last = 0; _cursor = null; _point = new FlxPoint(); _globalScreenPosition = new FlxPoint(); } /** * Clean up memory. */ public function destroy():void { _cursorContainer = null; _cursor = null; _point = null; _globalScreenPosition = null; } /** * Either show an existing cursor or load a new one. * * @param Graphic The image you want to use for the cursor. * @param Scale Change the size of the cursor. Default = 1, or native size. 2 = 2x as big, 0.5 = half size, etc. * @param XOffset The number of pixels between the mouse's screen position and the graphic's top left corner. * @param YOffset The number of pixels between the mouse's screen position and the graphic's top left corner. */ public function show(Graphic:Class=null,Scale:Number=1,XOffset:int=0,YOffset:int=0):void { _cursorContainer.visible = true; if(Graphic != null) load(Graphic,Scale,XOffset,YOffset); else if(_cursor == null) load(); } /** * Hides the mouse cursor */ public function hide():void { _cursorContainer.visible = false; } /** * Read only, check visibility of mouse cursor. */ public function get visible():Boolean { return _cursorContainer.visible; } /** * Load a new mouse cursor graphic * * @param Graphic The image you want to use for the cursor. * @param Scale Change the size of the cursor. * @param XOffset The number of pixels between the mouse's screen position and the graphic's top left corner. * @param YOffset The number of pixels between the mouse's screen position and the graphic's top left corner. */ public function load(Graphic:Class=null,Scale:Number=1,XOffset:int=0,YOffset:int=0):void { if(_cursor != null) _cursorContainer.removeChild(_cursor); if(Graphic == null) Graphic = ImgDefaultCursor; _cursor = new Graphic(); _cursor.x = XOffset; _cursor.y = YOffset; _cursor.scaleX = Scale; _cursor.scaleY = Scale; _cursorContainer.addChild(_cursor); } /** * Unload the current cursor graphic. If the current cursor is visible, * then the default system cursor is loaded up to replace the old one. */ public function unload():void { if(_cursor != null) { if(_cursorContainer.visible) load(); else { _cursorContainer.removeChild(_cursor) _cursor = null; } } } /** * Called by the internal game loop to update the mouse pointer's position in the game world. * Also updates the just pressed/just released flags. * * @param X The current X position of the mouse in the window. * @param Y The current Y position of the mouse in the window. * @param XScroll The amount the game world has scrolled horizontally. * @param YScroll The amount the game world has scrolled vertically. */ public function update(X:int,Y:int):void { _globalScreenPosition.x = X; _globalScreenPosition.y = Y; updateCursor(); if((_last == -1) && (_current == -1)) _current = 0; else if((_last == 2) && (_current == 2)) _current = 1; _last = _current; } /** * Internal function for helping to update the mouse cursor and world coordinates. */ protected function updateCursor():void { //actually position the flixel mouse cursor graphic _cursorContainer.x = _globalScreenPosition.x; _cursorContainer.y = _globalScreenPosition.y; //update the x, y, screenX, and screenY variables based on the default camera. //This is basically a combination of getWorldPosition() and getScreenPosition() var camera:FlxCamera = FlxG.camera; screenX = (_globalScreenPosition.x - camera.x)/camera.zoom; screenY = (_globalScreenPosition.y - camera.y)/camera.zoom; x = screenX + camera.scroll.x; y = screenY + camera.scroll.y; } /** * Fetch the world position of the mouse on any given camera. * NOTE: Mouse.x and Mouse.y also store the world position of the mouse cursor on the main camera. * * @param Camera If unspecified, first/main global camera is used instead. * @param Point An existing point object to store the results (if you don't want a new one created). * * @return The mouse's location in world space. */ public function getWorldPosition(Camera:FlxCamera=null,Point:FlxPoint=null):FlxPoint { if(Camera == null) Camera = FlxG.camera; if(Point == null) Point = new FlxPoint(); getScreenPosition(Camera,_point); Point.x = _point.x + Camera.scroll.x; Point.y = _point.y + Camera.scroll.y; return Point; } /** * Fetch the screen position of the mouse on any given camera. * NOTE: Mouse.screenX and Mouse.screenY also store the screen position of the mouse cursor on the main camera. * * @param Camera If unspecified, first/main global camera is used instead. * @param Point An existing point object to store the results (if you don't want a new one created). * * @return The mouse's location in screen space. */ public function getScreenPosition(Camera:FlxCamera=null,Point:FlxPoint=null):FlxPoint { if(Camera == null) Camera = FlxG.camera; if(Point == null) Point = new FlxPoint(); Point.x = (_globalScreenPosition.x - Camera.x)/Camera.zoom; Point.y = (_globalScreenPosition.y - Camera.y)/Camera.zoom; return Point; } /** * Resets the just pressed/just released flags and sets mouse to not pressed. */ public function reset():void { _current = 0; _last = 0; } /** * Check to see if the mouse is pressed. * * @return Whether the mouse is pressed. */ public function pressed():Boolean { return _current > 0; } /** * Check to see if the mouse was just pressed. * * @return Whether the mouse was just pressed. */ public function justPressed():Boolean { return _current == 2; } /** * Check to see if the mouse was just released. * * @return Whether the mouse was just released. */ public function justReleased():Boolean { return _current == -1; } /** * Event handler so FlxGame can update the mouse. * * @param FlashEvent A MouseEvent object. */ public function handleMouseDown(FlashEvent:MouseEvent):void { if(_current > 0) _current = 1; else _current = 2; } /** * Event handler so FlxGame can update the mouse. * * @param FlashEvent A MouseEvent object. */ public function handleMouseUp(FlashEvent:MouseEvent):void { if(_current > 0) _current = -1; else _current = 0; } /** * Event handler so FlxGame can update the mouse. * * @param FlashEvent A MouseEvent object. */ public function handleMouseWheel(FlashEvent:MouseEvent):void { wheel = FlashEvent.delta; } /** * If the mouse changed state or is pressed, return that info now * * @return An array of key state data. Null if there is no data. */ public function record():MouseRecord { if((_lastX == _globalScreenPosition.x) && (_lastY == _globalScreenPosition.y) && (_current == 0) && (_lastWheel == wheel)) return null; _lastX = _globalScreenPosition.x; _lastY = _globalScreenPosition.y; _lastWheel = wheel; return new MouseRecord(_lastX,_lastY,_current,_lastWheel); } /** * Part of the keystroke recording system. * Takes data about key presses and sets it into array. * * @param KeyStates Array of data about key states. */ public function playback(Record:MouseRecord):void { _current = Record.button; wheel = Record.wheel; _globalScreenPosition.x = Record.x; _globalScreenPosition.y = Record.y; updateCursor(); } } } ================================================ FILE: org/flixel/system/replay/FrameRecord.as ================================================ package org.flixel.system.replay { /** * Helper class for the new replay system. Represents all the game inputs for one "frame" or "step" of the game loop. * * @author Adam Atomic */ public class FrameRecord { /** * Which frame of the game loop this record is from or for. */ public var frame:int; /** * An array of simple integer pairs referring to what key is pressed, and what state its in. */ public var keys:Array; /** * A container for the 4 mouse state integers. */ public var mouse:MouseRecord; /** * Instantiate array new frame record. */ public function FrameRecord() { frame = 0; keys = null; mouse = null; } /** * Load this frame record with input data from the input managers. * * @param Frame What frame it is. * @param Keys Keyboard data from the keyboard manager. * @param Mouse Mouse data from the mouse manager. * * @return A reference to this FrameRecord object. * */ public function create(Frame:Number,Keys:Array=null,Mouse:MouseRecord=null):FrameRecord { frame = Frame; keys = Keys; mouse = Mouse; return this; } /** * Clean up memory. */ public function destroy():void { keys = null; mouse = null; } /** * Save the frame record data to array simple ASCII string. * * @return A String object containing the relevant frame record data. */ public function save():String { var output:String = frame+"k"; if(keys != null) { var object:Object; var i:uint = 0; var l:uint = keys.length; while(i < l) { if(i > 0) output += ","; object = keys[i++]; output += object.code+":"+object.value; } } output += "m"; if(mouse != null) output += mouse.x + "," + mouse.y + "," + mouse.button + "," + mouse.wheel; return output; } /** * Load the frame record data from array simple ASCII string. * * @param Data A String object containing the relevant frame record data. */ public function load(Data:String):FrameRecord { var i:uint; var l:uint; //get frame number var array:Array = Data.split("k"); frame = int(array[0] as String); //split up keyboard and mouse data array = (array[1] as String).split("m"); var keyData:String = array[0]; var mouseData:String = array[1]; //parse keyboard data if(keyData.length > 0) { //get keystroke data pairs array = keyData.split(","); //go through each data pair and enter it into this frame's key state var keyPair:Array; i = 0; l = array.length; while(i < l) { keyPair = (array[i++] as String).split(":"); if(keyPair.length == 2) { if(keys == null) keys = new Array(); keys.push({code:int(keyPair[0] as String),value:int(keyPair[1] as String)}); } } } //mouse data is just 4 integers, easy peezy if(mouseData.length > 0) { array = mouseData.split(","); if(array.length >= 4) mouse = new MouseRecord(int(array[0] as String),int(array[1] as String),int(array[2] as String),int(array[3] as String)); } return this; } } } ================================================ FILE: org/flixel/system/replay/MouseRecord.as ================================================ package org.flixel.system.replay { /** * A helper class for the frame records, part of the replay/demo/recording system. * * @author Adam Atomic */ public class MouseRecord { /** * The main X value of the mouse in screen space. */ public var x:int; /** * The main Y value of the mouse in screen space. */ public var y:int; /** * The state of the left mouse button. */ public var button:int; /** * The state of the mouse wheel. */ public var wheel:int; /** * Instantiate a new mouse input record. * * @param X The main X value of the mouse in screen space. * @param Y The main Y value of the mouse in screen space. * @param Button The state of the left mouse button. * @param Wheel The state of the mouse wheel. */ public function MouseRecord(X:int,Y:int,Button:int,Wheel:int) { x = X; y = Y; button = Button; wheel = Wheel; } } } ================================================ FILE: todo.txt ================================================ x +assets Player x +assets Grass eating animation x +assets Farmer x +assets Shovel/Build animation x +assets Rabbit w/ arrow x +assets Troll w/ crown x +assets Treeline x +assets Wall x +assets Hill x +assets Stakes x +assets Palisade x +assets Stone wall x +assets Strong stone wall x +assets Scaffolding x +assets Campfire x +assets Lanterns/Torches x +assets Fireflies x +assets Fog x +assets Castle x +assets Banner (1) x +assets Platform w/ stakes (2) x +assets Watchtower (2 + 1) x +assets Stone tower 2 + 2 x +assets Castle (6) x +assets Farmland x +assets Shops x +assets Sun/Moon x +assets Fix shop graphic to be more clear x +code Remove coin indicator when no money x +code Update cash sack at start x +code Replace castle walls x +code Wall decay x +code Wall guards x +code Menu x +code Coin destinations x +code Money Indicator x +code Rig ripple size to weather x +code Fix wonky weather x +code Make hunters on castle shoot faster x +code +assets sparkle effect in stead of flicker when get coin x +code big trolls shouldn't steal coins x +assets Tighten map +error checkWork: Cannot property on null reference x +visuals phase 20 too dark x +assets kindom border indicator x +balance phase 28 nothing happens x +balance phase 32 way too easy x +visuals phase 35 weather glitch x +balance phase 36 make longer/harder x +visuals phase 37 lower contrast x +visuals phase 40 glitch x +balance reduce troll cooldown x +visuals increase sparkle lifetime x +visuals weather glitch in phase 36 x +visuals weather glitch in phase 40 x +sound day music in phase 22 lasts too long x +sound day music in phase 27 clips too x +sound day music in phase 31 clips. x +sound day music clips in phase 35 x +sound make sound for civ. getting hit by troll x +balance increase jumpheight from night 2. It's about halfway up the lowest wall now x +balance more trolls in phase 24 x +balance trolls in phase 28 still don't scale walls x +balance wave in phase 36 is still pretty easy x +code check archer behavior, all seem to be guarding the right side. x +code check if walls are repaired x +code big trolls hitbox x +balance night 2 (green) too jumpy x +code castle coin indicator too high x +code cycle phase consists of final night, DAYPASTEL x +code fix jumping height x +code indicate low money x +code add some dust when building x +code text is broken x +code skip to night with progress x +code output progress x +asset duck/cower animation for beggars x +code buy indicator appears further to middle than it disappears, also, it disappears when buying x +code max rate for farmland x +balance day 5 should last a little longer x +balance day 6 should last a little longer too x +balance trolls should spread out a little more x +code walls should look broken sooner, have a little more health x +code resume from last daybreak with a nice fx x +code wall damage gibs spawn too low (something with baseline offset?) x +visual switch first boss night with D9's night x +code walls don't repair x +testers don't get why horse is slow x +testers don't see night count x +testers have too much money x +testers build castle too soon x +testers castle is too profitable x +testers shouldn't get tip (expansion) at night x +code mochi api x +visual ease up on the fx x +code music clip in night 4 x night eight too dark x night eight is way easy x daypastel is too green x Ok, totaal geen idee hoe dat bouwen werkt en als shit in de stijgers staat dan werkt het niet x en je zet dingen steeds per ongeluk in de stijgers x en wat die zeisen doen snap ik ook niet x firstly, als je naar de rand loopt stopt de camera wel maar de koning niet, die loopt van het scherm af gewoon door x de uitleg tekst hangt boven te drukke achtergrond om te lezen x misschien in de lucht of een fijner font (halve pixels ipv hele pixels zeg maar) x als je bij de eerste nacht rechts staat met je koning wordt je aangevallen terwijl die lange pan naar links gedaan wordt highscore screen is ugly x goal is not clear show coin count more clearly x make trolls jump on each other's heads x night before day 8 too cray fx x higher jumps in cyclic phase walls should let trolls out when retreating nerf 'inner castle' strategy ramp difficulty at high phases ================================================ FILE: weathers.json ================================================ { "NIGHTTEMP": { "saturation": "0.0", "darkness": "0.2", "sky": "0xFF6a6d55", "sunTint": "0xffffff", "haze": "0xFF999d7c", "fog": "1.0", "wind": "0.2", "horizon": "0xFF6a6d55", "ambient": "0xFFFF0000", "timeOfDay": "0.0", "contrast": "-0.2", "darknessColor": "0x88111114" }, "DAWNTEMP": { "saturation": "0.0", "darkness": "0.2", "sky": "0xFF6a6d55", "sunTint": "0xffffff", "haze": "0xFF999d7c", "fog": "1.0", "wind": "0.2", "horizon": "0xFF6a6d55", "ambient": "0xFFFF0000", "timeOfDay": "0.25", "contrast": "-0.2", "darknessColor": "0x88111114" }, "DAYTEMP": { "saturation": "0.0", "darkness": "0.2", "sky": "0xFF6a6d55", "sunTint": "0xffffff", "haze": "0xFF999d7c", "fog": "1.0", "wind": "0.2", "horizon": "0xFF6a6d55", "ambient": "0xFFFF0000", "timeOfDay": "0.5", "contrast": "-0.2", "darknessColor": "0x88111114" }, "DUSKTEMP": { "saturation": "0.0", "darkness": "0.2", "sky": "0xFF6a6d55", "sunTint": "0xffffff", "haze": "0xFF999d7c", "fog": "1.0", "wind": "0.2", "horizon": "0xFF6a6d55", "ambient": "0xFFFF0000", "timeOfDay": "0.75", "contrast": "-0.2", "darknessColor": "0x88111114" }, "FOGGY": { "saturation": "0.7", "darkness": "0.2", "sky": "0xFF6a6d55", "sunTint": "0xffffff", "haze": "0xFF999d7c", "fog": "1.0", "wind": "0.2", "horizon": "0xFF6a6d55", "ambient": "0x330000FF", "timeOfDay": "0.25", "contrast": "-0.2", "darknessColor": "0x88111114" } , "DAWN": { "saturation": "0.8", "darkness": "0.2", "sky": "0xFF8C8CA6", "sunTint": "0xff6d40", "haze": "0x66f3f1e8", "fog": "0.0", "wind": "0.1", "horizon": "0xFFCF7968", "ambient": "0x11FF0000", "timeOfDay": "0.28", "contrast": "0.5", "darknessColor": "0x88111114" } , "SUNNY": { "saturation": "0.8", "darkness": "0.0", "sky": "0xFF98BEEC", "sunTint": "0xfff766", "haze": "0xCCf3f1e8", "fog": "0.0", "wind": "1.0", "horizon": "0xFFC4DAF1", "ambient": "0x44FFBB7F", "timeOfDay": "0.6", "contrast": "0.8", "darknessColor": "0x88111114" } , "EVENING": { "saturation": "0.8", "darkness": "0.1", "sky": "0xFFFF7F51", "horizon": "0xFFFFDF54", "sunTint": "0xFFFF7038", "haze": "0x99FF9068", "fog": "0.0", "wind": "0.1", "ambient": "0x33DE5E37", "timeOfDay": "0.70", "contrast": "0.7", "darknessColor": "0x88111114" } , "NIGHT": { "saturation": "0.8", "darkness": "0.4", "sky": "0xFF005EA5", "sunTint": "0xDDDDFF", "haze": "0xFF333333", "fog": "0.2", "wind": "0.2", "horizon": "0xFF002E80", "ambient": "0x110000FF", "timeOfDay": "0", "contrast": "0.5", "darknessColor": "0x88111114" } , "DAWNLIGHTPINK": { "saturation": "0.8", "darkness": "0.1", "sky": "0xFF8C8CA6", "sunTint": "0xF9B340", "haze": "0xAAf3f1e8", "fog": "0.5", "wind": "0.1", "horizon": "0xFFCF7968", "ambient": "0x22FF84DA", "timeOfDay": "0.28", "contrast": "0.5", "darknessColor": "0x88111114" } , "DAYWINDYCLEAR": { "saturation": "1.0", "darkness": "0.0", "sky": "0xFF64A3EA", "sunTint": "0xF7E9AA", "haze": "0x22f3f1e8", "fog": "0.0", "wind": "0.5", "horizon": "0xFF86BAEF", "ambient": "0x33F99100", "timeOfDay": "0.45", "contrast": "1.0", "darknessColor": "0x88111114" } , "DUSKYELLOW": { "saturation": "0.9", "darkness": "0.0", "sky": "0xFF8BB8E8", "sunTint": "0xF4EED0", "haze": "0x88f3f1e8", "fog": "0.0", "wind": "0.1", "horizon": "0xFFEDC99A", "ambient": "0x44F79A42", "timeOfDay": "0.651", "contrast": "0.8", "darknessColor": "0x88111114" } , "EVENINGORANGE": { "saturation": "0.8", "darkness": "0.1", "sky": "0xFF8BB8E8", "horizon": "0xFFEDC99A", "sunTint": "0xFFFF7038", "haze": "0xFFFF9068", "fog": "0.0", "wind": "0.1", "ambient": "0x33DE5E37", "timeOfDay": "0.70", "contrast": "0.7", "darknessColor": "0x88111114" } , "NIGHTGREEN": { "saturation": "0.7", "darkness": "0.45", "sky": "0xFF005EA5", "sunTint": "0xDDDDFF", "haze": "0xFFB8F2BB", "fog": "0.4", "wind": "0.1", "horizon": "0xFF002E80", "ambient": "0x2254FFAF", "timeOfDay": "0.85", "contrast": "0.0", "darknessColor": "0x88263529" } , "DAWNGREY": { "saturation": "0.5", "darkness": "0.2", "sky": "0xFFC4AD99", "sunTint": "0xF9B340", "haze": "0xAAf3f1e8", "fog": "0.5", "wind": "0.1", "horizon": "0xFFCECECE", "ambient": "0x22FF84DA", "timeOfDay": "0.31", "contrast": "0.5", "darknessColor": "0x88111114" } , "DAYBLEAK": { "saturation": "0.7", "darkness": "0.0", "sky": "0xFFA0C2E8", "horizon": "0xFFA6C9ED", "sunTint": "0xF7E9AA", "haze": "0xFFf3f1e8", "fog": "1.0", "wind": "0.5", "ambient": "0x33F7E0C3", "timeOfDay": "0.45", "contrast": "0.0", "darknessColor": "0x88111114" } , "DUSKWARM": { "saturation": "0.9", "darkness": "0.0", "sky": "0xFF8BB8E8", "sunTint": "0xF4EED0", "haze": "0x88f3f1e8", "fog": "0.0", "wind": "0.1", "horizon": "0xFFEDC99A", "ambient": "0x44F79A42", "timeOfDay": "0.651", "contrast": "0.8", "darknessColor": "0x88111114" } ,"EVENINGBLACK": { "saturation": "0.7", "darkness": "0.3", "sky": "0xFF333333", "horizon": "0xFFEDC99A", "sunTint": "0xFFFF7038", "haze": "0xFFFF9090", "fog": "0.0", "wind": "0.1", "ambient": "0x339f6b5c", "timeOfDay": "0.70", "contrast": "0.0", "darknessColor": "0x88111114" } , "NIGHTDARK": { "saturation": "0.7", "darkness": "0.75", "sky": "0xFF002E33", "sunTint": "0x65a2cb", "haze": "0xFF555555", "fog": "0.0", "wind": "0.1", "horizon": "0xFF002E80", "ambient": "0x4454AACF", "timeOfDay": "0.85", "contrast": "0.4", "darknessColor": "0xFF263529" } , "DAWNBLEAK": { "saturation": "0.9", "darkness": "0.2", "sky": "0xFFC4AD99", "sunTint": "0xF9B340", "haze": "0xAAf3f1e8", "fog": "0.5", "wind": "0.1", "horizon": "0xFFCECECE", "ambient": "0x22FF84DA", "timeOfDay": "0.31", "contrast": "0.5", "darknessColor": "0x88111114" } , "DAWNEARLY": { "saturation": "0.9", "darkness": "0.2", "sky": "0xFFC4AD99", "sunTint": "0xF9B340", "haze": "0xAAf3f1e8", "fog": "0.5", "wind": "0.1", "horizon": "0xFFCECECE", "ambient": "0x22FF84DA", "timeOfDay": "0.19", "contrast": "0.5", "darknessColor": "0x88111114" } , "DAYSOFT": { "saturation": "0.7", "darkness": "0.0", "sky": "0xFFA0C2E8", "horizon": "0xFFA6C9ED", "sunTint": "0xF7E9AA", "haze": "0x22f3f1e8", "fog": "0.0", "wind": "0.1", "ambient": "0x33F7E0C3", "timeOfDay": "0.45", "contrast": "0.0", "darknessColor": "0x88111114" } ,"EVENINGMONOTONE": { "saturation": "0.7", "darkness": "0.3", "sky": "0xFF333333", "horizon": "0xFFEDEDED", "sunTint": "0xAAAAAA", "haze": "0xFFFF9090", "fog": "0.0", "wind": "0.1", "ambient": "0x33666666", "timeOfDay": "0.70", "contrast": "0.0", "darknessColor": "0x88111114" } , "NIGHTSUPERDARK": { "saturation": "0.7", "darkness": "0.7", "sky": "0xFF000000", "sunTint": "0x65a2cb", "haze": "0xFFFFFFFF", "fog": "0.0", "wind": "0.1", "horizon": "0xFF002E80", "ambient": "0x4454AACF", "timeOfDay": "0.85", "contrast": "0.4", "darknessColor": "0xFF263529" } , "EVENINGFOGGY": { "saturation": "0.7", "darkness": "0.3", "sky": "0xFFd56c47", "sunTint": "0xffd9c8", "haze": "0xFFd5d9ff", "fog": "1.0", "wind": "0.2", "horizon": "0xFF6a6d55", "ambient": "0x440000FF", "timeOfDay": "0.7", "contrast": "-0.1", "darknessColor": "0x88111114" } , "NIGHTFOGGY": { "saturation": "0.7", "darkness": "0.7", "sky": "0xFF25229d", "sunTint": "0xffd9c8", "haze": "0xFFd5d9ff", "fog": "1.0", "wind": "0.2", "horizon": "0xFF6a6d55", "ambient": "0x7763709d", "timeOfDay": "0.8", "contrast": "-0.1", "darknessColor": "0x88111114" } , "DAYMONOCHROME": { "saturation": "0.25", "darkness": "0.0", "sky": "0xFF80A2C8", "horizon": "0xFFA6C9ED", "sunTint": "0xF7E9AA", "haze": "0xFFf3f1e8", "fog": "1.0", "wind": "0.5", "ambient": "0x33F7E0C3", "timeOfDay": "0.45", "contrast": "0.4", "darknessColor": "0x88111114" } , "DUSKPINK": { "saturation": "0.9", "darkness": "0.0", "sky": "0xFF8C8CA6", "sunTint": "0xF9B340", "haze": "0xAAf3c1e8", "fog": "0.0", "wind": "0.1", "horizon": "0xFFEDC99A", "ambient": "0x44F79A42", "timeOfDay": "0.651", "contrast": "0.8", "darknessColor": "0x88111114" } , "NIGHTCLEAR": { "saturation": "0.8", "darkness": "0.4", "sky": "0xFF005EA5", "sunTint": "0xDDDDFF", "haze": "0x44434e87", "fog": "0.1", "wind": "0.3", "horizon": "0xFF002E80", "ambient": "0x110000FF", "timeOfDay": "0.1", "contrast": "-0.1", "darknessColor": "0x88111114" }, "DAYCLEARCOLD": { "saturation": "0.7", "darkness": "0.0", "sky": "0xFFC9E3EA", "horizon": "0xFFC9E3EA", "sunTint": "0xF7E9AA", "haze": "0x33C9E3EA", "fog": "0.0", "wind": "0.5", "ambient": "0x33F7E0C3", "timeOfDay": "0.45", "contrast": "0.2", "darknessColor": "0x88111114" } , "DAWNCLEARORANGE": { "saturation": "0.8", "darkness": "0.2", "sky": "0xFF97A7B4", "sunTint": "0xFAFDC9", "haze": "0x88FDB24C", "horizon": "0xFFF9A04F", "fog": "0.0", "wind": "0.1", "ambient": "0x11FF0000", "timeOfDay": "0.28", "contrast": "0.5", "darknessColor": "0x88111114" } , "DUSKCLEAR": { "saturation": "0.9", "darkness": "0.2", "sky": "0xFF513744", "sunTint": "0xF9B340", "haze": "0xAAC9976D", "fog": "0.0", "wind": "0.1", "horizon": "0xFFC69875", "ambient": "0x22360A00", "timeOfDay": "0.651", "contrast": "0.45", "darknessColor": "0x88111114" } , "NIGHTREDMOON": { "saturation": "0.9", "darkness": "0.4", "sky": "0xFF142744", "sunTint": "0xC73800", "haze": "0x665C5D9E", "fog": "0.1", "wind": "0.1", "horizon": "0xFFAD2E21", "ambient": "0x220E0B62", "timeOfDay": "0.1", "contrast": "0.2", "darknessColor": "0x880E0B62" } , "DAWNREDMOON": { "saturation": "0.9", "darkness": "0.2", "sky": "0xFF16549F", "sunTint": "0xE76833", "haze": "0xbb5C5D9E", "fog": "0.1", "wind": "0.1", "horizon": "0xFFFF9286", "ambient": "0x110E0B22", "timeOfDay": "0.2", "contrast": "0.0", "darknessColor": "0x880E0B62" } , "DAYORANGESKY": { "saturation": "0.7", "darkness": "0.1", "sky": "0xFFADA290", "sunTint": "0xF9F8E6", "haze": "0x445A432C", "fog": "0.3", "wind": "0.7", "horizon": "0xFFDCAB4F", "ambient": "0x110E0B22", "timeOfDay": "0.4", "contrast": "0.2", "darknessColor": "0x880E0B62" } , "DUSKFOGGY": { "saturation": "0.6", "contrast": "0.1", "ambient": "0x330E0B22", "sky": "0xFFB29C8F", "horizon": "0xFF3D6BCD", "haze": "0x00000000", "sunTint": "0xF9F8E6", "fog": "0.8", "wind": "0.4", "timeOfDay": "0.75", "darkness": "0.25", "darknessColor": "0x880E0B62" } , "NIGHTPURPLE": { "saturation": "0.8", "contrast": "1.4", "ambient": "0x33886AAA", "sky": "0xFF57577D", "horizon": "0xFF4D4658", "haze": "0x88886AAA", "sunTint": "0xF9F8E6", "fog": "0.0", "wind": "0.2", "timeOfDay": "0.9", "darkness": "0.3", "darknessColor": "0x88990BBB" } , "DAWNBRIGHT": { "saturation": "0.8", "contrast": "1.4", "ambient": "0xff886A00", "sky": "0xFF57577D", "horizon": "0xFFEEEE88", "haze": "0x00886AAA", "sunTint": "0xF9F8E6", "fog": "0.0", "wind": "0.2", "timeOfDay": "0.3", "darkness": "0.15", "darknessColor": "0x88000BBB" } , "DAYPASTEL": { "saturation": "1.0", "contrast": "1.0", "ambient": "0x44886A00", "sky": "0xFF3090F6", "horizon": "0xFFEEEE88", "haze": "0xDD657A8F", "sunTint": "0xF9F8E6", "fog": "0.0", "wind": "0.3", "timeOfDay": "0.55", "darkness": "0.0", "darknessColor": "0x88000BBB" } , "DUSKTAN": { "saturation": "0.8", "contrast": "0.5", "ambient": "0xFF886A00", "sky": "0xFF52424C", "horizon": "0xFFFFBA3B", "haze": "0x55C18F90", "sunTint": "0xF9F8E6", "fog": "0.0", "wind": "0.3", "timeOfDay": "0.7", "darkness": "0.1", "darknessColor": "0x88330B33" } , "NIGHTSHINE": { "saturation": "0.5", "contrast": "4.0", "ambient": "0x00000000", "sky": "0xFF000000", "horizon": "0xFFAAAAFF", "haze": "0x00886AAA", "sunTint": "0xF9F8E6", "fog": "0.0", "wind": "0.2", "timeOfDay": "0.9", "darkness": "0.35", "darknessColor": "0x88222255" } , "DAWNBROWN": { "saturation": "0.8", "contrast": "0.7", "ambient": "0x55AD3200", "sky": "0xFFC0AFBD", "horizon": "0xFF94A9B6", "haze": "0x99AAAAAA", "sunTint": "0xF9F8E6", "fog": "0.2", "wind": "0.2", "timeOfDay": "0.28", "darkness": "0.0", "darknessColor": "0x88222255" } , "DAYDUSTY": { "saturation": "0.8", "contrast": "0.2", "ambient": "0x55f5db04", "sky": "0xFF5d9df5", "horizon": "0xFF94A9B6", "haze": "0x99AAAAAA", "sunTint": "0xF9F8E6", "fog": "0.2", "wind": "0.2", "timeOfDay": "0.4", "darkness": "0.0", "darknessColor": "0x88222255" } , "DUSKRED": { "saturation": "0.8", "contrast": "0.1", "ambient": "0x55f5db04", "sky": "0xFFd06219", "horizon": "0xFFf2d407", "haze": "0x99AAAAAA", "sunTint": "0xf27612", "fog": "0.2", "wind": "0.2", "timeOfDay": "0.651", "darkness": "0.2", "darknessColor": "0x88000000" } , "NIGHTLONG": { "saturation": "1.0", "contrast": "0.3", "ambient": "0x55acc857", "sky": "0xFF7399c8", "horizon": "0xFF7399c8", "haze": "0xFF000000", "sunTint": "0x162039", "fog": "0.0", "wind": "0.1", "timeOfDay": "0.9", "darkness": "0.4", "darknessColor": "0x88000000" } }