The Children of DiscoDroid (Not competition entry)
The further development of Children of DiscoDroid.
This is not the LD23 competition entry
Full version:
http://www.springworldgames.com/cod
Timelapse video (making of):
http://www.youtube.com/watch?v=u0040HCQCQc
Playthrough video:
http://www.youtube.com/watch?v=1CVwv3KeApU
/**
* Copyright krasse ( http://wonderfl.net/user/krasse )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/tMnw
*/
package {
import flash.display.Sprite;
public class ChildrenOfDiscoDroid extends Sprite {
public var game:Game;
public function ChildrenOfDiscoDroid():void {
game = new Game();
addChild(game);
game.init();
}
}
}
import flash.geom.Matrix3D;
import flash.text.TextFieldAutoSize;
import flash.events.TimerEvent;
import flash.utils.Timer;
import flash.geom.Vector3D;
import flash.geom.Rectangle;
import flash.ui.Keyboard;
import flash.events.MouseEvent;
import flash.utils.setTimeout;
import flash.text.TextFormat;
import flash.text.TextField;
import flash.display.*;
import flash.display.BitmapDataChannel;
import flash.display.BitmapData;
import flash.events.KeyboardEvent;
import flupie.textanim.*;
import net.wonderfl.utils.FontLoader;
import caurina.transitions.*;
import org.si.sion.events.*;
import org.si.sound.*;
import org.si.sound.patterns.*;
import org.si.sion.*;
import org.si.sion.sequencer.*;
import org.si.sound.synthesizers.*;
import org.si.sion.utils.*;
import flash.events.Event;
import flash.display.Sprite;
import flash.display.Stage;
import away3d.cameras.*;
import away3d.lights.*;
import away3d.containers.*;
import away3d.primitives.*;
import away3d.core.base.*;
import away3d.core.draw.*;
import away3d.core.render.*;
import away3d.materials.*;
// Main Modes
var SHOWING_INTRO:int = 0;
var PLAYING:int = 1;
// Intro modes
// Play modes
var LEVEL_INTRO:int = 0;
var PLAYING_LEVEL:int = 1;
class PlayerData {
public var healthLevel:int = 0;
public var batteryLevel:int = 0;
public var speedLevel:int = 0;
public var fightLevel:int = 0;
public var musicRadiusLevel:int = 0;
public var musicStrengthLevel:int = 0;
}
class Game extends Sprite {
public var playerData:PlayerData = new PlayerData();
public var sionPresets:SiONPresetVoice;
public var mode:int = PLAYING;
public var introMode:int = 0;
public var playMode:int = LEVEL_INTRO;
public var counter:int = 0;
public var counter2:int = 0;
public var counter3:int = 0;
public var sionDriver:SiONDriver;
public var mainCamera:Camera3D;
public var view:View3D;
public var scene:Scene3D;
public var keysDown:Object = {};
public var fontAvailable:Object = {};
public var musicSequencer:MusicSequencer = null;
public var musicProvider:GameSequenceDataProvider = null;
public var worldParticles:Array = [];
public var screenParticles:Array = [];
public var movingObjects:Array = [];
public var solidObjects:Array = [];
public var globalActions:Array = [];
public var actionQueues:Object = {};
public var currentLevel:Level;
public var currentLevelIndex:int = 0;
public var switchToLevel:int;
public var shouldSwitchLevel:Boolean;
public var fontLoadedCount:int = 0;
public var sfxPlayer:SfxPlayer = new SfxPlayer();
public var interfaceContainer:DisplayObjectContainer;
public var backgroundContainer:DisplayObjectContainer;
public var losing:Boolean = false;
public var winning:Boolean = false;
public function Game() {
}
public function createScene():void {
winning = false;
losing = false;
musicSequencer.stop();
mainCamera = new Camera3D();
mainCamera.zoom = 4;
mainCamera.focus = 200;
if (backgroundContainer) {
removeChild(backgroundContainer);
}
backgroundContainer = new Sprite();
addChild(backgroundContainer);
scene = new Scene3D();
if (view) {
removeChild(view);
}
view = new View3D({camera:mainCamera, x:250, y:200, scene: scene});
// view.renderer = new QuadrantRenderer();
addChild(view);
if (interfaceContainer) {
removeChild(interfaceContainer);
}
interfaceContainer = new Sprite();
addChild(interfaceContainer);
}
public function playerWon():void {
if (!losing) {
winning = true;
sfxPlayer.playVictory(this);
var action:SerialAction = new SerialAction();
var textAction:TextEffectAction = new TextEffectAction();
textAction.y = 20;
textAction.sizeMultiplier = 2;
textAction.fontName = "Bebas";
var switchAction:SwitchLevelAction;
if (currentLevelIndex == 5) {
textAction.strings = ["You defeated them All!", "FANTASTIC!"];
textAction.actionDuration = 180;
switchAction = new SwitchLevelAction(0);
action.actions.push(textAction);
action.actions.push(switchAction);
currentLevel.globalActions.push(action);
} else {
textAction.strings = ["You won!", "GREAT!"];
switchAction = new SwitchLevelAction(currentLevelIndex + 1);
action.actions.push(textAction);
action.actions.push(switchAction);
currentLevel.globalActions.push(action);
}
}
}
public function playerDied():void {
if (!winning) {
losing = true;
sfxPlayer.playLoss(this);
var action:SerialAction = new SerialAction();
var textAction:TextEffectAction = new TextEffectAction();
textAction.y = 20;
textAction.sizeMultiplier = 2;
textAction.strings = ["Sorry, You Died..."];
textAction.fontName = "Bebas";
var restartAction:RestartLevelAction = new RestartLevelAction();
action.actions.push(textAction);
action.actions.push(restartAction);
currentLevel.globalActions.push(action);
}
}
public function getGroundZ(x:Number, y:Number):Number {
return currentLevel.groundMesh.getGroundZ(x, y);
}
public function restartLevel():void {
switchLevel(currentLevelIndex);
}
public function reset():void {
playerData = new PlayerData();
}
public function allRemoved(arr:Array):void {
for (var i:int = arr.length-1; i>= 0; i--) {
var o:GameObject = arr[i];
o.removed(this);
}
}
public function tickAll(arr:Array):void {
for (var i:int = arr.length-1; i>= 0; i--) {
var o:GameObject = arr[i];
o.tick(this);
if (o.removeMe) {
o.removed(this);
arr.splice(i, 1);
}
}
}
public var forts:Array = [
[
[1, 1, 0, 0, 0, 1, 1],
[1, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 2, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 1],
[1, 1, 1, 1, 1, 1, 1]
],
[
[1, 1, 1, 0, 0, 1, 1],
[1, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 1, 1, 0, 1],
[0, 0, 0, 1, 0, 0, 1],
[0, 0, 0, 1, 3, 0, 1],
[1, 0, 0, 1, 0, 0, 1],
[1, 1, 1, 1, 1, 1, 1]
],
[
[1, 1, 1, 1, 0, 1, 1, 1, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 2, 0, 1],
[1, 0, 1, 1, 1, 1, 0, 0, 1],
[1, 0, 1, 0, 0, 1, 0, 0, 1],
[1, 0, 1, 4, 0, 1, 0, 0, 1],
[1, 0, 1, 0, 0, 1, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 1, 0, 0, 0, 0, 0, 1, 0],
[0, 0, 1, 1, 1, 1, 1, 0, 0]
],
[
[1, 1, 1, 1, 0, 0, 1, 1, 1, 1],
[1, 0, 0, 1, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 2, 0, 0, 0],
[1, 1, 0, 1, 0, 0, 0, 0, 0, 0],
[0, 1, 2, 1, 0, 0, 0, 3, 0, 1],
[0, 1, 0, 1, 0, 0, 0, 0, 0, 1],
[0, 1, 1, 1, 1, 1, 1, 0, 0, 1],
[1, 1, 0, 0, 0, 0, 1, 0, 0, 1],
[1, 0, 0, 5, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
],
[
[0, 1, 1, 1, 0, 0, 1, 1, 1, 0],
[1, 0, 0, 1, 0, 0, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 3, 0, 0, 0],
[1, 1, 0, 1, 0, 0, 0, 0, 0, 0],
[0, 1, 2, 1, 0, 0, 0, 4, 0, 1],
[0, 1, 0, 1, 0, 0, 0, 0, 0, 1],
[0, 1, 1, 1, 1, 1, 1, 0, 0, 1],
[1, 1, 0, 0, 0, 0, 1, 0, 0, 1],
[1, 0, 0, 5, 0, 6, 0, 0, 0, 1],
[1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
[0, 1, 1, 1, 1, 1, 1, 1, 1, 0]
]
];
public function addFort(x:Number, y:Number, fortIndex:int, level:Level, spawner:ObstacleSpawner):void {
var sketch:Array = forts[fortIndex];
var wallSize:Number = 20;
var wallHeight:Number = 40;
var fortRect:Rectangle = new Rectangle(x, y, wallSize * sketch[0].length, wallSize * sketch.length);
var noPlaceRect:Rectangle = fortRect.clone();
noPlaceRect.inflate(wallSize * 2, wallSize * 2);
spawner.noPlaceRectangles.push(noPlaceRect);
var fortCenterX:Number = fortRect.x + fortRect.width * 0.5;
var fortCenterY:Number = fortRect.y + fortRect.height * 0.5;
var modifier:CircularConstantHeightModifier = new CircularConstantHeightModifier();
modifier.innerRadius = Math.sqrt(2.0) * Math.max(fortRect.width / 2, fortRect.height / 2) + wallSize;
modifier.outerRadius = modifier.innerRadius + 50;
modifier.centerX = fortCenterX;
modifier.centerY = fortCenterY;
modifier.targetHeight = -20;
level.groundMesh.heightModifiers.push(modifier);
for (var j:int = 0; j<sketch.length; j++) {
var arr:Array = sketch[j];
for (var i:int = 0; i<arr.length; i++) {
var item:int = arr[i];
var wallX:Number = i * wallSize + x;
var wallY:Number = j * wallSize + y;
var npcType:int = -1;
switch (item) {
case 0:
break;
case 1:
var wall:Wall = new Wall();
wall.position.x = wallX;
wall.position.y = wallY;
wall.size = wallSize;
wall.height = wallHeight;
level.solidObjects.push(wall);
break;
case 2:
npcType = BOSS_1;
break;
case 3:
npcType = BOSS_2;
break;
case 4:
npcType = BOSS_3;
break;
case 5:
npcType = BOSS_4;
break;
case 6:
npcType = BOSS_5;
break;
}
if (npcType != -1) {
var boss:NPC = new NPC();
boss.completeRemoveDistance = 99999999;
boss.npcType = npcType;
boss.position.x = wallX;
boss.position.y = wallY;
boss.modeWhenAlone = WAIT_MODE;
boss.mode = WAIT_MODE;
level.movingObjects.push(boss);
level.bosses.push(boss);
}
}
}
}
public function addToActionQueue(queues:Object, name:String, action:GameAction):void {
var queue:ActionQueue = queues[name];
if (!queue) {
queue = new ActionQueue();
queues[name] = queue;
}
queue.arr.push(action);
}
// index 0 is the intro scene level
public function loadLevel(index:int):void {
var level:Level = new Level();
var backgroundColor:uint = 0x000000;
var i:int = 0;
var rnd:Rndm = null;
if (index == 0) {
// Intro scene
var bmd:BitmapData = new BitmapData(stage.stageWidth, stage.stageHeight, true, 0xff000000);
rnd = new Rndm(3423);
for (i=0; i<100; i++) {
bmd.setPixel32(rnd.integer(0, stage.stageWidth), rnd.integer(0, stage.stageHeight), 0xffffffff);
}
var bitmap:Bitmap = new Bitmap();
bitmap.bitmapData = bmd;
backgroundContainer.addChild(bitmap);
var robot:Humanoid = new Humanoid();
robot.bodyColor = 0x336633;
robot.legColor = 0x888888;
robot.armColor = robot.legColor;
robot.headColor = 0xbbbbbb;
robot.dancingSpeedFactor = 0.01;
robot.animateDancing = true;
robot.sizeFactor = 2.0;
robot.position.x = 0;
robot.position.y = 0;
robot.position.z = 0;
level.cameraController = new SlowApproachCameraController(this, mainCamera, robot);
level.movingObjects.push(robot);
var velZ:Number = 0.2;
var planetDatas:Array = [
// x, y, z, rotVel, velZ, radius, color
[40, 200, 0, 0.1, velZ, 80, 0x442222],
[-140, 300, 200, 0.1, velZ, 30, 0x222222]
];
for (i = 0; i<planetDatas.length; i++) {
var data:Array = planetDatas[i];
var planet:Planet = new Planet();
planet.position.x = data[0];
planet.position.y = data[1];
planet.position.z = data[2];
planet.rotVel = data[3];
planet.velocity.z = data[4];
planet.radius = data[5];
planet.surfaceColor = data[6];
level.movingObjects.push(planet);
}
var textEffect:TextEffectAction = new TextEffectAction();
textEffect.fontName = "Aqua";
textEffect.y = 10;
textEffect.sizeMultiplier = 1.8;
textEffect.strings = ["Children of DiscoDroid"];
textEffect.actionDuration = 99999999;
// addToActionQueue(level.actionQueues, "global", textEffect);
level.globalActions.push(textEffect);
var textArr:Array = ["In the year 2043,",
"the people of Earth finally got tired of all dancing robots",
" ",
"They decided to launch all of them into space",
"On one such robot, called DiscoDroid, ",
"life has emerged on its metal body...",
" ",
"You must now help one of those lifeforms to survive",
"Evil creatures have invaded from the dark side of the robot...",
" ",
"Press any key to start..."];
for (i = 0; i<textArr.length; i++) {
textEffect = new TextEffectAction();
textEffect.fontName = "Bebas";
textEffect.y = 220 + i * 22;
textEffect.sizeMultiplier = 0.7;
textEffect.strings = [textArr[i]];
if (i == textArr.length - 1) {
textEffect.delay = 0;
textEffect.fontName = "Aqua";
} else {
textEffect.delay = 80 * i;
}
textEffect.actionDuration = 99999999;
// addToActionQueue(level.actionQueues, "global", textEffect);
level.globalActions.push(textEffect);
}
var action:SwitchLevelWhenKeyPressedAction = new SwitchLevelWhenKeyPressedAction(1);
action.actionDuration = 99999999;
level.globalActions.push(action);
musicProvider.song = INTRO_SONG;
musicSequencer.play();
} else {
var infoDuration:int = 120;
var textAction:TextEffectAction = new TextEffectAction();
textAction.actionDuration = infoDuration;
textAction.y = 20;
textAction.sizeMultiplier = 2;
textAction.strings = ["Level " + index];
textAction.fontName = "Bebas";
level.globalActions.push(textAction);
if (index == 1) {
var infoAction:TextEffectAction = new TextEffectAction();
infoAction.actionDuration = 9999999999;
infoAction.y = stage.stageHeight - 100;
infoAction.sizeMultiplier = 1;
infoAction.strings = ["Dance with 'A' (or 'D')", "Make dancers fight with 'S' (or 'F')"];
infoAction.fontName = "Bebas";
level.globalActions.push(infoAction);
}
musicProvider.song = DANCE_SONG;
backgroundColor = 0x333333;
level.groundMesh = new GroundMesh();
level.player = new Player();
level.player.position = new Vector3D(level.startX, level.startY, 0);
level.cameraController = new InGameCameraController(this, mainCamera);
rnd = new Rndm(3425);
var spawner:ObstacleSpawner = new ObstacleSpawner();
level.objectSpawners.push(spawner);
var groundMeshSeed:int = 3423523;
var spawnerSeed:int = 342342;
var spawnerEnemies:Array = [MONSTER_1, MONSTER_2];
var spawnerEnemyLikelihoods:Array = [2, 1];
switch (index) {
case 1:
addFort(0, 500, 0, level, spawner);
groundMeshSeed = 21352356;
spawnerSeed = 2838923;
break;
case 2:
addFort(-500, 0, 1, level, spawner);
groundMeshSeed = 214523;
spawnerSeed = 2834982;
spawnerEnemyLikelihoods = [2, 2, 1];
spawnerEnemies = [MONSTER_1, MONSTER_2, MONSTER_3];
break;
case 3:
addFort(-500, 1000, 2, level, spawner);
groundMeshSeed = 21423;
spawnerSeed = 283482;
spawnerEnemyLikelihoods = [1, 2, 2, 2, 1];
spawnerEnemies = [MONSTER_1, MONSTER_2, MONSTER_3, MONSTER_4, MONSTER_5];
break;
case 4:
addFort(-500, -500, 3, level, spawner);
groundMeshSeed = 2314523;
spawnerSeed = 283495682;
spawnerEnemyLikelihoods = [1, 1, 2, 3, 2, 2, 1];
spawnerEnemies = [MONSTER_1, MONSTER_2, MONSTER_3, MONSTER_4, MONSTER_5, MONSTER_6, MONSTER_7];
break;
case 5:
addFort(1000, 200, 4, level, spawner);
groundMeshSeed = 2214523;
spawnerSeed = 28349825;
spawnerEnemyLikelihoods = [1, 1, 2, 3, 3, 2, 1, 1];
spawnerEnemies = [MONSTER_1, MONSTER_2, MONSTER_3, MONSTER_4, MONSTER_5, MONSTER_6, MONSTER_7, MONSTER_8];
break;
}
spawner.enemyTypes = spawnerEnemies;
spawner.enemyTypeLikelihoods = spawnerEnemyLikelihoods;
spawner.seed = spawnerSeed;
level.groundMesh.seed = groundMeshSeed;
}
graphics.beginFill(backgroundColor);
graphics.drawRect(0, 0, stage.stageWidth, stage.stageHeight);
graphics.endFill();
currentLevel = level;
currentLevelIndex = index;
}
public function loadAFont(fl:FontLoader, fontName:String):void {
fl.load( fontName );
fl.addEventListener( Event.COMPLETE, function(ev :Event) :void {
fontLoadedCount++;
fontAvailable[fontName] = true;
});
}
public function init():void {
inittrace(this);
// var fontsToLoad = ["Aqua","Azuki","Cinecaption","Mona","Sazanami","YSHandy","VLGothic","IPAGP","IPAM","UmeUgo","UmePms","Bebas"];
var fontsToLoad:Array = ["Aqua","Bebas"];
for (var i:int = 0; i<fontsToLoad.length; i++) {
var fl:FontLoader = new FontLoader();
var fontName:String = fontsToLoad[i];
loadAFont(fl, fontName);
}
sionDriver = new SiONDriver();
sionDriver.play();
sionPresets = new SiONPresetVoice();
musicProvider = new GameSequenceDataProvider(Math.round(Math.random() * 99999999 + 134));
musicSequencer = new MusicSequencer(sionDriver, musicProvider);
createScene();
stage.addEventListener(Event.ENTER_FRAME, onEnterFrame);
stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
stage.addEventListener(KeyboardEvent.KEY_UP, onKeyUp);
stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseClicked);
stage.addEventListener(Event.RESIZE, onResize);
loadLevel(0);
}
public function switchLevel(newLevel:int):void {
switchToLevel = newLevel;
shouldSwitchLevel = true;
}
public function tick():void {
if (shouldSwitchLevel) {
if (currentLevel) {
createScene();
}
loadLevel(switchToLevel);
shouldSwitchLevel = false;
}
try {
tickAll(solidObjects);
tickAll(movingObjects);
tickAll(worldParticles);
tickAll(screenParticles);
for (var key:String in actionQueues) {
actionQueues[key].tick(this);
}
tickAll(globalActions);
} catch (error:Error) {
trace("Error global game tick " + error.name + " " + error.message);
}
try {
if (currentLevel) {
currentLevel.tick(this);
}
} catch (error:Error) {
trace("Error level tick " + error.name + " " + error.message);
}
musicSequencer.tick();
counter++;
}
public function onResize(e:Event):void {
}
public var tickTime:int = 0;
public var wantedTimeInterval:Number = 1000.0 / 25.0;
public function onEnterFrame(e:Event):void {
var currentTime:int = flash.utils.getTimer();
var ticks:int = 0;
while (tickTime < currentTime) {
tick();
tickTime += wantedTimeInterval;
ticks++;
if (ticks > 4) {
tickTime = currentTime;
break;
}
}
if (ticks > 0) {
view.render();
}
}
public function keyDown(key:int):Boolean {
return keysDown[key];
}
public function onMouseClicked(event:MouseEvent):void {
// trace("Mouse clicked " + stage.mouseX + ", " + stage.mouseY);
}
public function onKeyDown(event:KeyboardEvent):void {
keysDown[event.keyCode] = true;
}
public function onKeyUp(event:KeyboardEvent):void {
keysDown[event.keyCode] = false;
}
}
class ActionQueue {
public var action:GameAction;
public var arr:Vector.<GameAction> = new Vector.<GameAction>();
public function tick(game:Game):void {
if (!action) {
if (arr.length > 0) {
action = arr.shift();
}
} else {
action.tick(game);
if (action.removeMe) {
action.removed(game);
action = null;
}
}
}
}
class RectangularConstantHeightModifier implements HeightModifier {
public var innerRectangle:Rectangle = new Rectangle(0, 0, 100, 100);
public var outerBorder:Number = 20;
public var targetHeight:Number = -50;
private var outerRectangle:Rectangle = null;
public function applicable(x:Number, y:Number):Boolean {
if (!outerRectangle) {
outerRectangle = new Rectangle(innerRectangle.x - outerBorder, innerRectangle.y - outerBorder, innerRectangle.width + outerBorder * 2, innerRectangle.height + outerBorder * 2);
// trace("creating outer rectangle " + outerRectangle);
}
// if (Math.random() < 0.01) {
// trace("Checking applicable " + x + ", " + y + " rect: " + outerRectangle);
// }
return outerRectangle.contains(x, y);
}
public function applyModifier(x:Number, y:Number, z:Number):Number {
// trace("applying height modifier " + x + " " + y);
if (innerRectangle.contains(x, y)) {
// trace("returning height " + targetHeight);
return targetHeight;
} else {
// var fractionX:Number = Math.max(0, (outerBorder - Math.min(Math.abs(innerRectangle.x - x), Math.abs(innerRectangle.x + innerRectangle.width - x))) / outerBorder);
// var fractionY:Number = Math.max(0, (outerBorder - Math.min(Math.abs(innerRectangle.y - y), Math.abs(innerRectangle.y + innerRectangle.height - y))) / outerBorder);
return z;
}
}
}
class CircularConstantHeightModifier implements HeightModifier {
public var centerX:Number = 0;
public var centerY:Number = 0;
public var innerRadius:Number = 100;
public var outerRadius:Number = 150;
public var targetHeight:Number = -50;
public function applicable(x:Number, y:Number):Boolean {
// if (Math.random() < 0.01) {
// trace("Checking applicable " + x + ", " + y + " rect: " + outerRectangle);
// }
return distanceBetween(x, y, centerX, centerY) < outerRadius;
}
public function applyModifier(x:Number, y:Number, z:Number):Number {
// trace("applying height modifier " + x + " " + y);
var dist:Number = distanceBetween(x, y, centerX, centerY);
var fraction:Number = (dist - innerRadius) / (outerRadius - innerRadius);
if (fraction <= 0) {
return targetHeight;
} else if (fraction <= 1) {
return fraction * z + (1.0 - fraction) * targetHeight;
} else {
return z;
}
}
}
class GameObject {
public var team:int = 0;
public var removeMe:Boolean = false;
public var initialized:Boolean = false;
public var counter:uint = 0;
public function initIfNecessary(game:Game):void {
if (!initialized) {
init(game);
initialized = true;
}
}
public function init(game:Game):void {
}
public function tick(game:Game):void {
initIfNecessary(game);
counter++;
}
public function removed(game:Game):void {
// Remove yourself from the scene etc.
}
}
class CameraController extends GameObject {
public var game:Game;
public var camera:Camera3D;
public function CameraController(game:Game, camera:Camera3D):void {
this.game = game;
this.camera = camera;
}
override public function tick(game:Game):void {
super.tick(game);
}
}
class InGameCameraController extends CameraController {
public function InGameCameraController(game:Game, camera:Camera3D):void {
super(game, camera);
}
public var targetPosition:Vector3D = null;
override public function tick(game:Game):void {
super.tick(game);
var level:Level = game.currentLevel;
if (level && level.player) {
var player:Player = level.player;
var playerDirection:Vector3D = player.direction;
var playerPosition:Vector3D = player.position;
var wantedPosition:Vector3D = playerPosition.clone();
var directionIncrement:Number = 50;
wantedPosition.incrementBy(new Vector3D(player.direction.x * directionIncrement, player.direction.y * directionIncrement, 0));
if (targetPosition == null) {
targetPosition = wantedPosition.clone();
}
var smoothFactor:Number = 0.9;
targetPosition.x = smoothFactor * targetPosition.x + (1.0 - smoothFactor) * wantedPosition.x;
targetPosition.y = smoothFactor * targetPosition.y + (1.0 - smoothFactor) * wantedPosition.y;
var factor:Number = 2;
camera.zoom = 4;
camera.x = targetPosition.x;
camera.y = targetPosition.y - 70 * factor;
camera.z = targetPosition.z - 70 * factor;
// Do some cross product magic to get a nice up.
// We have the forward vector and need a vector that is perpendicular to forward and z-axis
// Alas: forward x z-axis gives the right vector. The result is then forward x right
camera.lookAt(new Vector3D(targetPosition.x, targetPosition.y + 40, targetPosition.z), new Vector3D(0, 1, -1));
}
}
}
class SlowApproachCameraController extends CameraController {
public var obj:PhysicalObject;
public function SlowApproachCameraController(game:Game, camera:Camera3D, obj:PhysicalObject):void {
super(game, camera);
this.obj = obj;
}
override public function tick(game:Game):void {
super.tick(game);
if (obj.counter > 1) {
var p:Vector3D = obj.position;
camera.x = p.x;
camera.y = p.y - 30;
camera.z = p.z - 30;
// Do some cross product magic to get a nice up.
// We have the forward vector and need a vector that is perpendicular to forward and z-axis
// Alas: forward x z-axis gives the right vector. The result is then forward x right
camera.lookAt(new Vector3D(p.x, p.y, p.z), new Vector3D(0, 1, -1));
// obj.container.rotationZ = counter * 0.5;
}
// trace("camera y " + camera.y);
}
}
class GameAction extends GameObject {
public var actionCounter:int = 0;
public var actionDuration:int = 60;
override public function tick(game:Game):void {
super.tick(game);
actionCounter++;
if (actionCounter > actionDuration) {
removeMe = true;
}
}
}
class SerialAction extends GameAction {
public var actions:Vector.<GameAction> = new Vector.<GameAction>();
public var actionIndex:int = 0;
override public function removed(game:Game):void {
for (var i:int = 0; i<actions.length; i++) {
actions[i].removed(game);
}
}
override public function tick(game:Game):void {
// Remove me when the last action is done...
if (actionIndex < actions.length) {
var action:GameAction = actions[actionIndex];
if (action.removeMe) {
actionIndex++;
} else {
action.tick(game);
}
} else {
removeMe = true;
}
}
}
var CENTER_SCREEN_MESSAGE:int = 0;
var SCREEN_INFO:int = 1;
var WORLD_INFO:int = 2;
class TextEffectAction extends GameAction {
public var x:Number = 0;
public var y:Number = 0;
public var sizeMultiplier:Number = 1.0;
public var extraYStepLength:Number = 10;
public var extraXStepLength:Number = 0;
public var strings:Array = [];
public var delay:int;
public var addedStuff:Array = [];
public var toAdd:Array = [];
public var theType:int = CENTER_SCREEN_MESSAGE;
public var initializedTF:Boolean = false;
public var fontName:String = "Aqua";
override public function init(game:Game):void {
super.init(game);
initTF(game);
}
public function initTF(game:Game):void {
if (game.fontAvailable[fontName]) {
switch (theType) {
case CENTER_SCREEN_MESSAGE:
var currentY:Number = y;
for (var i:int=0; i<strings.length; i++) {
var tf:TextField = new TextField();
tf.embedFonts = true;
tf.defaultTextFormat = new TextFormat( fontName, 20 * sizeMultiplier, 0xFFCC00);
tf.autoSize = TextFieldAutoSize.CENTER;
tf.text = strings[i];
tf.x = 0.5 * (game.stage.stageWidth - tf.textWidth);
tf.y = currentY;
tf.textColor = 0xffffff;
toAdd.push(tf);
currentY += tf.textHeight + extraYStepLength;
}
break;
default:
trace("Unknown text effect type " + theType);
break;
}
initializedTF = true;
}
}
override public function removed(game:Game):void {
for (var i:int=0; i<addedStuff.length; i++) {
var tf:TextField = addedStuff[i];
game.interfaceContainer.removeChild(tf);
}
}
override public function tick(game:Game):void {
super.tick(game);
if (! initializedTF) {
initTF(game);
} else {
switch (theType) {
case CENTER_SCREEN_MESSAGE:
if (actionCounter >= delay && toAdd.length > 0) {
for (var i:int = 0; i<toAdd.length; i++) {
game.interfaceContainer.addChild(toAdd[i]);
addedStuff.push(toAdd[i]);
}
toAdd.length = 0;
}
break;
}
}
// trace("Ticking text effect " + game.counter);
}
}
class SwitchLevelWhenKeyPressedAction extends GameAction {
public var levelIndex:int = 0;
public function SwitchLevelWhenKeyPressedAction(levelIndex:int = 0):void {
super();
this.levelIndex = levelIndex;
}
override public function tick(game:Game):void {
super.tick(game);
for (var key:String in game.keysDown) {
if (game.keysDown[key]) {
game.switchLevel(levelIndex);
removeMe = true;
break;
}
}
}
}
class RestartLevelAction extends GameAction {
public function RestartLevelAction():void {
super();
}
override public function tick(game:Game):void {
super.tick(game);
game.restartLevel();
removeMe = true;
}
}
class SwitchLevelAction extends GameAction {
public var levelIndex:int = 1;
public function SwitchLevelAction(levelIndex:int):void {
super();
this.levelIndex = levelIndex;
}
override public function tick(game:Game):void {
super.tick(game);
game.switchLevel(levelIndex);
removeMe = true;
}
}
class Level extends GameObject {
public var startX:Number = 50;
public var startY:Number = 50;
public var worldParticles:Array = [];
public var screenParticles:Array = [];
public var movingObjects:Array = [];
public var solidObjects:Array = [];
public var projectiles:Array = [];
public var objectSpawners:Array = [];
public var pickups:Array = [];
public var globalActions:Array = [];
public var actionQueues:Object = {};
public var cameraController:CameraController;
public var player:Player;
public var bosses:Array = [];
public var groundMesh:GroundMesh;
override public function removed(game:Game):void {
super.removed(game);
var arr:Vector.<Object3D> = game.scene.children;
for (var i:int=0; i<arr.length; i++) {
game.scene.removeChild(arr[i]);
}
// game.allRemoved(worldParticles);
// game.allRemoved(screenParticles);
game.allRemoved(movingObjects);
game.allRemoved(pickups);
// game.allRemoved(solidObjects);
// game.allRemoved(projectiles);
game.allRemoved(globalActions);
// for (var key:String in actionQueues) {
// game.allRemoved(actionQueues[key]);
// }
// player.removed(game);
// cameraController.removed(game);
// groundMesh.removed(game);
}
override public function tick(game:Game):void {
try {
if (player) {
player.tick(game);
}
} catch (error:Error) {
trace("Error player tick " + error.name + " " + error.message);
}
try {
if (cameraController) {
cameraController.tick(game);
}
} catch (error:Error) {
trace("Error cam control tick " + error.name + " " + error.message);
}
try {
game.tickAll(objectSpawners);
} catch (error:Error) {
trace("Error object spawners tick " + error.name + " " + error.message);
}
try {
game.tickAll(pickups);
} catch (error:Error) {
trace("Error pickups tick " + error.name + " " + error.message);
}
game.tickAll(solidObjects);
try {
game.tickAll(movingObjects);
} catch (error:Error) {
trace("Error moving objects tick " + error.name + " " + error.message);
}
game.tickAll(worldParticles);
game.tickAll(screenParticles);
game.tickAll(projectiles);
for (var key:String in actionQueues) {
actionQueues[key].tick(game);
}
game.tickAll(globalActions);
try {
if (groundMesh) {
groundMesh.tick(game);
}
} catch (error:Error) {
trace("Error mesh tick " + error.name + " " + error.message);
}
}
}
interface ObjectSpawner {
function tick(game:Game):void;
function objectRemoved(obj:PhysicalObject, spawnInfo:SpawnInfo):void;
}
class GridObjectSpawner extends GameObject implements ObjectSpawner {
public var noPlaceRectangles:Vector.<Rectangle> = new Vector.<Rectangle>();
public var spawnInfos:Vector.<SpawnInfo> = new Vector.<SpawnInfo>();
public var seed:int = 343243;
public var gridSize:Number = 300;
public var subGridCells:int = 20; // Totals cells 20 * 20
public var updatePeriod:int = 33;
public var restrictRadius:Number = 10;
public var spawnDistance:Number = 200;
public function getPosition(si:SpawnInfo, rnd:Rndm):Vector3D {
var subGridSize:Number = gridSize / subGridCells;
return new Vector3D(si.gridX * gridSize + si.subGridX * subGridSize + rnd.random() * subGridSize,
si.gridY * gridSize + si.subGridY * subGridSize + rnd.random() * subGridSize, 0);
}
public function findSpawnInfo(gridX:int, gridY:int, subGridX:int, subGridY:int):SpawnInfo {
for (var i:int = 0; i<spawnInfos.length; i++) {
var si:SpawnInfo = spawnInfos[i];
if (si.matchData(gridX, gridY, subGridX, subGridY)) {
return si;
}
}
return null;
}
public function getSpawnCount(gridX:int, gridY:int, game:Game):int {
return 5;
}
override public function tick(game:Game):void {
if ((game.counter % updatePeriod) == 0) {
try {
var distance:Number = spawnDistance;
var pos:Vector3D = game.currentLevel.player.position;
var minGridX:int = Math.floor((pos.x - distance) / gridSize);
var minGridY:int = Math.floor((pos.y - distance) / gridSize);
var maxGridX:int = Math.floor((pos.x + distance) / gridSize);
var maxGridY:int = Math.floor((pos.y + distance) / gridSize);
var subGridSize:Number = gridSize / subGridCells;
for (var gridX:int = minGridX; gridX <= maxGridX; gridX++) {
for (var gridY:int = minGridY; gridY <= maxGridY; gridY++) {
var count:int = getSpawnCount(gridX, gridY, game);
var newSeed:uint = Math.abs(seed + gridX + gridY * seed);
var rnd:Rndm = new Rndm(newSeed);
// trace("grid " + gridX + ", " + gridY + " gives seed " + newSeed + " counter: " + game.counter);
for (var i:int = 0; i<count; i++) {
var subGridX:int = rnd.integer(0, subGridCells);
var subGridY:int = rnd.integer(0, subGridCells);
var theX:Number = gridX * gridSize + subGridX * subGridSize;
var theY:Number = gridY * gridSize + subGridY * subGridSize;
var okToPlace:Boolean = true;
for (var j:int = 0; j<noPlaceRectangles.length; j++) {
if (noPlaceRectangles[j].contains(theX, theY)) {
okToPlace = false;
break;
}
}
if (okToPlace) {
var si:SpawnInfo = findSpawnInfo(gridX, gridY, subGridX, subGridY);
if (si) {
// Object already exists and should not be spawned again
} else {
si = new SpawnInfo(this, gridX, gridY, subGridX, subGridY);
spawnObject(si, game);
// trace("Not find spawninfo " + si + " i:" + i + " counter: " + game.counter);
spawnInfos.push(si);
}
}
}
}
}
} catch (error:Error) {
trace("Error grid spawner tick " + error.name + " " + error.message);
}
}
}
public function spawnObject(spawnInfo:SpawnInfo, game:Game):void {
}
public function objectRemoved(obj:PhysicalObject, spawnInfo:SpawnInfo):void {
// trace("someone calling objectRemoved() " + spawnInfo);
var found:Boolean = false;
for (var i:int = spawnInfos.length-1; i>=0; i--) {
var si:SpawnInfo = spawnInfos[i];
if (si.matchData(spawnInfo.gridX, spawnInfo.gridY, spawnInfo.subGridX, spawnInfo.subGridY)) {
spawnInfos.splice(i, 1);
found = true;
break;
}
}
if (!found) {
trace("Could not find object with spawnInfo " + spawnInfo);
}
// } else {
// trace("Removed object with spawnInfo " + spawnInfo);
// }
}
}
function getProbabilityDistribution(likelihoods:Array):Array {
var result:Array = [];
var length:int = likelihoods.length;
var sum:Number = 0.0;
var i:int = 0;
for (i = 0; i < length; i++) {
sum += likelihoods[i];
}
result[0] = likelihoods[0];
for (i = 1; i < length; i++) {
result[i] = (result[i - 1] + likelihoods[i]);
}
if (sum > 0.000000001) {
for (i = 0; i < length; i++) {
result[i] /= sum;
}
} else {
// Setting all to the same person
var increment:Number = 1.0 / length;
for (i = 0; i < length; i++) {
result[i] = (i+1) * increment;;
}
}
return result;
}
function getProbabilityFractions(likelihoods:Array):Array {
var result:Array = [];
var length:int = likelihoods.length;
var sum:Number = 0.0;
var i:int = 0;
for (i = 0; i < length; i++) {
sum += likelihoods[i];
}
if (sum > 0.000000001) {
for (i = 0; i < length; i++) {
result[i] = likelihoods[i] / sum;
}
} else {
// Setting all to the same person
for (i = 0; i < length; i++) {
result[i] = 1.0 / length;
}
}
// logit("ProbabilityFractions input: " + likelihoods + " result: " + result + "<br />");
return result;
}
function sampleIndexIntegerDistribution(rnd:Rndm, cumulative:Array):int {
var rndValue:Number = rnd.random();
for (var j:int = 0; j < cumulative.length; j++) {
if (rndValue < cumulative[j]) {
return j;
}
}
return 0; // This should never happen
}
class ObstacleSpawner extends GridObjectSpawner {
public static var PINE_TREE:int = 0;
public static var ROUND_TREE:int = 1;
public static var BATTERY:int = 0;
public static var HEALTH:int = 1;
public var enemyLikelihood:Number = 2;
public var pickupLikelihood:Number = 4;
public var obstacleLikelihood:Number = 8;
public var enemyTypes:Array = [MONSTER_1, MONSTER_2];
public var obstacleTypes:Array = [PINE_TREE, ROUND_TREE];
public var pickupTypes:Array = [BATTERY, HEALTH];
public var enemyTypeLikelihoods:Array = [2, 1];
public var obstacleTypeLikelihoods:Array = [1, 1];
public var pickupTypeLikelihoods:Array = [3, 2];
override public function getSpawnCount(gridX:int, gridY:int, game:Game):int {
return 5;
}
override public function spawnObject(spawnInfo:SpawnInfo, game:Game):void {
var level:Level = game.currentLevel;
var theSeed:uint = Math.abs(spawnInfo.gridX + spawnInfo.gridY * 47829 + spawnInfo.subGridX + spawnInfo.subGridY * 424353);
var rnd:Rndm = new Rndm(theSeed + seed);
var rndVal:Number = rnd.random();
var spawnType:int = sampleIndexIntegerDistribution(rnd,
getProbabilityDistribution([enemyLikelihood, pickupLikelihood, obstacleLikelihood]));
switch (spawnType) {
case 0: // Enemy
var npc:NPC = new NPC();
npc.spawnInfo = spawnInfo;
npc.position = spawnInfo.spawner.getPosition(spawnInfo, rnd);
npc.npcType = enemyTypes[sampleIndexIntegerDistribution(rnd,
getProbabilityDistribution(enemyTypeLikelihoods))];
level.movingObjects.push(npc);
// trace("Spawning object " + spawnInfo + " movers: " + level.movingObjects.length);
break;
case 1: // Pickup
var pickupType:int = sampleIndexIntegerDistribution(rnd,
getProbabilityDistribution(pickupTypeLikelihoods));
if (pickupType == 0) {
var batteryPickup:BatteryPickup = new BatteryPickup();
batteryPickup.spawnInfo = spawnInfo;
batteryPickup.position = spawnInfo.spawner.getPosition(spawnInfo, rnd);
level.pickups.push(batteryPickup);
} else if (pickupType == 1) {
var healthPickup:HealthPickup = new HealthPickup();
healthPickup.spawnInfo = spawnInfo;
healthPickup.position = spawnInfo.spawner.getPosition(spawnInfo, rnd);
level.pickups.push(healthPickup);
}
break;
case 2: // Obstacle
var tree:Tree = new Tree();
tree.spawnInfo = spawnInfo;
tree.type = rnd.random() < 0.5 ? 0 : 1;
tree.sizeFactor = 0.6 + 0.8 * rnd.random();
tree.position = spawnInfo.spawner.getPosition(spawnInfo, rnd);
level.solidObjects.push(tree);
break;
}
// trace("Spawning object " + spawnInfo + " solids: " + level.solidObjects.length);
}
}
class SpawnInfo {
public var gridX:int;
public var gridY:int;
public var subGridX:int;
public var subGridY:int
public var spawner:GridObjectSpawner;
public function matchData(gx:int, gy:int, sgx:int, sgy:int):Boolean {
return gx == gridX && gy == gridY && sgx == subGridX && sgy == subGridY;
}
public function toString():String {
return "{" + [gridX, gridY, subGridX, subGridY].join(", ") + "}";
}
public function SpawnInfo(spawner:GridObjectSpawner, gx:int, gy:int, sgx:int, sgy:int) {
this.spawner = spawner;
gridX = gx;
gridY = gy;
subGridX = sgx;
subGridY = sgy;
}
}
class PhysicalObject extends GameObject {
public var spawnInfo:SpawnInfo;
public var respawnWhenRemoved:Boolean = true;
public var position:Vector3D = new Vector3D();
public var direction:Vector3D = new Vector3D(0, 1, 0);
public var dimension:Vector3D = new Vector3D(1, 1, 1); // Sizes
public var container:ObjectContainer3D;
public var containerPartOfScene:Boolean = false;
public var containerCheckPeriod:int = 15;
public function getRectangle():Rectangle {
return new Rectangle(position.x - dimension.x * 0.5, position.y - dimension.y * 0.5, dimension.x, dimension.y);
}
public function getTestRectangle(stepX:Number, stepY:Number):Rectangle {
return new Rectangle(position.x - dimension.x * 0.5 + stepX, position.y - dimension.y * 0.5 + stepY, dimension.x, dimension.y);
}
public function shouldRemoveContainer(game:Game):Boolean {
return false;
}
public function shouldAddContainer(game:Game):Boolean {
return false;
}
public function shouldRemoveCompletely(game:Game):Boolean {
return false;
}
public function containerRemoved(game:Game):void {
}
public function containerAdded(game:Game):void {
}
public function removeCompletely(game:Game):void {
if (spawnInfo && respawnWhenRemoved) {
spawnInfo.spawner.objectRemoved(this, spawnInfo);
}
}
public function updateContainer(game:Game):void {
if (container.x != position.x) {
container.x = position.x;
}
if (container.y != position.y) {
container.y = position.y;
}
if (container.z != position.z) {
container.z = position.z;
}
try {
if (!removeMe && (counter % containerCheckPeriod) == 0) {
if (containerPartOfScene && shouldRemoveContainer(game)) {
try {
game.scene.removeChild(container);
containerRemoved(game);
containerPartOfScene = false;
} catch (error:Error) {
trace("Error should remove container body " + error.name + " " + error.message);
}
} else if (!containerPartOfScene && shouldAddContainer(game)) {
try {
game.scene.addChild(container);
containerAdded(game);
containerPartOfScene = true;
} catch (error:Error) {
trace("Error should add container body " + error.name + " " + error.message);
}
} else if (!containerPartOfScene && shouldRemoveCompletely(game)) {
try {
removeMe = true;
} catch (error:Error) {
trace("Error remove completely call " + error.name + " " + error.message);
}
}
}
} catch (error:Error) {
trace("Error physical object update container part 1" + error.name + " " + error.message);
}
}
public function updateMovement(stepX:Number, stepY:Number, stepZ:Number, game:Game):Array {
if (stepX != 0 || stepY != 0 || stepZ != 0) {
var rect:Rectangle = getTestRectangle(stepX, stepY);
var solids:Array = game.currentLevel.solidObjects;
for (var i:int = 0; i<solids.length; i++) {
var solid:PhysicalObject = solids[i];
var solidRect:Rectangle = solid.getRectangle();
if (rect.intersects(solidRect)) {
// Must set at least one of stepX, stepY to 0
var rect2:Rectangle = getTestRectangle(stepX, 0);
if (rect2.intersects(solidRect)) {
stepX = 0;
}
rect2 = getTestRectangle(0, stepY);
if (rect2.intersects(solidRect)) {
stepY = 0;
}
}
}
position.x += stepX;
position.y += stepY;
position.z += stepZ;
}
if (isNaN(position.x) || isNaN(position.y) || isNaN(position.z)) {
trace("Nan detected");
}
updateContainer(game);
return [stepX, stepY, stepZ];
}
override public function tick(game:Game):void {
super.tick(game);
}
override public function init(game:Game):void {
super.init(game);
container = new ObjectContainer3D();
if (spawnInfo) {
container.name = spawnInfo.toString();
}
}
override public function removed(game:Game):void {
super.removed(game);
if (containerPartOfScene) {
if (spawnInfo) {
game.scene.removeChildByName(container.name);
} else {
game.scene.removeChild(container);
}
containerRemoved(game);
containerPartOfScene = false;
}
removeCompletely(game);
}
}
// Removes and adds their geometry automatically
class SelfManagedPhysicalObject extends PhysicalObject {
public var containerRemoveDistance:Number = 300;
public var containerAddDistance:Number = 300;
public var completeRemoveDistance:Number = 700;
override public function shouldRemoveContainer(game:Game):Boolean {
if (game.currentLevel.player) {
var pos:Vector3D = game.currentLevel.player.position;
return Vector3D.distance(position, pos) > containerRemoveDistance;
} else {
return false;
}
}
override public function shouldAddContainer(game:Game):Boolean {
if (game.currentLevel.player) {
var pos:Vector3D = game.currentLevel.player.position;
return Vector3D.distance(position, pos) < containerAddDistance;
} else {
return false;
}
}
override public function shouldRemoveCompletely(game:Game):Boolean {
if (spawnInfo) {
var pos:Vector3D = game.currentLevel.player.position;
return Vector3D.distance(position, pos) > completeRemoveDistance;
} else {
return false;
}
}
override public function tick(game:Game):void {
super.tick(game);
updateContainer(game);
}
}
class SelfManagedPickup extends SelfManagedPhysicalObject {
public var pickedUp:Boolean = false;
override public function removeCompletely(game:Game):void {
super.removeCompletely(game);
// var arr:Array = game.currentLevel.pickups;
// arr.splice(arr.indexOf(this), 1);
}
public function wasPickedUp(game:Game):Boolean {
return false;
}
override public function tick(game:Game):void {
super.tick(game);
if (!pickedUp && Vector3D.distance(game.currentLevel.player.position, position) < 20) {
var pu:Boolean = wasPickedUp(game);
if (pu) {
pickedUp = true;
if (containerPartOfScene) {
game.scene.removeChild(container);
containerRemoved(game);
containerPartOfScene = false;
}
removeMe = true;
respawnWhenRemoved = false;
}
}
}
}
class BatteryPickup extends SelfManagedPickup {
public var sizeFactor:Number = 1.0;
public var amount:Number = 50;
override public function wasPickedUp(game:Game):Boolean {
var player:Player = game.currentLevel.player;
if (player.batteryLevel < player.maxBatteryLevel) {
game.currentLevel.player.increaseBatteryLevel(amount);
return true;
} else {
return false;
}
}
override public function init(game:Game):void {
super.init(game);
var groundZ:Number = game.getGroundZ(position.x, position.y);
position.z = groundZ;
var stemHeight:Number = 10;
var stem:Cylinder = new Cylinder();
stem.height = stemHeight * sizeFactor;
stem.radius = 3 * sizeFactor;
stem.z = -stem.height * 0.5 - 5;
stem.yUp = false;
stem.segmentsW = 5;
stem.segmentsH = 2;
stem.material = new WireColorMaterial(0x444444);
container.addChild(stem);
dimension = new Vector3D(stem.radius * 2, stem.radius * 2, 10);
}
}
class HealthPickup extends SelfManagedPickup {
public var sizeFactor:Number = 1.0;
public var amount:Number = 0.5;
override public function wasPickedUp(game:Game):Boolean {
var player:Player = game.currentLevel.player;
if (player.health < player.maxHealth) {
game.currentLevel.player.increaseHealth(amount);
return true;
} else {
return false;
}
}
override public function init(game:Game):void {
super.init(game);
var groundZ:Number = game.getGroundZ(position.x, position.y);
position.z = groundZ;
var stemHeight:Number = 10;
var stem:Cylinder = new Cylinder();
stem.height = stemHeight * sizeFactor;
stem.radius = 3 * sizeFactor;
stem.z = -stem.height * 0.5 - 5;
stem.yUp = false;
stem.segmentsW = 5;
stem.segmentsH = 2;
stem.material = new WireColorMaterial(0xff1111);
container.addChild(stem);
dimension = new Vector3D(stem.radius * 2, stem.radius * 2, 10);
}
}
class SelfManagedSolidObject extends SelfManagedPhysicalObject {
override public function removeCompletely(game:Game):void {
super.removeCompletely(game);
// var arr:Array = game.currentLevel.solidObjects;
// arr.splice(arr.indexOf(this), 1);
}
override public function tick(game:Game):void {
super.tick(game);
var rect:Rectangle = getRectangle();
var z:Number = container.z - dimension.z;
var projectiles:Array = game.currentLevel.projectiles;
for (var i:int = 0; i<projectiles.length; i++) {
var proj:Projectile = Projectile(projectiles[i]);
var pos:Vector3D = proj.position;
if (pos.z > z && rect.contains(pos.x, pos.y)) {
proj.hitsSolidObject(game, this);
}
}
}
}
var NORMAL_TREE:int = 0;
var PINE_TREE:int = 1;
class Tree extends SelfManagedSolidObject {
public var type:int = NORMAL_TREE;
public var sizeFactor:Number = 1.0;
override public function init(game:Game):void {
super.init(game);
var groundZ:Number = game.getGroundZ(position.x, position.y);
position.z = groundZ;
var stemHeight:Number = 30;
switch (type) {
case NORMAL_TREE:
break;
case PINE_TREE:
stemHeight = 15;
break;
}
var stem:Cylinder = new Cylinder();
stem.openEnded = true;
stem.height = stemHeight * sizeFactor;
stem.radius = 3 * sizeFactor;
stem.z = -stem.height * 0.5;
stem.yUp = false;
stem.segmentsW = 5;
stem.segmentsH = 2;
stem.material = new WireColorMaterial(0x335500);
container.addChild(stem);
if (type == NORMAL_TREE) {
var upper:Sphere = new Sphere();
upper.material = new WireColorMaterial(0x006700);
upper.radius = 20 * sizeFactor;
upper.yUp = false;
upper.z = -stem.height * 1.5 - upper.radius * 0.8;
container.addChild(upper);
} else if (type == PINE_TREE) {
var cone:Cone = new Cone();
cone.openEnded = true;
cone.material = new WireColorMaterial(0x006700);
cone.radius = 10 * sizeFactor;
cone.height = 35 * sizeFactor;
cone.rotationY = 180;
cone.segmentsW = 5;
cone.segmentsH = 2;
cone.yUp = false;
cone.z = -stem.height * 1.5 - cone.height * 0.5;
container.addChild(cone);
}
dimension = new Vector3D(stem.radius * 2, stem.radius * 2, 35 * sizeFactor);
}
}
class Wall extends SelfManagedSolidObject {
public var size:Number = 30;
public var height:Number = 40;
override public function init(game:Game):void {
super.init(game);
var groundZ:Number = game.getGroundZ(position.x, position.y);
position.z = groundZ;
var cube:Cube = new Cube();
cube.segmentsW = 1;
cube.segmentsD = 1;
cube.segmentsH = 1;
cube.height = size;
cube.width = size;
cube.depth = height;
cube.z = -cube.height * 0.5 - 5;
cube.material = new WireColorMaterial(0x333333);
container.addChild(cube);
dimension = new Vector3D(size, size, height);
}
}
function distanceBetween(x1:Number, y1:Number, x2:Number, y2:Number):Number {
var diffX:Number = x1 - x2;
var diffY:Number = y1 - y2;
return Math.sqrt(diffX * diffX + diffY * diffY);
}
function clampUint(x:Number, lower:Number, upper:Number):uint {
var result:uint = x < lower ? lower : (x > upper ? upper : x);
return result;
}
interface HeightModifier {
function applicable(x:Number, y:Number):Boolean;
function applyModifier(x:Number, y:Number, z:Number):Number;
}
interface ColorModifier {
function applicable(x:Number, y:Number):Boolean;
function applyModifier(x:Number, y:Number, z:Number, colArr:Vector.<Number>):void;
}
class GroundMesh extends PhysicalObject {
public var mesh:Mesh;
public var groundNoise:ClassicalNoise;
public var seed:int = 12345;
public var amplitude:int = 30;
public var frequency:Number = 0.01;
public var metalLevel:Number = -0.4;
// Stored in pairs of faces. Existing faces
public var existingFacePairs:Object = {};
public var heightModifiers:Vector.<HeightModifier> = new Vector.<HeightModifier>();
public var colorModifiers:Vector.<ColorModifier> = new Vector.<ColorModifier>();
public var faceCount:int = 0;
public function GroundMesh():void {
}
public function createNoiseIfNecessary():void {
if (!groundNoise) {
groundNoise = new ClassicalNoise(new Rndm(seed));
}
}
public function getGroundColor(x:Number, y:Number, z:Number):uint {
var height:Number = -z;
var red:Number = 0;
var green:Number = 0;
var blue:Number = 0;
if (height > metalLevel * amplitude) {
green = ((height / amplitude) + 1.0) * 0.5;
} else {
red = 0.5;
green = 0.5;
blue = 0.5;
}
// trace("ground color " + [red, green, blue].join(",") + " " + [x, y, z].join(","));
if (colorModifiers.length > 0) {
var colArr:Vector.<Number> = new Vector.<Number>();
for (var i:int =0; i<colorModifiers.length; i++) {
var cm:ColorModifier = colorModifiers[i];
if (cm.applicable(x, y)) {
cm.applyModifier(x, y, z, colArr);
}
}
red = colArr[0];
green = colArr[1];
blue = colArr[2];
}
var redUint:uint = clampUint(red * 255, 0, 255);
var greenUint:uint = clampUint(green * 255, 0, 255);
var blueUint:uint = clampUint(blue * 255, 0, 255);
return (redUint << 16) | (greenUint << 8) | blueUint;
}
public function getGroundZ(x:Number, y:Number):Number {
var noiseScale:Number = frequency;
var noiseAmp:Number = amplitude;
createNoiseIfNecessary();
var inputX:Number = noiseScale * x;
var inputY:Number = noiseScale * y;
var result:Number = noiseAmp * groundNoise.noise(inputX, inputY, 0);
var height:Number = -result;
if (height < metalLevel * noiseAmp) {
result = -metalLevel * noiseAmp;
}
for (var i:int =0; i<heightModifiers.length; i++) {
var hm:HeightModifier = heightModifiers[i];
if (hm.applicable(x, y)) {
result = hm.applyModifier(x, y, result);
}
}
return result;
}
override public function removed(game:Game):void {
super.removed(game);
}
private function createFaceIfNecessary(stepSize:int, gridX:int, gridY:int):void {
var xMap:Object = existingFacePairs[gridX];
if (!xMap) {
xMap = {};
existingFacePairs[gridX] = xMap;
}
var pair:Array = xMap[gridY];
if (!pair) {
pair = [];
var x:Number = gridX * stepSize;
var y:Number = gridY * stepSize;
var z:Number = getGroundZ(x, y);
var v0:Vertex = new Vertex(x, y, z);
var v1:Vertex = new Vertex(x + stepSize, y, getGroundZ(x + stepSize, y));
var v2:Vertex = new Vertex(x + stepSize, y + stepSize, getGroundZ(x + stepSize, y + stepSize));
var v3:Vertex = new Vertex(x, y + stepSize, getGroundZ(x, y + stepSize));
var f1:Face = new Face(v0, v1, v2);
var f2:Face = new Face(v0, v2, v3);
var groundColor:uint = getGroundColor(x, y, z);
var mat:WireColorMaterial = new WireColorMaterial(groundColor);
f1.material = mat;
f2.material = mat;
mesh.addFace(f1);
mesh.addFace(f2);
faceCount += 2;
pair[0] = f1;
pair[1] = f2;
xMap[gridY] = pair;
}
}
private function removeFacesIfNecessary(stepSize:int, distance:Number, testX:Number, testY:Number):void {
var gridX:Number = Math.floor(testX);
var gridY:Number = Math.floor(testY);
for (var existingGridX:String in existingFacePairs) {
var xMap:Object = existingFacePairs[existingGridX];
var found:Boolean = false;
for (var existingGridY:String in xMap) {
found = true;
var x:Number = parseInt(existingGridX) * stepSize;
var y:Number = parseInt(existingGridY) * stepSize;
if (distance < distanceBetween(x, y, testX, testY)) {
var pair:Array = xMap[existingGridY];
mesh.removeFace(pair[0]);
mesh.removeFace(pair[1]);
faceCount -= 2;
delete xMap[existingGridY];
}
}
if (!found) {
delete existingFacePairs[existingGridX];
}
}
// if (Math.random() < 0.02) {
// trace("Face count: " + faceCount + " vertex count: " + mesh.vertices.length);
// }
}
public function updateMesh(game:Game):void {
if (!mesh) {
mesh = new Mesh();
}
if ((game.counter % 11) == 0) {
if (mesh.vertices.length > faceCount * 5) {
// trace("Removed mesh faceCount: " + faceCount + " vertexCount: " + mesh.vertices.length);
faceCount = 0;
container.removeChild(mesh);
existingFacePairs = {};
mesh = new Mesh();
container.addChild(mesh);
}
var positiveYDistance:Number = 300;
var negativeYDistance:Number = 150;
var xDistance:Number = 180;
// var faceDistanceSq:Number = faceDistance * faceDistance;
var stepSize:int = 35;
var playerPos:Vector3D = game.currentLevel.player.position;
var playerGridMinX:Number = Math.floor((playerPos.x - xDistance) / stepSize);
var playerGridMaxX:Number = Math.ceil((playerPos.x + xDistance) / stepSize);
var playerGridMinY:Number = Math.floor((playerPos.y - negativeYDistance) / stepSize);
var playerGridMaxY:Number = Math.ceil((playerPos.y + positiveYDistance) / stepSize);
var i:int = 0;
for (i = playerGridMinX; i<playerGridMaxX; i++) {
for (var j:int = playerGridMinY; j<playerGridMaxY; j++) {
createFaceIfNecessary(stepSize, i, j);
}
}
removeFacesIfNecessary(stepSize, positiveYDistance * 1.2, playerPos.x, playerPos.y);
}
}
override public function init(game:Game):void {
super.init(game);
updateMesh(game);
container.addChild(mesh);
game.scene.addChild(container);
containerAdded(game);
containerPartOfScene = true;
}
override public function tick(game:Game):void {
super.tick(game);
updateMesh(game);
}
}
class MovingPhysicalObject extends PhysicalObject {
public var velocity:Vector3D = new Vector3D();
public var gravity:Vector3D = new Vector3D(0, 0, 0.05);
public var collidesWithGround:Boolean = true;
public var collidesWithObstacles:Boolean = true;
public var inAir:Boolean = false;
public var airAcceleration:Vector3D = new Vector3D();
public var airResistanceFactor:Number = 0.1;
override public function updateMovement(stepX:Number, stepY:Number, stepZ:Number, game:Game):Array {
try {
if (!inAir) {
stepZ = 0.0;
if (collidesWithGround) {
// The object follows the ground
try {
if (game.currentLevel.groundMesh) {
var futureX:Number = position.x + stepX;
var futureY:Number = position.y + stepY;
var groundZ:Number = game.currentLevel.groundMesh.getGroundZ(futureX, futureY);
position.z = groundZ;
}
} catch (error:Error) {
trace("Error moving update movement mesh stuff " + error.name + " " + error.message);
}
}
} else {
// Flying freely in the air
position.incrementBy(velocity);
velocity.incrementBy(gravity);
// trace("flying movement " + gravity.z + " " + velocity.z);
if (game.currentLevel.groundMesh) {
var gZ:Number = game.currentLevel.groundMesh.getGroundZ(position.x, position.y);
if (position.z > gZ) {
hitsGround(game, gZ);
}
}
}
} catch (error:Error) {
trace("Error moving update movement " + error.name + " " + error.message);
}
return super.updateMovement(stepX, stepY, stepZ, game);
}
public function hitsGround(game:Game, groundZ:Number):void {
}
public function hitsPhysicalObject(game:Game, obj:PhysicalObject):void {
}
override public function tick(game:Game):void {
super.tick(game);
}
}
class Planet extends MovingPhysicalObject {
public var radius:Number = 40;
public var rotVel:Number = 0.1;
public var surfaceColor:uint = 0xffffff;
public function Planet():void {
super();
inAir = true;
gravity = new Vector3D();
}
override public function tick(game:Game):void {
super.tick(game);
container.rotationY += rotVel;
super.updateMovement(0, 0, 0, game);
}
override public function init(game:Game):void {
super.init(game);
var sphere:Sphere = new Sphere();
sphere.radius = radius;
sphere.material = new WireColorMaterial(surfaceColor);
container.addChild(sphere);
game.scene.addChild(container);
containerAdded(game);
containerPartOfScene = true;
}
}
class Particle extends MovingPhysicalObject {
public var particleDuration:int = 60;
public function Particle():void {
super();
inAir = true;
}
override public function tick(game:Game):void {
super.tick(game);
particleDuration--;
if (particleDuration <= 0) {
removeMe = true;
}
super.updateMovement(0, 0, 0, game);
}
}
var FIRE_PARTICLE:int = 0;
var SMOKE_PARTICLE:int = 1;
class CommonParticle extends Particle {
public var type:int = FIRE_PARTICLE;
public function CommonParticle():void {
super();
}
override public function tick(game:Game):void {
super.tick(game);
}
}
class Object3DParticle extends Particle {
public var object:Object3D;
public function Object3DParticle(o:Object3D, vel:Vector3D, grav:Vector3D):void {
super();
this.object = o;
this.velocity = vel;
this.gravity = grav;
position.x = o.x;
position.y = o.y;
position.z = o.z;
}
override public function tick(game:Game):void {
super.tick(game);
object.x = position.x;
object.y = position.y;
object.z = position.z;
// trace("object " + object.z);
}
override public function hitsGround(game:Game, groundZ:Number):void {
super.hitsGround(game, groundZ);
velocity = new Vector3D();
gravity = new Vector3D();
}
}
class Projectile extends MovingPhysicalObject {
public var damage:Number = 0.1;
public var sizeFactor:Number = 1.0;
public var owner:GameObject;
public function Projectile(owner:GameObject):void {
this.owner = owner;
inAir = true;
}
override public function tick(game:Game):void {
try {
super.tick(game);
// if (Math.random() < 0.01) {
// trace("proj position.z: " + position.z);
// }
} catch (error:Error) {
trace("Error projectile tick " + error.name + " " + error.message);
}
try {
super.updateMovement(0, 0, 0, game);
} catch (error:Error) {
trace("Error projectile update movement " + error.name + " " + error.message);
}
}
override public function hitsGround(game:Game, groundZ:Number):void {
super.hitsGround(game, groundZ);
}
public function hitsLivingObject(game:Game, obj:LivingObject):void {
}
public function hitsSolidObject(game:Game, obj:PhysicalObject):void {
}
}
class Axe extends Projectile {
public function Axe(owner:GameObject) {
super(owner);
}
override public function tick(game:Game):void {
super.tick(game);
container.rotationY -= 30;
}
override public function init(game:Game):void {
super.init(game);
var height:Number = 10 * sizeFactor;
var shaft:Cube = new Cube();
shaft.width = height;
shaft.height = height * 0.15;
shaft.depth = height * 0.15;
shaft.material = new WireColorMaterial(0x884422);
container.addChild(shaft);
var blade:Cube = new Cube();
blade.width = height * 0.3;
blade.height = height * 0.1;
blade.depth = height * 0.7;
blade.material = new WireColorMaterial(0x444444);
var pos:Vector3D = new Vector3D(height * 0.5, 0, 0);
blade.position = pos;
container.addChild(blade);
// container.rotationZ =
container.rotationZ = 180 * Math.atan2(direction.y, direction.x) / Math.PI;
updateContainer(game);
game.scene.addChild(container);
containerAdded(game);
containerPartOfScene = true;
}
override public function hitsLivingObject(game:Game, obj:LivingObject):void {
if (owner != obj && !removeMe) {
removeMe = true;
obj.doDamage(game, damage, velocity);
// trace("Axe hit something living...");
game.sfxPlayer.playAxeHitsLiving(game);
}
}
override public function hitsGround(game:Game, groundZ:Number):void {
super.hitsGround(game, groundZ);
removeMe = true;
// trace("Removing axe " + groundZ + " " + position.z);
game.sfxPlayer.playAxeHitsGround(game);
}
override public function hitsSolidObject(game:Game, obj:PhysicalObject):void {
super.hitsSolidObject(game, obj);
removeMe = true;
// trace("Removing axe " + position.z);
game.sfxPlayer.playAxeHitsGround(game);
}
}
class Stone extends PhysicalObject {
}
class Hole extends PhysicalObject {
override public function tick(game:Game):void {
super.tick(game);
}
}
class Fire extends PhysicalObject {
override public function tick(game:Game):void {
super.tick(game);
}
}
class Goal extends PhysicalObject {
override public function tick(game:Game):void {
super.tick(game);
}
}
class Sign extends PhysicalObject {
public var texts:Array = [];
override public function tick(game:Game):void {
super.tick(game);
}
}
class Pickup extends PhysicalObject {
override public function tick(game:Game):void {
super.tick(game);
}
}
class LivingObject extends MovingPhysicalObject {
public var maxHealth:Number = 1.0;
public var health:Number = 1.0;
public var dead:Boolean = false;
public var dying:Boolean = false;
public var dyingCounter:int = 0;
public var maxDyingCounter:int = 0;
public var projectileHitDimension:Vector3D;
public function doDamage(game:Game, damage:Number, damageDir:Vector3D):void {
if (!dead && !dying) {
health -= damage;
if (health <= 0.0) {
dying = true;
dyingCounter = 30;
maxDyingCounter = dyingCounter;
health = 0.0;
startsDying(game);
}
}
}
public function startsDying(game:Game):void {
}
public function die(game:Game):void {
if (this == game.currentLevel.player) {
game.playerDied();
} else {
// Check if all bosses are dead
var allDead:Boolean = true;
for (var i:int = 0; i<game.currentLevel.bosses.length; i++) {
var boss:NPC = game.currentLevel.bosses[i];
if (!boss.dead) {
allDead = false;
break;
}
}
if (allDead) {
game.playerWon();
}
game.sfxPlayer.playDeath(game);
removeMe = true;
spawnInfo = null;
}
}
public function getProjectileHitRectangle():Rectangle {
return new Rectangle(position.x - projectileHitDimension.x * 0.5, position.y - projectileHitDimension.y * 0.5,
projectileHitDimension.x, projectileHitDimension.y);
}
override public function tick(game:Game):void {
super.tick(game);
var rect:Rectangle = getProjectileHitRectangle();
if (dying) {
dyingCounter--;
// trace("Dying. " + dyingCounter);
if (dyingCounter <= 0) {
dead = true;
dying = false;
die(game);
// trace("dead");
}
} else if (!dead) {
var z:Number = container.z - dimension.z;
var projectiles:Array = game.currentLevel.projectiles;
for (var i:int = 0; i<projectiles.length; i++) {
var proj:Projectile = Projectile(projectiles[i]);
if (proj.owner.team != team) { // Only hit in other teams
var pos:Vector3D = proj.position;
if (pos.z > z && rect.contains(pos.x, pos.y)) {
proj.hitsLivingObject(game, this);
}
}
}
}
}
}
var WALKING:int = 0;
var RUNNING:int = 1;
var DANCING:int = 2;
var STANDING:int = 4;
class Humanoid extends LivingObject {
public var sizeFactor:Number = 1.0;
public var dancingSpeedFactor:Number = 1.0;
public var legColor:uint = 0xff0000;
public var armColor:uint = 0xffffff;
public var bodyColor:uint = 0xffffff;
public var headColor:uint = 0x345623;
public var leftArm:Cylinder;
public var rightArm:Cylinder;
public var leftLeg:Cylinder;
public var rightLeg:Cylinder;
public var head:Sphere;
public var body:Cylinder;
public var animationState:int = STANDING;
public var animateAttacking:Boolean = false;
public var animateDancing:Boolean = false;
public var headZ:Number = 0;
public var hit:Boolean = false;
public var hitCounter:int = 0;
public var maxHitCounter:int = 0;
public var hitDirection:Vector3D;
public var blinking:Boolean = false;
public var blinkCounter:int = 0;
public var blinkMaterial:WireColorMaterial;
public var headMaterial:WireColorMaterial;
public var bodyMaterial:WireColorMaterial;
public var legMaterial:WireColorMaterial;
public var armMaterial:WireColorMaterial;
override public function doDamage(game:Game, damage:Number, damageDir:Vector3D):void {
super.doDamage(game, damage, damageDir);
if (!dead && !dying) {
hit = true;
hitCounter = 3;
maxHitCounter = hitCounter;
hitDirection = damageDir.clone();
hitDirection.normalize();
}
blinking = true;
blinkCounter = 5;
}
public function createHumanoidGeometry(game:Game):void {
var armLength:Number = 8 * sizeFactor;
var armRadius:Number = 1.3 * sizeFactor;
var legLength:Number = 10 * sizeFactor;
var legRadius:Number = 1.5 * sizeFactor;
var torsoLength:Number = 10 * sizeFactor;
var torsoRadius:Number = 4 * sizeFactor;
var headLength:Number = 6 * sizeFactor;
var offset:Number = -5 * sizeFactor;
blinkMaterial = new WireColorMaterial(0xffffff);
blinkMaterial.wireColor = 0xffffff;
body = new Cylinder();
bodyMaterial = new WireColorMaterial(bodyColor);
body.material = bodyMaterial;
body.segmentsW = 5;
body.height = torsoLength;
body.radius = torsoRadius;
body.z = -torsoLength - legLength - offset;
body.yUp = false;
container.addChild(body);
head = new Sphere();
headMaterial = new WireColorMaterial(headColor);
head.material = headMaterial;
head.yUp = false;
head.segmentsH = 5;
head.segmentsW = 5;
head.radius = headLength * 0.5;
head.z = -legLength - torsoLength - armRadius - headLength- offset;
container.addChild(head);
headZ = -legLength - torsoLength - armRadius - headLength - offset - head.radius;
armMaterial = new WireColorMaterial(armColor);
leftArm = new Cylinder();
leftArm.material = armMaterial;
leftArm.yUp = false;
leftArm.segmentsW = 5;
leftArm.height = armLength;
leftArm.radius = armRadius;
leftArm.z = -legLength - torsoLength - armRadius- offset;
leftArm.y = body.radius;
leftArm.rotationX = 45;
container.addChild(leftArm);
rightArm = new Cylinder();
rightArm.material = armMaterial;
rightArm.yUp = true;
rightArm.segmentsW = 5;
rightArm.height = armLength;
rightArm.radius = armRadius;
rightArm.z = -legLength - torsoLength - armRadius- offset;
rightArm.y = -body.radius;
rightArm.rotationX = 45;
container.addChild(rightArm);
legMaterial = new WireColorMaterial(legColor);
leftLeg = new Cylinder();
leftLeg.material = legMaterial;
leftLeg.yUp = false;
leftLeg.segmentsW = 5;
leftLeg.height = legLength;
leftLeg.radius = legRadius;
leftLeg.z = -legLength * 1.3- offset;
leftLeg.y = body.radius * 0.5;
leftLeg.rotationX = 10;
container.addChild(leftLeg);
rightLeg = new Cylinder();
rightLeg.material = legMaterial;
rightLeg.yUp = false;
rightLeg.segmentsW = 5;
rightLeg.height = legLength;
rightLeg.radius = legRadius;
rightLeg.z = -legLength * 1.3- offset;
rightLeg.y = -body.radius * 0.5;
rightLeg.rotationX = -10;
container.addChild(rightLeg);
projectileHitDimension = new Vector3D(torsoRadius * 3, torsoRadius * 3, Math.abs(headZ));
dimension = new Vector3D(torsoRadius * 2, torsoRadius * 2, Math.abs(headZ));
}
override public function init(game:Game):void {
try {
super.init(game);
} catch (error:Error) {
trace("Error humanoid super init " + error.name + " " + error.message);
}
// Adding the body of the player
try {
createHumanoidGeometry(game);
} catch (error:Error) {
trace("Error humanoid create geometry" + error.name + " " + error.message);
}
try {
updateContainer(game);
game.scene.addChild(container);
containerAdded(game);
containerPartOfScene = true;
} catch (error:Error) {
trace("Error humanoid init update container" + error.name + " " + error.message);
}
}
override public function updateMovement(stepX:Number, stepY:Number, stepZ:Number, game:Game):Array {
if (!dying && !dead) {
var result:Array = super.updateMovement(stepX, stepY, stepZ, game);
var newStepX:Number = result[0];
var newStepY:Number = result[1];
if (newStepX != 0 || newStepY != 0) {
if (Math.sqrt(newStepX * newStepX + newStepY * newStepY) > 2) {
animationState = RUNNING;
} else {
animationState = WALKING;
}
} else if (newStepX == 0 && newStepY == 0) {
animationState = STANDING;
}
return result;
} else {
return [0, 0, 0];
}
}
public function getRandomDieVelocity(game:Game):Vector3D {
var angle:Number = Math.random() * Math.PI * 2;
var upAngle:Number = 0.5 * Math.PI - Math.PI * 0.25 * Math.random();
var speed:Number = 2 + Math.random() * 3;
var upFrac:Number = Math.cos(upAngle);
return new Vector3D(Math.cos(angle) * upFrac * speed, Math.sin(angle) * upFrac * speed, -Math.sin(upAngle) * speed);
}
override public function startsDying(game:Game):void {
var particles:Array = game.currentLevel.worldParticles;
var gravity:Vector3D = new Vector3D(0, 0, 0.3);
particles.push(new Object3DParticle(leftArm, getRandomDieVelocity(game), gravity));
particles.push(new Object3DParticle(rightArm, getRandomDieVelocity(game), gravity));
particles.push(new Object3DParticle(leftLeg, getRandomDieVelocity(game), gravity));
particles.push(new Object3DParticle(rightLeg, getRandomDieVelocity(game), gravity));
particles.push(new Object3DParticle(body, getRandomDieVelocity(game), gravity));
particles.push(new Object3DParticle(head, getRandomDieVelocity(game), gravity));
}
override public function tick(game:Game):void {
super.tick(game);
if (blinking) {
blinkCounter--;
head.material = blinkMaterial;
body.material = blinkMaterial;
leftArm.material = blinkMaterial;
rightArm.material = blinkMaterial;
leftLeg.material = blinkMaterial;
rightLeg.material = blinkMaterial;
if (blinkCounter <= 0) {
blinking = false;
}
if (!blinking) {
head.material = headMaterial;
body.material = bodyMaterial;
leftArm.material = armMaterial;
rightArm.material = armMaterial;
leftLeg.material = legMaterial;
rightLeg.material = legMaterial;
}
}
if (dying || dead) {
// leftArm.z += 0.5;
// leftArm.y += 0.5;
} else {
if (hit) {
hitCounter--;
var hitFraction:Number = hitCounter / maxHitCounter;
container.rotationY = -hitFraction * 30;
if (hitCounter == 0) {
hit = false;
}
container.rotationZ = 180 * Math.atan2(hitDirection.y, hitDirection.x) / Math.PI;
} else {
container.rotationZ = 180 * Math.atan2(direction.y, direction.x) / Math.PI;
}
switch (animationState) {
case RUNNING:
leftArm.rotationZ = 40 * Math.sin(game.counter * 0.6);
rightArm.rotationZ = 40 * Math.sin(game.counter * 0.6);
leftLeg.rotationY = 40 * Math.sin(game.counter * 0.6);
rightLeg.rotationY = -40 * Math.sin(game.counter * 0.6);
break;
case WALKING:
leftArm.rotationZ = 40 * Math.sin(game.counter * 0.3);
rightArm.rotationZ = 40 * Math.sin(game.counter * 0.3);
leftLeg.rotationY = 40 * Math.sin(game.counter * 0.3);
rightLeg.rotationY = -40 * Math.sin(game.counter * 0.3);
break;
case STANDING:
leftArm.rotationZ = 10;
rightArm.rotationZ = 10;
leftLeg.rotationY = 0;
rightLeg.rotationY = 0;
break;
}
if (animateDancing) {
leftArm.rotationZ = 40 * Math.sin(dancingSpeedFactor * game.counter * 0.8);
rightArm.rotationZ = 40 * Math.sin(dancingSpeedFactor * game.counter * 0.8);
leftLeg.rotationY = 40 * Math.sin(dancingSpeedFactor * game.counter * 0.8);
rightLeg.rotationY = -40 * Math.sin(dancingSpeedFactor * game.counter * 0.8);
container.rotationZ = 50 * Math.sin(dancingSpeedFactor * game.counter * 0.4);
// rightArm.rotationZ = 180 + 40 * Math.sin(game.counter * 0.8);
}
}
}
}
class MeterBar extends Sprite {
public var text:String = "Value";
public var barColor:uint = 0xff0000;
public var fraction:Number = 1.0;
public var maxWidth:Number = 80;
public var maxHeight:Number = 15;
public function setFraction(f:Number):void {
this.fraction = f;
graphics.clear();
graphics.beginFill(barColor);
graphics.drawRect(0, 0, fraction * maxWidth, maxHeight);
graphics.endFill();
}
public function tick(game:Game):void {
}
}
class Player extends Humanoid {
public var maxBatteryLevel:Number = 100.0;
public var batteryLevel:Number = 100.0;
public var dancers:Vector.<NPC> = new Vector.<NPC>();
public var dancing:Boolean = false;
public var fighting:Boolean = false;
public var danceSpeed:Number = 3;
public var maxSpeed:Number = 5;
public var sceneLight:DirectionalLight3D;
public var haveBall:Boolean = false;
public var danceBall:ObjectContainer3D = null;
public var sphere:Sphere;
public var sphereRadius:Number = 10.0;
public var lightConeHeight:Number = 80.0;
public var lightCones:Array = [];
public var lightFrequencies:Array = [];
public var lightPhases:Array = [];
public var healthBar:MeterBar;
public var batteryBar:MeterBar;
public var bossPointers:Vector.<ObjectContainer3D> = new Vector.<ObjectContainer3D>();
public var bossPointerCones:Vector.<Cone> = new Vector.<Cone>();
public var haveBossPointers:Vector.<Boolean> = new Vector.<Boolean>();
public function Player():void {
super();
maxHealth = 1.0;
health = maxHealth;
team = 1;
}
public function increaseHealth(amount:Number):void {
health += amount;
if (health > maxHealth) {
health = maxHealth;
}
healthBar.setFraction(health / maxHealth);
}
public function increaseBatteryLevel(amount:Number):void {
batteryLevel += amount;
if (batteryLevel > maxBatteryLevel) {
batteryLevel = maxBatteryLevel;
}
batteryBar.setFraction(batteryLevel / maxBatteryLevel);
}
override public function doDamage(game:Game, damage:Number, damageDir:Vector3D):void {
super.doDamage(game, damage, damageDir);
healthBar.setFraction(health / maxHealth);
}
override public function init(game:Game):void {
// Set colors and size here
sizeFactor = 1.0;
super.init(game);
healthBar = new MeterBar();
healthBar.x = 10;
healthBar.y = 10;
game.interfaceContainer.addChild(healthBar);
healthBar.setFraction(1.0);
batteryBar = new MeterBar();
batteryBar.x = 10;
batteryBar.y = 30;
batteryBar.barColor = 0x999999;
game.interfaceContainer.addChild(batteryBar);
batteryBar.setFraction(1.0);
}
override public function removed(game:Game):void {
super.removed(game);
game.interfaceContainer.removeChild(healthBar);
}
override public function tick(game:Game):void {
try {
super.tick(game);
} catch (error:Error) {
trace("Error player super tick " + error.name + " " + error.message);
}
healthBar.tick(game);
var stepX:Number = 0;
var stepY:Number = 0;
var speed:Number = maxSpeed;
if (dancing) {
speed = danceSpeed;
}
var wasDancing:Boolean = dancing;
if (game.keysDown[Keyboard.UP]) {
// trace("moving up");
stepY += speed;
}
if (game.keysDown[Keyboard.DOWN]) {
stepY += -speed;
}
if (game.keysDown[Keyboard.LEFT]) {
stepX += -speed;
}
if (game.keysDown[Keyboard.RIGHT]) {
stepX += speed;
}
// Reset all dancers
for (var i:int = 0; i<dancers.length; i++) {
var d:NPC = dancers[i];
d.mode = d.modeWhenAlone;
d.team = 0;
}
dancers.length = 0;
dancing = false;
if ((game.keysDown["68"] || game.keysDown["65"]) && !dead && !dying) {
if (batteryLevel > 0.0) {
dancing = true;
batteryLevel -= 0.15;
batteryBar.setFraction(batteryLevel / maxBatteryLevel);
}
}
if (!wasDancing && dancing) {
game.musicSequencer.play();
//game.sfxPlayer.playDancing(game);
}
if (wasDancing && !dancing) {
game.musicSequencer.stop();
}
fighting = false;
if (game.keysDown["70"] || game.keysDown["83"]) {
if (dancing) {
// All dancers fight!
fighting = true;
}
}
if (dancing) {
var movers:Array = game.currentLevel.movingObjects;
var newMode:int = DANCE_MODE;
if (fighting) {
newMode = DANCE_FIGHT_MODE;
}
for (var k:int=0; k<movers.length; k++) {
if (movers[k] is NPC) {
var mover:NPC = NPC(movers[k]);
if (!mover.resistsDance) {
var danceRadius:Number = mover.musicResistDistance;
var dVec:Vector3D = new Vector3D(mover.position.x - position.x, mover.position.y - position.y);
var dist:Number = dVec.length;
if (dist < danceRadius) {
mover.mode = newMode;
mover.danceSpeed = danceSpeed;
dancers.push(mover);
mover.team = 1;
}
}
}
}
if (!haveBall) {
if (danceBall == null) {
danceBall = new ObjectContainer3D();
sphere = new Sphere();
var mat:WireColorMaterial = new WireColorMaterial(0xffffff);
mat.thickness = 0;
mat.alpha = 0.5;
sphere.material = mat;
sphere.radius = sphereRadius;
danceBall.addChild(sphere);
var lightCount:int = 5;
for (i=0; i<lightCount; i++) {
var lightCone:Cone = new Cone();
lightCone.height = lightConeHeight;
lightCone.radius = 7;
lightCone.yUp = false;
var frac:Number = i / lightCount;
var matrix:Matrix3D = new Matrix3D();
matrix.identity();
matrix.appendTranslation(0, 0, -lightCone.height * 0.5 - sphereRadius);
matrix.appendRotation(135, new Vector3D(1, 0, 0));
matrix.appendRotation(frac * 360, new Vector3D(0, 0, 1));
var coneMat:WireColorMaterial = new WireColorMaterial(0x00ffff);
coneMat.wireAlpha = 0.4;
coneMat.alpha = 0.1;
lightCone.material = coneMat;
lightCone.transform = matrix;
danceBall.addChild(lightCone);
lightCones.push(lightCone);
}
}
for (i=0; i<lightCones.length; i++) {
lightPhases[i] = Math.random() * 40;
lightFrequencies[i] = Math.random() * 0.15 + 0.05;
}
game.scene.addChild(danceBall);
haveBall = true;
}
} else {
if (haveBall) {
game.scene.removeChild(danceBall);
haveBall = false;
}
}
animateDancing = dancing;
if (stepX != 0 && stepY != 0) {
var sqrt2:Number = Math.sqrt(2.0);
stepX /= sqrt2;
stepY /= sqrt2;
}
if (stepX != 0 || stepY != 0) {
var temp:Vector3D = new Vector3D(stepX, stepY);
temp.normalize();
direction.x = temp.x;
direction.y = temp.y;
}
var realSteps:Array;
try {
realSteps = super.updateMovement(stepX, stepY, 0, game);
} catch (error:Error) {
trace("Error player update movement " + error.name + " " + error.message);
}
if (haveBall) {
// Update ball
danceBall.position = new Vector3D(position.x, position.y, position.z - 80);
danceBall.rotationZ = game.counter * 2;
for (i = 0; i<lightCones.length; i++) {
var cone:Cone = lightCones[i];
matrix = new Matrix3D();
frac = i / lightCones.length;
matrix.identity();
matrix.appendTranslation(0, 0, -lightConeHeight * 0.5 - sphereRadius);
matrix.appendRotation(135 + 20 * Math.sin(lightFrequencies[i] * counter + lightPhases[i]), new Vector3D(1, 0, 0));
matrix.appendRotation(frac * 360, new Vector3D(0, 0, 1));
cone.transform = matrix;
}
}
// if (dancers.length > 0) {
// trace("Dancers: " + dancers.length);
// }
for (var j:int = 0; j<dancers.length; j++) {
var dancer:NPC = dancers[j];
dancer.danceStepX = realSteps[0];
dancer.danceStepY = realSteps[1];
}
var bossCount:int = game.currentLevel.bosses.length;
bossPointers.length = bossCount;
haveBossPointers.length = bossCount;
bossPointerCones.length = bossCount;
for (i = 0; i<bossCount; i++) {
var boss:NPC = game.currentLevel.bosses[i];
var bossVec:Vector3D = boss.position.subtract(position);
if (!boss.dead && bossVec.length > 150) {
var bossPointer:ObjectContainer3D = bossPointers[i];
if (!haveBossPointers[i]) {
if (!bossPointer) {
bossPointer = new ObjectContainer3D();
var bossPointerCone:Cone = new Cone();
bossPointerCones[i] = bossPointerCone;
bossPointerCone.radius = 1.8;
bossPointerCone.height = 14;
bossPointerCone.segmentsW = 5;
bossPointer.addChild(bossPointerCone);
bossPointers[i] = bossPointer;
}
game.scene.addChild(bossPointer);
haveBossPointers[i] = true;
}
bossVec.normalize();
var pointerDist:Number = 70;
bossPointer.x = position.x + bossVec.x * pointerDist;
bossPointer.y = position.y + bossVec.y * pointerDist;
bossPointer.z = Math.min(position.z, game.getGroundZ(bossPointer.x, bossPointer.y) - 10);
var oscDist:Number = 3 * Math.sin(game.counter * 0.2);
bossPointer.x += oscDist * bossVec.x;
bossPointer.y += oscDist * bossVec.y;
bossPointer.rotationZ = -90 + 180 * Math.atan2(bossVec.y, bossVec.x) / Math.PI;
} else {
if (haveBossPointers[i]) {
game.scene.removeChild(bossPointers[i]);
haveBossPointers[i] = false;
}
}
}
// if (Math.random() < 0.05) {
// trace("player is ticked...");
// }
}
}
// Removes and adds their geometry automatically
class SelfManagedHumanoid extends Humanoid {
public var containerRemoveDistance:Number = 300;
public var containerAddDistance:Number = 300;
public var completeRemoveDistance:Number = 2000;
override public function removeCompletely(game:Game):void {
super.removeCompletely(game);
// var arr:Array = game.currentLevel.movingObjects;
// arr.splice(arr.indexOf(this), 1);
if (containerPartOfScene) {
game.scene.removeChild(container);
containerPartOfScene = false;
}
}
override public function shouldRemoveContainer(game:Game):Boolean {
if (game.currentLevel.player) {
var pos:Vector3D = game.currentLevel.player.position;
return Vector3D.distance(position, pos) > containerRemoveDistance;
} else {
return false;
}
}
override public function shouldAddContainer(game:Game):Boolean {
if (game.currentLevel.player) {
var pos:Vector3D = game.currentLevel.player.position;
return Vector3D.distance(position, pos) < containerAddDistance;
} else {
return false;
}
}
override public function shouldRemoveCompletely(game:Game):Boolean {
if (game.currentLevel.player) {
var pos:Vector3D = game.currentLevel.player.position;
return Vector3D.distance(position, pos) > completeRemoveDistance;
} else {
return false;
}
}
override public function tick(game:Game):void {
super.tick(game);
updateContainer(game);
}
}
var BOSS_1:int = 1;
var BOSS_2:int = 2;
var BOSS_3:int = 3;
var BOSS_4:int = 4;
var BOSS_5:int = 5;
var MONSTER_1:int = 21;
var MONSTER_2:int = 22;
var MONSTER_3:int = 23;
var MONSTER_4:int = 24;
var MONSTER_5:int = 25;
var MONSTER_6:int = 26;
var MONSTER_7:int = 27;
var MONSTER_8:int = 28;
var PATROL_MODE:int = 0;
var WAIT_MODE:int = 1;
var ATTACK_MODE:int = 2;
var FIGHT_MODE:int = 3;
var DANCE_MODE:int = 4;
var DANCE_FIGHT_MODE:int = 5;
var HITTING_ATTACK:int = 0;
var PROJECTILE_ATTACK:int = 1;
var AXE_PROJECTILE:int = 0;
var MOVE_RIGHT:int = 0;
var MOVE_DOWN:int = 1;
var MOVE_LEFT:int = 2;
var MOVE_UP:int = 3;
var patrolPatterns:Array = [
{pattern: [MOVE_RIGHT, MOVE_DOWN, MOVE_LEFT, MOVE_UP], lengths: [60, 120, 60, 120]},
{pattern: [MOVE_LEFT, MOVE_DOWN, MOVE_RIGHT, MOVE_UP], lengths: [60, 120, 60, 120]},
{pattern: [MOVE_RIGHT, MOVE_DOWN, MOVE_LEFT, MOVE_UP], lengths: [120, 120, 120, 120]},
{pattern: [MOVE_LEFT, MOVE_DOWN, MOVE_RIGHT, MOVE_UP], lengths: [120, 60, 120, 60]},
{pattern: [MOVE_RIGHT, MOVE_LEFT], lengths: [120, 120]},
{pattern: [MOVE_LEFT, MOVE_RIGHT], lengths: [120, 120]},
{pattern: [MOVE_LEFT, MOVE_RIGHT, MOVE_UP, MOVE_DOWN], lengths: [120, 120, 120, 120]}
];
class NPC extends SelfManagedHumanoid {
public var npcType:int = MONSTER_1;
public var realVelocity:Vector3D = new Vector3D();
public var resistsDance:Boolean = false;
public var danceStepX:Number = 0;
public var danceStepY:Number = 0;
public var danceSpeed:Number = 1;
public var maxSpeed:Number = 1;
public var maxAttackSpeed:Number = 1.3;
public var mode:int = PATROL_MODE;
public var modeWhenAlone:int = PATROL_MODE;
public var attackType:int = PROJECTILE_ATTACK;
public var projectileType:int = AXE_PROJECTILE;
public var damage:Number = 0.1;
public var modeUpdateInterval:int = 13;
public var sightLength:Number = 80;
public var leaveAloneLength:Number = 200;
public var fightDistance:Number = 50;
public var closestFriendDistance:Number = 25;
public var musicResistDistance:Number = 100;
public var attackCounter:int = 1000;
public var attackInterval:int = 50;
public var patrolDirection:Vector3D = new Vector3D(1, 0);
public var patrolIndex:int = 0;
public var patrolCounter:int = 0;
public var patrolPattern:Vector.<int> = Vector.<int>([MOVE_RIGHT, MOVE_DOWN, MOVE_LEFT, MOVE_UP]);
public var patrolStepsPattern:Vector.<int> = Vector.<int>([60, 120, 60, 120]);
public var currentMovement:Vector3D = new Vector3D();
public var currentDirection:Vector3D = new Vector3D(1, 0);
public var projectileSpeed:Number = 3;
public var hasHealthBar:Boolean = true;
public var healthBar:MeterBar;
public var healthBarVisible:Boolean = false;
public function updateHealthBar(game:Game):void {
if (healthBarVisible && hasHealthBar) {
// var mat:Matrix3D = game.mainCamera.viewMatrix;
var screenVert:Vector3D = game.mainCamera.screen(game.scene, new Vertex(container.x, container.y, container.z + headZ - 20)); // + headZ));
// var screenPos:Vector3D = mat.transformVertex(position);
healthBar.x = screenVert.x + game.stage.stageWidth * 0.5; // - healthBar.maxWidth * 0.5;
healthBar.y = screenVert.y + game.stage.stageHeight * 0.5;
// trace(screenPos.x + " " + screenPos.y);
healthBar.setFraction(health / maxHealth);
}
}
override public function containerAdded(game:Game):void {
super.containerAdded(game);
if (hasHealthBar) {
if (!healthBar) {
healthBar = new MeterBar();
healthBar.maxWidth = 70;
healthBar.maxHeight = 6;
}
game.interfaceContainer.addChild(healthBar);
healthBarVisible = true;
updateHealthBar(game);
}
}
override public function containerRemoved(game:Game):void {
super.containerRemoved(game);
if (healthBar) {
game.interfaceContainer.removeChild(healthBar);
}
healthBarVisible = false;
}
public function getHealthFromLevel(level:int):Number {
return level * 0.1 + 0.3;
}
public function getAttackIntervalFromLevel(level:int):int {
return Math.max(5, 60 - level * 2);
}
public function getDamageFromLevel(level:int):Number {
return 0.1 + 0.05 * level;
}
public function getFightDistanceFromLevel(level:int):Number {
return 50 + 12 * level;
}
public function getSpeedFromLevel(level:int):Number {
return 1 + level * 0.2;
}
public function getProjectileSpeedFromLevel(level:int):Number {
return 1.5 + level * 0.5;
}
public function getSightLengthFromLevel(level:int):Number {
return 80 + level * 10;
}
public function getMusicResistDistanceFromLevel(level:int):Number {
return 100 - level * 10;
}
override public function init(game:Game):void {
// Set colors and size here
var healthLevel:int = 1;
var attackIntervalLevel:int = 1;
var damageLevel:int = 1;
var speedLevel:int = 1;
var projectileSpeedLevel:int = 1;
var fightDistanceLevel:int = 1;
var sightLengthLevel:int = 1;
var musicResistDistanceLevel:int = 1;
var patrolIndex:int = 0;
sizeFactor = 1.0;
headColor = 0xffffff;
armColor = 0x444444;
bodyColor = 0xff3333;
legColor = 0x000000;
switch (npcType) {
case MONSTER_1:
headColor = 0xffffff;
armColor = 0x444444;
bodyColor = 0xff3333;
legColor = 0x111111;
patrolIndex = 0;
sightLengthLevel = 1;
break;
case MONSTER_2:
headColor = 0x00ff00;
bodyColor = 0x111111;
armColor = 0xff3333;
sizeFactor = 1.2;
healthLevel = 2;
attackIntervalLevel = 2;
speedLevel = 2;
musicResistDistanceLevel = 2;
fightDistanceLevel = 2;
patrolIndex = 1;
sightLengthLevel = 2;
projectileSpeedLevel = 2;
break;
case MONSTER_3:
sizeFactor = 1.3;
attackIntervalLevel = 3;
projectileSpeedLevel = 2;
speedLevel = 5;
musicResistDistanceLevel = 3;
fightDistanceLevel = 3;
patrolIndex = 2;
sightLengthLevel = 3;
healthLevel = 3;
break;
case MONSTER_4:
sizeFactor = 1.3;
attackIntervalLevel = 4;
damageLevel = 2;
patrolIndex = 3;
speedLevel = 4;
fightDistanceLevel = 4;
musicResistDistanceLevel = 4;
sightLengthLevel = 5;
projectileSpeedLevel = 3;
healthLevel = 4;
break;
case MONSTER_5:
sizeFactor = 1.4;
attackIntervalLevel = 5;
patrolIndex = 4;
speedLevel = 5;
fightDistanceLevel = 4;
musicResistDistanceLevel = 5;
sightLengthLevel = 6;
projectileSpeedLevel = 4;
healthLevel = 5;
break;
case MONSTER_6:
sizeFactor = 1.5;
attackIntervalLevel = 6;
patrolIndex = 5;
speedLevel = 6;
fightDistanceLevel = 4;
musicResistDistanceLevel = 6;
sightLengthLevel = 6;
projectileSpeedLevel = 5;
healthLevel = 6;
break;
case MONSTER_7:
sizeFactor = 1.6;
attackIntervalLevel = 7;
patrolIndex = 6;
speedLevel = 6;
fightDistanceLevel = 5;
musicResistDistanceLevel = 6;
sightLengthLevel = 6;
projectileSpeedLevel = 5;
healthLevel = 7;
break;
case MONSTER_8:
sizeFactor = 1.7;
attackIntervalLevel = 8;
patrolIndex = 4;
speedLevel = 6;
fightDistanceLevel = 6;
musicResistDistanceLevel = 6;
sightLengthLevel = 6;
projectileSpeedLevel = 5;
healthLevel = 8;
break;
case BOSS_1:
sizeFactor = 1.7;
headColor = 0xff0000;
armColor = 0x222222;
bodyColor = 0x33ff33;
legColor = 0x0000ff;
attackIntervalLevel = 4;
resistsDance = true;
modeWhenAlone = WAIT_MODE;
speedLevel = 2;
fightDistanceLevel = 4;
damageLevel = 3;
sightLengthLevel = 4;
projectileSpeedLevel = 3;
healthLevel = 3;
break;
case BOSS_2:
sizeFactor = 2;
headColor = 0x000000;
armColor = 0xff0000;
bodyColor = 0xffffff;
legColor = 0x00ff00;
attackIntervalLevel = 6;
resistsDance = true;
modeWhenAlone = WAIT_MODE;
speedLevel = 4;
fightDistanceLevel = 6;
damageLevel = 5;
sightLengthLevel = 5;
projectileSpeedLevel = 5;
healthLevel = 6;
break;
case BOSS_3:
sizeFactor = 2.5;
headColor = 0xff0000;
armColor = 0x222222;
bodyColor = 0x33ff33;
legColor = 0x0000ff;
attackIntervalLevel = 8;
resistsDance = true;
modeWhenAlone = WAIT_MODE;
speedLevel = 5;
fightDistanceLevel = 6;
damageLevel = 7;
sightLengthLevel = 6;
projectileSpeedLevel = 5;
healthLevel = 8;
break;
case BOSS_4:
sizeFactor = 3;
headColor = 0xff0000;
armColor = 0x222222;
bodyColor = 0x33ff33;
legColor = 0x0000ff;
attackIntervalLevel = 10;
resistsDance = true;
modeWhenAlone = WAIT_MODE;
speedLevel = 6;
fightDistanceLevel = 6;
damageLevel = 9;
sightLengthLevel = 6;
projectileSpeedLevel = 5;
healthLevel = 10;
break;
case BOSS_5:
sizeFactor = 3.5;
headColor = 0xff0000;
armColor = 0x222222;
bodyColor = 0x33ff33;
legColor = 0x0000ff;
attackIntervalLevel = 10;
resistsDance = true;
modeWhenAlone = WAIT_MODE;
speedLevel = 6;
fightDistanceLevel = 8;
damageLevel = 10;
sightLengthLevel = 7;
projectileSpeedLevel = 5;
healthLevel = 15;
break;
}
fightDistance = getFightDistanceFromLevel(fightDistanceLevel);
attackInterval = getAttackIntervalFromLevel(attackIntervalLevel);
health = getHealthFromLevel(healthLevel);
damage = getDamageFromLevel(damageLevel);
maxSpeed = getSpeedFromLevel(speedLevel);
maxAttackSpeed = maxSpeed * 1.2;
projectileSpeed = getProjectileSpeedFromLevel(projectileSpeedLevel);
musicResistDistance = getMusicResistDistanceFromLevel(musicResistDistanceLevel);
sightLength = getSightLengthFromLevel(sightLengthLevel);
leaveAloneLength = sightLength * 1.8;
patrolPattern = Vector.<int>(patrolPatterns[patrolIndex].pattern);
patrolStepsPattern = Vector.<int>(patrolPatterns[patrolIndex].lengths);
try {
super.init(game);
} catch (error:Error) {
trace("Error npc super init " + error.name + " " + error.message);
}
}
public function updateMode(game:Game):void {
var player:Player = game.currentLevel.player;
var playerPos:Vector3D = player.position;
var distanceToPlayer:Number = Vector3D.distance(playerPos, position);
var playerVisible:Boolean = distanceToPlayer < sightLength;
var playerDead:Boolean = player.dead;
switch (mode) {
case PATROL_MODE:
case WAIT_MODE:
if (playerVisible) {
mode = ATTACK_MODE;
}
break;
case ATTACK_MODE:
if (distanceToPlayer > leaveAloneLength) {
mode = modeWhenAlone;
}
if (distanceToPlayer <= fightDistance) {
mode = FIGHT_MODE;
}
break;
case FIGHT_MODE:
if (distanceToPlayer > fightDistance) {
mode = ATTACK_MODE;
}
break;
}
if (playerDead) {
mode = modeWhenAlone;
}
}
public function getWantedMovement(game:Game, movement:Vector3D, direction:Vector3D):void {
var wantedDir:Vector3D = new Vector3D();
var shouldStandStill:Boolean = false;
switch (mode) {
case WAIT_MODE:
shouldStandStill = true;
break;
case PATROL_MODE:
patrolCounter++;
var patrolSteps:int = patrolStepsPattern[patrolIndex];
if (patrolCounter > patrolSteps) {
patrolIndex = (patrolIndex + 1) % patrolPattern.length;
patrolCounter = 0;
}
patrolSteps = patrolStepsPattern[patrolIndex];
var intDir:int = patrolPattern[patrolIndex];
switch (intDir) {
case MOVE_RIGHT:
patrolDirection.x = 1;
patrolDirection.y = 0;
break;
case MOVE_DOWN:
patrolDirection.x = 0;
patrolDirection.y = -1;
break;
case MOVE_LEFT:
patrolDirection.x = -1;
patrolDirection.y = 0;
break;
case MOVE_UP:
patrolDirection.x = 0;
patrolDirection.y = 1;
break;
}
wantedDir.x = patrolDirection.x;
wantedDir.y = patrolDirection.y;
break;
case ATTACK_MODE:
case FIGHT_MODE:
var playerPos:Vector3D = game.currentLevel.player.position;
var distanceToPlayer:Number = Vector3D.distance(playerPos, position);
var diffVec:Vector3D = new Vector3D(playerPos.x - position.x, playerPos.y - position.y);
var sign:Number = 1;
if (distanceToPlayer < 0.5 * fightDistance) {
sign = -1;
} else if (distanceToPlayer < 0.8 * fightDistance) {
shouldStandStill = true;
}
wantedDir.x = sign * diffVec.x;
wantedDir.y = sign * diffVec.y;
diffVec.normalize();
direction.x = diffVec.x; // Face the player while possibly moving away from him
direction.y = diffVec.y;
break;
case DANCE_MODE:
case DANCE_FIGHT_MODE:
wantedDir.x = danceStepX;
wantedDir.y = danceStepY;
if (danceStepX == 0 && danceStepY == 0) {
shouldStandStill = true;
}
break;
}
if (wantedDir.length > 0.001) {
var speed:Number = maxSpeed;
if (mode == DANCE_MODE || mode == DANCE_FIGHT_MODE) {
speed = danceSpeed;
}
if (mode == FIGHT_MODE || mode == ATTACK_MODE) {
speed = maxAttackSpeed;
}
wantedDir.normalize();
movement.x = wantedDir.x * speed;
movement.y = wantedDir.y * speed;
if (mode != ATTACK_MODE && mode != FIGHT_MODE) {
direction.x = wantedDir.x;
direction.y = wantedDir.y;
}
}
if (shouldStandStill) {
movement.x = 0;
movement.y = 0;
}
var movers:Array = game.currentLevel.movingObjects;
for (var i:int=0; i<movers.length; i++) {
var mover:MovingPhysicalObject = MovingPhysicalObject(movers[i]);
if (mover != this) {
// Move away from other guys
var dVec:Vector3D = new Vector3D(mover.position.x - position.x, mover.position.y - position.y);
var dist:Number = dVec.length;
if (dist < closestFriendDistance) {
dVec.normalize();
movement.x -= dVec.x;
movement.y -= dVec.y;
}
}
}
// movement.x += Math.random() * 0.2 - 0.1;
// movement.y += Math.random() * 0.2 - 0.1;
}
public function getProjectile(game:Game, targetPos:Vector3D):Projectile {
var axe:Axe = new Axe(this);
var pos:Vector3D = position.clone();
pos.z -= 25 * sizeFactor;
axe.position = pos;
var diff:Vector3D = targetPos.clone();
diff.decrementBy(pos);
diff.normalize();
var vertLength:Number = Math.sqrt(diff.x * diff.x + diff.y * diff.y);
var dir:Vector3D = direction.clone();
dir.z = 0;
dir.normalize();
if (mode == DANCE_FIGHT_MODE) {
diff.z = 0;
vertLength = 1.0;
}
dir = new Vector3D(dir.x * vertLength, dir.y * vertLength, diff.z);
dir.normalize();
axe.direction = dir;
// trace("axe damage " + damage);
var velocity:Vector3D = axe.direction.clone();
velocity.normalize();
velocity.scaleBy(projectileSpeed);
velocity.incrementBy(realVelocity);
axe.velocity = velocity;
game.sfxPlayer.playThrowAxe(game);
return axe;
}
public function performAttack(game:Game, targetPos:Vector3D):void {
switch (attackType) {
case PROJECTILE_ATTACK:
// Add projectile
var projectile:Projectile = getProjectile(game, targetPos);
projectile.damage = damage;
projectile.sizeFactor = sizeFactor;
// trace("Throwing axe " + projectile.position);
game.currentLevel.projectiles.push(projectile);
break;
}
}
public function updateAction(game:Game):void {
animateAttacking = false;
animateDancing = false;
if (mode == FIGHT_MODE || mode == DANCE_FIGHT_MODE) {
animateAttacking = true;
if (attackCounter > attackInterval) {
var targetPos:Vector3D = game.currentLevel.player.position.clone();
targetPos.z += game.currentLevel.player.headZ;
performAttack(game, targetPos);
attackCounter = 0;
}
}
if (mode == DANCE_FIGHT_MODE || mode == DANCE_MODE) {
animateDancing = true;
}
attackCounter++;
}
override public function tick(game:Game):void {
try {
super.tick(game);
} catch (error:Error) {
trace("Error npc super tick " + error.name + " " + error.message);
}
if (game.currentLevel.player) {
if ((game.counter % modeUpdateInterval) == 0) {
updateMode(game);
}
// var movement:Vector3D = new Vector3D();
getWantedMovement(game, currentMovement, currentDirection);
direction.x = currentDirection.x;
direction.y = currentDirection.y;
updateAction(game);
updateHealthBar(game);
}
try {
var realMovement:Array = super.updateMovement(currentMovement.x, currentMovement.y, 0, game);
realVelocity = new Vector3D(realMovement[0], realMovement[1], 0);
} catch (error:Error) {
trace("Error npc update movement " + error.name + " " + error.message);
}
// if (Math.random() < 0.02) {
// trace("npc is ticked... " + game.currentLevel.movingObjects.length);
// }
}
}
class SfxPlayer {
// sionDriver.noteOn(m.note, m.voice, length, delay, 0, m.track);
public function playVictory(game:Game):void {
var guitar:SiONVoice = game.sionPresets["valsound.guitar1"];
game.sionDriver.noteOn(60, guitar, 4, 0, 0, 0);
game.sionDriver.noteOn(64, guitar, 4, 4, 0, 0);
game.sionDriver.noteOn(67, guitar, 4, 8, 0, 0);
game.sionDriver.noteOn(72, guitar, 4, 12, 0, 0);
}
public function playLoss(game:Game):void {
var guitar:SiONVoice = game.sionPresets["valsound.guitar2"];
game.sionDriver.noteOn(60, guitar, 4, 0, 0, 0);
game.sionDriver.noteOn(63, guitar, 4, 4, 0, 0);
game.sionDriver.noteOn(66, guitar, 4, 8, 0, 0);
}
public function playDeath(game:Game):void {
var guitar:SiONVoice = game.sionPresets["valsound.guitar3"];
game.sionDriver.noteOn(60, guitar, 2, 0, 0, 0);
game.sionDriver.noteOn(64, guitar, 2, 0, 0, 0);
game.sionDriver.noteOn(68, guitar, 2, 0, 0, 0);
}
public function playAxeHitsLiving(game:Game):void {
var voice:SiONVoice = game.sionPresets["saw"];
game.sionDriver.noteOn(10, voice, 1, 0, 0, 0);
}
public function playAxeHitsGround(game:Game):void {
var voice:SiONVoice = game.sionPresets["snoise"];
game.sionDriver.noteOn(10, voice, 1, 0, 0, 0);
}
public function playThrowAxe(game:Game):void {
var voice:SiONVoice = game.sionPresets["noise"];
game.sionDriver.noteOn(10, voice, 1, 0, 0, 0);
}
}
var DANCE_SONG:int = 0;
var INTRO_SONG:int = 1;
class GameSequenceDataProvider implements SequenceDataProvider {
public var minorProbability:Number = 0.5;
private var presets:SiONPresetVoice;
public var bassDrum:SiONVoice;
public var snareDrum:SiONVoice;
public var hihat:SiONVoice;
public var bass:SiONVoice;
public var piano:SiONVoice;
public var guitar:SiONVoice;
public var pad:SiONVoice;
public var song:int = DANCE_SONG;
public var rnd:Rndm;
public function GameSequenceDataProvider(seed:int = 123456):void {
rnd = new Rndm(seed);
presets = new SiONPresetVoice();
bassDrum = presets["bassdrumm"];
snareDrum = presets["snare"];
hihat = presets["closedhh"];
bass = presets["valsound.bass1"];
piano = presets["valsound.piano1"];
guitar = presets["valsound.guitar1"];
pad = presets["valsound.strpad1"];
}
public function getRandomPiano(rnd:Rndm):SiONVoice {
return presets["valsound.piano" + rnd.integer(1, 20)];
}
public function getRandomBass(rnd:Rndm):SiONVoice {
return presets["valsound.bass" + rnd.integer(1, 54)];
}
public function getRandomGuitar(rnd:Rndm):SiONVoice {
return presets["valsound.guitar" + rnd.integer(1, 4)];
}
// All lengths are given in 16th of a beat
public var bassMotifs:Array = [
{lengths: [64], indices: [0, 0], rests: [0], strengths: [1]}, // Bass
{lengths: [32, 32], indices: [0, 0], rests: [0], strengths: [1]}, // Bass
{lengths: [16, 16, 16, 16], indices: [0, 0, 0, 0], rests: [0], strengths: [1]}, // Bass
{lengths: [32, 32], indices: [0, 2], rests: [0], strengths: [1]}, // Bass fifths
{lengths: [16, 16, 16, 16], indices: [0, 2, 0, 2], rests: [0], strengths: [1]}, // Bass fifths
{lengths: [32, 32], indices: [0, 3], rests: [0], strengths: [1]}, // Bass octaves
{lengths: [16, 16, 16, 16], indices: [0, 3, 0, 3], rests: [0], strengths: [1]} // Bass octaves
];
public var arpeggioMotifs:Array = [
{lengths: [16, 16, 16, 16], indices: [0, 1, 2, 1], rests: [0], strengths: [1]},
{lengths: [8, 8, 8, 8, 8, 8, 8, 8], indices: [0, 1, 2, 1], rests: [0], strengths: [1]},
{lengths: [16, 8, 8, 16, 8, 8], indices: [0, 1, 2, 1], rests: [0], strengths: [1]},
{lengths: [16, 16, 16, 16], indices: [0, -1, 1, 0], rests: [0], strengths: [1]},
{lengths: [8, 8, 8, 8, 8, 8, 8, 8], indices: [0, -1, 1, 0], rests: [0], strengths: [1]},
{lengths: [16, 8, 8, 16, 8, 8], indices: [0, -1, 1, 0], rests: [0], strengths: [1]},
{lengths: [16, 16, 16, 16], indices: [1, 0, -1, -2], rests: [0], strengths: [1]},
{lengths: [8, 8, 8, 8, 8, 8, 8, 8], indices: [1, 0, -1, -2], rests: [0], strengths: [1]},
{lengths: [16, 8, 8, 16, 8, 8], indices: [1, 0, -1, -2], rests: [0], strengths: [1]},
{lengths: [16, 16, 16, 16], indices: [-1, 0, 1, 2], rests: [0], strengths: [1]},
{lengths: [8, 8, 8, 8, 8, 8, 8, 8], indices: [-1, 0, 1, 2], rests: [0], strengths: [1]},
{lengths: [16, 8, 8, 16, 8, 8], indices: [-1, 0, 1, 2], rests: [0], strengths: [1]}
];
public var blockChordMotifs:Array = [
{lengths: [64], chordNoteCount: [3], rests: [0], strengths: [1]},
{lengths: [32, 32], chordNoteCount: [3, 4], rests: [0], strengths: [1]},
{lengths: [48, 16], chordNoteCount: [3], rests: [0], strengths: [1]},
{lengths: [32, 16, 8, 8], chordNoteCount: [3], rests: [0], strengths: [1]},
{lengths: [16, 16, 32], chordNoteCount: [3], rests: [0], strengths: [1]},
{lengths: [32, 32/3, 32/3, 32/3], chordNoteCount: [3], rests: [0, 0, 1, 0], strengths: [1]},
{lengths: [24, 8, 32/3, 32/3, 32/3], chordNoteCount: [3], rests: [0], strengths: [1]}
];
public var voiceMotifs:Array = [
{lengths: [16 * 4], indices: [0], rests: [0], strengths: [1.0]},
{lengths: [16 * 2, 16, 16], indices: [0, 1, 0], rests: [0], strengths: [1.0]},
{lengths: [16 * 3, 16], indices: [0, -1], rests: [0], strengths: [1.0]}
];
public var percussionMotifs: Array = [
{lengths: [16, 16, 16, 16], indices: [0, 0, 0, 0], strengths: [1, 0.75, 0.85, 0.7], rests: [0]}, // bass drum, four to the floor
{lengths: [16, 16, 16, 16], indices: [-1, 1, -1, 1], strengths: [1, 0.75, 0.85, 0.7], rests: [1, 0, 1, 0] }, // Snare
{lengths: [8, 8, 8, 8, 8, 8, 8, 8], indices: [2, 2, 2, 2, 2, 2, 2, 2], strengths: [0.7], rests: [0]} // hihat 8ths
];
public var fillMotifs: Array = [
{lengths: [16, 16, 16, 16], indices: [1], strengths: [1, 0.75, 0.85, 0.7], rests: [1, 0, 0, 0]}, // Snare fill 1
{lengths: [16, 16, 8, 8, 8, 4, 4], indices: [1], strengths: [1, 0.75, 0.85, 0.7], rests: [0]} // Snare fill 2
];
// All harmony lengths are in beats
public var harmonicRythms: Array = [
{lengths: [4, 4, 4, 4], chordRoots: [0, 1, 3, 4], worksWithMinor: false, strengths: [1] },
{lengths: [4, 4, 4, 4], chordRoots: [0, 3, 4, 0], worksWithMinor: true, strengths: [1] },
{lengths: [4, 4, 4, 4], chordRoots: [0, -2, 3, 4], worksWithMinor: true, strengths: [1] },
{lengths: [4, 4, 4, 4], chordRoots: [0, 4, 0, 4], worksWithMinor: true, strengths: [1] },
{lengths: [4, 4, 4, 4], chordRoots: [0, 3, 0, 4], worksWithMinor: true, strengths: [1] },
{lengths: [4, 4, 4, 4], chordRoots: [0, 4, 0, 3], worksWithMinor: true, strengths: [1] },
{lengths: [4, 4, 4, 4], chordRoots: [0, -2, 1, 3], worksWithMinor: false, strengths: [1] }
];
// Determines the pattern of the motifs to use, not the actual indices of the motifs.
// The it is just a matter of choosing the 0, 1, and 2 motifs
public var motifPatterns:Array = [
[0, 0, 0, 0],
[0, 0, 0, 1],
[0, 1, 0, 1],
[0, 1, 0, 2],
[0, 0, 1, 0],
[0, 1, 2, 1]
];
public function getIndexPattern(motifCount:int):Array {
var pattern:Array = motifPatterns[rnd.integer(0, motifPatterns.length)];
var count:int = 0;
var i:int = 0;
for (i = 0; i<pattern.length; i++) {
count = Math.max(count, pattern[i] + 1);
}
var arr:Array = [];
for (i = 0; i<count; i++) {
arr[i] = rnd.integer(0, motifCount); // Doesn't make them unique, but wth
// trace("count " + motifCount + " result: " + arr[i]);
}
var result:Array = [];
for (i = 0; i<pattern.length; i++) {
result[i] = arr[pattern[i]]
}
return result;
}
public function renderChordMotif(result:SequenceData, motifIndex:int, chord:ChordInfo, timeMillis:Number, beatMillis:Number, voice:SiONVoice, octaves:int, motif:Object):void {
for (var j:int = 0; j<motif.lengths.length; j++) {
var length:Number = motif.lengths[j];
var millisLength:Number = (length * beatMillis) / 16.0;
var isRest:Boolean = (motif.rests[j % motif.rests.length] == 1);
if (!isRest) {
var index:int = motif.indices[j % motif.indices.length];
var note:int = chord.getAbsoluteNoteFromChordRootIndex(index);
note += octaves * 12;
result.messages.push(new NoteOn(note, 127, timeMillis, millisLength, voice, 0));
}
timeMillis += millisLength;
}
}
public function renderArpeggioMotif(result:SequenceData, motifIndex:int, chord:ChordInfo, timeMillis:Number, beatMillis:Number, voice:SiONVoice, octaves:int):void {
var motif:Object = arpeggioMotifs[motifIndex];
renderChordMotif(result, motifIndex, chord, timeMillis, beatMillis, voice, octaves, motif);
}
public function renderBassMotif(result:SequenceData, motifIndex:int, chord:ChordInfo, timeMillis:Number, beatMillis:Number, voice:SiONVoice, octaves:int):void {
var motif:Object = bassMotifs[motifIndex];
renderChordMotif(result, motifIndex, chord, timeMillis, beatMillis, voice, octaves, motif);
}
public function renderBlockChordMotif(result:SequenceData, motifIndex:int, chord:ChordInfo, timeMillis:Number, beatMillis:Number, voice:SiONVoice):void {
var blockChordMotif:Object = blockChordMotifs[motifIndex];
for (var j:int = 0; j<blockChordMotif.lengths.length; j++) {
var length:Number = blockChordMotif.lengths[j];
var millisLength:Number = (length * beatMillis) / 16.0;
var isRest:Boolean = (blockChordMotif.rests[j % blockChordMotif.rests.length] == 1);
if (!isRest) {
var chordNoteCount:int = blockChordMotif.chordNoteCount[j % blockChordMotif.chordNoteCount.length];
for (var k:int =0; k<chordNoteCount; k++) {
var note:int = chord.getAbsoluteNoteFromChordRootIndex(k);
result.messages.push(new NoteOn(note, 127, timeMillis, millisLength, voice, 0));
}
}
timeMillis += millisLength;
}
}
public function renderVoiceMotif(result:SequenceData, motifIndex:int, harmonyIndex:int, chordNote:int):void {
}
public function renderPercussion(result:SequenceData, percussionIndex:int, timeMillis:Number, beatMillis:Number):void {
var motif:Object = percussionMotifs[percussionIndex];
for (var i:int = 0; i<motif.lengths.length; i++) {
var lengthMillis:Number = (motif.lengths[i] * beatMillis) / 16;
var voice:SiONVoice = bassDrum;
var index:int = motif.indices[i % motif.indices.length];
var isRest:Boolean = (motif.rests[i % motif.rests.length] == 1);
switch (index) {
case 0:
voice = bassDrum;
break;
case 1:
voice = snareDrum;
break;
case 2:
voice = hihat;
break;
default:
voice = null;
break;
}
if (voice != null && !isRest) {
result.messages.push(new NoteOn(40, 127, timeMillis, beatMillis, voice, 0));
}
timeMillis += lengthMillis;
}
}
public function getSequenceData():SequenceData {
var result:SequenceData = new SequenceData();
piano = getRandomPiano(rnd);
bass = getRandomBass(rnd);
guitar = getRandomGuitar(rnd);
var chordInfo:ChordInfo = new ChordInfo();
var chords:Array = [];
var i:int = 0;
var note:int = 60;
var j:int = 0;
var chord:ChordInfo = null;
var hrBeats:int = 0;
var hr:Object = harmonicRythms[rnd.integer(0, harmonicRythms.length)];
var scale:Array = [0, 2, 4, 5, 7, 9, 11];
if (hr.worksWithMinor) {
// Select minor or major scale
if (rnd.random() < minorProbability) {
scale = [0, 2, 3, 5, 7, 8, 10];
}
}
var scaleBase:int = 60;
if (rnd.random() < 0.2 && song != INTRO_SONG) {
scaleBase = 61;
}
for (i = 0; i<hr.lengths.length; i++) {
chord = new ChordInfo();
chord.scale = scale;
chord.scaleBase = scaleBase;
chord.chordRoot = hr.chordRoots[i % hr.chordRoots.length];
chord.beats = hr.lengths[i];
chords.push(chord);
hrBeats += chord.beats;
}
result.length = hrBeats * 500;
var beatMillis:Number = result.length / result.beatCount;
var bassIndices:Array = getIndexPattern(bassMotifs.length);
var arpeggioIndices:Array = getIndexPattern(arpeggioMotifs.length);
var blockChordIndices:Array = getIndexPattern(blockChordMotifs.length);
var addBass:Boolean = true;
var addArpeggio:Boolean = true;
var addBlockChords:Boolean = true;
var addBassDrum:Boolean = true;
var addSnare:Boolean = true;
var addHihats:Boolean = true;
var addFill:Boolean = true;
var rndVal:Number = rnd.random();
if (rndVal < 0.1) {
addArpeggio = false;
addBlockChords = false;
} else if (rndVal < 0.2) {
addBlockChords = false;
addBass = false;
} else if (rndVal < 0.3) {
addArpeggio = false;
addBass = false;
} else if (rndVal < 0.4) {
addArpeggio = false;
} else if (rndVal < 0.5) {
addBlockChords = false;
} else if (rndVal < 0.6) {
addBass = false;
}
rndVal = rnd.random();
if (rndVal < 0.1) {
addBassDrum = false;
addSnare = false;
} else if (rndVal < 0.2) {
addHihats = false;
addBassDrum = false;
} else if (rndVal < 0.3) {
addHihats = false;
addSnare = false;
} else if (rndVal < 0.4) {
addSnare = false;
} else if (rndVal < 0.5) {
addBassDrum = false;
} else if (rndVal < 0.6) {
addHihats = false;
}
if (song == INTRO_SONG) {
addBass = false;
addBassDrum = true;
addHihats = false;
addSnare = false;
addBlockChords = false;
addArpeggio = false;
}
for (i = 0; i<hrBeats; i++) {
var currentTime:Number = beatMillis * i;
var harmonyIndex:int = i / 4;
if ((i % 4) == 0) {
// Start of harmony, get the chord
chord = chords[harmonyIndex];
// Add percussion motifs
if (addBassDrum) {
renderPercussion(result, 0, currentTime, beatMillis);
}
if (addSnare) {
renderPercussion(result, 1, currentTime, beatMillis);
}
if (addHihats) {
renderPercussion(result, 2, currentTime, beatMillis);
}
if (addBass) {
// Add bass
renderBassMotif(result, bassIndices[harmonyIndex], chord, currentTime, beatMillis, bass, -2);
}
if (addArpeggio) {
// Add arpeggio
renderArpeggioMotif(result, arpeggioIndices[harmonyIndex], chord, currentTime, beatMillis, guitar, 1);
}
if (addBlockChords) {
// Add block chord motif
renderBlockChordMotif(result, blockChordIndices[harmonyIndex], chord, currentTime, beatMillis, piano);
}
}
}
return result;
}
}
interface SequenceDataProvider {
function getSequenceData():SequenceData;
}
class NoteOn {
public var note:int = 60;
public var velocity:int = 64;
public var time:uint = 0;
public var length:uint = 250;
public var voice:SiONVoice = null;
public var track:int = 0;
public function NoteOn(note:int = 60, velocity:int = 64, time:uint = 0, length:uint = 250, voice:SiONVoice = null, track:int = 0) {
this.note = note;
this.velocity = velocity;
this.time = time;
this.length = length;
this.voice = voice;
this.track = track;
}
public function copy():NoteOn {
return new NoteOn(note, velocity, time, length, voice, track);
}
}
class SequenceData {
public var messages:Vector.<NoteOn> = new Vector.<NoteOn>();
public var length:uint = 8000;
public var beatCount:uint = 16;
public function split(time:uint):Array {
var result:SequenceData = new SequenceData();
var rest:SequenceData = new SequenceData();
for (var i:int=0; i<messages.length; i++) {
var message:NoteOn = messages[i];
if (message.time <= time) {
result.messages.push(message);
} else {
rest.messages.push(message);
}
}
return [result, rest];
}
}
var SEQUENCER_STOP_MODE:int = 0;
var SEQUENCER_PLAY_MODE:int = 1;
class MusicSequencer {
private var sequenceData:SequenceData = null;
private var sequenceTime:uint = 0;
private var millisPer16thBeat:Number;
private var beatsPerSecond:Number;
private var sionDriver:SiONDriver = null;
private var dataProvider:SequenceDataProvider = null;
private var mode:int = SEQUENCER_STOP_MODE;
public function MusicSequencer(driver:SiONDriver, dataProvider:SequenceDataProvider):void {
this.sionDriver = driver;
this.dataProvider = dataProvider;
}
public function stop():void {
mode = SEQUENCER_STOP_MODE;
}
public function play():void {
mode = SEQUENCER_PLAY_MODE;
}
public function tick():void {
if (mode == SEQUENCER_STOP_MODE) {
sequenceData = null;
} else {
var currentTime:uint = flash.utils.getTimer();
var extraOffset:uint = 400;
if (!sequenceData) {
sequenceData = dataProvider.getSequenceData();
sequenceTime = currentTime;
millisPer16thBeat = (sequenceData.length / sequenceData.beatCount) / 4.0;
beatsPerSecond = 1000 * (sequenceData.beatCount / sequenceData.length);
}
if (currentTime + extraOffset > sequenceTime + sequenceData.length) {
// Time to get the next data
sequenceTime = sequenceTime + sequenceData.length;
sequenceData = dataProvider.getSequenceData();
millisPer16thBeat = (sequenceData.length / sequenceData.beatCount) / 4.0;
beatsPerSecond = 1000 * (sequenceData.beatCount / sequenceData.length);
}
sionDriver.bpm = beatsPerSecond * 60;
var splitted:Array = sequenceData.split(currentTime - sequenceTime + extraOffset);
sequenceData = splitted[1];
var toRender:SequenceData = splitted[0];
var messages:Vector.<NoteOn> = toRender.messages;
for (var i:int=0; i<messages.length; i++) {
var m:NoteOn = messages[i];
var length:Number = m.length / millisPer16thBeat;
var delayMillis:Number = m.time + sequenceTime - currentTime;
var delay:Number = Math.max(0, delayMillis / millisPer16thBeat);
// trace(length + " " + sionDriver.bpm + " " + delay);
var track:SiMMLTrack = sionDriver.noteOn(m.note, m.voice, length, delay, 0, m.track);
track.velocity = m.velocity;
}
}
}
}
function positiveMod(a:int, b:int):int { return a >= 0 ? a % b : (b + a % b) % b ; }
class ChordInfo { // Musical stuff
public var beats:int = 4; public var scaleBase:int = 60; public var scale:Array = [0, 2, 4, 5, 7, 9, 11];
public var chordRoot:int = 0; public var chordOffsets:Array = [0, 2, 4];
public function getChordRootPositionAbsoluteOffsets():Array {
var result:Array = []; var scaleIndices:Array = getChordRootPositionScaleIndices();
var first:int = scaleIndices[0]; var firstAbsolute:int = getAbsoluteNote(scaleBase, scale, first);
var diff:int = firstAbsolute - scaleBase;
for (var i:int=0; i<scaleIndices.length; i++) { result[i] = getAbsoluteNote(scaleBase, scale, scaleIndices[i]) - firstAbsolute + diff; }
return result; }
public function getChordRootPositionScaleIndices():Array { var result:Array = [];
for (var i:int = 0; i<chordOffsets.length; i++) { result.push(chordRoot + chordOffsets[i]); }
return result; }
public function getAbsoluteNoteFromChordRootIndex(index:int):int {
var chordOffsets:Array = getChordRootPositionAbsoluteOffsets(); var first:int = chordOffsets[0];
for (var i:int=0; i<chordOffsets.length; i++) { chordOffsets[i] -= first; }
return getAbsoluteNote(scaleBase + first, chordOffsets, index); }
public function pitchClassDistance(c1:int, c2:int):int { return Math.min(Math.abs(c1 - c2), 12 - Math.abs(c1 - c2)); }
public function getAbsoluteNote(absoluteBaseNote:int, offsets:Array, index:int):int { var offsetIndex:int = 0;
var octaveOffset:int = 0; offsetIndex = positiveMod(index, offsets.length);
if (index >= 0) { octaveOffset = Math.floor(index / offsets.length);
} else { octaveOffset = -Math.floor((-index + offsets.length - 1) / offsets.length);
} return absoluteBaseNote + 12 * octaveOffset + offsets[offsetIndex]; }
public function getClosestNoteWithPitchClasses(absoluteNote:int, pitchClasses:Array):int {
var notePitchClass:int = absoluteNote % 12; var minDistance:int = 99999; var closestPitchClass:int = 0;
for (var i:int = 0; i < pitchClasses.length; i++) { var distance:int = pitchClassDistance(notePitchClass, pitchClasses[i]);
if (distance < minDistance) { minDistance = distance; closestPitchClass = pitchClasses[i]; } }
if (((absoluteNote + minDistance) % 12) == closestPitchClass) { return absoluteNote + minDistance;
} else if (((absoluteNote - minDistance) % 12) == closestPitchClass) { return absoluteNote - minDistance;
} else { return Math.floor(absoluteNote / 12) * 12 + closestPitchClass; } }
public function getAbsoluteNoteFromScaleIndex(index:int):int { return getAbsoluteNote(scaleBase, scale, index); }
public function offsetScale(absoluteNote:int, offset:int):int { var result:int = absoluteNote; var indexChr:Array = getScaleIndexAndChromaticOffsetForAbsoluteNote(result);
var scaleIndex:int = indexChr[0] + offset; var absNote:int = getAbsoluteNoteFromScaleIndex(scaleIndex);
result = absNote; return result; }
public function offsetChord(absoluteNote:int, offset:int):int { var result:int = absoluteNote;
var indexChr:Array = getChordRootIndexAndChromaticOffsetForAbsoluteNote(result);
result = getAbsoluteNoteFromChordRootIndex(indexChr[0] + offset); return result; }
public function getChordRootIndexAndChromaticOffsetForAbsoluteNote(absoluteNote:int):Array { var increments:Array = getChordRootPositionAbsoluteOffsets();
var firstInc:int = increments[0]; var baseNote:int = scaleBase + firstInc;
for (var i:int=0; i<increments.length; i++) { increments[i] -= firstInc; }
var result:Array = getScaleIndexAndChromaticOffsetForAbsoluteNoteStatic(absoluteNote, baseNote, increments);
return result; }
public function getScaleIndexAndChromaticOffsetForAbsoluteNote(absoluteNote:int):Array { return getScaleIndexAndChromaticOffsetForAbsoluteNoteStatic(absoluteNote, scaleBase, scale); }
public static function getScaleIndexAndChromaticOffsetForAbsoluteNoteStatic(absoluteNote:int, baseNote:int, increments:Array):Array {
var chromaticOffset:int = 0; var resultIndex:int = 0; var absDiff:int = absoluteNote - baseNote; var diffOctave:int = 0;
var normalizedNote:int = absDiff; while (normalizedNote < 0) { normalizedNote += 12; diffOctave--; }
while (normalizedNote > 11) { normalizedNote -= 12; diffOctave++; } var shortestAbsDistance:int = 9999999;
for (var i:int = 0; i < increments.length; i++) {
if (increments[i] == normalizedNote) { resultIndex = i + diffOctave * increments.length; chromaticOffset = 0; break;
} else { var diff:int = normalizedNote - increments[i];
if (Math.abs(diff) < shortestAbsDistance) { shortestAbsDistance = Math.abs(diff);
resultIndex = i + diffOctave * increments.length; chromaticOffset = diff; } } }
return [resultIndex, chromaticOffset]; }
}
function inittrace(s:Game):void { WTrace.initTrace(s);} var trace:Function;
class WTrace { // Mr trace
private static var FONT:String = "Fixedsys";
private static var SIZE:Number = 12;
private static var TextFields:Array = [];
private static var trace_stage:Game;
public static function initTrace(stg:Game):void { trace_stage = stg; trace = wtrace; }
private static function scrollup():void { if (TextFields.length > 30) { var removeme:TextField = TextFields.shift(); trace_stage.interfaceContainer.removeChild(removeme);
removeme = null; } for(var x:Number=0;x<TextFields.length;x++) { (TextFields[x] as TextField).y -= SIZE*1.2;}}
public static function wtrace(... args):void { var s:String=""; var tracefield:TextField; for (var i:int;i < args.length;i++) {
if (i != 0) s+=" "; s+=args[i].toString(); } tracefield= new TextField(); tracefield.autoSize = "left"; tracefield.text = s;
tracefield.y = trace_stage.stage.stageHeight - 40; var tf:TextFormat = new TextFormat(FONT, SIZE);tracefield.setTextFormat(tf);
trace_stage.interfaceContainer.addChild(tracefield); scrollup(); TextFields.push(tracefield); } }
class Rndm { // Mr uniform
protected var _seed:uint=0;
protected var _currentSeed:uint=0;
public function Rndm(seed:uint=1) { _seed = _currentSeed = seed; }
public function get seed():uint {return _seed;}
public function set seed(value:uint):void {_seed = _currentSeed = value;}
public function get currentSeed():uint { return _currentSeed; }
public function random():Number { return (_currentSeed = (_currentSeed * 16807) % 2147483647)/0x7FFFFFFF+0.000000000233; }
// float(50); // returns a number between 0-50 exclusive
// float(20,50); // returns a number between 20-50 exclusive
public function float(min:Number,max:Number=NaN):Number { if (isNaN(max)) { max = min; min=0; } return random()*(max-min)+min; }
// boolean(); // returns true or false (50% chance of true)
// boolean(0.8); // returns true or false (80% chance of true)
public function boolean(chance:Number=0.5):Boolean {return (random() < chance); }
// integer(50); // returns an integer between 0-49 inclusive
// integer(20,50); // returns an integer between 20-49 inclusive
public function integer(min:Number,max:Number=NaN):int {if (isNaN(max)) { max = min; min=0; } return Math.floor(float(min,max)); }
// reset(); // resets the number series, retaining the same seed
public function reset():void {_seed = _currentSeed;}}
class ClassicalNoise { // Mr Perlin
private var grad3:Array; private var perm:Array; private var p:Array;
public function ClassicalNoise(r:Rndm):void { // Classic Perlin noise in 3D, for comparison
grad3 = [[1,1,0],[-1,1,0],[1,-1,0],[-1,-1,0], [1,0,1],[-1,0,1],[1,0,-1],[-1,0,-1], [0,1,1],[0,-1,1],[0,1,-1],[0,-1,-1]];
this.p = []; var i:int; for (i=0; i<256; i++) { p[i] = Math.floor(r.random()*256);}
perm = []; for(i=0; i<512; i++) { perm[i]=p[i & 255]; } }
public function dot(g:Array, x:Number, y:Number, z:Number):Number { return g[0]*x + g[1]*y + g[2]*z; }
public function mix(a:Number, b:Number, t:Number):Number { return (1.0-t)*a + t*b; }
public function fade(t:Number):Number { return t*t*t*(t*(t*6.0-15.0)+10.0); }
public function noise(x:Number, y:Number, z:Number):Number { var X:Number = Math.floor(x);var Y:Number = Math.floor(y); var Z:Number = Math.floor(z);
x = x - X; y = y - Y; z = z - Z; X = X & 255;Y = Y & 255; Z = Z & 255;
var gi000:Number = this.perm[X+this.perm[Y+this.perm[Z]]] % 12; var gi001:Number = this.perm[X+this.perm[Y+this.perm[Z+1]]] % 12;
var gi010:Number = this.perm[X+this.perm[Y+1+this.perm[Z]]] % 12; var gi011:Number = this.perm[X+this.perm[Y+1+this.perm[Z+1]]] % 12;
var gi100:Number = this.perm[X+1+this.perm[Y+this.perm[Z]]] % 12; var gi101:Number = this.perm[X+1+this.perm[Y+this.perm[Z+1]]] % 12;
var gi110:Number = this.perm[X+1+this.perm[Y+1+this.perm[Z]]] % 12; var gi111:Number = this.perm[X+1+this.perm[Y+1+this.perm[Z+1]]] % 12;
var n000:Number= this.dot(this.grad3[gi000], x, y, z); var n100:Number= this.dot(this.grad3[gi100], x-1, y, z);
var n010:Number= this.dot(this.grad3[gi010], x, y-1, z); var n110:Number= this.dot(this.grad3[gi110], x-1, y-1, z);
var n001:Number= this.dot(this.grad3[gi001], x, y, z-1); var n101:Number= this.dot(this.grad3[gi101], x-1, y, z-1);
var n011:Number= this.dot(this.grad3[gi011], x, y-1, z-1); var n111:Number= this.dot(this.grad3[gi111], x-1, y-1, z-1);
var u:Number = this.fade(x); var v:Number = this.fade(y); var w:Number = this.fade(z);
var nx00:Number = this.mix(n000, n100, u); var nx01:Number = this.mix(n001, n101, u); var nx10:Number = this.mix(n010, n110, u);
var nx11:Number = this.mix(n011, n111, u); var nxy0:Number = this.mix(nx00, nx10, v); var nxy1:Number = this.mix(nx01, nx11, v);
var nxyz:Number = this.mix(nxy0, nxy1, w); return nxyz; } }