Created
April 22, 2012 20:53
-
-
Save Blecki/2466828 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package | |
{ | |
import Box2D.Collision.Shapes.*; | |
import Box2D.Common.Math.*; | |
import Box2D.Dynamics.*; | |
import Box2D.Dynamics.Joints.*; | |
import flash.display.BitmapData; | |
import flash.display.Sprite; | |
import flash.events.IMEEvent; | |
import flash.geom.Matrix; | |
import flash.geom.Point; | |
import flash.geom.Rectangle; | |
import gum.controls.Button; | |
import gum.controls.CheckBox; | |
import gum.FlashPunk.GraphicControl; | |
import gum.FlashPunk.UIEntity; | |
import net.flashpunk.FP; | |
import net.flashpunk.graphics.*; | |
import net.flashpunk.Sfx; | |
import net.flashpunk.utils.*; | |
import net.flashpunk.World; | |
/** | |
* ... | |
* @author AJC | |
*/ | |
public class TinyWorld extends World | |
{ | |
private var _main:Main = null; | |
public var physicsWorld:b2World = new b2World(new b2Vec2(0, 10), true); | |
public var debugSprite:Sprite = new Sprite(); | |
public var contactListener:TinyContactListener = new TinyContactListener(); | |
public var selectedChunk:Chunk = null; | |
public var possibleConnections:Array = new Array(); | |
public var grabbableBlocks:Array = new Array(); | |
public var particleEmitter:Emitter; | |
public var tug:Tug = null; | |
public var tugJoints:Array = null; | |
public var mouseVec:b2Vec2 = new b2Vec2(0, 0); | |
public var targetOffset:Object = null; | |
public var emitParticles:Boolean = false; | |
public var breakJoints:Boolean = true; | |
public var gravity:Boolean = true; | |
public var fullSnap:Boolean = false; | |
public var aimChunk:Chunk = null; | |
public var bgBitmap:BitmapData = FP.getBitmap(Assets.BACKGROUND); | |
private static const FLYAROUND:int = 0; | |
private static const ATTACHED:int = 1; | |
public var gameState:int = FLYAROUND; | |
public var wasInput:Number = 0; | |
public var worldType:int = FP.rand(3); | |
public var core:Core = null; | |
public var indicatorBackground:Image = new Image(Assets.CHARS, new Rectangle(0, 0, 20, 20)); | |
public var pauseMenu:UIEntity = new UIEntity(280, 210, 240, 200); | |
public var paused:Boolean = false; | |
public var muted:Boolean = false; | |
public var muteBox:CheckBox = null; | |
public var firstUpdate:Boolean = true; | |
public var snapSound:Sfx = new Sfx(Assets.SNAP_SOUND); | |
public var thrustSound:Sfx = new Sfx(Assets.THRUSTER_SOUND); | |
public var dockSound:Sfx = new Sfx(Assets.DOCK_SOUND); | |
//public var bumpSound:SoundPool = new SoundPool(Assets.BUMP_SOUND); | |
public var connectionGraph:BitmapData = new BitmapData(22, 22, false, 0xFF000000); | |
public var cameraDestination:Point = new Point(0, 0); | |
public var score:int = 0; | |
public var numbers:Array = []; | |
public var hilites:Array = []; | |
public var meteorTime:Number = 0; | |
public var scoreImage:Image = new Image(BitmapFont.MakeTextImage("SCORE\\ ", 0xFFFFFFFF, 10000)); | |
public var currentSong:int = 0; | |
public var meteorCount:int = 0; | |
public var songs:Array = []; | |
public var musicFadeTime:Number = 0.5; // 1/time_to_fade | |
public var musicVolume:Number = 1; | |
public var burnzezSucks:Boolean = true; | |
public function StopMusic():void | |
{ | |
songs[0].stop(); | |
if (!burnzezSucks) songs[1].stop(); | |
} | |
public function StartMusic():void | |
{ | |
songs.push(new Sfx(Assets.SONG1)); | |
songs[0].loop(0); | |
if (!burnzezSucks) | |
{ | |
songs.push(new Sfx(Assets.SONG2)); | |
songs[1].loop(0); | |
} | |
} | |
public function ChangeMusic(newSong:int):void | |
{ | |
if (!burnzezSucks) currentSong = newSong; | |
} | |
public function UpdateMusic():void | |
{ | |
if (songs[currentSong].volume < musicVolume) songs[currentSong].volume = songs[currentSong].volume + FP.elapsed * musicFadeTime; | |
var othersong:int = currentSong == 0 ? 1 : 0; | |
if (!burnzezSucks) if (songs[othersong].volume > 0) songs[othersong].volume = songs[othersong].volume - FP.elapsed * musicFadeTime; | |
if (muted) | |
{ | |
songs[0].volume = 0; | |
if (!burnzezSucks) songs[1].volume = 0; | |
} | |
} | |
public function TinyWorld(planetType:int, muted:Boolean) | |
{ | |
this.muted = muted; | |
worldType = planetType; | |
for (var i:int = 0; i < 10; ++i) | |
{ | |
var nImage:Image = new Image(Assets.CHARS, new Rectangle(20 * i, 20, 20, 20)); | |
numbers.push(nImage); | |
} | |
var hilite:Image = new Image(Assets.WORLDTILES, new Rectangle(10 * 20, 2 * 20, 20, 20)); | |
hilite.centerOrigin(); | |
hilites.push(hilite); | |
hilite = new Image(Assets.WORLDTILES, new Rectangle(11 * 20, 2 * 20, 2 * 20, 20)); | |
hilite.centerOrigin(); | |
hilites.push(hilite); | |
hilite = new Image(Assets.WORLDTILES, new Rectangle(10 * 20, 3 * 20, 3 * 20, 20)); | |
hilite.centerOrigin(); | |
hilites.push(hilite); | |
hilite = new Image(Assets.WORLDTILES, new Rectangle(13 * 20, 2 * 20, 2 * 20, 2 * 20)); | |
hilite.centerOrigin(); | |
hilites.push(hilite); | |
hilite = new Image(Assets.WORLDTILES, new Rectangle(13 * 20, 2 * 20, 2 * 20, 2 * 20)); | |
hilite.centerOrigin(); | |
hilites.push(hilite); | |
Input.define("Up", Key.W, Key.UP); | |
Input.define("RotateLeft", Key.A, Key.LEFT); | |
Input.define("RotateRight", Key.D, Key.RIGHT); | |
Input.define("StrafeLeft", Key.Q, Key.J ); | |
Input.define("StrafeRight", Key.E, Key.K); | |
Input.define("Attach", Key.SPACE); | |
Input.define("Down", Key.S, Key.DOWN); | |
Input.define("Snap", Key.C, Key.M); | |
Input.define("Meteor", Key.Z, Key.B); | |
Input.define("Pause", Key.P); | |
var _this = this; | |
pauseMenu.AddGuiItem(new GraphicControl(new Rectangle(0, 0, 240, 200), | |
new Image(Assets.PAUSEBG))); | |
pauseMenu.AddGuiItem(new Button(new Rectangle(30, 10, 180, 20), | |
{ | |
background: BitmapFont.MakeTextImage("MAIN MENU", 0xFFFFFFFF, 160), | |
hoverBackground: BitmapFont.MakeTextImage("MAIN MENU", 0xFFFF0000, 160), | |
OnClick: function(button:Button):void { StopMusic(); FP.world = new MainMenu(_this.muted); } | |
})); | |
pauseMenu.AddGuiItem(new GraphicControl(new Rectangle(10, 40, 220, 200), | |
new Image(BitmapFont.MakeTextImage( | |
"WASD\\ARROWS MOVE\nQE\\JK STRAFE\nSPACE ATTACH\nC\\M LET GO\nZ\\B METEORS\nP UNPAUSE", | |
0xFFFFFFFF, 220)))); | |
muteBox = new CheckBox(new Rectangle(10, 200 - 30, 220, 20), | |
{ | |
background: BitmapFont.MakeTextImage("MUTE MUSIC", 0xFFFFFFFF, 1000) | |
}); | |
pauseMenu.AddGuiItem(muteBox); | |
muteBox.checked = muted; | |
/*var debugDraw:b2DebugDraw = new b2DebugDraw(); | |
debugDraw.SetSprite(debugSprite); | |
debugDraw.SetDrawScale(Assets.TILE_WIDTH); | |
debugDraw.SetFillAlpha(0.0); | |
debugDraw.SetLineThickness(1.0); | |
debugDraw.SetFlags(b2DebugDraw.e_shapeBit | b2DebugDraw.e_jointBit); | |
physicsWorld.SetDebugDraw(debugDraw);*/ | |
var pShape:b2PolygonShape = new b2PolygonShape(); | |
pShape.SetAsEdge( | |
new b2Vec2(0, Assets.PLAY_HEIGHT / Assets.TILE_HEIGHT), | |
new b2Vec2(Assets.PLAY_WIDTH / Assets.TILE_WIDTH, Assets.PLAY_HEIGHT / Assets.TILE_HEIGHT)); | |
physicsWorld.GetGroundBody().CreateFixture2(pShape, 0); | |
pShape.SetAsEdge( | |
new b2Vec2(0, 0), | |
new b2Vec2(0, Assets.PLAY_HEIGHT / Assets.TILE_HEIGHT)); | |
physicsWorld.GetGroundBody().CreateFixture2(pShape, 0); | |
pShape.SetAsEdge( | |
new b2Vec2(Assets.PLAY_WIDTH / Assets.TILE_WIDTH, 0), | |
new b2Vec2(Assets.PLAY_WIDTH / Assets.TILE_WIDTH, Assets.PLAY_HEIGHT / Assets.TILE_HEIGHT)); | |
physicsWorld.GetGroundBody().CreateFixture2(pShape, 0); | |
pShape.SetAsEdge( | |
new b2Vec2(0, 0), | |
new b2Vec2(Assets.PLAY_WIDTH / Assets.TILE_WIDTH, 0)); | |
physicsWorld.GetGroundBody().CreateFixture2(pShape, 0); | |
physicsWorld.SetContactListener(contactListener); | |
physicsWorld.SetContinuousPhysics(true); | |
var planetGrid:Array = []; | |
for (var i:int = 0; i < Assets.PLANET_SIZE * Assets.PLANET_SIZE; ++i) | |
planetGrid[i] = 0; | |
var markChunk:Function = function (x:int, y:int, width:int, height:int):void | |
{ | |
for (var i:int = x; i < x + width; ++i) | |
for (var j:int = y; j < y + height; ++j) | |
planetGrid[ (j * Assets.PLANET_SIZE) + i ] = 1; | |
} | |
var checkChunk:Function = function (x:int, y:int, width:int, height:int):Boolean | |
{ | |
for (var i:int = x; i < x + width; ++i) | |
for (var j:int = y; j < y + height; ++j) | |
{ | |
if (i < 0 || i >= Assets.PLANET_SIZE || j < 0 || j >= Assets.PLANET_SIZE) return false; | |
if (planetGrid[ (j * Assets.PLANET_SIZE) + i ] == 1) return false; | |
} | |
return true; | |
} | |
markChunk(Assets.PLANET_SIZE / 2 - 1, Assets.PLANET_SIZE / 2 - 1, 2, 2); | |
core = new Core(this); | |
add(core); | |
for (var x:int = 0; x < Assets.PLANET_SIZE; ++x) | |
{ | |
for (var y:int = 0; y < Assets.PLANET_SIZE; ++y) | |
{ | |
if (!checkChunk(x, y, 1, 1)) continue; | |
var btype:int = FP.rand(6); | |
CheckTypes: while (true) | |
{ | |
var w:int = 0; | |
var h:int = 0; | |
var xbtype:int = 0; | |
switch (btype) | |
{ | |
case 0: | |
w = 1; | |
h = 1; | |
xbtype = 0; | |
break; | |
case 1: | |
w = 2; | |
h = 1; | |
xbtype = 1; | |
break; | |
case 2: | |
w = 1; | |
h = 2; | |
xbtype = 6; | |
break; | |
case 3: | |
w = 3; | |
h = 1; | |
xbtype = 2; | |
break; | |
case 4: | |
w = 1; | |
h = 3; | |
xbtype = 7; | |
break; | |
case 5: | |
w = 2; | |
h = 2; | |
xbtype = 3; | |
break; | |
} | |
if (checkChunk(x, y, w, h)) | |
{ | |
markChunk(x, y, w, h); | |
addChunk(x, y, xbtype); | |
break CheckTypes; | |
} | |
btype += 1; | |
if (btype == 6) btype = 0; | |
} | |
} | |
} | |
particleEmitter = new Emitter(Assets.CHARS, 20, 20); | |
particleEmitter.newType("fire", [21, 22, 23, 24, 25]); | |
//particleEmitter.setAlpha("fire", 1, 0, Ease.quadOut); | |
//particleEmitter.setColor("fire", 0xFFFF0000, 0, Ease.quadOut); | |
particleEmitter.setMotion("fire", 0, 12, 2, 360, 4, 1, Ease.quadOut); | |
particleEmitter.newType("puff", [2, 3, 4, 5, 6, 7]); | |
particleEmitter.setMotion("puff", 0, 12, 2, 360, 4, 1, Ease.quadOut); | |
addGraphic(particleEmitter).layer = -2; | |
tug = new Tug(this); | |
add(tug); | |
SpawnMeteor(); | |
StartMusic(); | |
} | |
private function addChunk(x:int, y:int, btype:int):Chunk | |
{ | |
var pX:Number = (x - (Assets.PLANET_SIZE/2)) * 1.1; | |
var pY:Number = (y - (Assets.PLANET_SIZE/2)) * 1.1; | |
pX = ((Assets.PLAY_WIDTH / 2) / Assets.TILE_WIDTH) + pX; | |
pY = ((Assets.PLAY_HEIGHT / 2) / Assets.TILE_HEIGHT) + pY; | |
var chunk:Chunk = new Chunk(this, btype, worldType); | |
if (btype != 6 && btype != 7 && btype != 1) | |
chunk.physicsBody.SetPosition( | |
new b2Vec2( | |
pX + (chunk.tilesWidth / 2) + (IsOdd(chunk.tilesWidth) ? 0.5 : 0), | |
pY + (chunk.tilesHeight / 2) + (IsOdd(chunk.tilesHeight) ? 0.5 : 0) | |
)); | |
else | |
chunk.physicsBody.SetPosition( | |
new b2Vec2( | |
pX + (chunk.tilesHeight / 2) + (IsOdd(chunk.tilesHeight) ? 0.5 : 0), | |
pY + (chunk.tilesWidth / 2) + (IsOdd(chunk.tilesWidth) ? 0.5 : 0) | |
)); | |
add(chunk); | |
chunk.update(); | |
return chunk; | |
} | |
private function SpawnChunk():void | |
{ | |
var chunk:Chunk = new Chunk(this, FP.rand(4), FP.rand(3)); | |
var mAngle:Number = FP.random * Math.PI * 2; | |
var pos:b2Vec2 = b2Vec2.Make(0, -1); | |
pos.MulM(b2Mat22.FromAngle(mAngle)); | |
var velocity:b2Vec2 = Negate(pos); | |
chunk.physicsBody.SetPosition(vAdd(b2Vec2.Make(Assets.PLAY_WIDTH / Assets.TILE_WIDTH / 2, | |
Assets.PLAY_HEIGHT / Assets.TILE_HEIGHT / 2), Multiply(pos, Assets.PLAY_HEIGHT / Assets.TILE_HEIGHT / 2))); | |
chunk.physicsBody.ApplyImpulse(Multiply(velocity, 2000), chunk.physicsBody.GetPosition()); | |
add(chunk); | |
} | |
private static var tempVec:b2Vec2 = new b2Vec2(); | |
private static var mat:b2Mat22 = new b2Mat22(); | |
public static function normalizeAngle(angle:Number):Number | |
{ | |
return Math.atan2(Math.sin(angle), Math.cos(angle)); | |
} | |
public static function CrossZ(a:b2Vec2, b:b2Vec2):Number | |
{ | |
return (b.y * a.x) - (b.x * a.y); | |
} | |
public static function AngleBetweenVectors(a:b2Vec2, b:b2Vec2):Number | |
{ | |
a.Normalize(); | |
b.Normalize(); | |
var dotProduct:Number = b2Math.Dot(a, b); | |
if (dotProduct < -1.0) dotProduct = -1.0; | |
if (dotProduct > 1.0) dotProduct = 1.0; | |
var angle:Number = Math.acos(dotProduct); | |
if (CrossZ(a, b) < 0) return -angle; | |
return angle; | |
} | |
public static function Perpendicular(a:b2Vec2):b2Vec2 | |
{ | |
return b2Vec2.Make(a.y, -a.x); | |
} | |
public static function Negate(a:b2Vec2):b2Vec2 | |
{ | |
return b2Vec2.Make( -a.x, -a.y); | |
} | |
public static function Multiply(a:b2Vec2, b:Number):b2Vec2 | |
{ | |
return b2Vec2.Make(b * a.x, b * a.y); | |
} | |
public static function vAdd(a:b2Vec2, b:b2Vec2):b2Vec2 | |
{ | |
return b2Vec2.Make(a.x + b.x, a.y + b.y); | |
} | |
private function SpawnMeteor():void | |
{ | |
var meteor:Meteor = new Meteor(this); | |
var mAngle:Number = FP.random * Math.PI * 2; | |
var pos:b2Vec2 = b2Vec2.Make(0, -1); | |
pos.MulM(b2Mat22.FromAngle(mAngle)); | |
var velocity:b2Vec2 = Negate(pos); | |
meteor.physicsBody.SetPosition(vAdd(b2Vec2.Make(Assets.PLAY_WIDTH / Assets.TILE_WIDTH / 2, | |
Assets.PLAY_HEIGHT / Assets.TILE_HEIGHT / 2), Multiply(pos, Assets.PLAY_HEIGHT / Assets.TILE_HEIGHT / 2))); | |
meteor.physicsBody.ApplyImpulse(Multiply(velocity, 2000), meteor.physicsBody.GetPosition()); | |
add(meteor); | |
meteorTime = 30 + (FP.random * 10); | |
} | |
public function ClearTraversed():void | |
{ | |
for (var body:b2Body = physicsWorld.GetBodyList(); body != null; body = body.GetNext()) | |
if (body.GetUserData() is Chunk) (body.GetUserData() as Chunk).traversed = false; | |
} | |
override public function update():void | |
{ | |
if (firstUpdate) | |
{ | |
FP.camera.x = cameraDestination.x = (Assets.PLAY_WIDTH / 2) - (800 / 2); | |
FP.camera.y = cameraDestination.y = (Assets.PLAY_HEIGHT / 2) - (600 / 2); | |
firstUpdate = false; | |
} | |
muted = muteBox.checked; | |
UpdateMusic(); | |
if (paused) | |
{ | |
pauseMenu.update(); | |
if (Input.pressed("Pause")) paused = false; | |
//super.update(); | |
return; | |
} | |
if (Input.pressed("Pause")) | |
{ | |
paused = true; | |
//super.update(); | |
return; | |
} | |
if (meteorCount > 0 || meteorTime < 3.5) ChangeMusic(1); | |
else ChangeMusic(0); | |
meteorTime -= FP.elapsed; | |
if (meteorTime < 0) for (var s:int = 0; s < 5; ++s) SpawnMeteor(); | |
if (Input.check("Meteor")) SpawnMeteor(); | |
ClearTraversed(); | |
var worldCenter:b2Vec2 = b2Vec2.Make(Assets.PLAY_WIDTH / 2 / Assets.TILE_WIDTH, Assets.PLAY_HEIGHT / 2 / Assets.TILE_HEIGHT); | |
for (var body:b2Body = physicsWorld.GetBodyList(); body != null; body = body.GetNext()) | |
{ | |
body.m_torque = 0; | |
if (body.GetUserData() is Meteor) continue; | |
var gravityDirection:b2Vec2 = worldCenter.Copy(); | |
gravityDirection.Subtract(body.GetPosition()); | |
var gLength:Number = gravityDirection.Length(); | |
if (gLength > Assets.PLAY_HEIGHT / 2 / Assets.TILE_HEIGHT) | |
{ | |
var impulse:b2Vec2 = gravityDirection.Copy(); | |
impulse.Normalize(); | |
impulse.Multiply(gLength - (Assets.PLAY_HEIGHT / 2 / Assets.TILE_HEIGHT)); | |
body.ApplyImpulse(impulse, body.GetPosition()); | |
} | |
if (body.GetUserData() is Chunk && gravity) | |
{ | |
var lSquared:Number = gravityDirection.LengthSquared(); | |
gravityDirection.Multiply(1 / lSquared); | |
body.gravity = Multiply(gravityDirection, Assets.GRAVITATIONAL_CONSTANT); | |
} | |
//body.SetAngle(normalizeAngle(body.GetAngle())); | |
if (body.GetAngularVelocity() > 2) | |
{ | |
body.SetAngularVelocity(2); | |
} | |
if (body.GetAngularVelocity() < -2) | |
body.SetAngularVelocity( -2); | |
body.SetAngularVelocity(body.GetAngularVelocity() * 0.9); | |
if (body.GetLinearVelocity().Length() > 5) | |
{ | |
var lvel:b2Vec2 = body.GetLinearVelocity(); | |
lvel.Normalize(); | |
lvel.Multiply(5); | |
body.SetLinearVelocity(lvel); | |
} | |
} | |
for each (var pair:CollisionPair in contactListener.collisionPairs) | |
{ | |
if (pair.a is Meteor)(pair.a as Meteor).Explode(); | |
if (pair.b is Meteor)(pair.b as Meteor).Explode(); | |
if (!(pair.a is Chunk) || !(pair.b is Chunk)) continue; | |
//bumpSound.play(); | |
//bumpSound.playAt(pair.worldPoint.x * 20, pair.worldPoint.y * 20); | |
} | |
var tugHeading:b2Vec2 = b2Vec2.Make(0, -1); | |
tugHeading.MulM(b2Mat22.FromAngle(tug.physicsBody.GetAngle())); | |
if (gameState == FLYAROUND) | |
{ | |
cameraDestination.x = tug.x - (800 / 2); | |
cameraDestination.y = tug.y - (600 / 2); | |
wasInput -= FP.elapsed; | |
(tug.graphic as Spritemap).frame = 0; | |
tug.clearJets(); | |
if (Input.check("Up")) | |
{ | |
tug.setJet(0); | |
tug.physicsBody.ApplyImpulse(Multiply(tugHeading, 0.7), tug.physicsBody.GetPosition()); | |
wasInput = 0.1; | |
} | |
if (Input.check("Down")) | |
{ | |
tug.setJet(2); | |
tug.physicsBody.ApplyImpulse(Negate(Multiply(tugHeading, 0.7)), tug.physicsBody.GetPosition()); | |
wasInput = 0.1; | |
} | |
if (Input.check("RotateLeft")) | |
{ | |
tug.physicsBody.ApplyTorque(-100); | |
} | |
if (Input.check("RotateRight")) | |
{ | |
tug.physicsBody.ApplyTorque(100); | |
} | |
if (Input.check("StrafeLeft")) | |
{ | |
tug.setJet(1); | |
tug.physicsBody.ApplyImpulse(Multiply(Perpendicular(tugHeading), 0.7), tug.physicsBody.GetPosition()); | |
wasInput = 0.1; | |
} | |
if (Input.check("StrafeRight")) | |
{ | |
tug.setJet( -1); | |
tug.physicsBody.ApplyImpulse(Multiply(Negate(Perpendicular(tugHeading)), 0.7), tug.physicsBody.GetPosition()); | |
wasInput = 0.1; | |
} | |
if (wasInput <= 0) | |
{ | |
tug.physicsBody.SetLinearVelocity(Multiply(tug.physicsBody.GetLinearVelocity(), 0.9)); | |
thrustSound.stop(); | |
} | |
else | |
{ | |
if (!thrustSound.playing) thrustSound.loop(0.25, 0, 50); | |
} | |
//if (aimChunk != null) | |
// aimChunk.ClearSelected(); | |
aimChunk = null; | |
var minfraction:Number = Number.MAX_VALUE; | |
physicsWorld.RayCast(function (fixture:b2Fixture, point:b2Vec2, normal:b2Vec2, fraction:Number):Number | |
{ | |
if (fixture.GetUserData() is Chunk) | |
{ | |
if (fraction < minfraction) | |
{ | |
aimChunk = fixture.GetUserData() as Chunk; | |
minfraction = fraction; | |
} | |
} | |
return fraction; | |
}, | |
tug.physicsBody.GetPosition(), vAdd(tug.physicsBody.GetPosition(), Multiply(tugHeading, 2))); | |
if (aimChunk != null) | |
{ | |
//aimChunk.SetActive(); | |
if (Input.pressed("Attach")) | |
{ | |
selectedChunk = aimChunk; | |
targetOffset = selectedChunk.CalculateTargetOffset(selectedChunk.physicsBody.GetLocalPoint( | |
tug.physicsBody.GetPosition())); | |
gameState = ATTACHED; | |
//aimChunk.ClearSelected(); | |
(tug.graphic as Spritemap).frame = targetOffset.frame; | |
dockSound.play(); | |
} | |
} | |
} | |
else if (gameState == ATTACHED) | |
{ | |
cameraDestination.x = selectedChunk.x - (800 / 2); | |
cameraDestination.y = selectedChunk.y - (600 / 2); | |
//position tug | |
tug.physicsBody.SetPosition(selectedChunk.physicsBody.GetWorldPoint(targetOffset.offset)); | |
tug.physicsBody.SetAngle(Math.PI + AngleBetweenVectors(b2Vec2.Make(0, -1), | |
selectedChunk.physicsBody.GetWorldVector(targetOffset.normal))); | |
tug.clearJets(); | |
if (Input.pressed("Snap")) | |
{ | |
dockSound.play(); | |
gameState = FLYAROUND; | |
} | |
if (Input.check("Up")) | |
{ | |
tug.setJet(0); | |
selectedChunk.physicsBody.ApplyImpulse(Multiply(tugHeading, 0.35), selectedChunk.physicsBody.GetPosition()); | |
} | |
if (Input.check("Down")) | |
{ | |
tug.setJet(2); | |
selectedChunk.physicsBody.ApplyImpulse(Negate(Multiply(tugHeading, 0.35)), selectedChunk.physicsBody.GetPosition()); | |
} | |
if (Input.check("RotateLeft")) | |
{ | |
selectedChunk.physicsBody.ApplyTorque(-25 * selectedChunk.physicsBody.GetMass()); | |
} | |
if (Input.check("RotateRight")) | |
{ | |
selectedChunk.physicsBody.ApplyTorque(25 * selectedChunk.physicsBody.GetMass()); | |
} | |
if (Input.check("StrafeLeft")) | |
{ | |
tug.setJet(1); | |
selectedChunk.physicsBody.ApplyImpulse(Multiply(Perpendicular(tugHeading), 0.35), selectedChunk.physicsBody.GetPosition()); | |
} | |
if (Input.check("StrafeRight")) | |
{ | |
tug.setJet( -1); | |
selectedChunk.physicsBody.ApplyImpulse(Multiply(Negate(Perpendicular(tugHeading)), 0.35), selectedChunk.physicsBody.GetPosition()); | |
} | |
if (tug.jets.length == 0) | |
thrustSound.stop(); | |
else if (!thrustSound.playing) thrustSound.loop(0.25, 0, 50); | |
possibleConnections = []; | |
FindPossibleConnections(selectedChunk, possibleConnections); | |
if (Input.pressed("Attach")) | |
{ | |
if (fullSnap) for each (var conn:Object in possibleConnections) SnapConnection(conn); | |
else if (possibleConnections.length > 0) SnapConnection(possibleConnections[0]); | |
connectionGraph.fillRect(new Rectangle(0, 0, connectionGraph.width, connectionGraph.height), 0xFF000000); | |
ClearTraversed(); | |
DrawConnectedBlocks(connectionGraph, selectedChunk, selectedChunk); | |
var planetScore:Object = DetectPlanet(connectionGraph); | |
if (planetScore.count >= 9) | |
{ | |
var connected:Array = []; | |
TraverseConnectionGraph(selectedChunk, connected, function(chunk:Chunk):void { } ); | |
if (connected.indexOf(core) == -1) | |
{ | |
score += planetScore.score; | |
for each (var chunk:Chunk in connected) | |
DestroyChunk(chunk); | |
for (var c:int = 0; c < connected.length; ++c) SpawnChunk(); | |
var pops:int = 1; | |
add(new PopText(tug.x, tug.y, planetScore.score.toString())); | |
if (planetScore.singleColor) | |
add(new PopText(tug.x, tug.y + (20 * pops++), "SAME TYPE BONUS!")); | |
if (planetScore.excess == 0) | |
add(new PopText(tug.x, tug.y + (20 * pops++), "RECTANGULAR BONUS!")); | |
if (planetScore.square) | |
add(new PopText(tug.x, tug.y + (20 * pops++), "SQUARE BONUS!")); | |
if (planetScore.perfect) | |
add(new PopText(tug.x, tug.y + (20 * pops++), "PERFECT BONUS!")); | |
} | |
} | |
possibleConnections = []; | |
} | |
} | |
//Break stressed joints | |
if (breakJoints) | |
{ | |
var stressedJoints:Array = []; | |
for (var joint:b2Joint = physicsWorld.GetJointList(); joint != null; joint = joint.GetNext()) | |
{ | |
if (joint.GetReactionForce(1).Length() > 10 || joint.GetReactionTorque(1) > 5) | |
stressedJoints.push(joint); | |
} | |
for each (var joint:b2Joint in stressedJoints) | |
{ | |
physicsWorld.DestroyJoint(joint); | |
//Maintain graph of connected chunks. | |
var chunkA:Chunk = joint.GetBodyA().GetUserData() as Chunk; | |
var chunkB:Chunk = joint.GetBodyB().GetUserData() as Chunk; | |
searchA: for (var i:int = 0; i < chunkA.connectedChunks.length; ++i) | |
{ | |
if (chunkA.connectedChunks[i].chunk === chunkB) | |
{ | |
chunkA.connectedChunks.splice(i, 1); | |
break searchA; | |
} | |
} | |
searchB: for (var i:int = 0; i < chunkB.connectedChunks.length; ++i) | |
{ | |
if (chunkB.connectedChunks[i].chunk === chunkA) | |
{ | |
chunkB.connectedChunks.splice(i, 1); | |
break searchB; | |
} | |
} | |
} | |
} | |
physicsWorld.Step(FP.elapsed, 10, 10); | |
if (gameState == ATTACHED) | |
{ | |
//position tug | |
tug.physicsBody.SetPosition(selectedChunk.physicsBody.GetWorldPoint(targetOffset.offset)); | |
tug.physicsBody.SetAngle(Math.PI + AngleBetweenVectors(b2Vec2.Make(0, -1), | |
selectedChunk.physicsBody.GetWorldVector(targetOffset.normal))); | |
} | |
if (cameraDestination.x != FP.camera.x) | |
{ | |
var xDiff:Number = FP.camera.x - cameraDestination.x; | |
if (Math.abs(xDiff) < 2) FP.camera.x = cameraDestination.x; | |
else FP.camera.x -= xDiff / Math.abs(xDiff) * 2; | |
} | |
if (cameraDestination.y != FP.camera.y) | |
{ | |
var yDiff:Number = FP.camera.y - cameraDestination.y; | |
if (Math.abs(yDiff) < 2) FP.camera.y = cameraDestination.y; | |
else FP.camera.y -= yDiff / Math.abs(yDiff) * 2; | |
} | |
FP.clampInRect(FP.camera, (Assets.PLAY_WIDTH / 2) - (Assets.PLAY_HEIGHT / 2), 0, Assets.PLAY_HEIGHT - 800, Assets.PLAY_HEIGHT - 600); | |
super.update(); | |
} | |
private function DestroyChunk(chunk:Chunk):void | |
{ | |
if (gameState == ATTACHED && chunk == selectedChunk) | |
{ | |
gameState = FLYAROUND; | |
selectedChunk = null; | |
} | |
for each (var conn:Object in chunk.connectedChunks) | |
{ | |
searchB: for (var i:int = 0; i < conn.chunk.connectedChunks.length; ++i) | |
{ | |
if (conn.chunk.connectedChunks[i].chunk === chunk) | |
{ | |
conn.chunk.connectedChunks.splice(i, 1); | |
break searchB; | |
} | |
} | |
physicsWorld.DestroyJoint(conn.joint); | |
} | |
var topLeft:b2Vec2 = b2Vec2.Make( -chunk.tilesWidth / 2, -chunk.tilesHeight / 2); | |
topLeft = chunk.physicsBody.GetWorldPoint(topLeft); | |
var xVec:b2Vec2 = chunk.physicsBody.GetWorldVector(b2Vec2.Make(1, 0)); | |
var yVec:b2Vec2 = chunk.physicsBody.GetWorldVector(b2Vec2.Make(0, 1)); | |
for (var x:Number = 0.5; x < chunk.tilesWidth; ++x) | |
for (var y:Number = 0.5; y < chunk.tilesHeight; ++y) | |
{ | |
var pos:b2Vec2 = topLeft.Copy(); | |
pos.Add(Multiply(xVec, x)); | |
pos.Add(Multiply(yVec, y)); | |
particleEmitter.emit("puff", pos.x * 20, pos.y * 20); | |
} | |
physicsWorld.DestroyBody(chunk.physicsBody); | |
remove(chunk); | |
} | |
private function DetectPlanet(bitmap:BitmapData):Object | |
{ | |
var pixelCount:int = 0; | |
var totalScore:int = 100 * 9; | |
var xmin:int = 100; | |
var xmax:int = -100; | |
var ymin:int = 100; | |
var ymax:int = -100; | |
var foundColor:uint = 0xFF000000; | |
var singleColor:Boolean = true; | |
for (var x:int = 0; x < bitmap.width; ++x) | |
for (var y:int = 0; y < bitmap.height; ++y) | |
if (bitmap.getPixel32(x, y) != 0xFF000000) | |
{ | |
if (foundColor != 0xFF000000 && bitmap.getPixel32(x, y) != foundColor) | |
singleColor = false; | |
else | |
foundColor = bitmap.getPixel32(x, y); | |
pixelCount += 1; | |
if (x > xmax) xmax = x; | |
if (x < xmin) xmin = x; | |
if (y > ymax) ymax = y; | |
if (y < ymin) ymin = y; | |
} | |
totalScore = pixelCount * 100; | |
var area:int = (xmax - xmin + 1) * (ymax - ymin + 1); | |
var excess:int = area - pixelCount; | |
totalScore -= excess * 10; | |
if (singleColor) totalScore *= 2; | |
if (excess == 0) totalScore *= 2; | |
var square:Boolean = (excess == 0) && ((xmax - xmin + 1) == (ymax - ymin + 1)); | |
if (square) totalScore *= 4; | |
if (singleColor && square) totalScore *= 4; | |
return { | |
count: pixelCount, | |
score: totalScore, | |
excess: excess, | |
singleColor: singleColor, | |
square: square, | |
perfect: singleColor && square | |
} | |
} | |
private static var tMatrix:Matrix = new Matrix(); | |
override public function render():void | |
{ | |
FP.buffer.fillRect(new Rectangle(0, 0, 800, 600), 0xFF000000); | |
if (firstUpdate) { | |
//super.render(); | |
return; | |
} | |
tMatrix.identity(); | |
tMatrix.scale(Assets.PLAY_WIDTH / bgBitmap.width, Assets.PLAY_HEIGHT / bgBitmap.height); | |
tMatrix.translate( -FP.camera.x, -FP.camera.y); | |
FP.buffer.draw(bgBitmap, tMatrix); | |
super.render(); | |
//physicsWorld.DrawDebugData(); | |
//FP.buffer.draw(debugSprite); | |
if (gameState == ATTACHED) | |
{ | |
if (fullSnap) for each (var pair:Object in possibleConnections) DrawPossibleConnection(pair); | |
else if (possibleConnections.length > 0) DrawPossibleConnection(possibleConnections[0]); | |
//tMatrix.identity(); | |
//tMatrix.scale(8, 8); | |
//FP.buffer.draw(connectionGraph, tMatrix); | |
} | |
else | |
{ | |
//draw selection hilite | |
if (aimChunk != null) | |
{ | |
var hilite:Image = hilites[aimChunk.blockType]; | |
hilite.angle = -aimChunk.physicsBody.GetAngle() * (180.0 / Math.PI); | |
hilite.render(FP.buffer, new Point(aimChunk.x, aimChunk.y), FP.camera); | |
} | |
} | |
for (var body:b2Body = physicsWorld.GetBodyList(); body != null; body = body.GetNext()) | |
if (body.GetUserData() is Chunk && !(body.GetUserData() as Chunk).onCamera) | |
DrawIndicator(body.GetUserData() as Chunk); | |
//if (!core.onCamera) DrawIndicator(core); | |
//Draw score | |
scoreImage.render(FP.buffer, new Point(0, 0), new Point(0, 0)); | |
DrawNumbers(scoreImage.width + 12, 0, numbers, score); | |
if (paused) | |
pauseMenu.render(); | |
} | |
public function DrawNumbers(x:int, y:int, numbers:Array, num:int) | |
{ | |
var s:String = num.toString(); | |
for (var i:int = 0; i < s.length; ++i) | |
numbers[s.charCodeAt(i) - 0x30].render(FP.buffer, new Point(x + (20 * i), y), new Point(0, 0)); | |
} | |
public function DrawIndicator(chunk:Chunk):void | |
{ | |
var indicatorPosition:b2Vec2 = b2Vec2.Make(0, 0); | |
var screenCenter:b2Vec2 = b2Vec2.Make(FP.camera.x + 400, FP.camera.y + 300); | |
var padding:int = 32; | |
if (chunk.x < screenCenter.x - (400 - padding)) indicatorPosition.x = screenCenter.x - (400 - padding); | |
else if (chunk.x > screenCenter.x + (400 - padding)) indicatorPosition.x = screenCenter.x + (400 - padding); | |
else indicatorPosition.x = chunk.x; | |
if (chunk.y < screenCenter.y - (300 - padding)) indicatorPosition.y = screenCenter.y - (300 - padding); | |
else if (chunk.y > screenCenter.y + (300 - padding)) indicatorPosition.y = screenCenter.y + (300 - padding); | |
else indicatorPosition.y = chunk.y; | |
var indicatorVector:b2Vec2 = chunk.physicsBody.GetPosition().Copy(); | |
indicatorVector.Multiply(Assets.TILE_WIDTH); | |
indicatorVector.Subtract(indicatorPosition); | |
indicatorVector.Normalize(); | |
var indicatorAngle:Number = AngleBetweenVectors(b2Vec2.Make(0, -1), indicatorVector); | |
tMatrix.identity(); | |
tMatrix.translate( -8, -8); | |
if (chunk is Core) tMatrix.scale(2, 2); | |
tMatrix.rotate(indicatorAngle); | |
tMatrix.translate(indicatorPosition.x - FP.camera.x, indicatorPosition.y - FP.camera.y); | |
FP.buffer.draw(indicatorBackground._buffer, tMatrix); | |
} | |
private static function IsOdd(x:int):Boolean { return x % 2 == 1; } | |
private function TraverseConnectionGraph(chunk:Chunk, visited:Array, callback:Function):void | |
{ | |
if (visited.indexOf(chunk) != -1) return; | |
visited.push(chunk); | |
callback(chunk); | |
for each (var connectedChunk:Object in chunk.connectedChunks) | |
TraverseConnectionGraph(connectedChunk.chunk, visited, callback); | |
} | |
private function ConnectedToCore(chunk:Chunk):Boolean | |
{ | |
if (chunk === core) return true; | |
var foundCore:Boolean = false; | |
TraverseConnectionGraph(chunk, [], function(pChunk:Chunk):void | |
{ | |
if (pChunk === core) foundCore = true; | |
}); | |
return foundCore; | |
} | |
private function FindPossibleConnections(chunk:Chunk, into:Array):void | |
{ | |
if (chunk.traversed) return; | |
chunk.traversed = true; | |
for each (var pair:CollisionPair in contactListener.collisionPairs.filter( | |
function(item:CollisionPair, i, v):Boolean | |
{ | |
if (!(item.a is Chunk) || !(item.b is Chunk)) return false; | |
if (item.a === chunk || item.b === chunk) return true; | |
return false; | |
})) | |
{ | |
var found:Boolean = false; | |
chunk.connectedChunks.forEach(function (item:Object, i, v):void { | |
if (item.chunk === pair.a || item.chunk === pair.b) found = true; } ); | |
if (found) continue; | |
var obj:Object = CalculatePotentialConnection(pair); | |
if (obj != null && (pair.a as Chunk).HasOverlappingEdge(pair.b as Chunk)) | |
{ | |
into.push(obj); | |
} | |
} | |
for each (var conn:Object in chunk.connectedChunks) | |
FindPossibleConnections(conn.chunk, into); | |
} | |
private function CalculatePotentialConnection(pair:CollisionPair):Object | |
{ | |
if (pair.a == null || pair.b == null) return null; | |
var obj:Object = new Object(); | |
obj.bodyA = (pair.a as Chunk).physicsBody; | |
obj.bodyB = (pair.b as Chunk).physicsBody; | |
var localA:b2Vec2 = obj.bodyA.GetLocalPoint(pair.worldPoint); | |
var localB:b2Vec2 = obj.bodyB.GetLocalPoint(pair.worldPoint); | |
var chunkA:Chunk = pair.a as Chunk; | |
var chunkB:Chunk = pair.b as Chunk; | |
if (IsOdd(chunkA.tilesWidth)) localA.x += 0.5; | |
if (IsOdd(chunkA.tilesHeight)) localA.y += 0.5; | |
if (IsOdd(chunkB.tilesWidth)) localB.x += 0.5; | |
if (IsOdd(chunkB.tilesHeight)) localB.y += 0.5; | |
localA.x = Math.round(localA.x); | |
localA.y = Math.round(localA.y); | |
localB.x = Math.round(localB.x); | |
localB.y = Math.round(localB.y); | |
if (IsOdd(chunkA.tilesWidth)) localA.x -= 0.5; | |
if (IsOdd(chunkA.tilesHeight)) localA.y -= 0.5; | |
if (IsOdd(chunkB.tilesWidth)) localB.x -= 0.5; | |
if (IsOdd(chunkB.tilesHeight)) localB.y -= 0.5; | |
var worldA:b2Vec2 = obj.bodyA.GetWorldPoint(localA); | |
worldA.Subtract(obj.bodyB.GetWorldPoint(localB)); | |
if (worldA.Length() > 0.5) return null; | |
var refAngle:Number = obj.bodyB.GetAngle() - obj.bodyA.GetAngle(); | |
//refAngle = normalizeAngle(refAngle); | |
var rAngle:Number = refAngle * (180.0 / Math.PI); | |
var rAngleB:Number = rAngle / 90.0; | |
rAngleB = Math.round(rAngleB); | |
rAngleB *= 90.0; | |
var diff:Number = rAngle - rAngleB; | |
if (Math.abs(diff) < 15) | |
{ | |
obj.localA = localA; | |
obj.localB = localB; | |
obj.refAngle = rAngleB * (Math.PI / 180.0); | |
return obj; | |
} | |
return null; | |
} | |
public function DrawConnectedBlocks(bitmap:BitmapData, chunk:Chunk, relative:Chunk) | |
{ | |
if (chunk.traversed) return; | |
chunk.traversed = true; | |
var relativeAngle:Number = relative.physicsBody.GetAngle() - chunk.physicsBody.GetAngle(); | |
var relativePosition:b2Vec2 = relative.physicsBody.GetPosition().Copy(); | |
relativePosition.Subtract(chunk.physicsBody.GetPosition()); | |
relativePosition.MulM(b2Mat22.FromAngle(-relative.physicsBody.GetAngle())); | |
var classifiedAngle:int = chunk.ClassifyAngle((normalizeAngle(relativeAngle) + 2 * Math.PI) / (Math.PI / 2)); | |
var minX:Number = 0; | |
var minY:Number = 0; | |
var maxX:Number = 0; | |
var maxY:Number = 0; | |
if (classifiedAngle == 0) | |
{ | |
minX = relativePosition.x - (chunk.tilesWidth / 2); | |
minY = relativePosition.y - (chunk.tilesHeight / 2); | |
maxX = relativePosition.x + (chunk.tilesWidth / 2); | |
maxY = relativePosition.y + (chunk.tilesHeight / 2); | |
} | |
else | |
{ | |
minY = relativePosition.y - (chunk.tilesWidth / 2); | |
minX = relativePosition.x - (chunk.tilesHeight / 2); | |
maxY = relativePosition.y + (chunk.tilesWidth / 2); | |
maxX = relativePosition.x + (chunk.tilesHeight / 2); | |
} | |
if (maxX < minX) { | |
var t:int = maxX; | |
maxX = minX; | |
minX = t; | |
} | |
if (maxY < minY) { | |
var t:int = maxY; | |
maxY = minY; | |
minY = t; | |
} | |
var offsetX:Number = bitmap.width / 2; | |
var offsetY:Number = bitmap.height / 2; | |
if (IsOdd(relative.tilesWidth)) offsetX += 0.5; | |
if (IsOdd(relative.tilesHeight)) offsetY += 0.5; | |
var fillColor:uint = 0xFF000000; | |
if (chunk.planetType == 0) fillColor += 0x00440000; | |
if (chunk.planetType == 1) fillColor += 0x00880000; | |
if (chunk.planetType == 2) fillColor += 0x00CC0000; | |
if (chunk.biome == 0) fillColor += 0x00003300; | |
if (chunk.biome == 1) fillColor += 0x00006600; | |
if (chunk.biome == 2) fillColor += 0x00009900; | |
if (chunk.biome == 3) fillColor += 0x0000CC00; | |
if (chunk.biome == 4) fillColor += 0x0000FF00; | |
bitmap.fillRect(new Rectangle(minX + offsetX, minY + offsetY, maxX - minX, maxY - minY), fillColor); | |
for each (var conn:Object in chunk.connectedChunks) | |
DrawConnectedBlocks(bitmap, conn.chunk, relative); | |
} | |
public function SnapConnection(conn:Object):void | |
{ | |
//Check for existing joint | |
for (var joint:b2Joint = physicsWorld.GetJointList(); joint != null; joint = joint.GetNext()) | |
{ | |
if (joint.GetBodyA() === conn.bodyA && joint.GetBodyB() === conn.bodyB) return; | |
if (joint.GetBodyA() === conn.bodyB && joint.GetBodyB() === conn.bodyA) return; | |
} | |
var jointDef:b2WeldJointDef = new b2WeldJointDef(); | |
jointDef.bodyA = conn.bodyA; | |
jointDef.bodyB = conn.bodyB; | |
jointDef.localAnchorA = conn.localA; | |
jointDef.localAnchorB = conn.localB; | |
jointDef.referenceAngle = conn.refAngle; | |
jointDef.collideConnected = true; | |
var joint:b2Joint = physicsWorld.CreateJoint(jointDef); | |
(conn.bodyA.GetUserData() as Chunk).connectedChunks.push( | |
{ | |
chunk: conn.bodyB.GetUserData() as Chunk, | |
relativeAngle: conn.refAngle, | |
localAnchor: conn.localA, | |
otherLocalAnchor: conn.localB, | |
joint: joint | |
}); | |
(conn.bodyB.GetUserData() as Chunk).connectedChunks.push( | |
{ | |
chunk: conn.bodyA.GetUserData() as Chunk, | |
relativeAngle: conn.refAngle + Math.PI, | |
localAnchor: conn.localB, | |
otherLocalAnchor: conn.localA, | |
joint: joint | |
}); | |
var edge:Object = (conn.bodyA.GetUserData() as Chunk).FindConnectedEdgeLine(conn.bodyB.GetUserData() as Chunk); | |
if (edge != null) for (var c:Number = edge.min; c < edge.max; c += 0.25) | |
{ | |
particleEmitter.emit("puff", (edge.point.x + (edge.dir.x * c)) * 20 - 10, | |
(edge.point.y + (edge.dir.y * c)) * 20 - 10); | |
} | |
snapSound.play(); | |
} | |
public function DrawPossibleConnection(pair:Object):void | |
{ | |
var chunkA:Chunk = pair.bodyA.GetUserData() as Chunk; | |
var chunkB:Chunk = pair.bodyB.GetUserData() as Chunk; | |
var found:Boolean = false; | |
chunkA.connectedChunks.forEach(function (item:Object, i, v):void { if (item.chunk === chunkB) found = true; } ); | |
if (!found) | |
{ | |
chunkA.DrawConnectedEdge(chunkB, 0); | |
chunkB.DrawConnectedEdge(chunkA, 0); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment