flash on 2011-6-18
bpm by difficulty: 80-easy, 120-medium, 180-hard
stats:
scoreEasy
scoreMedium
scoreHard
timeEasy
timeMedium
timeHard
/*
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("");
}