In case Flash no longer exists; a copy of this site is included in the Flashpoint archive's "ultimate" collection.

Dead Code Preservation :: Archived AS3 works from wonderfl.net

flash on 2011-6-18

bpm by difficulty: 80-easy, 120-medium, 180-hard
stats:
scoreEasy
scoreMedium
scoreHard
timeEasy
timeMedium
timeHard
Get Adobe Flash player
by excessdenied 21 Jun 2011
    Embed
/*
bpm by difficulty: 80-easy, 120-medium, 180-hard
stats:
scoreEasy
scoreMedium
scoreHard
timeEasy
timeMedium
timeHard
*/
package  
{
    import flash.display.MovieClip;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.text.TextField;
    import flash.text.TextFieldAutoSize;
    import flash.text.TextFormat;
    
    [SWF(width='465',height='465',backgroundColor='0x000000',frameRate='40')]
    public class NewClass extends MovieClip {
        
        protected var world:MusicalWorld;
        protected var textField:TextField;
        
        public function NewClass() {
            addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
        }
        
        protected function onAddedToStage(event:Event):void {
            removeEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
            
            world = new MusicalWorld();
            world.scaleX = world.scaleY = 1;
            //world.x = (465-World.WIDTH*World.M2PX*world.scaleX)/2;
            //world.y = (465-World.HEIGHT*World.M2PX*world.scaleY)/2;
            addChild(world);
            world.startWorld();
            world.createPlayer();
            
            textField = new TextField();
            var textFormat:TextFormat = textField.getTextFormat();
            textFormat.font = '_typewriter';
            textFormat.color = 0xeeeeee;
            textFormat.size = 32;
            textField.selectable = false;
            textField.defaultTextFormat = textFormat;
            textField.x = World.WIDTH*World.M2PX+10;
            textField.autoSize = TextFieldAutoSize.LEFT;
            textField.wordWrap = false;
            addChild(textField);
            textField.text = 'score:';
            
            addEventListener(Event.ENTER_FRAME, onEnterFrame);
        }
        
        protected function onEnterFrame(event:Event):void {
            var seconds:Number = world.totalTimeSteps*world.timeStep;
            var minutes:int = seconds/60;
            seconds = seconds%60;
            textField.htmlText = 
                'time:'
                +'\n<p align="right">'+sprintf('%02d:%02d.%01d', int(seconds/60), int(seconds-int(seconds/60)), (seconds-int(seconds))*10)+'</p>'
                +'\n'+'score:'
                +'\n<p align="right">'+sprintf('%07d', world.score)+'</p>'
                +'\n'+'lives:'+world.health
                +'\n'+'bpm:'+world.bpm
        }
    }
}

import Box2D.Collision.Shapes.b2FilterData;
import Box2D.Collision.b2ContactPoint;
import Box2D.Common.Math.b2Vec2;
import Box2D.Dynamics.b2Body;

import com.actionsnippet.qbox.QuickBox2D;
import com.actionsnippet.qbox.QuickContacts;
import com.actionsnippet.qbox.QuickObject;
import com.greensock.TweenLite;
import com.greensock.TweenMax;
import com.greensock.TweenNano;

import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.DisplayObject;
import flash.display.GradientType;
import flash.display.MovieClip;
import flash.display.Shape;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.KeyboardEvent;
import flash.events.MouseEvent;
import flash.filters.BlurFilter;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.text.TextField;
import flash.text.TextFormat;
import flash.ui.Keyboard;
import flash.utils.Dictionary;

import org.si.sion.SiONDriver;
import org.si.sion.SiONVoice;
import org.si.sion.sequencer.SiMMLTrack;
import org.si.sion.utils.Scale;
import org.si.sion.utils.SiONPresetVoice;
import org.si.sound.DrumMachine;

class Category {
    public static const NONE:Number = 0x0000;
    public static const ALL:Number = 0xffff;
    public static const PLAYER:Number = 0x0001;
    public static const ENEMY:Number = 0x0002;
    public static const WALL:Number = 0x0004;
    public static const BULLET:Number = 0x0008;
}

class World {
    public static const PX2M:Number = 1/30; // metres/pixel
    public static const M2PX:Number = 1/PX2M;
    public static const DEG2RAD:Number = Math.PI/180; // radians/degree
    public static const WIDTH:Number = 310 * World.PX2M;
    public static const HEIGHT:Number = 465 * World.PX2M;
    public static const BPM:Number = 120;
}

class Track {
    public static const BULLET:int = 0;
    public static const BULLET_HIT:int = 10;
    public static const ENEMY_TOPLEFT:int = 1;
    public static const ENEMY_TOPRIGHT:int = 2;
    public static const ENEMY_BOTTOMLEFT:int = 3;
    public static const ENEMY_BOTTOMRIGHT:int = 4;
}

class PlayerEvent extends Event {
    public static const PLAYER_DESTROYED:String = "playerDestroyed";
    public static const ENEMY_DESTROYED:String = "enemyDestroyed";
    
    public function PlayerEvent(type:String) {
        super(type, false, false);
    }
}

class WallFactory {
    public static const SIZE:Number = 20 * World.PX2M;
    public static const TOP:int = 1;
    public static const LEFT:int = 2;
    public static const BOTTOM:int = 4;
    public static const RIGHT:int = 8;
    public static const OTHER:int = 16;
    
