Zero Gravity Escape
Its a post compo edition of my entree to LudumDare 21
Real entree here http://www.ludumdare.com/compo/ludum-dare-21/?action=preview&uid=232
I would not really call it a game , more of a prototype of one, and not a very good one at that :D
Seems I managed to get in to a top 10% of entress in "innovation". Guess those who voted so never played Toribash :D
Anyways worked a little on presentation for post compo entree. As it was pure code by the end of LD21 decided to have little bit of fun and add textures and sounds trough pure code too. Results? This weights 75K without a font :D
Things used and done:
Used Box2D
Used AS3 SFXr for sounds http://code.google.com/p/as3sfxr/
GreeenShock TweenLite
bit101 components for buttons
Other then that, ~1200 lines of my code among which is Texture class for generating star sky, ship alloy and crates textures.
P.S Switch to fullscreen, this game was not meant to work in 465x465 window :)
/**
* Copyright wonderwhyer ( http://wonderfl.net/user/wonderwhyer )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/n1Mj
*/
package{
import Box2D.Dynamics.*
import Box2D.Collision.*
import Box2D.Collision.Shapes.*
import Box2D.Dynamics.Joints.*
import Box2D.Dynamics.Contacts.*
import Box2D.Common.Math.*
import com.bit101.components.ComboBox;
import com.bit101.components.PushButton;
import com.greensock.*;
import com.greensock.easing.Back;
import flash.events.Event;
import flash.display.*;
import flash.events.KeyboardEvent;
import flash.filters.BevelFilter;
import flash.filters.DropShadowFilter;
import flash.filters.GlowFilter;
import flash.geom.Rectangle;
import flash.text.*;
import flash.display.MovieClip;
import flash.events.MouseEvent;
import com.bit101.components.Knob;
import flash.utils.getTimer;
import flash.geom.Point;
import flash.system.Capabilities;
/**
* ...
* @author EduardRuzga www.wonderwhy-er.com
* Game made for LudumDare 21 compo,
* post compo edition with some textures and sounds added
*/
[SWF(width="465", height="465", frameRate="30", backgroundColor="0x000000")]
public class Main extends MovieClip {
public var bodyParts:Array = [];
public var bodyPartsHash:Object = {};
public var debug:TextField;
public var playing:Boolean = true;
public var m_currId:int = 0;
public var sandBox:SandBox;
static public var m_sprite:Sprite;
static public var ui:Sprite;
static public var dragedJoint:Sprite;
public static var _instance:Main;
public var selecting:Boolean = false;
public var selectionStartX:Number = 0;
public var selectionStartY:Number = 0;
[Embed(systemFont="serif", fontWeight="bold", fontName="font1", mimeType="application/x-font", embedAsCFF="false")]
private static var font:Class;
public var glow:GlowFilter = new GlowFilter(0xFF0000);
public var skyTexture:BitmapData;
public var sky:Sprite;
public var startScreen:Sprite;
public var GameName:TextField;
public var GameNameTexture:Sprite;
public var StartGameText:TextField;
public var pause:PushButton;
public var fullScreenBtn:PushButton;
public var soundBtn:PushButton;
public var reStart:PushButton;
public function Main(){
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.align = StageAlign.TOP_LEFT
stage.addEventListener(Event.RESIZE, resize);
//StartGame();
startScreen = new Sprite();
addChild(startScreen);
GameName = new TextField();
GameName.defaultTextFormat = new TextFormat("font1", 50, 0xFFFFFF, true);
_instance = this;
GameName.text = "Zero\n Gravity\n Escape";
GameName.autoSize = TextFieldAutoSize.LEFT;
GameName.embedFonts = true;
GameName.selectable = false;
GameNameTexture = new Sprite();
var bmd:BitmapData = Textures.getShipAlloy(100, 100,1);
bmd = Textures.getShipAlloy(100, 100,1);
GameNameTexture.graphics.beginBitmapFill(bmd);
GameNameTexture.graphics.drawRect(0, 0, GameName.width, GameName.height);
GameNameTexture.graphics.endFill();
GameNameTexture.x = GameName.x;
GameNameTexture.y = GameName.y;
startScreen.addChild(GameNameTexture);
startScreen.addChild(GameName);
GameNameTexture.mask = GameName;
StartGameText = new TextField();
StartGameText.defaultTextFormat = new TextFormat("font1", 30, 0xFFFFFF, true);
StartGameText.text = "Click anywhere to start";
startScreen.addChild(StartGameText);
StartGameText.y = GameName.y + GameName.height + 50;
StartGameText.autoSize = TextFieldAutoSize.CENTER;
StartGameText.embedFonts = true;
StartGameText.selectable = false;
StartGameText.x = GameName.x + (GameName.width - StartGameText.width) / 2;
startScreen.x = (stage.stageWidth - startScreen.width) / 2;
startScreen.y = (stage.stageHeight - startScreen.height) / 2;
stage.addEventListener(MouseEvent.CLICK, StartGame);
var t:uint = getTimer();
SFX.init();
trace((getTimer()-t));
}
public function resize(e:Event):void
{
if (startScreen) {
startScreen.x = (stage.stageWidth - startScreen.width) / 2;
startScreen.y = (stage.stageHeight - startScreen.height) / 2;
}
}
public function StartGame(e:Event = null):void
{
removeChild(startScreen);
addEventListener(Event.ENTER_FRAME, update, false, 0, true);
stage.removeEventListener(MouseEvent.CLICK, StartGame);
skyTexture = Textures.getStarSky(500, 500);
sky = new Sprite();
sky.graphics.beginBitmapFill(skyTexture);
sky.graphics.drawRect(0, 0, 1100 + Capabilities.screenResolutionX, 1000 + Capabilities.screenResolutionY);
sky.graphics.endFill();
addChild(sky);
sky.mouseEnabled = sky.mouseChildren = false;
m_sprite = new Sprite();
m_sprite.name = "Engine";
addChild(m_sprite);
m_sprite.mouseEnabled = false;
ui = new Sprite();
ui.mouseEnabled = ui.mouseChildren = false;
addChild(ui);
// input
var hints:TextField = new TextField();
hints.mouseEnabled = false;
hints.autoSize = TextFieldAutoSize.LEFT;
hints.textColor = 0xFFFFFF;
hints.defaultTextFormat = new TextFormat(null, 14);
hints.text = "Story: you are on a space station,\n gravity went off, \nnow you need to get to the button to turn it on again"
+"\n\n Controls:\n- click on joints and drag to set angles\n-click anywhere and drag to select multiple joints and relax or fixate them\n-use space to pause the game for more precise and simulatious commands joint edditing\n-press R to restart";
ui.addChild(hints);
stage.addEventListener(MouseEvent.MOUSE_UP, OnMouseRelease);
debug = new TextField();
debug.autoSize = TextFieldAutoSize.LEFT;
debug.x = 300;
addChild(debug);
stage.addEventListener(KeyboardEvent.KEY_DOWN, OnKeyDown);
stage.addEventListener(MouseEvent.MOUSE_DOWN, OnStageDrag);
pause = new PushButton(this, 5, 5, "Pause(Space)", Pause);
fullScreenBtn = new PushButton(this, pause.x+pause.width + 5, 5, "Fullscreen", fullScreen);
soundBtn = new PushButton(this, fullScreenBtn.x + fullScreenBtn.width + 5, 5, "Turn sounds off", sounds);
reStart = new PushButton(this,soundBtn.x+soundBtn.width + 5, 5, "Restart(R)", Restart);
}
public function sounds(e:Event=null):void
{
SfxrSynth.Mute = !SfxrSynth.Mute;
soundBtn.label = SfxrSynth.Mute?"Turn sounds on":"Turn sounds off";
}
public function fullScreen(e:Event = null):void
{
if (stage.displayState == StageDisplayState.FULL_SCREEN)
{
stage.displayState = StageDisplayState.NORMAL;
fullScreenBtn.label = "Fullscreen";
}
else
{
stage.displayState = StageDisplayState.FULL_SCREEN;
fullScreenBtn.label = "Exit Fullscreen";
}
}
public function Pause(e:Event=null):void
{
playing = !playing;
pause.label = playing?"Pase(Space)":"Play(Space)";
}
public function OnKeyDown(e:KeyboardEvent):void
{
switch(e.keyCode)
{
case 32:
Pause();
break;
case 82:
Restart();
break;
}
}
public function Restart(e:Event = null):void
{
sandBox = null;
pressed = false;
while (m_sprite.numChildren>0)
{
m_sprite.removeChildAt(0);
}
bodyParts = [];
}
public function formatRadians(n:Number):Number
{
return Math.round(n * 180 / Math.PI);
}
public var pressed:Boolean = false;
public function ButtonPress():void
{
if (!pressed)
{
SFX.powerUp.play();
pressed = true;
TweenLite.to(SandBox._instance.gravity, 1, { delay:1, x:0,y:10, ease:Back.easeOut} );
//var t:GTween = new GTween(SandBox._instance.gravity, 1, { }, { delay:1, ease:fl.motion.easing.Back.easeOut } );
//t.proxy.x = 0;
//t.proxy.y = 10;
var spr:Sprite = new Sprite();
spr.graphics.beginFill(0);
spr.graphics.drawRect(0, 0, stage.stageWidth, stage.stageHeight);
var theEnd:TextField = new TextField();
theEnd.textColor = 0xFFFFFF;
theEnd.defaultTextFormat = new TextFormat(null, 20);
theEnd.text = "To be conitnued... Hopefully. \nClick to see how clumsy austraunauts are with gravity :D";
theEnd.autoSize = TextFieldAutoSize.LEFT;
theEnd.x = (stage.stageWidth - theEnd.width) / 2;
theEnd.y = (stage.stageHeight - theEnd.height) / 2;
spr.addChild(theEnd);
addChild(spr);
spr.alpha = 0;
TweenLite.to(spr, 1, { delay:0, x:0,y:10, ease:Back.easeOut } );
//t = new GTween(spr, 1, { }, { delay:0, ease:fl.motion.easing.Back.easeOut } );
//t.proxy.x = 0;
//t.proxy.y = 10;
spr.addEventListener(MouseEvent.CLICK, Remove);
TweenLite.to(spr, 1, { delay:2, alpha:1, ease:Back.easeOut } );
//t = new GTween(spr, 1, { }, { delay:2, ease:fl.motion.easing.Back.easeOut } );
//t.proxy.alpha = 1;
}
}
public function Remove(e:Event):void
{
removeChild(e.currentTarget as Sprite);
}
public function update(e:Event):void{
m_sprite.graphics.clear()
// if null, set new test
if (!sandBox) {
sandBox = new SandBox();
var itms:Array = [];
for (var key:String in sandBox.muscles)
{
itms.push(key);
var info:Object = { name:key, "joint":sandBox.muscles[key], "angle":0, "state":false };
bodyPartsHash[key] = info;
bodyParts.push( info );
}
var walls:Sprite = new Sprite();
var texture:BitmapData = Textures.getShipAlloy(100, 100);
var body:b2Body = sandBox.m_world.m_bodyList;
while (body)
{
var rect:Rectangle = body.m_userData as Rectangle;
if (rect != null)
{
walls.graphics.beginBitmapFill(texture);
walls.graphics.drawRect(rect.x, rect.y, rect.width, rect.height);
}
body = body.GetNext();
}
walls.filters = [new DropShadowFilter(0,45,0,1,10,10,1.5,1,true)/*Textures.bvl*/,new GlowFilter(0,1,300,300,2)];
var walls2:Sprite = new Sprite();
walls2.filters = [new DropShadowFilter(0,45,0,1,10,10,1.5,1,true)/*new BevelFilter(5, 45, 0xffffff, 1, 0, 1, 5, 5)*/];
rect = walls.getBounds(walls);
walls2.graphics.beginBitmapFill(Textures.getShipAlloy(100, 100,1));
walls2.graphics.drawRect(rect.x, rect.y, rect.width, rect.height);
walls2.graphics.drawCircle(200, 200, 50 );
walls2.graphics.drawCircle(650, 200, 50 );
walls2.graphics.drawCircle(400, 600, 50 );
walls2.graphics.drawCircle(850, 600, 50 );
walls2.mouseChildren = walls2.mouseEnabled = walls.mouseEnabled = walls.mouseChildren = false;
walls2.cacheAsBitmap = walls.cacheAsBitmap = true;
m_sprite.addChild(walls2);
m_sprite.addChild(walls);
}
ui.graphics.clear();
var scale:Number = 30;
// update current test
if (playing)
{
sandBox.Update();
var headPos:b2Vec2 = (bodyPartsHash["neck"].joint as b2Joint).GetBody1().GetPosition().Copy();
headPos.Multiply(scale);
ui.x = m_sprite.x = -headPos.x+stage.stageWidth/2;
ui.y = m_sprite.y = -headPos.y+stage.stageHeight/2;
sky.x = -headPos.x / 2 - Capabilities.screenResolutionX / 2;
sky.y = -headPos.y / 2 - Capabilities.screenResolutionY / 2;
}
body = sandBox.m_world.m_bodyList;
while (body)
{
if (body.m_userData as Sprite != null)
{
var sp:Sprite = body.m_userData as Sprite;
if(sp){
if (sp.parent != m_sprite)
{
m_sprite.addChild(sp);
}
var pos:b2Vec2 = body.GetPosition();
sp.x = pos.x*scale;
sp.y = pos.y * scale;
sp.rotation = body.GetAngle() * (180 / Math.PI);
}
}
body = body.GetNext();
}
var dir:b2Vec2;
if (dragedJoint)
{
if(!dragedJoint.hitTestPoint(dragedJoint.mouseX,dragedJoint.mouseY,true))
{
var i:uint = int(dragedJoint.name);
var bodyPartInfo:Object = bodyParts[i];
if(!bodyPartInfo.state && (getTimer()-mouseDownTime)>100)
bodyPartInfo.state = true;
var joint:b2RevoluteJoint = bodyPartInfo.joint as b2RevoluteJoint;
var angle:Number;
var vec:b2Vec2 = new b2Vec2(dragedJoint.x - m_sprite.mouseX, dragedJoint.y - m_sprite.mouseY);
var newAngle:Number = Math.atan2(vec.y, vec.x);
var baseVec:b2Vec2 = joint.m_localAnchor2.Copy();
baseVec.Subtract(joint.GetBody2().GetLocalCenter());
var baseAgnle:Number = Math.atan2(baseVec.y, baseVec.x);
//hack some angle problems after too much rotations
if (bodyPartInfo.name.indexOf("Elbow") < 0 && bodyPartInfo.name.indexOf("Shoulder") < 0)
{
baseAgnle += Math.PI;
}
var finalAngle:Number = (newAngle + baseAgnle) - bringDownRadian(joint.GetBody1().GetAngle());
if (finalAngle > Math.PI)
finalAngle -= Math.PI * 2;
else if (finalAngle < -Math.PI)
finalAngle += Math.PI * 2;
finalAngle = Math.max(joint.GetLowerLimit(), Math.min(joint.GetUpperLimit(), finalAngle));
bodyPartInfo.angle = finalAngle;
ui.graphics.lineStyle(1, 0xFF0000);
ui.graphics.moveTo(dragedJoint.x, dragedJoint.y);
ui.graphics.lineTo(m_sprite.mouseX, m_sprite.mouseY);
}
}
var selectionRect:Rectangle;
if (selecting)
{
ui.graphics.lineStyle(1, 0xFFFF00);
selectionRect = new Rectangle(Math.min(m_sprite.mouseX, selectionStartX), Math.min(m_sprite.mouseY, selectionStartY), Math.abs(m_sprite.mouseX - selectionStartX), Math.abs(m_sprite.mouseY - selectionStartY));
ui.graphics.drawRect(selectionRect.x,selectionRect.y,selectionRect.width,selectionRect.height);
}
for (i = 0; i < bodyParts.length; i++)
{
bodyPartInfo = bodyParts[i];
joint = bodyPartInfo.joint as b2RevoluteJoint;
sp = joint.m_userData as Sprite;
if (sp)
{
if (sp.parent != m_sprite)
{
m_sprite.addChild(sp);
sp.name = i.toString();
sp.addEventListener(MouseEvent.MOUSE_DOWN, OnJointDrag);
}
if (selecting)
{
var bounds:Rectangle = sp.getBounds(m_sprite);
if (bounds.intersects(selectionRect))
{
sp.filters = [glow];
}
else
{
sp.filters = [];
}
}
else
sp.filters = [];
var center:b2Vec2 = joint.GetAnchor1();
center.Add(joint.GetAnchor2());
center.Multiply(0.5);
sp.x = center.x*scale;
sp.y = center.y * scale;
dir = (joint as b2RevoluteJoint).GetBody2().GetPosition().Copy();
dir.Subtract((joint as b2RevoluteJoint).GetAnchor2());
var ang:Number = Math.atan2(dir.y,dir.x)* (180 / Math.PI);
sp.rotation = ang;// (joint as b2RevoluteJoint).GetBody2().GetAngle() * (180 / Math.PI);
sp.alpha = 0.5 + (bodyPartInfo.state?0.5:0);
}
if (bodyParts[i].state)
{
(joint as b2RevoluteJoint).m_enableMotor = true;
var angleDif:Number = joint.GetJointAngle() - Number(bodyPartInfo.angle);///(180/Math.PI);
//trace("dif:"+angleDif);
if(angleDif!=0)
joint.SetMotorSpeed( -5 * angleDif);
}
else
{
(joint as b2RevoluteJoint).m_enableMotor = false;
}
}
}
public function bringDownRadian(n:Number):Number
{
var res:Number = n%(Math.PI*2);
return res;
}
public function OnStageDrag(e:Event):void
{
if (e.target == stage)
{
selecting = true;
selectionStartX = m_sprite.mouseX;
selectionStartY = m_sprite.mouseY;
}
}
public function OnMouseRelease(e:Event):void
{
if(dragedJoint)
{
var i:uint = int(dragedJoint.name);
var bodyPartInfo:Object = bodyParts[i];
var joint:b2RevoluteJoint = bodyPartInfo.joint as b2RevoluteJoint;
joint.SetMotorSpeed(0);
dragedJoint = null;
}
if (selecting)
{
selecting = false;
var selected:Array = [];
var activeCount:int = 0;
var unactiveCount:int = 0;
var selectionRect:Rectangle = new Rectangle(Math.min(m_sprite.mouseX, selectionStartX), Math.min(m_sprite.mouseY, selectionStartY), Math.abs(m_sprite.mouseX - selectionStartX), Math.abs(m_sprite.mouseY - selectionStartY));
for (i = 0; i < bodyParts.length; i++)
{
bodyPartInfo = bodyParts[i];
joint = bodyPartInfo.joint as b2RevoluteJoint;
var sp:Sprite = joint.m_userData as Sprite;
sp.filters = [];
var bounds:Rectangle = sp.getBounds(m_sprite);
if (bounds.intersects(selectionRect))
{
selected.push(bodyPartInfo);
if (bodyPartInfo.state)
activeCount++
else
unactiveCount++
}
}
var newState:Boolean = unactiveCount>activeCount;
for (i = 0; i < selected.length; i++)
{
selected[i].state = newState;
selected[i].angle = selected[i].joint.GetJointAngle();
}
}
}
public var mouseDownTime:Number;
public function OnJointDrag(e:Event):void
{
mouseDownTime = getTimer();
dragedJoint = e.currentTarget as Sprite;
var i:uint = int(dragedJoint.name);
var bodyPartInfo:Object = bodyParts[i];
var joint:b2RevoluteJoint = bodyPartInfo.joint as b2RevoluteJoint;
if (bodyPartInfo.state)
{
bodyPartInfo.state = false;
}
else
{
bodyPartInfo.state = true;
bodyPartInfo.angle = joint.GetJointAngle();
}
}
}
}
import Box2D.Dynamics.*;
import Box2D.Collision.*;
import Box2D.Collision.Shapes.*;
import Box2D.Dynamics.Joints.*;
import Box2D.Dynamics.Contacts.*;
import Box2D.Common.*;
import Box2D.Common.Math.*;
import com.greensock.*;
import com.greensock.easing.*;
import flash.filters.DropShadowFilter;
import flash.geom.Rectangle;
import flash.utils.setTimeout;
import Main;
import flash.utils.getTimer
import flash.display.*;
class SandBox{
public var muscles:Array;
public var m_world:b2World;
public var m_bomb:b2Body;
public var m_mouseJoint:b2MouseJoint;
public var m_iterations:int = 10;
public var m_timeStep:Number = 1/30;
public var m_physScale:Number = 30;
// Sprite to draw in to
public var m_sprite:Sprite;
public var m_door1Motor:b2PrismaticJoint;
public static var CostumeColor:uint = 0xcccccc;
public var contactListener:ContactListener;
public var BodyParts:Array;
public var button:b2Body;
public static var _instance:SandBox;
public var gravity:b2Vec2;
public function SandBox(){
m_sprite = Main.m_sprite;
_instance = this;
var worldAABB:b2AABB = new b2AABB();
worldAABB.lowerBound.Set(-1000.0, -1000.0);
worldAABB.upperBound.Set(1000.0, 1000.0);
// Define the gravity vector
gravity = new b2Vec2(0.0, 10.0);
// Allow bodies to sleep
var doSleep:Boolean = true;
// Construct a world object
m_world = new b2World(worldAABB, gravity, doSleep);
// set debug draw
/*var dbgDraw:b2DebugDraw = new b2DebugDraw();
var dbgSprite:Sprite = new Sprite();
m_sprite.addChild(dbgSprite);
dbgDraw.m_sprite = m_sprite;
dbgDraw.m_drawScale = 30.0;
dbgDraw.m_fillAlpha = 1;
dbgDraw.m_lineThickness = 1.0;
dbgDraw.m_drawFlags = b2DebugDraw.e_shapeBit | b2DebugDraw.e_jointBit;
m_world.SetDebugDraw(dbgDraw);*/
contactListener = new ContactListener();
m_world.SetContactListener(contactListener);
TweenLite.to(gravity, 1, { delay:1, x:0,y:0, ease:Back.easeOut } );
//var t:GTween = new GTween(gravity, 1, { }, { delay:1, ease:fl.motion.easing.Back.easeOut } );
//t.proxy.x = 0;
//t.proxy.y = 0;
setTimeout(SFX.powerDown.play, 1000, false);
// Create border of boxes
var bd:b2BodyDef;
var circ:b2CircleDef = new b2CircleDef();
var box:b2PolygonDef = new b2PolygonDef();
var jd:b2RevoluteJointDef = new b2RevoluteJointDef();
var friction:Number = 0.7;
var restitiution :Number = 0.3;
muscles = [];
BodyParts = [];
{
var startX:Number = 250;
var startY:Number = 200;
// BODIES
// Head
circ.radius = 12.5 / m_physScale;
circ.density = 1.0;
circ.friction = friction;
circ.restitution = restitiution;
//circ.filter.groupIndex = -1;
bd = new b2BodyDef();
bd.allowSleep = false;
bd.position.Set(startX / m_physScale, (startY+3) / m_physScale);
var head:b2Body = m_world.CreateBody(bd);
head.m_userData = CreateHelmet(12.5);
head.CreateShape(circ);
head.SetMassFromShapes();
BodyParts.push(head);
// Torso1
box.SetAsBox(20 / m_physScale, 10 / m_physScale);
box.density = 1.0;
box.friction = friction; box.restitution = 0.1;
//box.filter.groupIndex = -1;
bd = new b2BodyDef();
bd.position.Set(startX / m_physScale, (startY + 28) / m_physScale);
var torso1:b2Body = m_world.CreateBody(bd);
torso1.CreateShape(box);
torso1.SetMassFromShapes();
BodyParts.push(torso1);
torso1.m_userData = CreateBoxSprite(CostumeColor,20,10);
// Torso2
box.SetAsBox(10 / m_physScale, 10 / m_physScale);
bd = new b2BodyDef();
bd.position.Set(startX / m_physScale, (startY + 43) / m_physScale);
var torso2:b2Body = m_world.CreateBody(bd);
torso2.CreateShape(box);
torso2.SetMassFromShapes();
torso2.m_userData = CreateBoxSprite(CostumeColor, 10, 10);
BodyParts.push(torso2);
// Torso3
bd = new b2BodyDef();
box.SetAsBox(15 / m_physScale, 10 / m_physScale);
bd.position.Set(startX / m_physScale, (startY + 58) / m_physScale);
var torso3:b2Body = m_world.CreateBody(bd);
torso3.CreateShape(box);
torso3.SetMassFromShapes();
torso3.m_userData = CreateBoxSprite(CostumeColor, 15, 10);
BodyParts.push(torso3);
// UpperArm
box.SetAsBox(18 / m_physScale, 6.5 / m_physScale);
box.density = 1.0;
box.friction = friction; box.restitution = 0.1;
//box.filter.groupIndex = -1;
bd = new b2BodyDef();
// L
bd.position.Set((startX - 33) / m_physScale, (startY + 20) / m_physScale);
var upperArmL:b2Body = m_world.CreateBody(bd);
upperArmL.CreateShape(box);
upperArmL.SetMassFromShapes();
upperArmL.m_userData = CreateBoxSprite(CostumeColor, 18, 6.5);
BodyParts.push(upperArmL);
// R
bd.position.Set((startX + 33) / m_physScale, (startY + 20) / m_physScale);
var upperArmR:b2Body = m_world.CreateBody(bd);
upperArmR.CreateShape(box);
upperArmR.SetMassFromShapes();
upperArmR.m_userData = CreateBoxSprite(CostumeColor, 18, 6.5);
BodyParts.push(upperArmR);
// LowerArm
box.SetAsBox(18/ m_physScale, 6 / m_physScale);
box.density = 1.0;
box.friction = friction; box.restitution = 0.1;
//box.filter.groupIndex = -1;
bd = new b2BodyDef();
// L
bd.position.Set((startX - 61) / m_physScale, (startY + 20) / m_physScale);
var lowerArmL:b2Body = m_world.CreateBody(bd);
lowerArmL.CreateShape(box);
lowerArmL.SetMassFromShapes();
lowerArmL.m_userData = CreateBoxSprite(CostumeColor, 17, 6);
BodyParts.push(lowerArmL);
// R
bd.position.Set((startX + 61) / m_physScale, (startY + 20) / m_physScale);
var lowerArmR:b2Body = m_world.CreateBody(bd);
lowerArmR.CreateShape(box);
lowerArmR.SetMassFromShapes();
lowerArmR.m_userData = CreateBoxSprite(CostumeColor, 17, 6);
BodyParts.push(lowerArmR);
//palms
// UpperLeg
box.SetAsBox(8 / m_physScale, 24 / m_physScale);
box.density = 2.0;
box.friction = friction;
box.restitution = restitiution;
box.filter.groupIndex = -1;
bd = new b2BodyDef();
// L
bd.position.Set((startX - 8) / m_physScale, (startY + 86) / m_physScale);
var upperLegL:b2Body = m_world.CreateBody(bd);
upperLegL.CreateShape(box);
upperLegL.SetMassFromShapes();
upperLegL.m_userData = CreateBoxSprite(CostumeColor, 7.5, 22);
BodyParts.push(upperLegL);
// R
bd.position.Set((startX + 8) / m_physScale, (startY + 86) / m_physScale);
var upperLegR:b2Body = m_world.CreateBody(bd);
upperLegR.CreateShape(box);
upperLegR.SetMassFromShapes();
upperLegR.m_userData = CreateBoxSprite(CostumeColor, 7.5, 22);
BodyParts.push(upperLegR);
// LowerLeg
box.SetAsBox(7 / m_physScale, 26 / m_physScale);
box.density = 2.0;
box.friction = friction;
box.restitution = restitiution;
box.filter.groupIndex = 0;
bd = new b2BodyDef();
// L
bd.position.Set((startX - 7.5) / m_physScale, (startY + 127) / m_physScale);
var lowerLegL:b2Body = m_world.CreateBody(bd);
lowerLegL.CreateShape(box);
lowerLegL.SetMassFromShapes();
lowerLegL.m_userData = CreateBoxSprite(CostumeColor, 8, 26);
BodyParts.push(lowerLegL);
// R
bd.position.Set((startX + 7.5) / m_physScale, (startY + 127) / m_physScale);
var lowerLegR:b2Body = m_world.CreateBody(bd);
lowerLegR.CreateShape(box);
lowerLegR.SetMassFromShapes();
lowerLegR.m_userData = CreateBoxSprite(CostumeColor, 8, 26);
BodyParts.push(lowerLegR);
// JOINTS
jd.enableLimit = true;
jd.maxMotorTorque = 50.0;
jd.motorSpeed = 0.0;
jd.enableMotor = true;
// Head to shoulders
jd.lowerAngle = -40 / (180/Math.PI);
jd.upperAngle = 40 / (180/Math.PI);
jd.Initialize(torso1, head, new b2Vec2(startX / m_physScale, (startY + 15) / m_physScale));
var neck:b2Joint = m_world.CreateJoint(jd);;
muscles["neck"] = neck;
var jointPointSize:Number = 6;
neck.m_userData = CreateCircleSprite(0xFFFF00, jointPointSize,true,true);
// Upper arm to shoulders
// L
jd.maxMotorTorque = 300.0;
jd.lowerAngle = -85 / (180/Math.PI);
jd.upperAngle = 130 / (180/Math.PI);
jd.Initialize(torso1, upperArmL, new b2Vec2((startX - 18) / m_physScale, (startY + 20) / m_physScale));
muscles["LShoulder"] = m_world.CreateJoint(jd);
muscles["LShoulder"].m_userData = CreateCircleSprite(0xFFFF00,jointPointSize,true,true);
//jd.enableMotor = false;
// R
jd.lowerAngle = -130 / (180/Math.PI);
jd.upperAngle = 85 / (180/Math.PI);
jd.Initialize(torso1, upperArmR, new b2Vec2((startX + 18) / m_physScale, (startY + 20) / m_physScale));
muscles["RShoulder"] = m_world.CreateJoint(jd);
muscles["RShoulder"].m_userData = CreateCircleSprite(0xFFFF00, jointPointSize,true,true);
// Lower arm to upper arm
// L
jd.lowerAngle = -130 / (180/Math.PI);
jd.upperAngle = 130 / (180/Math.PI);
jd.Initialize(upperArmL, lowerArmL, new b2Vec2((startX - 45) / m_physScale, (startY + 20) / m_physScale));
muscles["LElbow"] = m_world.CreateJoint(jd);
muscles["LElbow"].m_userData = CreateCircleSprite(0xFFFF00,jointPointSize,true,true);
// R
jd.lowerAngle = -130 / (180/Math.PI);
jd.upperAngle = 130 / (180/Math.PI);
jd.Initialize(upperArmR, lowerArmR, new b2Vec2((startX + 45) / m_physScale, (startY + 20) / m_physScale));
muscles["RElbow"] = m_world.CreateJoint(jd);
muscles["RElbow"].m_userData = CreateCircleSprite(0xFFFF00, jointPointSize,true,true);
// Shoulders/stomach
jd.maxMotorTorque = 50.0;
jd.lowerAngle = -15 / (180/Math.PI);
jd.upperAngle = 15 / (180/Math.PI);
jd.Initialize(torso1, torso2, new b2Vec2(startX / m_physScale, (startY + 35) / m_physScale));
muscles["UPress"] = m_world.CreateJoint(jd);
muscles["UPress"].m_userData = CreateCircleSprite(0xFFFF00, jointPointSize,true,true);
// Stomach/hips
jd.Initialize(torso2, torso3, new b2Vec2(startX / m_physScale, (startY + 50) / m_physScale));
muscles["DPress"] = m_world.CreateJoint(jd);
muscles["DPress"].m_userData = CreateCircleSprite(0xFFFF00,jointPointSize,true,true);
// Torso to upper leg
// L
jd.maxMotorTorque = 400.0;
jd.lowerAngle = -25 / (180/Math.PI);
jd.upperAngle = 135 / (180/Math.PI);
jd.Initialize(torso3, upperLegL, new b2Vec2((startX - 8) / m_physScale, (startY + 72) / m_physScale));
muscles["LLeg"] = m_world.CreateJoint(jd);
muscles["LLeg"].m_userData = CreateCircleSprite(0xFFFF00, jointPointSize,true,true);
// R
jd.lowerAngle = -135 / (180/Math.PI);
jd.upperAngle = 25 / (180/Math.PI);
jd.Initialize(torso3, upperLegR, new b2Vec2((startX + 8) / m_physScale, (startY + 72) / m_physScale));
muscles["RLeg"] = m_world.CreateJoint(jd);
muscles["RLeg"].m_userData = CreateCircleSprite(0xFFFF00, jointPointSize,true,true);
// Upper leg to lower leg
// L
jd.lowerAngle = -115 / (180/Math.PI);
jd.upperAngle = 25 / (180/Math.PI);
jd.Initialize(upperLegL, lowerLegL, new b2Vec2((startX - 8) / m_physScale, (startY + 105) / m_physScale));
muscles["LKnee"] = m_world.CreateJoint(jd);
muscles["LKnee"].m_userData = CreateCircleSprite(0xFFFF00, jointPointSize,true,true);
// R
jd.lowerAngle = -25 / (180/Math.PI);
jd.upperAngle = 115 / (180/Math.PI);
jd.Initialize(upperLegR, lowerLegR, new b2Vec2((startX + 8) / m_physScale, (startY + 105) / m_physScale));
muscles["RKnee"] = m_world.CreateJoint(jd);
muscles["RKnee"].m_userData = CreateCircleSprite(0xFFFF00, jointPointSize,true,true);
}
var texture:BitmapData = Textures.getShipAlloy(100, 100);
CreateWall(0, 0, 50, 800, texture);
CreateWall(0, 0, 1000, 50, texture);
CreateWall(0, 800, 1050, 50, texture);
CreateWall(1000, 0, 50, 850, texture);
CreateWall(0, 400, 800, 30, texture);
CreateWall(370, 0, 30, 200, texture);
CreateWall(770, 200, 30, 200, texture);
var boxPileStart:Number = 100;
for (var i:uint = 0; i < 4; i++)
{
CreateBox(boxPileStart + i * 40, 370, 40, 40, 2,0.1, 0x220000);
}
for (i = 0; i < 3; i++)
{
CreateBox(boxPileStart + i * 40, 330, 40, 40, 2,0.1, 0x220000);
}
CreateBox(boxPileStart+60, 290, 40, 40, 2,0.1, 0x220000);
for (i = 0; i < 15; i++)
{
CreateBox(100+Math.random()*900, 100+Math.random()*700, Math.random()*50+50, 50+Math.random()*50,1,0.1, 0x440000).ApplyForce(new b2Vec2(Math.random()*5, Math.random()*5), new b2Vec2(Math.random()*5, Math.random()*5));
}
// button
circ = new b2CircleDef();
circ.radius = 12 / m_physScale;
circ.isSensor = true;
bd = new b2BodyDef();
bd.position.Set(200/m_physScale, 600 / m_physScale);
button = m_world.CreateBody(bd);
button.m_userData = CreateButton(12);
button.CreateShape(circ);
button.SetMassFromShapes();
}
public function CreateBox(x:Number, y:Number, width:Number, height:Number, density:uint = 1,restitution:Number=0.1, color:uint = 0):b2Body
{
var bd:b2BodyDef;
var box:b2PolygonDef = new b2PolygonDef();
var jd:b2RevoluteJointDef = new b2RevoluteJointDef();
var friction:Number = 0.7;
var restitiution :Number = 0.3;
bd = new b2BodyDef();
bd.allowSleep = false;
bd.position.Set((x+width*0.5) / m_physScale, (y+height*0.5) / m_physScale);
box.SetAsBox(width*0.5 / m_physScale, height*0.5 / m_physScale);
box.density = density;
box.friction = friction;
box.restitution = restitution;
var Box:b2Body = m_world.CreateBody(bd);
Box.CreateShape(box);
Box.SetMassFromShapes();
Box.m_userData = CreateCrateSprite( width*0.5, height*0.5,0xDDDDDD);
return Box;
}
public static function CreateCrateSprite(hWidth:Number, hHeight:Number,color:uint):Sprite
{
var sp:Sprite = new Sprite();
var g:Graphics = sp.graphics;
g.lineStyle(4,color,1,true);
g.beginBitmapFill(Textures.getMetalWireTexture(color),null,true,true);
g.drawRoundRect(-hWidth, -hHeight, hWidth * 2, hHeight * 2,13,13);
g.endFill();
sp.filters = [new DropShadowFilter(0, 45, 0, 1, 6, 6, 1.5, 1, true)]
sp.cacheAsBitmap = true;
return sp;
}
public function CreateWall(x:Number, y:Number, width:Number, height:Number, texture:BitmapData):void
{
var wallSd:b2PolygonDef = new b2PolygonDef();
var wallBd:b2BodyDef = new b2BodyDef();
var wallB:b2Body;
//wallSd.
wallBd.position.Set((x+width/2)/m_physScale, (y+height/2)/m_physScale);
wallSd.SetAsBox(width*0.5/m_physScale, height*0.5/m_physScale);
wallB = m_world.CreateBody(wallBd);
wallB.CreateShape(wallSd);
wallB.SetMassFromShapes();
wallB.m_userData = new Rectangle(x, y, width, height);
}
public static function CreateCircleSprite(color:uint, r:Number, interactive:Boolean = false, line:Boolean = false ):Sprite
{
var res:Sprite = new Sprite();
res.graphics.beginFill(color);
res.graphics.drawCircle( 0, 0, r);
res.graphics.endFill();
if (line)
{
res.graphics.lineStyle(1, 0);
res.graphics.moveTo(-r, 0);
res.graphics.lineTo(r, 0);
}
res.buttonMode = res.mouseEnabled = res.mouseChildren = interactive;
return res;
}
public static function CreateButton(r:Number):Sprite
{
var res:Sprite = new Sprite();
res.graphics.beginFill(0x8080dd);
res.graphics.drawCircle( 0, 0, r);
res.graphics.endFill();
res.graphics.beginFill(0xaa0000);
res.graphics.drawCircle(0, 0, -r * 0.8);
res.filters = [new DropShadowFilter()];
return res;
}
public static function CreateHelmet(r:Number):Sprite
{
var res:Sprite = new Sprite();
res.graphics.beginFill(CostumeColor);
res.graphics.drawCircle( 0, 0, r);
res.graphics.endFill();
res.graphics.beginFill(0x0000aa);
res.graphics.drawEllipse(-r*0.9, -r*0.7, r * 1.8, r * 1.6);
res.graphics.endFill();
res.graphics.beginFill(0xFFFFFF);
res.graphics.drawCircle( -r * 0.4, -r * 0.2, r * 0.3);
return res;
}
public static function CreateBoxSprite(color:uint, hWidth:Number, hHeight:Number,interactive:Boolean=false,texture:BitmapData=null):Sprite
{
var res:Sprite = new Sprite();
if (texture)
res.graphics.beginBitmapFill(texture);
else
res.graphics.beginFill(color);
res.graphics.drawRect( -hWidth, -hHeight, hWidth * 2, hHeight * 2);
res.buttonMode = res.mouseEnabled = res.mouseChildren = interactive;
res.graphics.endFill();
return res;
}
public function Update():void{
var physStart:uint = getTimer();
m_world.Step(m_timeStep, m_iterations);
}
}
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.filters.BevelFilter;
import flash.filters.BlurFilter;
import flash.geom.ColorTransform;
import flash.display.Graphics;
import flash.display.BlendMode;
import flash.geom.Matrix;
import flash.geom.Point;
class Textures
{
public static var bmd:BitmapData;
public static var sp:Sprite = new Sprite();
public static var blr:BlurFilter = new BlurFilter(2,2);
public static var sp2:Sprite = new Sprite();
public static var bmd2:BitmapData;
public static var clrt:ColorTransform = new ColorTransform(0.9,0.9,0.9);
public static var bvl:BevelFilter = new BevelFilter(4,45,0xE9D6CF,1,0x352726,1,4,4);
public static var m:Matrix = new Matrix();
public static function getStarSky(width:Number,height:Number):BitmapData
{
sp2.addChild(sp);
bmd = new BitmapData(width,height,false,0);
bmd2 = new BitmapData(bmd.width,bmd.height,true,0);
sp.graphics.clear();
var space:Number = bmd.width*bmd.height;
var g:Graphics = sp.graphics;
g.beginFill(0xFFFFFF);
var stars:Number = 10*space/10000;
for(var i:uint = 0;i<stars;i++)
{
var size:Number = Math.random()*2;
var xx:Number = Math.random()*(bmd.width-10)+5;
var yy:Number = Math.random()*(bmd.width-10)+5;
g.drawCircle(xx,yy,size);
}
sp.filters = [blr];
bmd.draw(sp2);
bmd2.fillRect(bmd2.rect,0);
bmd2.perlinNoise(500,500,3,Math.random()*int.MAX_VALUE,true,true,7);
bmd.draw(bmd2,null,null,BlendMode.MULTIPLY);
bmd2.colorTransform(bmd2.rect,clrt);
bmd.draw(bmd2,null,null,BlendMode.HARDLIGHT);
return bmd;
}
public static function getMetalWireTexture(color:uint):BitmapData
{
var g:Graphics = sp.graphics;
g.clear();
g.beginFill(color);
g.drawRect(-3,-3,23,21);
g.drawCircle(0,7,3);
g.drawCircle(8,7,3);
g.drawCircle(16,7,3);
g.drawCircle(4,0,3);
g.drawCircle(12,0,3);
g.drawCircle(4,14,3);
g.drawCircle(12, 14, 3);
m.tx = m.ty = 0;
m.b = m.c = 0;
m.a = m.d = 0.7;
sp.filters = [new BevelFilter(4,45,0xffffff,1,0,1,6,6)];
var bmd:BitmapData = new BitmapData(11,10,false,0);
bmd.draw(sp, m);
sp.filters = [];
m.a = m.d = 1;
return bmd;
}
public static function getShipAlloy(width:Number, height:Number, steps:Number = 3):BitmapData
{
bmd = new BitmapData(width,height,false,0);
bmd2 = new BitmapData(bmd.width*3, bmd.height*3, true, 0);
bmd2.fillRect(bmd2.rect,0);
bmd.fillRect(bmd.rect,0x605788);
for(var k:uint = 0;k<steps;k++){
sp.graphics.clear();
var g:Graphics = sp.graphics;
var rects:Number = 20;
g.clear();
for(var i:uint = 0;i<rects;i++)
{
g.beginFill(0x605788);
if(Math.random()<0.6)
g.lineStyle(1,0x7B738F);
else
g.lineStyle(0,0,0);
var w:Number = Math.random()*20+10;
var h:Number = Math.random()*20+10;
var xx:Number = Math.random()*(bmd.width+10-w)-5;
var yy:Number = Math.random()*(bmd.width+10-h)-5;
g.drawRect(xx,yy,w,h);
}
for(i=0;i<3;i++)
{
for(var j:uint=0;j<3;j++)
{
m.tx = bmd.width*i;
m.ty = bmd.height*j;
bmd2.draw(sp,m,null,BlendMode.HARDLIGHT);
}
}
//bmd2.applyFilter(bmd2,bmd2.rect,new Point(),bvl);
m.tx = -bmd.width;
m.ty = -bmd.height;
bmd.draw(bmd2,m);
}
return bmd;
}
}
import Box2D.Collision.b2ContactPoint;
import Box2D.Dynamics.b2ContactListener;
import Box2D.Dynamics.Contacts.b2ContactResult;
/**
* ...
* @author EduardRuzga www.wonderwhy-er.com
* Game made for LudumDare 21 compo
*/
class ContactListener extends b2ContactListener
{
public override function Add(point:b2ContactPoint) : void
{
// handle add point
//trace("contact");
var l:Number = point.velocity.Length();
var i1:Boolean = SandBox._instance.BodyParts.indexOf(point.shape1.m_body) > 0;
var i2:Boolean = SandBox._instance.BodyParts.indexOf(point.shape2.m_body) > 0;
if ( i1 || i2 )
{
if (point.shape1.m_body == SandBox._instance.button || point.shape2.m_body == SandBox._instance.button)
{
Main._instance.ButtonPress();
}
else
{
//trace(l);
if (l > 2 && (i1 != i2) )
{
SFX.bodySound.play(l/10);
}
}
}
else
{
if (l > 2)
SFX.crateSound.play(l/10);
}
}
public override function Persist(point:b2ContactPoint) : void
{
// handle persist point
}
public override function Remove(point:b2ContactPoint) : void
{
// handle remove point
}
public override function Result(point:b2ContactResult) : void
{
// handle results
}
}
class SFX
{
public static var bodySound:SfxrSynth;
public static var crateSound:SfxrSynth;
public static var powerUp:SfxrSynth;
public static var powerDown:SfxrSynth;
public static function init():void
{
bodySound = new SfxrSynth();
bodySound.params.setSettingsString("3,,0.1022,,0.23,0.09,,-0.6283,,0.4,0.31,0.02,,,0.02,,,,1,,,,,0.34");
bodySound.cacheSound();
crateSound = new SfxrSynth();
crateSound.params.setSettingsString("3,,0.1368,0.7009,0.16,0.0907,,0.1222,,,,,,,,,0.2074,-0.1865,1,,,,,0.17");
crateSound.cacheSound();
powerUp = new SfxrSynth();
powerUp.params.setSettingsString("0,0.52,0.27,,0.81,0.2,,0.089,,,,,,0.5889,,,,,1,,,,,0.24");
powerUp.cacheSound();
powerDown = new SfxrSynth();
powerDown.params.setSettingsString("0,0.09,0.1,,0.6,0.2,,0.089,,,,,,0.13,-0.48,,-0.04,,1,,,,-0.02,0.19");
powerDown.cacheSound();
}
}
import flash.display.Shape;
import flash.events.Event;
import flash.events.SampleDataEvent;
import flash.media.Sound;
import flash.media.SoundChannel;
import flash.utils.ByteArray;
import flash.utils.Endian;
import flash.utils.getTimer;
/**
* SfxrSynth
*
* Copyright 2010 Thomas Vian
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @author Thomas Vian
*/
class SfxrSynth
{
//--------------------------------------------------------------------------
//
// Sound Parameters
//
//--------------------------------------------------------------------------
private var _params:SfxrParams = new SfxrParams; // Params instance
private var _sound:Sound; // Sound instance used to play the sound
private var _channel:SoundChannel; // SoundChannel instance of playing Sound
private var _mutation:Boolean; // If the current sound playing or caching is a mutation
private var _cachedWave:ByteArray; // Cached wave data from a cacheSound() call
private var _cachingNormal:Boolean; // If the synth is caching a normal sound
private var _cachingMutation:int; // Current caching ID
private var _cachedMutation:ByteArray; // Current caching wave data for mutation
private var _cachedMutations:Vector.<ByteArray>; // Cached mutated wave data
private var _cachedMutationsNum:uint; // Number of cached mutations
private var _cachedMutationAmount:Number; // Amount to mutate during cache
private var _cachingAsync:Boolean; // If the synth is currently caching asynchronously
private var _cacheTimePerFrame:uint; // Maximum time allowed per frame to cache sound asynchronously
private var _cachedCallback:Function; // Function to call when finished caching asynchronously
private var _cacheTicker:Shape; // Shape used for enterFrame event
private var _waveData:ByteArray; // Full wave, read out in chuncks by the onSampleData method
private var _waveDataPos:uint; // Current position in the waveData
private var _waveDataLength:uint; // Number of bytes in the waveData
private var _waveDataBytes:uint; // Number of bytes to write to the soundcard
private var _original:SfxrParams; // Copied properties for mutation base
//--------------------------------------------------------------------------
//
// Synth Variables
//
//--------------------------------------------------------------------------
private var _finished:Boolean; // If the sound has finished
private var _masterVolume:Number; // masterVolume * masterVolume (for quick calculations)
private var _waveType:uint; // The type of wave to generate
private var _envelopeVolume:Number; // Current volume of the envelope
private var _envelopeStage:int; // Current stage of the envelope (attack, sustain, decay, end)
private var _envelopeTime:Number; // Current time through current enelope stage
private var _envelopeLength:Number; // Length of the current envelope stage
private var _envelopeLength0:Number; // Length of the attack stage
private var _envelopeLength1:Number; // Length of the sustain stage
private var _envelopeLength2:Number; // Length of the decay stage
private var _envelopeOverLength0:Number; // 1 / _envelopeLength0 (for quick calculations)
private var _envelopeOverLength1:Number; // 1 / _envelopeLength1 (for quick calculations)
private var _envelopeOverLength2:Number; // 1 / _envelopeLength2 (for quick calculations)
private var _envelopeFullLength:Number; // Full length of the volume envelop (and therefore sound)
private var _sustainPunch:Number; // The punch factor (louder at begining of sustain)
private var _phase:int; // Phase through the wave
private var _pos:Number; // Phase expresed as a Number from 0-1, used for fast sin approx
private var _period:Number; // Period of the wave
private var _periodTemp:Number; // Period modified by vibrato
private var _maxPeriod:Number; // Maximum period before sound stops (from minFrequency)
private var _slide:Number; // Note slide
private var _deltaSlide:Number; // Change in slide
private var _minFreqency:Number; // Minimum frequency before stopping
private var _vibratoPhase:Number; // Phase through the vibrato sine wave
private var _vibratoSpeed:Number; // Speed at which the vibrato phase moves
private var _vibratoAmplitude:Number; // Amount to change the period of the wave by at the peak of the vibrato wave
private var _changeAmount:Number; // Amount to change the note by
private var _changeTime:int; // Counter for the note change
private var _changeLimit:int; // Once the time reaches this limit, the note changes
private var _squareDuty:Number; // Offset of center switching point in the square wave
private var _dutySweep:Number; // Amount to change the duty by
private var _repeatTime:int; // Counter for the repeats
private var _repeatLimit:int; // Once the time reaches this limit, some of the variables are reset
private var _phaser:Boolean; // If the phaser is active
private var _phaserOffset:Number; // Phase offset for phaser effect
private var _phaserDeltaOffset:Number; // Change in phase offset
private var _phaserInt:int; // Integer phaser offset, for bit maths
private var _phaserPos:int; // Position through the phaser buffer
private var _phaserBuffer:Vector.<Number>; // Buffer of wave values used to create the out of phase second wave
private var _filters:Boolean; // If the filters are active
private var _lpFilterPos:Number; // Adjusted wave position after low-pass filter
private var _lpFilterOldPos:Number; // Previous low-pass wave position
private var _lpFilterDeltaPos:Number; // Change in low-pass wave position, as allowed by the cutoff and damping
private var _lpFilterCutoff:Number; // Cutoff multiplier which adjusts the amount the wave position can move
private var _lpFilterDeltaCutoff:Number; // Speed of the low-pass cutoff multiplier
private var _lpFilterDamping:Number; // Damping muliplier which restricts how fast the wave position can move
private var _lpFilterOn:Boolean; // If the low pass filter is active
private var _hpFilterPos:Number; // Adjusted wave position after high-pass filter
private var _hpFilterCutoff:Number; // Cutoff multiplier which adjusts the amount the wave position can move
private var _hpFilterDeltaCutoff:Number; // Speed of the high-pass cutoff multiplier
private var _noiseBuffer:Vector.<Number>; // Buffer of random values used to generate noise
private var _superSample:Number; // Actual sample writen to the wave
private var _sample:Number; // Sub-sample calculated 8 times per actual sample, averaged out to get the super sample
private var _sampleCount:uint; // Number of samples added to the buffer sample
private var _bufferSample:Number; // Another supersample used to create a 22050Hz wave
private var _volumeM:Number = 1;
public static var Mute:Boolean = false;
//--------------------------------------------------------------------------
//
// Getters / Setters
//
//--------------------------------------------------------------------------
/** The sound parameters */
public function get params():SfxrParams { return _params; }
public function set params(value:SfxrParams):void
{
_params = value;
_params.paramsDirty = true;
}
//--------------------------------------------------------------------------
//
// Sound Methods
//
//--------------------------------------------------------------------------
/**
* Plays the sound. If the parameters are dirty, synthesises sound as it plays, caching it for later.
* If they're not, plays from the cached sound.
* Won't play if caching asynchronously.
*/
public function play(volume:Number=1):void
{
if (Mute) return;
_volumeM = volume;
//trace(_params.getSettingsString());
if (_cachingAsync) return;
stop();
_mutation = false;
if (_params.paramsDirty || _cachingNormal || !_cachedWave)
{
// Needs to cache new data
_cachedWave = new ByteArray;
_cachingNormal = true;
_waveData = null;
reset(true);
}
else
{
// Play from cached data
_waveData = _cachedWave;
_waveData.position = 0;
_waveDataLength = _waveData.length;
_waveDataBytes = 24576;
_waveDataPos = 0;
}
if (!_sound) (_sound = new Sound()).addEventListener(SampleDataEvent.SAMPLE_DATA, onSampleData);
_channel = _sound.play();
}
/**
* Plays a mutation of the sound. If the parameters are dirty, synthesises sound as it plays, caching it for later.
* If they're not, plays from the cached sound.
* Won't play if caching asynchronously.
* @param mutationAmount Amount of mutation
* @param mutationsNum The number of mutations to cache before picking from them
*/
public function playMutated(mutationAmount:Number = 0.05, mutationsNum:uint = 15):void
{
stop();
if (_cachingAsync) return;
_mutation = true;
_cachedMutationsNum = mutationsNum;
if (_params.paramsDirty || !_cachedMutations)
{
// New set of mutations
_cachedMutations = new Vector.<ByteArray>();
_cachingMutation = 0;
}
if (_cachingMutation != -1)
{
// Continuing caching new mutations
_cachedMutation = new ByteArray;
_cachedMutations[_cachingMutation] = _cachedMutation;
_waveData = null;
_original = _params.clone();
_params.mutate(mutationAmount);
reset(true);
}
else
{
// Play from random cached mutation
_waveData = _cachedMutations[uint(_cachedMutations.length * Math.random())];
_waveData.position = 0;
_waveDataLength = _waveData.length;
_waveDataBytes = 24576;
_waveDataPos = 0;
}
if (!_sound) (_sound = new Sound()).addEventListener(SampleDataEvent.SAMPLE_DATA, onSampleData);
_channel = _sound.play();
}
/**
* Stops the currently playing sound
*/
public function stop():void
{
if(_channel)
{
_channel.stop();
_channel = null;
}
if(_original)
{
_params.copyFrom(_original);
_original = null;
}
}
/**
* If there is a cached sound to play, reads out of the data.
* If there isn't, synthesises new chunch of data, caching it as it goes.
* @param e SampleDataEvent to write data to
*/
private function onSampleData(e:SampleDataEvent):void
{
if(_waveData)
{
if(_waveDataPos + _waveDataBytes > _waveDataLength) _waveDataBytes = _waveDataLength - _waveDataPos;
if (_waveDataBytes > 0)
{
if(_volumeM==1)
e.data.writeBytes(_waveData, _waveDataPos, _waveDataBytes);
else
{
_waveData.position = _waveDataPos;
var floats:Number = _waveDataBytes / 4;
for (var i:uint = 0; i < floats; i++)
{
e.data.writeFloat(_waveData.readFloat() * _volumeM);
}
}
}
_waveDataPos += _waveDataBytes;
}
else
{
var length:uint;
var l:uint;
if (_mutation)
{
if (_original)
{
_waveDataPos = _cachedMutation.position;
if (synthWave(_cachedMutation, 3072, true))
{
_params.copyFrom(_original);
_original = null;
_cachingMutation++;
if ((length = _cachedMutation.length) < 24576)
{
// If the sound is smaller than the buffer length, add silence to allow it to play
_cachedMutation.position = length;
for(i = 0, l = 24576 - length; i < l; i++) _cachedMutation.writeFloat(0.0);
}
if (_cachingMutation >= _cachedMutationsNum)
{
_cachingMutation = -1;
}
}
_waveDataBytes = _cachedMutation.length - _waveDataPos;
e.data.writeBytes(_cachedMutation, _waveDataPos, _waveDataBytes);
}
}
else
{
if (_cachingNormal)
{
_waveDataPos = _cachedWave.position;
if (synthWave(_cachedWave, 3072, true))
{
if ((length = _cachedWave.length) < 24576)
{
// If the sound is smaller than the buffer length, add silence to allow it to play
_cachedWave.position = length;
for(i = 0, l = 24576 - length; i < l; i++) _cachedWave.writeFloat(0.0);
}
_cachingNormal = false;
}
_waveDataBytes = _cachedWave.length - _waveDataPos;
e.data.writeBytes(_cachedWave, _waveDataPos, _waveDataBytes);
}
}
}
}
//--------------------------------------------------------------------------
//
// Cached Sound Methods
//
//--------------------------------------------------------------------------
/**
* Cache the sound for speedy playback.
* If a callback is passed in, the caching will be done asynchronously, taking maxTimePerFrame milliseconds
* per frame to cache, them calling the callback when it's done.
* If not, the whole sound is cached imidiately - can freeze the player for a few seconds, especially in debug mode.
* @param callback Function to call when the caching is complete
* @param maxTimePerFrame Maximum time in milliseconds the caching will use per frame
*/
public function cacheSound(callback:Function = null, maxTimePerFrame:uint = 5):void
{
stop();
if (_cachingAsync) return;
reset(true);
_cachedWave = new ByteArray();
if (Boolean(callback))
{
_mutation = false;
_cachingNormal = true;
_cachingAsync = true;
_cacheTimePerFrame = maxTimePerFrame;
_cachedCallback = callback;
if (!_cacheTicker) _cacheTicker = new Shape;
_cacheTicker.addEventListener(Event.ENTER_FRAME, cacheSection);
}
else
{
_cachingNormal = false;
_cachingAsync = false;
synthWave(_cachedWave, _envelopeFullLength, true);
var length:uint = _cachedWave.length;
if(length < 24576)
{
// If the sound is smaller than the buffer length, add silence to allow it to play
_cachedWave.position = length;
for(var i:uint = 0, l:uint = 24576 - length; i < l; i++) _cachedWave.writeFloat(0.0);
}
}
}
/**
* Caches a series of mutations on the source sound.
* If a callback is passed in, the caching will be done asynchronously, taking maxTimePerFrame milliseconds
* per frame to cache, them calling the callback when it's done.
* If not, the whole sound is cached imidiately - can freeze the player for a few seconds, especially in debug mode.
* @param mutationsNum Number of mutations to cache
* @param mutationAmount Amount of mutation
* @param callback Function to call when the caching is complete
* @param maxTimePerFrame Maximum time in milliseconds the caching will use per frame
*/
public function cacheMutations(mutationsNum:uint, mutationAmount:Number = 0.05, callback:Function = null, maxTimePerFrame:uint = 5):void
{
stop();
if (_cachingAsync) return;
_cachedMutationsNum = mutationsNum;
_cachedMutations = new Vector.<ByteArray>();
if (Boolean(callback))
{
_mutation = true;
_cachingMutation = 0;
_cachedMutation = new ByteArray;
_cachedMutations[0] = _cachedMutation;
_cachedMutationAmount = mutationAmount;
_original = _params.clone();
_params.mutate(mutationAmount);
reset(true);
_cachingAsync = true;
_cacheTimePerFrame = maxTimePerFrame;
_cachedCallback = callback;
if (!_cacheTicker) _cacheTicker = new Shape;
_cacheTicker.addEventListener(Event.ENTER_FRAME, cacheSection);
}
else
{
var original:SfxrParams = _params.clone();
for(var i:uint = 0; i < _cachedMutationsNum; i++)
{
_params.mutate(mutationAmount);
cacheSound();
_cachedMutations[i] = _cachedWave;
_params.copyFrom(original);
}
_cachingMutation = -1;
}
}
/**
* Performs the asynchronous cache, working for up to _cacheTimePerFrame milliseconds per frame
* @param e enterFrame event
*/
private function cacheSection(e:Event):void
{
var cacheStartTime:uint = getTimer();
while (getTimer() - cacheStartTime < _cacheTimePerFrame)
{
if (_mutation)
{
_waveDataPos = _cachedMutation.position;
if (synthWave(_cachedMutation, 500, true))
{
_params.copyFrom(_original);
_params.mutate(_cachedMutationAmount);
reset(true);
_cachingMutation++;
_cachedMutation = new ByteArray;
_cachedMutations[_cachingMutation] = _cachedMutation;
if (_cachingMutation >= _cachedMutationsNum)
{
_cachingMutation = -1;
_cachingAsync = false;
_params.paramsDirty = false;
_cachedCallback();
_cachedCallback = null;
_cacheTicker.removeEventListener(Event.ENTER_FRAME, cacheSection);
return;
}
}
}
else
{
_waveDataPos = _cachedWave.position;
if (synthWave(_cachedWave, 500, true))
{
_cachingNormal = false;
_cachingAsync = false;
_cachedCallback();
_cachedCallback = null;
_cacheTicker.removeEventListener(Event.ENTER_FRAME, cacheSection);
return;
}
}
}
}
//--------------------------------------------------------------------------
//
// Synth Methods
//
//--------------------------------------------------------------------------
/**
* Resets the runing variables from the params
* Used once at the start (total reset) and for the repeat effect (partial reset)
* @param totalReset If the reset is total
*/
private function reset(totalReset:Boolean):void
{
// Shorter reference
var p:SfxrParams = _params;
_period = 100.0 / (p.startFrequency * p.startFrequency + 0.001);
_maxPeriod = 100.0 / (p.minFrequency * p.minFrequency + 0.001);
_slide = 1.0 - p.slide * p.slide * p.slide * 0.01;
_deltaSlide = -p.deltaSlide * p.deltaSlide * p.deltaSlide * 0.000001;
if (p.waveType == 0)
{
_squareDuty = 0.5 - p.squareDuty * 0.5;
_dutySweep = -p.dutySweep * 0.00005;
}
if (p.changeAmount > 0.0) _changeAmount = 1.0 - p.changeAmount * p.changeAmount * 0.9;
else _changeAmount = 1.0 + p.changeAmount * p.changeAmount * 10.0;
_changeTime = 0;
if(p.changeSpeed == 1.0) _changeLimit = 0;
else _changeLimit = (1.0 - p.changeSpeed) * (1.0 - p.changeSpeed) * 20000 + 32;
if(totalReset)
{
p.paramsDirty = false;
_masterVolume = p.masterVolume * p.masterVolume;
_waveType = p.waveType;
if (p.sustainTime < 0.01) p.sustainTime = 0.01;
var totalTime:Number = p.attackTime + p.sustainTime + p.decayTime;
if (totalTime < 0.18)
{
var multiplier:Number = 0.18 / totalTime;
p.attackTime *= multiplier;
p.sustainTime *= multiplier;
p.decayTime *= multiplier;
}
_sustainPunch = p.sustainPunch;
_phase = 0;
_minFreqency = p.minFrequency;
_filters = p.lpFilterCutoff != 1.0 || p.hpFilterCutoff != 0.0;
_lpFilterPos = 0.0;
_lpFilterDeltaPos = 0.0;
_lpFilterCutoff = p.lpFilterCutoff * p.lpFilterCutoff * p.lpFilterCutoff * 0.1;
_lpFilterDeltaCutoff = 1.0 + p.lpFilterCutoffSweep * 0.0001;
_lpFilterDamping = 5.0 / (1.0 + p.lpFilterResonance * p.lpFilterResonance * 20.0) * (0.01 + _lpFilterCutoff);
if (_lpFilterDamping > 0.8) _lpFilterDamping = 0.8;
_lpFilterDamping = 1.0 - _lpFilterDamping;
_lpFilterOn = p.lpFilterCutoff != 1.0;
_hpFilterPos = 0.0;
_hpFilterCutoff = p.hpFilterCutoff * p.hpFilterCutoff * 0.1;
_hpFilterDeltaCutoff = 1.0 + p.hpFilterCutoffSweep * 0.0003;
_vibratoPhase = 0.0;
_vibratoSpeed = p.vibratoSpeed * p.vibratoSpeed * 0.01;
_vibratoAmplitude = p.vibratoDepth * 0.5;
_envelopeVolume = 0.0;
_envelopeStage = 0;
_envelopeTime = 0;
_envelopeLength0 = p.attackTime * p.attackTime * 100000.0;
_envelopeLength1 = p.sustainTime * p.sustainTime * 100000.0;
_envelopeLength2 = p.decayTime * p.decayTime * 100000.0 + 10;
_envelopeLength = _envelopeLength0;
_envelopeFullLength = _envelopeLength0 + _envelopeLength1 + _envelopeLength2;
_envelopeOverLength0 = 1.0 / _envelopeLength0;
_envelopeOverLength1 = 1.0 / _envelopeLength1;
_envelopeOverLength2 = 1.0 / _envelopeLength2;
_phaser = p.phaserOffset != 0.0 || p.phaserSweep != 0.0;
_phaserOffset = p.phaserOffset * p.phaserOffset * 1020.0;
if(p.phaserOffset < 0.0) _phaserOffset = -_phaserOffset;
_phaserDeltaOffset = p.phaserSweep * p.phaserSweep * p.phaserSweep * 0.2;
_phaserPos = 0;
if(!_phaserBuffer) _phaserBuffer = new Vector.<Number>(1024, true);
if(!_noiseBuffer) _noiseBuffer = new Vector.<Number>(32, true);
for(var i:uint = 0; i < 1024; i++) _phaserBuffer[i] = 0.0;
for(i = 0; i < 32; i++) _noiseBuffer[i] = Math.random() * 2.0 - 1.0;
_repeatTime = 0;
if (p.repeatSpeed == 0.0) _repeatLimit = 0;
else _repeatLimit = int((1.0-p.repeatSpeed) * (1.0-p.repeatSpeed) * 20000) + 32;
}
}
/**
* Writes the wave to the supplied buffer ByteArray
* @param buffer A ByteArray to write the wave to
* @param waveData If the wave should be written for the waveData
* @return If the wave is finished
*/
private function synthWave(buffer:ByteArray, length:uint, waveData:Boolean = false, sampleRate:uint = 44100, bitDepth:uint = 16):Boolean
{
_finished = false;
_sampleCount = 0;
_bufferSample = 0.0;
for(var i:uint = 0; i < length; i++)
{
if (_finished) return true;
// Repeats every _repeatLimit times, partially resetting the sound parameters
if(_repeatLimit != 0)
{
if(++_repeatTime >= _repeatLimit)
{
_repeatTime = 0;
reset(false);
}
}
// If _changeLimit is reached, shifts the pitch
if(_changeLimit != 0)
{
if(++_changeTime >= _changeLimit)
{
_changeLimit = 0;
_period *= _changeAmount;
}
}
// Acccelerate and apply slide
_slide += _deltaSlide;
_period *= _slide;
// Checks for frequency getting too low, and stops the sound if a minFrequency was set
if(_period > _maxPeriod)
{
_period = _maxPeriod;
if(_minFreqency > 0.0) _finished = true;
}
_periodTemp = _period;
// Applies the vibrato effect
if(_vibratoAmplitude > 0.0)
{
_vibratoPhase += _vibratoSpeed;
_periodTemp = _period * (1.0 + Math.sin(_vibratoPhase) * _vibratoAmplitude);
}
_periodTemp = int(_periodTemp);
if(_periodTemp < 8) _periodTemp = 8;
// Sweeps the square duty
if (_waveType == 0)
{
_squareDuty += _dutySweep;
if(_squareDuty < 0.0) _squareDuty = 0.0;
else if (_squareDuty > 0.5) _squareDuty = 0.5;
}
// Moves through the different stages of the volume envelope
if(++_envelopeTime > _envelopeLength)
{
_envelopeTime = 0;
switch(++_envelopeStage)
{
case 1: _envelopeLength = _envelopeLength1; break;
case 2: _envelopeLength = _envelopeLength2; break;
}
}
// Sets the volume based on the position in the envelope
switch(_envelopeStage)
{
case 0: _envelopeVolume = _envelopeTime * _envelopeOverLength0; break;
case 1: _envelopeVolume = 1.0 + (1.0 - _envelopeTime * _envelopeOverLength1) * 2.0 * _sustainPunch; break;
case 2: _envelopeVolume = 1.0 - _envelopeTime * _envelopeOverLength2; break;
case 3: _envelopeVolume = 0.0; _finished = true; break;
}
// Moves the phaser offset
if (_phaser)
{
_phaserOffset += _phaserDeltaOffset;
_phaserInt = int(_phaserOffset);
if(_phaserInt < 0) _phaserInt = -_phaserInt;
else if (_phaserInt > 1023) _phaserInt = 1023;
}
// Moves the high-pass filter cutoff
if(_filters && _hpFilterDeltaCutoff != 0.0)
{
_hpFilterCutoff *= _hpFilterDeltaCutoff;
if(_hpFilterCutoff < 0.00001) _hpFilterCutoff = 0.00001;
else if(_hpFilterCutoff > 0.1) _hpFilterCutoff = 0.1;
}
_superSample = 0.0;
for(var j:int = 0; j < 8; j++)
{
// Cycles through the period
_phase++;
if(_phase >= _periodTemp)
{
_phase = _phase - _periodTemp;
// Generates new random noise for this period
if(_waveType == 3)
{
for(var n:uint = 0; n < 32; n++) _noiseBuffer[n] = Math.random() * 2.0 - 1.0;
}
}
// Gets the sample from the oscillator
switch(_waveType)
{
case 0: // Square wave
{
_sample = ((_phase / _periodTemp) < _squareDuty) ? 0.5 : -0.5;
break;
}
case 1: // Saw wave
{
_sample = 1.0 - (_phase / _periodTemp) * 2.0;
break;
}
case 2: // Sine wave (fast and accurate approx)
{
_pos = _phase / _periodTemp;
_pos = _pos > 0.5 ? (_pos - 1.0) * 6.28318531 : _pos * 6.28318531;
_sample = _pos < 0 ? 1.27323954 * _pos + .405284735 * _pos * _pos : 1.27323954 * _pos - 0.405284735 * _pos * _pos;
_sample = _sample < 0 ? .225 * (_sample *-_sample - _sample) + _sample : .225 * (_sample * _sample - _sample) + _sample;
break;
}
case 3: // Noise
{
_sample = _noiseBuffer[Math.min(uint(_phase * 32 / int(_periodTemp)),31)];
break;
}
}
// Applies the low and high pass filters
if (_filters)
{
_lpFilterOldPos = _lpFilterPos;
_lpFilterCutoff *= _lpFilterDeltaCutoff;
if(_lpFilterCutoff < 0.0) _lpFilterCutoff = 0.0;
else if(_lpFilterCutoff > 0.1) _lpFilterCutoff = 0.1;
if(_lpFilterOn)
{
_lpFilterDeltaPos += (_sample - _lpFilterPos) * _lpFilterCutoff;
_lpFilterDeltaPos *= _lpFilterDamping;
}
else
{
_lpFilterPos = _sample;
_lpFilterDeltaPos = 0.0;
}
_lpFilterPos += _lpFilterDeltaPos;
_hpFilterPos += _lpFilterPos - _lpFilterOldPos;
_hpFilterPos *= 1.0 - _hpFilterCutoff;
_sample = _hpFilterPos;
}
// Applies the phaser effect
if (_phaser)
{
_phaserBuffer[_phaserPos&1023] = _sample;
_sample += _phaserBuffer[(_phaserPos - _phaserInt + 1024) & 1023];
_phaserPos = (_phaserPos + 1) & 1023;
}
_superSample += _sample;
}
// Averages out the super samples and applies volumes
_superSample = _masterVolume * _envelopeVolume * _superSample * 0.125;
// Clipping if too loud
if(_superSample > 1.0) _superSample = 1.0;
else if(_superSample < -1.0) _superSample = -1.0;
if(waveData)
{
// Writes same value to left and right channels
buffer.writeFloat(_superSample);
buffer.writeFloat(_superSample);
}
else
{
_bufferSample += _superSample;
_sampleCount++;
// Writes mono wave data to the .wav format
if(sampleRate == 44100 || _sampleCount == 2)
{
_bufferSample /= _sampleCount;
_sampleCount = 0;
if(bitDepth == 16) buffer.writeShort(int(32000.0 * _bufferSample));
else buffer.writeByte(_bufferSample * 127 + 128);
_bufferSample = 0.0;
}
}
}
return false;
}
//--------------------------------------------------------------------------
//
// .wav File Methods
//
//--------------------------------------------------------------------------
/**
* Returns a ByteArray of the wave in the form of a .wav file, ready to be saved out
* @param sampleRate Sample rate to generate the .wav at
* @param bitDepth Bit depth to generate the .wav at
* @return Wave in a .wav file
*/
public function getWavFile(sampleRate:uint = 44100, bitDepth:uint = 16):ByteArray
{
stop();
reset(true);
if (sampleRate != 44100) sampleRate = 22050;
if (bitDepth != 16) bitDepth = 8;
var soundLength:uint = _envelopeFullLength;
if (bitDepth == 16) soundLength *= 2;
if (sampleRate == 22050) soundLength /= 2;
var filesize:int = 36 + soundLength;
var blockAlign:int = bitDepth / 8;
var bytesPerSec:int = sampleRate * blockAlign;
var wav:ByteArray = new ByteArray();
// Header
wav.endian = Endian.BIG_ENDIAN;
wav.writeUnsignedInt(0x52494646); // Chunk ID "RIFF"
wav.endian = Endian.LITTLE_ENDIAN;
wav.writeUnsignedInt(filesize); // Chunck Data Size
wav.endian = Endian.BIG_ENDIAN;
wav.writeUnsignedInt(0x57415645); // RIFF Type "WAVE"
// Format Chunk
wav.endian = Endian.BIG_ENDIAN;
wav.writeUnsignedInt(0x666D7420); // Chunk ID "fmt "
wav.endian = Endian.LITTLE_ENDIAN;
wav.writeUnsignedInt(16); // Chunk Data Size
wav.writeShort(1); // Compression Code PCM
wav.writeShort(1); // Number of channels
wav.writeUnsignedInt(sampleRate); // Sample rate
wav.writeUnsignedInt(bytesPerSec); // Average bytes per second
wav.writeShort(blockAlign); // Block align
wav.writeShort(bitDepth); // Significant bits per sample
// Data Chunk
wav.endian = Endian.BIG_ENDIAN;
wav.writeUnsignedInt(0x64617461); // Chunk ID "data"
wav.endian = Endian.LITTLE_ENDIAN;
wav.writeUnsignedInt(soundLength); // Chunk Data Size
synthWave(wav, _envelopeFullLength, false, sampleRate, bitDepth);
wav.position = 0;
return wav;
}
}
/**
* SfxrParams
*
* Copyright 2010 Thomas Vian
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @author Thomas Vian
*/
class SfxrParams
{
//--------------------------------------------------------------------------
//
// Properties
//
//--------------------------------------------------------------------------
/** If the parameters have been changed since last time (shouldn't used cached sound) */
public var paramsDirty:Boolean;
private var _waveType :uint = 0; // Shape of the wave (0:square, 1:saw, 2:sin or 3:noise)
private var _masterVolume :Number = 0.5; // Overall volume of the sound (0 to 1)
private var _attackTime :Number = 0.0; // Length of the volume envelope attack (0 to 1)
private var _sustainTime :Number = 0.0; // Length of the volume envelope sustain (0 to 1)
private var _sustainPunch :Number = 0.0; // Tilts the sustain envelope for more 'pop' (0 to 1)
private var _decayTime :Number = 0.0; // Length of the volume envelope decay (yes, I know it's called release) (0 to 1)
private var _startFrequency :Number = 0.0; // Base note of the sound (0 to 1)
private var _minFrequency :Number = 0.0; // If sliding, the sound will stop at this frequency, to prevent really low notes (0 to 1)
private var _slide :Number = 0.0; // Slides the note up or down (-1 to 1)
private var _deltaSlide :Number = 0.0; // Accelerates the slide (-1 to 1)
private var _vibratoDepth :Number = 0.0; // Strength of the vibrato effect (0 to 1)
private var _vibratoSpeed :Number = 0.0; // Speed of the vibrato effect (i.e. frequency) (0 to 1)
private var _changeAmount :Number = 0.0; // Shift in note, either up or down (-1 to 1)
private var _changeSpeed :Number = 0.0; // How fast the note shift happens (only happens once) (0 to 1)
private var _squareDuty :Number = 0.0; // Controls the ratio between the up and down states of the square wave, changing the tibre (0 to 1)
private var _dutySweep :Number = 0.0; // Sweeps the duty up or down (-1 to 1)
private var _repeatSpeed :Number = 0.0; // Speed of the note repeating - certain variables are reset each time (0 to 1)
private var _phaserOffset :Number = 0.0; // Offsets a second copy of the wave by a small phase, changing the tibre (-1 to 1)
private var _phaserSweep :Number = 0.0; // Sweeps the phase up or down (-1 to 1)
private var _lpFilterCutoff :Number = 0.0; // Frequency at which the low-pass filter starts attenuating higher frequencies (0 to 1)
private var _lpFilterCutoffSweep:Number = 0.0; // Sweeps the low-pass cutoff up or down (-1 to 1)
private var _lpFilterResonance :Number = 0.0; // Changes the attenuation rate for the low-pass filter, changing the timbre (0 to 1)
private var _hpFilterCutoff :Number = 0.0; // Frequency at which the high-pass filter starts attenuating lower frequencies (0 to 1)
private var _hpFilterCutoffSweep:Number = 0.0; // Sweeps the high-pass cutoff up or down (-1 to 1)
//--------------------------------------------------------------------------
//
// Getters / Setters
//
//--------------------------------------------------------------------------
/** Shape of the wave (0:square, 1:saw, 2:sin or 3:noise) */
public function get waveType():uint { return _waveType; }
public function set waveType(value:uint):void { _waveType = value > 3 ? 0 : value; paramsDirty = true; }
/** Overall volume of the sound (0 to 1) */
public function get masterVolume():Number { return _masterVolume; }
public function set masterVolume(value:Number):void { _masterVolume = clamp1(value); paramsDirty = true; }
/** Length of the volume envelope attack (0 to 1) */
public function get attackTime():Number { return _attackTime; }
public function set attackTime(value:Number):void { _attackTime = clamp1(value); paramsDirty = true; }
/** Length of the volume envelope sustain (0 to 1) */
public function get sustainTime():Number { return _sustainTime; }
public function set sustainTime(value:Number):void { _sustainTime = clamp1(value); paramsDirty = true; }
/** Tilts the sustain envelope for more 'pop' (0 to 1) */
public function get sustainPunch():Number { return _sustainPunch; }
public function set sustainPunch(value:Number):void { _sustainPunch = clamp1(value); paramsDirty = true; }
/** Length of the volume envelope decay (yes, I know it's called release) (0 to 1) */
public function get decayTime():Number { return _decayTime; }
public function set decayTime(value:Number):void { _decayTime = clamp1(value); paramsDirty = true; }
/** Base note of the sound (0 to 1) */
public function get startFrequency():Number { return _startFrequency; }
public function set startFrequency(value:Number):void { _startFrequency = clamp1(value); paramsDirty = true; }
/** If sliding, the sound will stop at this frequency, to prevent really low notes (0 to 1) */
public function get minFrequency():Number { return _minFrequency; }
public function set minFrequency(value:Number):void { _minFrequency = clamp1(value); paramsDirty = true; }
/** Slides the note up or down (-1 to 1) */
public function get slide():Number { return _slide; }
public function set slide(value:Number):void { _slide = clamp2(value); paramsDirty = true; }
/** Accelerates the slide (-1 to 1) */
public function get deltaSlide():Number { return _deltaSlide; }
public function set deltaSlide(value:Number):void { _deltaSlide = clamp2(value); paramsDirty = true; }
/** Strength of the vibrato effect (0 to 1) */
public function get vibratoDepth():Number { return _vibratoDepth; }
public function set vibratoDepth(value:Number):void { _vibratoDepth = clamp1(value); paramsDirty = true; }
/** Speed of the vibrato effect (i.e. frequency) (0 to 1) */
public function get vibratoSpeed():Number { return _vibratoSpeed; }
public function set vibratoSpeed(value:Number):void { _vibratoSpeed = clamp1(value); paramsDirty = true; }
/** Shift in note, either up or down (-1 to 1) */
public function get changeAmount():Number { return _changeAmount; }
public function set changeAmount(value:Number):void { _changeAmount = clamp2(value); paramsDirty = true; }
/** How fast the note shift happens (only happens once) (0 to 1) */
public function get changeSpeed():Number { return _changeSpeed; }
public function set changeSpeed(value:Number):void { _changeSpeed = clamp1(value); paramsDirty = true; }
/** Controls the ratio between the up and down states of the square wave, changing the tibre (0 to 1) */
public function get squareDuty():Number { return _squareDuty; }
public function set squareDuty(value:Number):void { _squareDuty = clamp1(value); paramsDirty = true; }
/** Sweeps the duty up or down (-1 to 1) */
public function get dutySweep():Number { return _dutySweep; }
public function set dutySweep(value:Number):void { _dutySweep = clamp2(value); paramsDirty = true; }
/** Speed of the note repeating - certain variables are reset each time (0 to 1) */
public function get repeatSpeed():Number { return _repeatSpeed; }
public function set repeatSpeed(value:Number):void { _repeatSpeed = clamp1(value); paramsDirty = true; }
/** Offsets a second copy of the wave by a small phase, changing the tibre (-1 to 1) */
public function get phaserOffset():Number { return _phaserOffset; }
public function set phaserOffset(value:Number):void { _phaserOffset = clamp2(value); paramsDirty = true; }
/** Sweeps the phase up or down (-1 to 1) */
public function get phaserSweep():Number { return _phaserSweep; }
public function set phaserSweep(value:Number):void { _phaserSweep = clamp2(value); paramsDirty = true; }
/** Frequency at which the low-pass filter starts attenuating higher frequencies (0 to 1) */
public function get lpFilterCutoff():Number { return _lpFilterCutoff; }
public function set lpFilterCutoff(value:Number):void { _lpFilterCutoff = clamp1(value); paramsDirty = true; }
/** Sweeps the low-pass cutoff up or down (-1 to 1) */
public function get lpFilterCutoffSweep():Number { return _lpFilterCutoffSweep; }
public function set lpFilterCutoffSweep(value:Number):void { _lpFilterCutoffSweep = clamp2(value); paramsDirty = true; }
/** Changes the attenuation rate for the low-pass filter, changing the timbre (0 to 1) */
public function get lpFilterResonance():Number { return _lpFilterResonance; }
public function set lpFilterResonance(value:Number):void { _lpFilterResonance = clamp1(value); paramsDirty = true; }
/** Frequency at which the high-pass filter starts attenuating lower frequencies (0 to 1) */
public function get hpFilterCutoff():Number { return _hpFilterCutoff; }
public function set hpFilterCutoff(value:Number):void { _hpFilterCutoff = clamp1(value); paramsDirty = true; }
/** Sweeps the high-pass cutoff up or down (-1 to 1) */
public function get hpFilterCutoffSweep():Number { return _hpFilterCutoffSweep; }
public function set hpFilterCutoffSweep(value:Number):void { _hpFilterCutoffSweep = clamp2(value); paramsDirty = true; }
//--------------------------------------------------------------------------
//
// Generator Methods
//
//--------------------------------------------------------------------------
/**
* Sets the parameters to generate a pickup/coin sound
*/
public function generatePickupCoin():void
{
resetParams();
_startFrequency = 0.4 + Math.random() * 0.5;
_sustainTime = Math.random() * 0.1;
_decayTime = 0.1 + Math.random() * 0.4;
_sustainPunch = 0.3 + Math.random() * 0.3;
if(Math.random() < 0.5)
{
_changeSpeed = 0.5 + Math.random() * 0.2;
_changeAmount = 0.2 + Math.random() * 0.4;
}
}
/**
* Sets the parameters to generate a laser/shoot sound
*/
public function generateLaserShoot():void
{
resetParams();
_waveType = uint(Math.random() * 3);
if(_waveType == 2 && Math.random() < 0.5) _waveType = uint(Math.random() * 2);
_startFrequency = 0.5 + Math.random() * 0.5;
_minFrequency = _startFrequency - 0.2 - Math.random() * 0.6;
if(_minFrequency < 0.2) _minFrequency = 0.2;
_slide = -0.15 - Math.random() * 0.2;
if(Math.random() < 0.33)
{
_startFrequency = 0.3 + Math.random() * 0.6;
_minFrequency = Math.random() * 0.1;
_slide = -0.35 - Math.random() * 0.3;
}
if(Math.random() < 0.5)
{
_squareDuty = Math.random() * 0.5;
_dutySweep = Math.random() * 0.2;
}
else
{
_squareDuty = 0.4 + Math.random() * 0.5;
_dutySweep =- Math.random() * 0.7;
}
_sustainTime = 0.1 + Math.random() * 0.2;
_decayTime = Math.random() * 0.4;
if(Math.random() < 0.5) _sustainPunch = Math.random() * 0.3;
if(Math.random() < 0.33)
{
_phaserOffset = Math.random() * 0.2;
_phaserSweep = -Math.random() * 0.2;
}
if(Math.random() < 0.5) _hpFilterCutoff = Math.random() * 0.3;
}
/**
* Sets the parameters to generate an explosion sound
*/
public function generateExplosion():void
{
resetParams();
_waveType = 3;
if(Math.random() < 0.5)
{
_startFrequency = 0.1 + Math.random() * 0.4;
_slide = -0.1 + Math.random() * 0.4;
}
else
{
_startFrequency = 0.2 + Math.random() * 0.7;
_slide = -0.2 - Math.random() * 0.2;
}
_startFrequency *= _startFrequency;
if(Math.random() < 0.2) _slide = 0.0;
if(Math.random() < 0.33) _repeatSpeed = 0.3 + Math.random() * 0.5;
_sustainTime = 0.1 + Math.random() * 0.3;
_decayTime = Math.random() * 0.5;
_sustainPunch = 0.2 + Math.random() * 0.6;
if(Math.random() < 0.5)
{
_phaserOffset = -0.3 + Math.random() * 0.9;
_phaserSweep = -Math.random() * 0.3;
}
if(Math.random() < 0.33)
{
_changeSpeed = 0.6 + Math.random() * 0.3;
_changeAmount = 0.8 - Math.random() * 1.6;
}
}
/**
* Sets the parameters to generate a powerup sound
*/
public function generatePowerup():void
{
resetParams();
if(Math.random() < 0.5) _waveType = 1;
else _squareDuty = Math.random() * 0.6;
if(Math.random() < 0.5)
{
_startFrequency = 0.2 + Math.random() * 0.3;
_slide = 0.1 + Math.random() * 0.4;
_repeatSpeed = 0.4 + Math.random() * 0.4;
}
else
{
_startFrequency = 0.2 + Math.random() * 0.3;
_slide = 0.05 + Math.random() * 0.2;
if(Math.random() < 0.5)
{
_vibratoDepth = Math.random() * 0.7;
_vibratoSpeed = Math.random() * 0.6;
}
}
_sustainTime = Math.random() * 0.4;
_decayTime = 0.1 + Math.random() * 0.4;
}
/**
* Sets the parameters to generate a hit/hurt sound
*/
public function generateHitHurt():void
{
resetParams();
_waveType = uint(Math.random() * 3);
if(_waveType == 2) _waveType = 3;
else if(_waveType == 0) _squareDuty = Math.random() * 0.6;
_startFrequency = 0.2 + Math.random() * 0.6;
_slide = -0.3 - Math.random() * 0.4;
_sustainTime = Math.random() * 0.1;
_decayTime = 0.1 + Math.random() * 0.2;
if(Math.random() < 0.5) _hpFilterCutoff = Math.random() * 0.3;
}
/**
* Sets the parameters to generate a jump sound
*/
public function generateJump():void
{
resetParams();
_waveType = 0;
_squareDuty = Math.random() * 0.6;
_startFrequency = 0.3 + Math.random() * 0.3;
_slide = 0.1 + Math.random() * 0.2;
_sustainTime = 0.1 + Math.random() * 0.3;
_decayTime = 0.1 + Math.random() * 0.2;
if(Math.random() < 0.5) _hpFilterCutoff = Math.random() * 0.3;
if(Math.random() < 0.5) _lpFilterCutoff = 1.0 - Math.random() * 0.6;
}
/**
* Sets the parameters to generate a blip/select sound
*/
public function generateBlipSelect():void
{
resetParams();
_waveType = uint(Math.random() * 2);
if(_waveType == 0) _squareDuty = Math.random() * 0.6;
_startFrequency = 0.2 + Math.random() * 0.4;
_sustainTime = 0.1 + Math.random() * 0.1;
_decayTime = Math.random() * 0.2;
_hpFilterCutoff = 0.1;
}
/**
* Resets the parameters, used at the start of each generate function
*/
protected function resetParams():void
{
paramsDirty = true;
_waveType = 0;
_startFrequency = 0.3;
_minFrequency = 0.0;
_slide = 0.0;
_deltaSlide = 0.0;
_squareDuty = 0.0;
_dutySweep = 0.0;
_vibratoDepth = 0.0;
_vibratoSpeed = 0.0;
_attackTime = 0.0;
_sustainTime = 0.3;
_decayTime = 0.4;
_sustainPunch = 0.0;
_lpFilterResonance = 0.0;
_lpFilterCutoff = 1.0;
_lpFilterCutoffSweep = 0.0;
_hpFilterCutoff = 0.0;
_hpFilterCutoffSweep = 0.0;
_phaserOffset = 0.0;
_phaserSweep = 0.0;
_repeatSpeed = 0.0;
_changeSpeed = 0.0;
_changeAmount = 0.0;
}
//--------------------------------------------------------------------------
//
// Randomize Methods
//
//--------------------------------------------------------------------------
/**
* Randomly adjusts the parameters ever so slightly
*/
public function mutate(mutation:Number = 0.05):void
{
if (Math.random() < 0.5) startFrequency += Math.random() * mutation*2 - mutation;
if (Math.random() < 0.5) minFrequency += Math.random() * mutation*2 - mutation;
if (Math.random() < 0.5) slide += Math.random() * mutation*2 - mutation;
if (Math.random() < 0.5) deltaSlide += Math.random() * mutation*2 - mutation;
if (Math.random() < 0.5) squareDuty += Math.random() * mutation*2 - mutation;
if (Math.random() < 0.5) dutySweep += Math.random() * mutation*2 - mutation;
if (Math.random() < 0.5) vibratoDepth += Math.random() * mutation*2 - mutation;
if (Math.random() < 0.5) vibratoSpeed += Math.random() * mutation*2 - mutation;
if (Math.random() < 0.5) attackTime += Math.random() * mutation*2 - mutation;
if (Math.random() < 0.5) sustainTime += Math.random() * mutation*2 - mutation;
if (Math.random() < 0.5) decayTime += Math.random() * mutation*2 - mutation;
if (Math.random() < 0.5) sustainPunch += Math.random() * mutation*2 - mutation;
if (Math.random() < 0.5) lpFilterCutoff += Math.random() * mutation*2 - mutation;
if (Math.random() < 0.5) lpFilterCutoffSweep += Math.random() * mutation*2 - mutation;
if (Math.random() < 0.5) lpFilterResonance += Math.random() * mutation*2 - mutation;
if (Math.random() < 0.5) hpFilterCutoff += Math.random() * mutation*2 - mutation;
if (Math.random() < 0.5) hpFilterCutoffSweep += Math.random() * mutation*2 - mutation;
if (Math.random() < 0.5) phaserOffset += Math.random() * mutation*2 - mutation;
if (Math.random() < 0.5) phaserSweep += Math.random() * mutation*2 - mutation;
if (Math.random() < 0.5) repeatSpeed += Math.random() * mutation*2 - mutation;
if (Math.random() < 0.5) changeSpeed += Math.random() * mutation*2 - mutation;
if (Math.random() < 0.5) changeAmount += Math.random() * mutation*2 - mutation;
}
/**
* Sets all parameters to random values
*/
public function randomize():void
{
paramsDirty = true;
_waveType = uint(Math.random() * 4);
_attackTime = pow(Math.random()*2-1, 4);
_sustainTime = pow(Math.random()*2-1, 2);
_sustainPunch = pow(Math.random()*0.8, 2);
_decayTime = Math.random();
_startFrequency = (Math.random() < 0.5) ? pow(Math.random()*2-1, 2) : (pow(Math.random() * 0.5, 3) + 0.5);
_minFrequency = 0.0;
_slide = pow(Math.random()*2-1, 5);
_deltaSlide = pow(Math.random()*2-1, 3);
_vibratoDepth = pow(Math.random()*2-1, 3);
_vibratoSpeed = Math.random()*2-1;
_changeAmount = Math.random()*2-1;
_changeSpeed = Math.random()*2-1;
_squareDuty = Math.random()*2-1;
_dutySweep = pow(Math.random()*2-1, 3);
_repeatSpeed = Math.random()*2-1;
_phaserOffset = pow(Math.random()*2-1, 3);
_phaserSweep = pow(Math.random()*2-1, 3);
_lpFilterCutoff = 1 - pow(Math.random(), 3);
_lpFilterCutoffSweep = pow(Math.random()*2-1, 3);
_lpFilterResonance = Math.random()*2-1;
_hpFilterCutoff = pow(Math.random(), 5);
_hpFilterCutoffSweep = pow(Math.random()*2-1, 5);
if(_attackTime + _sustainTime + _decayTime < 0.2)
{
_sustainTime = 0.2 + Math.random() * 0.3;
_decayTime = 0.2 + Math.random() * 0.3;
}
if((_startFrequency > 0.7 && _slide > 0.2) || (_startFrequency < 0.2 && _slide < -0.05))
{
_slide = -_slide;
}
if(_lpFilterCutoff < 0.1 && _lpFilterCutoffSweep < -0.05)
{
_lpFilterCutoffSweep = -_lpFilterCutoffSweep;
}
}
//--------------------------------------------------------------------------
//
// Settings String Methods
//
//--------------------------------------------------------------------------
/**
* Returns a string representation of the parameters for copy/paste sharing
* @return A comma-delimited list of parameter values
*/
public function getSettingsString():String
{
var string:String = String(waveType);
string += "," + to4DP(_attackTime) + "," + to4DP(_sustainTime)
+ "," + to4DP(_sustainPunch) + "," + to4DP(_decayTime)
+ "," + to4DP(_startFrequency) + "," + to4DP(_minFrequency)
+ "," + to4DP(_slide) + "," + to4DP(_deltaSlide)
+ "," + to4DP(_vibratoDepth) + "," + to4DP(_vibratoSpeed)
+ "," + to4DP(_changeAmount) + "," + to4DP(_changeSpeed)
+ "," + to4DP(_squareDuty) + "," + to4DP(_dutySweep)
+ "," + to4DP(_repeatSpeed) + "," + to4DP(_phaserOffset)
+ "," + to4DP(_phaserSweep) + "," + to4DP(_lpFilterCutoff)
+ "," + to4DP(_lpFilterCutoffSweep) + "," + to4DP(_lpFilterResonance)
+ "," + to4DP(_hpFilterCutoff)+ "," + to4DP(_hpFilterCutoffSweep)
+ "," + to4DP(_masterVolume);
return string;
}
/**
* Parses a settings string into the parameters
* @param string Settings string to parse
* @return If the string successfully parsed
*/
public function setSettingsString(string:String):Boolean
{
var values:Array = string.split(",");
if (values.length != 24) return false;
waveType = uint(values[0]) || 0;
attackTime = Number(values[1]) || 0;
sustainTime = Number(values[2]) || 0;
sustainPunch = Number(values[3]) || 0;
decayTime = Number(values[4]) || 0;
startFrequency = Number(values[5]) || 0;
minFrequency = Number(values[6]) || 0;
slide = Number(values[7]) || 0;
deltaSlide = Number(values[8]) || 0;
vibratoDepth = Number(values[9]) || 0;
vibratoSpeed = Number(values[10]) || 0;
changeAmount = Number(values[11]) || 0;
changeSpeed = Number(values[12]) || 0;
squareDuty = Number(values[13]) || 0;
dutySweep = Number(values[14]) || 0;
repeatSpeed = Number(values[15]) || 0;
phaserOffset = Number(values[16]) || 0;
phaserSweep = Number(values[17]) || 0;
lpFilterCutoff = Number(values[18]) || 0;
lpFilterCutoffSweep = Number(values[19]) || 0;
lpFilterResonance = Number(values[20]) || 0;
hpFilterCutoff = Number(values[21]) || 0;
hpFilterCutoffSweep = Number(values[22]) || 0;
masterVolume = Number(values[23]) || 0;
return true;
}
//--------------------------------------------------------------------------
//
// Copying Methods
//
//--------------------------------------------------------------------------
/**
* Returns a copy of this SfxrParams with all settings duplicated
* @return A copy of this SfxrParams
*/
public function clone():SfxrParams
{
var out:SfxrParams = new SfxrParams();
out.copyFrom(this);
return out;
}
/**
* Copies parameters from another instance
* @param params Instance to copy parameters from
*/
public function copyFrom(params:SfxrParams, makeDirty:Boolean = false):void
{
_waveType = params.waveType;
_attackTime = params.attackTime;
_sustainTime = params.sustainTime;
_sustainPunch = params.sustainPunch;
_decayTime = params.decayTime;
_startFrequency = params.startFrequency;
_minFrequency = params.minFrequency;
_slide = params.slide;
_deltaSlide = params.deltaSlide;
_vibratoDepth = params.vibratoDepth;
_vibratoSpeed = params.vibratoSpeed;
_changeAmount = params.changeAmount;
_changeSpeed = params.changeSpeed;
_squareDuty = params.squareDuty;
_dutySweep = params.dutySweep;
_repeatSpeed = params.repeatSpeed;
_phaserOffset = params.phaserOffset;
_phaserSweep = params.phaserSweep;
_lpFilterCutoff = params.lpFilterCutoff;
_lpFilterCutoffSweep = params.lpFilterCutoffSweep;
_lpFilterResonance = params.lpFilterResonance;
_hpFilterCutoff = params.hpFilterCutoff;
_hpFilterCutoffSweep = params.hpFilterCutoffSweep;
_masterVolume = params.masterVolume;
if (makeDirty) paramsDirty = true;
}
//--------------------------------------------------------------------------
//
// Util Methods
//
//--------------------------------------------------------------------------
/**
* Clams a value to betwen 0 and 1
* @param value Input value
* @return The value clamped between 0 and 1
*/
private function clamp1(value:Number):Number { return (value > 1.0) ? 1.0 : ((value < 0.0) ? 0.0 : value); }
/**
* Clams a value to betwen -1 and 1
* @param value Input value
* @return The value clamped between -1 and 1
*/
private function clamp2(value:Number):Number { return (value > 1.0) ? 1.0 : ((value < -1.0) ? -1.0 : value); }
/**
* Quick power function
* @param base Base to raise to power
* @param power Power to raise base by
* @return The calculated power
*/
private function pow(base:Number, power:int):Number
{
switch(power)
{
case 2: return base*base;
case 3: return base*base*base;
case 4: return base*base*base*base;
case 5: return base*base*base*base*base;
}
return 1.0;
}
/**
* Returns the number as a string to 4 decimal places
* @param value Number to convert
* @return Number to 4dp as a string
*/
private function to4DP(value:Number):String
{
if (value < 0.0001 && value > -0.0001) return "";
var string:String = String(value);
var split:Array = string.split(".");
if (split.length == 1)
{
return string;
}
else
{
var out:String = split[0] + "." + split[1].substr(0, 4);
while (out.substr(out.length - 1, 1) == "0") out = out.substr(0, out.length - 1);
return out;
}
}
}