Skip to content

Instantly share code, notes, and snippets.

@Blecki
Created April 22, 2012 20:53
Show Gist options
  • Save Blecki/2466828 to your computer and use it in GitHub Desktop.
Save Blecki/2466828 to your computer and use it in GitHub Desktop.
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