    protected var qb2d:QuickBox2D;
    
    public function WallFactory(qb2d:QuickBox2D) {
        this.qb2d = qb2d;
    }
    
    public function shift():Array {
        return getInnerWalls(new Rectangle(0, 0, World.WIDTH, World.HEIGHT), SIZE);
    }
    
    protected function getInnerWalls(bounds:Rectangle, thickness:Number):Array {
        var walls:Array = [];
        var wall:QuickObject;
        // top
        wall = getWall(new Rectangle(bounds.left, bounds.top, bounds.width, thickness), TOP);
        walls.push(wall);
        // left
        wall = getWall(new Rectangle(bounds.left, bounds.top, thickness, bounds.height), LEFT);
        walls.push(wall);
        // bottom
        wall = getWall(new Rectangle(bounds.left, bounds.bottom-thickness, bounds.width, thickness), BOTTOM);
        walls.push(wall);
        // right
        wall = getWall(new Rectangle(bounds.right-thickness, bounds.top, thickness, bounds.height), RIGHT);
        walls.push(wall);
        return walls;
    }
    
    protected function getWall(bounds:Rectangle, type:int=OTHER):QuickObject {
        return qb2d.addBox({
            x: (bounds.left+bounds.right)/2,
            y: (bounds.top+bounds.bottom)/2,
            width: bounds.width,
            height: bounds.height,
            density: 0,
            restitution: 0,
            friction: 0,
            categoryBits: Category.WALL,
            maskBits: Category.ALL,
            fillColor: 0x666666,
            fillAlpha: 1,
            lineColor: 0xffffff,
            lineAlpha: 0,
            lineThickness: 4,
            type: type
        });
    }
}

class PlayerFactory {
    public static const SIZE:Number = 20 * World.PX2M;
    public static const SPEED:Number = 150 * World.PX2M;
    protected var qb2d:QuickBox2D;
    
    public function PlayerFactory(qb2d:QuickBox2D) {
        this.qb2d = qb2d;
    }
    
    public function shift():QuickObject {
        var core:QuickObject = qb2d.addCircle({
            isSleeping: false,
            allowSleep: false,
            x: World.WIDTH/2,
            y: World.HEIGHT*3/4,
            //width: SIZE,
            //height: SIZE,
            radius: SIZE/2,
            density: 1,
            friction: 1,
            restitution: 0,
            linearDamping: 0,
            draggable: true,
            fixedRotation: true,
            categoryBits: Category.PLAYER,
            maskBits: Category.WALL|Category.ENEMY,
            lineColor: 0xFF5555,
            lineThickness: 0,
            lineAlpha: 0,
            fillColor: 0xffffff,
            fillAlpha: 1,
            health: 5,
            score: 0
        });
        return core;
    }
}

class BulletFactory {
    public static const SIZE:Number = PlayerFactory.SIZE/5;
    public static const SPEED:Number = PlayerFactory.SPEED*2;
    public static const BEATS:int = 1;
    
    protected var qb2d:QuickBox2D;
    
    public function BulletFactory(qb2d:QuickBox2D) {
        this.qb2d = qb2d;
    }
    
    public function shift(x:Number, y:Number, spread:Number):Array {
        var bullets:Array = [];
        var angle:Number = -90*World.DEG2RAD// + (Math.random()*spread*2-spread);
        var speed:Number = SPEED;
        bullets.push(getBullet(x, y, speed, angle));
        for (var i:Number=5*World.DEG2RAD; i<spread; i+=5*World.DEG2RAD) {
            bullets.push(getBullet(x, y, speed, angle-i));
            bullets.push(getBullet(x, y, speed, angle+i));
        }
        return bullets;
    }
    
    protected function getBullet(x:Number, y:Number, speed:Number, angle:Number):QuickObject {
        var velocity:b2Vec2 = new b2Vec2(Math.cos(angle)*speed, Math.sin(angle)*speed);
        var bullet:QuickObject = qb2d.addBox({
            x: x,
            y: y,
            angle: angle,
            width: SIZE*3,
            height: SIZE,
            density: 0.00001,
            isBullet: true,
            categoryBits: Category.BULLET,
            maskBits: Category.ENEMY|Category.WALL,
            fillColor: 0xAA0000,
            lineAlpha: 0,
            damage: 1,
            track: Track.BULLET
        });
        bullet.body.SetLinearVelocity(velocity);
        return bullet;
    }
}

class EnemyFactory {
    public static const SIZE:Number = PlayerFactory.SIZE;
    public static const SPEED:Number = PlayerFactory.SPEED*0.75;
    public static const BEATS:int = 1;
    
    protected var qb2d:QuickBox2D;
    protected var queue:Array;
    
    public function EnemyFactory(qb2d:QuickBox2D) {
        this.qb2d = qb2d;
        queue = [];
    }
    
    public function shift(totalBeats:int = 0):Array {
        if (queue.length==0) {
            pushEmpty();
            pushRandom(totalBeats);
        }
        var enemies:Array = queue.shift();
        if (enemies != null) {
            for each (var enemy:QuickObject in enemies) {
                enemy.body.WakeUp();
                var filter:b2FilterData = enemy.shape.GetFilterData();
                filter.maskBits = Category.PLAYER|Category.WALL|Category.BULLET;
                enemy.shape.SetFilterData(filter);
            }
        }
        return enemies;
    }
    
    protected function pushEmpty():void {
        var count:int = Math.floor(Math.random()*10+1);
        for (var i:int=0; i<count; i++) {
            queue.push([]);
        }
    }
    
    protected function pushRandom(totalBeats:int):void {
        var speed:Number = SPEED;
        var accel:Number = (Math.random() < 0.5) ? 0 : speed*1.5;
        var velAngle:Number = 90*Math.floor(Math.random()*3+1)/3;
        velAngle *= World.DEG2RAD; // straight down or towards bottom right
        var accelAngle:Number = (Math.random() < 0.5) ? 0 + Math.floor(2)/3*45 : 180 - Math.floor(2)/3*45;
        accelAngle *= World.DEG2RAD; // left or right
        var x:Number = Math.floor(Math.random()*4)/3*(World.WIDTH/2-WallFactory.SIZE-SIZE/2) + WallFactory.SIZE+SIZE/2;
        var y:Number = SIZE/2;
        
        const totalPatterns:int = qb2d.totalTimeSteps*qb2d.timeStep / 10;
        var pattern:int = Math.random()*totalPatterns;
        var count:int = Math.floor(Math.random()*5+1)+5;
        for (var i:int=0; i<count; i++) {
            var enemies:Array = [];
            var topLeft:QuickObject = getStationary(x, y);
            topLeft.params.track = Track.ENEMY_TOPLEFT;
            setVelocity(topLeft, speed, velAngle);
            setAcceleration(topLeft, accel, accelAngle);
            setTrack(topLeft, Track.ENEMY_TOPLEFT);
            var topRight:QuickObject = getReflection(topLeft, new Point(World.WIDTH/2, topLeft.y), 90*World.DEG2RAD);
            setTrack(topRight, Track.ENEMY_TOPRIGHT);
            var bottomLeft:QuickObject = getReflection(topLeft, new Point(topLeft.x, World.HEIGHT/2), 0);
            setTrack(bottomLeft, Track.ENEMY_BOTTOMLEFT);
            var bottomRight:QuickObject = getReflection(topRight, new Point(topRight.x, World.HEIGHT/2), 0)
            setTrack(bottomRight, Track.ENEMY_BOTTOMRIGHT);
            
            if (pattern == 0) {
                // top
                enemies.push(topLeft);
                enemies.push(topRight);
                bottomLeft.destroy();
                bottomRight.destroy();
            } else if (pattern == 1) {
                // left
                enemies.push(topLeft);
                enemies.push(bottomLeft);
                topRight.destroy();
                bottomRight.destroy();
            } else if (pattern == 2) {
                // right
                enemies.push(topRight);
                enemies.push(bottomRight);
                topLeft.destroy();
                bottomLeft.destroy();
            } else if (pattern == 3) {
                // bottom
                enemies.push(bottomLeft);
                enemies.push(bottomRight);
                topLeft.destroy();
                topRight.destroy();
            } else if (pattern == 4) {
                // topLeft, bottomRight
                enemies.push(topLeft);
                enemies.push(bottomRight);
                topRight.destroy();
                bottomLeft.destroy();
            } else if (pattern == 5) {
                // topRight, bottomLeft
                enemies.push(topRight);
                enemies.push(bottomLeft);
                topLeft.destroy();
                bottomRight.destroy();
            } else {
                enemies.push(topLeft);
                enemies.push(topRight);
                enemies.push(bottomLeft);
                enemies.push(bottomRight);
            }
            queue.push(enemies);
        }
    }
    
    protected function setVelocity(enemy:QuickObject, speed:Number, angle:Number):void {
        var velocity:b2Vec2 = new b2Vec2(Math.cos(angle)*speed, Math.sin(angle)*speed);
        enemy.angle = angle;
        enemy.body.SetLinearVelocity(velocity);
    }
    
    protected function setAcceleration(enemy:QuickObject, accel:Number, angle:Number):void {
        enemy.params.force = new b2Vec2(enemy.body.GetMass()*accel);
    }
    
    protected function setTrack(enemy:QuickObject, track:int):void {
        enemy.params.track = track;
    }
    
    protected function getStationary(x:Number, y:Number):QuickObject {
        var params:Object = getDefaultParams();
        params.x = x;
        params.y = y;
        var enemy:QuickObject = qb2d.addCircle(params);
        return enemy;
    }
    
    protected function getReflection(enemy:QuickObject, offset:Point, angle:Number):QuickObject {
        var reflectOrigin:Matrix = new Matrix(
            Math.cos(2*angle), Math.sin(2*angle),
            Math.sin(2*angle), -Math.cos(2*angle)
        );
        var reflector:Matrix = new Matrix();
        reflector.translate(-offset.x, -offset.y);
        reflector.concat(reflectOrigin);
        reflector.translate(offset.x, offset.y);
        
        var location:Point = reflector.transformPoint(new Point(enemy.x, enemy.y));
        var oldVelocity:b2Vec2 = enemy.body.GetLinearVelocity();
        var velocity:Point = reflectOrigin.transformPoint(new Point(oldVelocity.x, oldVelocity.y));
        var oldForce:b2Vec2 = enemy.params.force;
        var force:Point = (oldForce == null) ? null : reflectOrigin.transformPoint(new Point(oldForce.x, oldForce.y));
        
        var params:Object = shallowCopy(enemy.params);
        params.x = location.x;
        params.y = location.y;
        params.force = (force == null) ? undefined : new b2Vec2(force.x, force.y);
        var reflection:QuickObject = qb2d.addCircle(params);
        reflection.body.SetLinearVelocity(new b2Vec2(velocity.x, velocity.y));
        return reflection;
    }
    
    protected function getDefaultParams():Object {
        return {
            isSleeping: true,
            radius: SIZE/2,
            density: 1,
            restitution: 1,
            draggable: false,
            categoryBits: Category.ENEMY,
            maskBits: Category.NONE,
            lineColor: Math.random()*0xaaaaaa+0x555555,
            lineThickness: 4,
            fillAlpha: 0,
            health: 1,
            damage: 1
        }
    }
    
    protected function shallowCopy(params:Object):Object {
        var copy:Object = {};
        for (var i:String in params) {
            copy[i] = params[i];
        }
        return copy;
    }
}

class ExplosionFactory {
    protected var flash:BitmapData;
    
    public function ExplosionFactory():void {
        flash = new BitmapData(128, 128, true, 0);
        for (var n:Number=0; n<6.283185307179586; n+=0.004363323129985824) {
            var r:Number = (1-Math.random()*Math.random()) * 63;
            var mat:Matrix = new Matrix();
            mat.createGradientBox(128-r-r, 128-r-r, 0, r, r);
            var shp:Shape = new Shape();
            shp.graphics.clear();
            shp.graphics.lineStyle(1);
            shp.graphics.lineGradientStyle(GradientType.RADIAL, [0x808080,0x808080], [1,0], [0,255], mat);
            shp.graphics.moveTo(63.5, 63.5);
            shp.graphics.lineTo(Math.sin(n)*90+63.5, Math.cos(n)*90+63.5);
            flash.draw(shp, null, null, "add");
        }
    }
    
    public function shift(contact:b2ContactPoint, color:int):DisplayObject {
        var bitmap:Bitmap = new Bitmap(flash);
        var sprite:Sprite = new Sprite();
        sprite.addChild(bitmap);
        bitmap.x = bitmap.y = -64;
        sprite.width = sprite.height = 80 * (1+Math.random()*0.2-0.1);
        sprite.rotation = Math.random()*360;
        sprite.x = contact.position.x*World.M2PX;
        sprite.y = contact.position.y*World.M2PX;
        sprite.alpha = 0;
        TweenMax.to(sprite, 0, {tint:color});
        TweenMax.to(sprite, 0.2, {alpha:0.5});
        TweenMax.to(sprite, 0.2, {alpha:0, overwrite:false, delay:0.2, onComplete:function():void {
            if (sprite.parent != null) sprite.parent.removeChild(sprite);
        }});
        return sprite;
    }
}

class PhysicalWorld extends MovieClip {
    protected var player:QuickObject;
    protected var walls:Dictionary;
    protected var bullets:Dictionary;
    protected var enemies:Dictionary;
    protected var enemyFactory:EnemyFactory;
    protected var bulletFactory:BulletFactory;
    protected var explosionFactory:ExplosionFactory;
    
    protected var qb2d:QuickBox2D;
    protected var contacts:QuickContacts;
    protected var keysDown:Dictionary;
    protected var background:Bitmap;
    protected var clip:MovieClip = new MovieClip();
    
    public function PhysicalWorld() {
        addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
    }
    
    public function get health():int {
        return player.params.health;
    }
    
    public function get score():int {
        return player.params.score;
    }
    
    public function get totalTimeSteps():int {
        return qb2d.totalTimeSteps;
    }
    
    public function get timeStep():Number {
        return qb2d.timeStep;
    }
    
    public function set timeStep(value:Number):void {
        qb2d.timeStep = value;
    }
    
    public function startWorld():void {
        qb2d.start();
    }
    
    public function stopWorld():void {
        qb2d.stop();
    }
    
    public function createPlayer():void {
        destroyPlayer();
        player = new PlayerFactory(qb2d).shift();
        qb2d.setMouse(player.x*World.M2PX, player.y*World.M2PX);
        qb2d.createMouse(null);
    }
    
    public function destroyPlayer():void {
        if (player == null) return;
        player.destroy();
        player = null;
    }
    
    public function addEnemies():void {
        var enemy:QuickObject;
        var newEnemies:Array;
        newEnemies = enemyFactory.shift();
        for each (enemy in newEnemies) {
            enemies[enemy.body] = enemy;
        }
        onEnemiesAdded(newEnemies);
    }
    
    public function addBullets():void {
        var spread:Number = (player.body.GetLinearVelocity().Length()*World.M2PX > 1) ? player.body.GetLinearVelocity().Length()*World.M2PX/30*World.DEG2RAD : 0;
        var newBullets:Array = bulletFactory.shift(player.x, player.y, spread);
        for each (var bullet:QuickObject in newBullets) {
            bullets[bullet.body] = bullet;
        }
        onBulletsAdded(newBullets);
    }
    
    protected function onAddedToStage(event:Event):void {
        removeEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
        
        qb2d = new QuickBox2D(clip, {
            debug: false,
            gravityX: 0,
            gravityY: 0,
            frim: true,
            timeStep: 1/40,
            iterations: 20,
            bounds: [0, 0, World.WIDTH, World.HEIGHT]
        });
        qb2d.addEventListener(QuickBox2D.STEP, onStep);
        qb2d.addEventListener(Event.RENDER, onRender);
        contacts = qb2d.addContactListener();
        contacts.addEventListener(QuickContacts.ADD, onCollide);
        
        walls = new Dictionary();
        bullets = new Dictionary();
        enemies = new Dictionary();
        
        explosionFactory = new ExplosionFactory();
        bulletFactory = new BulletFactory(qb2d);
        enemyFactory = new EnemyFactory(qb2d);
        for each (var wall:QuickObject in new WallFactory(qb2d).shift()) {
            walls[wall.body] = wall;
        }
        
        keysDown = new Dictionary();
        
        background = new Bitmap(new BitmapData(465, 460, false, 0));
        addChild(background);
        addChild(clip);
        
        stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
        stage.addEventListener(KeyboardEvent.KEY_UP, onKeyUp);
    }
    
    protected function onRender(event:Event):void {
        background.x = 0;
        background.y = 0;
        var bitmap:BitmapData = background.bitmapData;
        bitmap.scroll(0, 5);
        bitmap.applyFilter(bitmap, bitmap.rect, new Point(0, 0), new BlurFilter(4, 4, 4));
        bitmap.fillRect(new Rectangle(0, 0, 465, 5), 0);
        /*for each (var enemy:QuickObject in enemies) {
            bitmap.draw(enemy.userData, new Matrix(1, 0, 0, 1, enemy.x*World.M2PX, enemy.y*World.M2PX));
        }*/
        if (player != null) {
            //bitmap.draw(player.userData, new Matrix(1, 0, 0, 1, player.x*World.M2PX, player.y*World.M2PX));
        }
        background.bitmapData = bitmap;
    }
    
    protected function onKeyDown(event:KeyboardEvent):void {
        keysDown[event.keyCode] = true;
    }
    
    protected function onKeyUp(event:KeyboardEvent):void {
        keysDown[event.keyCode] = false;
    }
    
    protected function onMouseMove(event:MouseEvent):void {
        var force:b2Vec2 = new b2Vec2(event.localX*World.PX2M-player.x, event.localY*World.PX2M-player.y);
        //force.Multiply(player.body.GetMass());
        //player.body.ApplyForce(force, player.body.GetWorldCenter());
    }
    
    protected function onCollide(event:Event):void {
        var enemy:QuickObject = find(enemies, contacts.currentPoint);
        var bullet:QuickObject = find(bullets, contacts.currentPoint);
        var wall:QuickObject = find(walls, contacts.currentPoint);
        var player:QuickObject = (player != null) ? ((contacts.inCurrentContact(player)) ? player : null) : null;
        
        if (enemy != null) {
            if (player != null) {
                onEnemyContactPlayer(enemy, player)
            }
            else if (bullet != null) {
                onEnemyContactBullet(enemy, bullet);
            }
            else if (wall != null) {
                onEnemyContactWall(enemy, wall);
            }
        }
        else if (bullet != null) {
            if (wall != null) {
                onBulletContactWall(bullet, wall);
            }
        }
    }
    
    protected function onStep(event:Event):void {
        if (player != null) {
            if (qb2d.totalTimeSteps%Math.floor(1/qb2d.timeStep/3) == 0) {
                addBullets();
                if (keysDown[Keyboard.SPACE] === false) delete keysDown[Keyboard.SPACE];
            }
            var velocity:b2Vec2 = new b2Vec2();
            if (keysDown[Keyboard.LEFT]) {
                velocity.x = -PlayerFactory.SPEED;
            }
            if (keysDown[Keyboard.RIGHT]) {
                velocity.x = PlayerFactory.SPEED;
            }
            if (keysDown[Keyboard.UP]) {
                velocity.y = -PlayerFactory.SPEED;
            }
            if (keysDown[Keyboard.DOWN]) {
                velocity.y = PlayerFactory.SPEED;
            }
            player.body.SetLinearVelocity(velocity);
        }
        
        if (qb2d.totalTimeSteps%Math.floor(1/qb2d.timeStep/3) == 0) {
            addEnemies();
        }
        for each (var enemy:QuickObject in enemies) {
            if (enemy.params['force'] is b2Vec2) {
                var force:b2Vec2 = enemy.params['force'];
                enemy.body.ApplyForce(force, enemy.body.GetWorldCenter());
            }
        }
    }
    
    protected function onPlayerDestroyed(player:QuickObject):void {
        dispatchEvent(new PlayerEvent(PlayerEvent.PLAYER_DESTROYED));
    }
    
    protected function onEnemyDestroyed(enemy:QuickObject):void {
        this.addChild(explosionFactory.shift(contacts.currentPoint, enemy.params.lineColor));
        dispatchEvent(new PlayerEvent(PlayerEvent.ENEMY_DESTROYED));
    }
    
    protected function onEnemyContactPlayer(enemy:QuickObject, player:QuickObject):void {
        player.params['health'] -= enemy.params['damage'];
        if (player.params['health'] <= 0) {
            destroyPlayer();
            onPlayerDestroyed(player);
        }
        destroy(enemies, enemy);
    }
    
    protected function onEnemyContactBullet(enemy:QuickObject, bullet:QuickObject):void {
        enemy.params['health'] -= bullet.params['damage'];
        if (enemy.params['health'] <= 0) {
            destroy(enemies, enemy);
            onEnemyDestroyed(enemy);
        }
        destroy(bullets, bullet);
    }
    
    protected function onEnemyContactWall(enemy:QuickObject, wall:QuickObject):void {
        this.addChild(explosionFactory.shift(contacts.currentPoint, enemy.params.lineColor));
        if (wall.params.type == WallFactory.BOTTOM || wall.params.type == WallFactory.TOP) {
            destroy(enemies, enemy);
        }
    }
    
    protected function onEnemiesAdded(enemies:Array):void {
    }
    
    protected function onBulletContactWall(bullet:QuickObject, wall:QuickObject):void {
        destroy(bullets, bullet);
    }
    
    protected function onBulletsAdded(bullet:Array):void {
    }
    
    private function find(store:Dictionary, contact:b2ContactPoint):QuickObject {
        var body1:b2Body = contact.shape1.GetBody();
        var body2:b2Body = contact.shape2.GetBody();
        return (store[body1] !== undefined) ? store[body1] : store[body2];
    }
    
    private function destroy(store:Dictionary, object:QuickObject):void {
        delete store[object.body];
        object.destroy();
    }
}

class MusicalWorld extends PhysicalWorld {
    private var driver:SiONDriver;
    private var voice:SiONVoice;
    private var scale:Scale;
    private var drum:DrumMachine;
    private var voices:SiONPresetVoice; 
    
    public function get bpm():Number {
        return driver.bpm;
    }
    
    public function set bpm(value:Number):void {
        driver.bpm = value;
    }
    
    override public function startWorld():void {
        super.startWorld();
        
        driver.play(null, true);
        driver.fadeIn(1);
        drum.play();
    }
    
    override public function stopWorld():void {
        driver.fadeOut(1);
    }
    
    override protected function onAddedToStage(event:Event):void {
        super.onAddedToStage(event);
        
        driver = (SiONDriver.mutex == null) ? new SiONDriver() : SiONDriver.mutex;
        driver.bpm = 120;
        driver.noteOnExceptionMode = SiONDriver.NEM_SHIFT;
      
        voices = new SiONPresetVoice();
        scale = new Scale('Amp');
        
        // DrumMachine
        //----------------------------------------
        // The DrumMachine is a 3 tracks sequencer plays drum patterns specifyed by independent number for each bass, snare and hihat.
        // DrumMachine は,バス,スネア,ハイハットそれぞれに独立した番号でドラムパターンを指定するを3声シーケンサです.
        drum = new DrumMachine();
        drum.bassPatternNumber = 4;   // bass drum pattern number
        drum.snarePatternNumber = 8;  // snare drum pattern number
        drum.hihatPatternNumber = 4;  // hihat drum pattern number
        drum.changePatternOnNextSegment = true;  // don't change pattern immediately after changing ****PatternNumber property
        drum.volume = 0.8;
        drum.quantize = 1;
        // In this sample, we use default voices of DrumMachine. You can also apply any voices for each track.
        // このサンプルでは,DrumMachine のデフォルトボイスを使用しています.各トラックに独自のボイスを割り当てることも出来ます.
        // All DrumMachine's default voices are 1 operator voice, so you can play very light-weighted rhythm track.
        // DrumMachine のデフォルトボイスは全て1オペレータで合成するため,非常に軽快にリズムトラックを再生できます.
        drum.bassVoiceNumber = 0;   // bass drum voice number
        drum.snareVoiceNumber = 2;  // snare drum voice number
        drum.hihatVoiceNumber = 0;  // hihat drum voice number
    }
    
    override protected function onStep(event:Event):void {
        super.onStep(event);
        
        var seconds:int = 1/timeStep*10;
        if (qb2d.totalTimeSteps%(10/qb2d.timeStep) == 0) {
            drum.bassPatternNumber = Math.random()*drum.bassPatternNumberMax;
            drum.snarePatternNumber = Math.random()*drum.snarePatternNumberMax;
            drum.hihatPatternNumber = Math.random()*drum.hihatPatternNumberMax;
            //driver.bpm += 10;
            //qb2d.timeStep = 3/driver.bpm;
        }
    }
    
    override protected function onPlayerDestroyed(player:QuickObject):void {
        super.onPlayerDestroyed(player);
        
        driver.fadeOut(1);
    }
    
    override protected function onEnemyDestroyed(enemy:QuickObject):void {
        super.onEnemyDestroyed(enemy);
        
        if (player != null) player.params.score += driver.bpm;
    }
    
    override protected function onEnemyContactWall(enemy:QuickObject, wall:QuickObject):void {
        super.onEnemyContactWall(enemy, wall);
        
        if (driver.isPlaying == false) return;
        var track:SiMMLTrack = driver.noteOn(scale.getNote(getScaleIndex(enemy)), voices['midi.lead8'], 1, 0, 1, Track.BULLET_HIT);
        track.pan = 128*enemy.x/World.WIDTH - 64;
        track.velocity = 36 * enemy.body.GetLinearVelocity().Length()/EnemyFactory.SPEED;
    }
    
    override protected function onEnemiesAdded(enemies:Array):void {
        super.onEnemiesAdded(enemies);
    }
    
    override protected function onBulletsAdded(bullets:Array):void {
        super.onBulletsAdded(bullets);
        
        if (driver.isPlaying == false) return;
        var track:SiMMLTrack = driver.noteOn(scale.getNote(-10), voices['midi.lead5'], 1, 0, 1, Track.BULLET);
        track.pan = 128*bullets[int(bullets.length/2)].x/(World.WIDTH) - 64;
        track.velocity = 48;
    }
    
    override protected function onEnemyContactBullet(enemy:QuickObject, bullet:QuickObject):void {
        super.onEnemyContactBullet(enemy, bullet);
        
        if (driver.isPlaying == false) return;
        var track:SiMMLTrack = driver.noteOn(scale.getNote(getScaleIndex(enemy)), voices['midi.lead7'], 1, 0, 1, Track.BULLET_HIT);
        track.pan = 128*enemy.x/World.WIDTH - 64;
        track.velocity = 36 * enemy.body.GetLinearVelocity().Length()/EnemyFactory.SPEED;
    }
    
    protected function getScaleIndex(enemy:QuickObject):int {
        var r:int = (enemy.params.lineColor&0xff0000) >> 16;
        var g:int = (enemy.params.lineColor&0x00ff00) >> 8;
        var b:int = (enemy.params.lineColor&0x0000ff);
        var scaleIndex:int = enemy.params.lineColor * 5/0xffffff;
        if (r > g && r > b) {
            if (g > b) scaleIndex = 0;
            else scaleIndex = 1;
        } else if (g > r && g > b) {
            if (r > b) scaleIndex = 2;
            else scaleIndex = 3;
        } else {
            if (r > g) scaleIndex = 4;
            else scaleIndex = 5;
        }
        return scaleIndex;
    }
}

function sprintf(raw : String, ...rest) : String{
    /**
     * Pretty ugly!
     *   basicaly
     *   % -> the start of a substitution hole
     *   (some_var_name) -> [optional] used in named substitutions
     *   .xx -> [optional] the precision with witch numbers will be formated  
     *   x -> the formatter (string, hexa, float, date part)
     */
    var SUBS_RE : RegExp = /%(?!^%)(\((?P<var_name>[\w]+[\w_\d]+)\))?(?P<padding>[0-9]{1,2})?(\.(?P<precision>[0-9]+))?(?P<formater>[sxofaAbBcdHIjmMpSUwWxXyYZ])/ig;
    
    //Return empty string if raw is null, we don't want errors here
    if( raw == null ){
        return "";    
    }
    //trace("\n\n" + 'input:"'+ raw+  '" , args:', rest.join(", ")) ;
    var matches : Array = [];
    var result : Object = SUBS_RE.exec(raw);
    var match : Match;
    var runs : int = 0;
    var numMatches : int = 0;
    var numberVariables : int = rest.length;
    // quick check if we find string subs amongst the text to match (something like %(foo)s
    var isPositionalSubts : Boolean = !Boolean(raw.match(/%\(\s*[\w\d_]+\s*\)/));
    var replacementValue : *;
    var formater : String;
    var varName : String;
    var precision : String;
    var padding : String;
    var paddingNum : int;
    var paddingChar:String;
    
    // matched through the string, creating Match objects for easier later reuse
    while (Boolean(result)){
        match = new Match();
        match.startIndex = result.index;
        match.length = String(result[0]).length;
        match.endIndex = match.startIndex + match.length;
        match.content = String(result[0]);
        // try to get substitution properties
        formater = result.formater;
        varName = result.var_name;
        precision = result.precision;
        padding = result.padding;
        //trace('formater:', formater, ', varName:', varName, ', precision:', precision, 'padding:', padding);
        if (padding){
            if (padding.length == 1){
                paddingNum = int(padding);
                paddingChar = " ";
            }else{
                paddingNum = int (padding.substr(-1, 1));
                paddingChar = padding.substr(-2, 1)
                if (paddingChar != "0"){
                    paddingNum *= int(paddingChar);
                    paddingChar = " "
                }
            } 
        }
        if (isPositionalSubts){
            // by position, grab next subs:
            replacementValue = rest[matches.length];
            
        }else{
            // be hash / properties 
            replacementValue = rest[0] == null ? undefined : rest[0][varName];
        }
        // check for bad variable names
        if (replacementValue == undefined){
            replacementValue = "";
        }
        if (replacementValue != undefined){
            
            // format the string accodingly to the formatter
            if (formater == STRING_FORMATTER){
                match.replacement = padString(replacementValue.toString(), paddingNum, paddingChar);
            }else if (formater == FLOAT_FORMATER){
                // floats, check if we need to truncate precision
                if (precision){
                    match.replacement = padString( 
                        Number(replacementValue).toFixed( int(precision)),
                        paddingNum, 
                        paddingChar);
                }else{
                    match.replacement = padString(replacementValue.toString(), paddingNum, paddingChar);
                }
            }else if (formater == INTEGER_FORMATER){
                match.replacement = padString(int(replacementValue).toString(), paddingNum, paddingChar);
            }else if (formater == OCTAL_FORMATER){
                match.replacement = "0" + int(replacementValue).toString(8);
            }else if (formater == HEXA_FORMATER){
                match.replacement = "0x" + int(replacementValue).toString(16);
            }else if(DATES_FORMATERS.indexOf(formater) > -1){
                switch (formater){
                    case DATE_DAY_FORMATTER:
                        match.replacement = replacementValue.date;
                        break
                    case DATE_FULLYEAR_FORMATTER:
                        match.replacement = replacementValue.fullYear;
                        break
                    case DATE_YEAR_FORMATTER:
                        match.replacement = replacementValue.fullYear.toString().substr(2,2);
                        break
                    case DATE_MONTH_FORMATTER:
                        match.replacement = replacementValue.month + 1;
                        break
                    case DATE_HOUR24_FORMATTER:
                        match.replacement = replacementValue.hours;
                        break
                    case DATE_HOUR_FORMATTER:
                        var hours24 : Number = replacementValue.hours;
                        match.replacement =  (hours24 -12).toString();
                        break
                    case DATE_HOUR_AMPM_FORMATTER:
                        match.replacement =  (replacementValue.hours  >= 12 ? "p.m" : "a.m");
                        break
                    case DATE_TOLOCALE_FORMATTER:
                        match.replacement = replacementValue.toLocaleString();
                        break
                    case DATE_MINUTES_FORMATTER:
                        match.replacement = replacementValue.minutes;
                        break
                    case DATE_SECONDS_FORMATTER:
                        match.replacement = replacementValue.seconds;
                        break    
                }
            }else{
                //no good replacement
            }
            matches.push(match);
        }
        
        // just a small check in case we get stuck: kludge!
        runs ++;
        if (runs > 10000){
            //something is wrong, breaking out
            break;
        }
        numMatches ++;
        // iterates next match
        result = SUBS_RE.exec(raw);
    }
    // in case there's nothing to substitute, just return the initial string
    if(matches.length == 0){
        //no matches, returning raw
        return raw;
    }
    // now actually do the substitution, keeping a buffer to be joined at 
    //the end for better performance
    var buffer : Array = [];
    var lastMatch : Match;  
    // beggininf os string, if it doesn't start with a substitition
    var previous : String = raw.substr(0, matches[0].startIndex);
    var subs : String;
    for each(match in matches){
        // finds out the previous string part and the next substitition
        if (lastMatch){
            previous = raw.substring(lastMatch.endIndex  ,  match.startIndex);
        }
        buffer.push(previous);
        buffer.push(match.replacement);
        lastMatch = match;
        
    }
    // buffer the tail of the string: text after the last substitution
    buffer.push(raw.substr(match.endIndex, raw.length - match.endIndex));
    //trace('returning: "'+ buffer.join("")+'"');
    return buffer.join("");
}

// internal usage
/** @private */
const BAD_VARIABLE_NUMBER : String = "The number of variables to be replaced and template holes don't match";
/** Converts to a string*/
const STRING_FORMATTER : String = "s";
/** Outputs as a Number, can use the precision specifier: %.2sf will output a float with 2 decimal digits.*/
const FLOAT_FORMATER : String = "f";
/** Outputs as an Integer.*/
const INTEGER_FORMATER : String = "d";
/** Converts to an OCTAL number */
const OCTAL_FORMATER : String = "o";
/** Converts to a Hexa number (includes 0x) */
const HEXA_FORMATER : String = "x";
/** @private */
const DATES_FORMATERS : String = "aAbBcDHIjmMpSUwWxXyYZ";
/** Day of month, from 0 to 30 on <code>Date</code> objects.*/
const DATE_DAY_FORMATTER : String = "D";
/** Full year, e.g. 2007 on <code>Date</code> objects.*/
const DATE_FULLYEAR_FORMATTER : String = "Y";
/** Year, e.g. 07 on <code>Date</code> objects.*/
const DATE_YEAR_FORMATTER : String = "y";
/** Month from 1 to 12 on <code>Date</code> objects.*/
const DATE_MONTH_FORMATTER : String = "m";
/** Hours (0-23) on <code>Date</code> objects.*/
const DATE_HOUR24_FORMATTER : String = "H";
/** Hours 0-12 on <code>Date</code> objects.*/
const DATE_HOUR_FORMATTER : String = "I";
/** a.m or p.m on <code>Date</code> objects.*/
const DATE_HOUR_AMPM_FORMATTER : String = "p";
/** Minutes on <code>Date</code> objects.*/
const DATE_MINUTES_FORMATTER : String = "M";
/** Seconds on <code>Date</code> objects.*/
const DATE_SECONDS_FORMATTER : String = "S";
/** A string rep of a <code>Date</code> object on the current locale.*/
const DATE_TOLOCALE_FORMATTER : String = "c";

var version : String = "$Id$";

/** @private
 * Internal class that normalizes matching information.
 */
class Match{
    public var startIndex : int;
    public var endIndex : int;
    public var length : int;
    public var content : String;
    public var replacement : String;
    public var before : String;
    public function toString() : String{
        return "Match [" + startIndex + " - " + endIndex + "] (" + length + ") " + content + ", replacement:" +replacement + ";"
    }
}

/** @private */
function padString(str:String, paddingNum:int, paddingChar:String=" "):String
{
    if(paddingChar == null) return str;
    
    var i:int;
    var buf:Array = [];
    for (i = 0; i < Math.abs(paddingNum) - str.length; i++)
        buf.push(paddingChar);
    
    if (paddingNum < 0){
        buf.unshift(str);
    }
    else{
        buf.push(str);
    }    
    return buf.join("");
}