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

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

forked from: n0mlb1rd

Explosion renderer: http://wonderfl.net/c/g9Cm

Ball texture used in Painter class created from http://www.flickr.com/photos/c0t0s0d0/2363125133/ and http://www.flickr.com/photos/crystalflickr/53184232/
Get Adobe Flash player
by codenoxnet 12 May 2013
/**
 * Copyright codenoxnet ( http://wonderfl.net/user/codenoxnet )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/i4B9
 */

// forked from yonatan's n0mlb1rd
// forked from keim_at_Si's Nomltest on Flash

// Explosion renderer: http://wonderfl.net/c/g9Cm
// Wonderfl score SWF: http://wonderfl.net/c/hxAb forked from http://wonderfl.net/c/cuY4
// Ball texture used in Painter class created from http://www.flickr.com/photos/c0t0s0d0/2363125133/ and http://www.flickr.com/photos/crystalflickr/53184232/

package {
    import flash.display.*;
    import flash.text.*;
    import flash.events.*;
    import org.si.b3.*;

    [SWF(width="465", height="465", frameRate="30", backgroundColor="0")]
    public class main extends Sprite {
        private var tf:TextField;

        function main() {
            Wonderfl.capture_delay(30);
            stage.scaleMode = "noScale";
            stage.quality = "medium";
            tf = new TextField();
            tf.text = "arranging, please wait...";
            tf.scaleX = tf.scaleY = 2;
            tf.textColor = 0xA09080;
            tf.width = 225;
            tf.autoSize = "center";
            tf.y = (450-tf.height)/3;
            addChild(tf);
            resManager = new ResourceManager(onResourceLoaded, loaderInfo.parameters);
            scrManager = new ScoreManager();
            actManager = new ActorManager();
            painter = new Painter();
            occ = new Sprite();
            occ.addChild(bg = new Background(3));
            x=y=8;
        }
        
        public function onResourceLoaded() : void {
            removeChild(tf);
            mc = new CMLMovieClipEx(occ, 0, 0, 450, 450, 0xffffff, true, setup);
            mc.screen = mc.bitmapData = new BitmapData(450, 450, true, 0); // hack - should work as long as setSize isn't called.
            mc.control.map(CMLMovieClip.KEY_ESCAPE, "Q");
            mc.scene.register("title",     new TitleScene());
            mc.scene.register("main",      new MainScene());
            mc.scene.register("gameover",  new GameoverScene());
            mc.scene.id = "title";
            fx = new VolumetricPointLight(450, 450, occ, [0xc06030, 0x3060c0]);
            fx.setBufferSize(0x2000);
            fx.intensity = 4;
            addChild(fx);
        }
        
        public function setup() : void {
            resManager.sionDriver.bpm = 152;
            resManager.sionDriver.play();
        }
    }
}


// import --------------------------------------------------------------------------------
import flash.net.*;
import flash.geom.*;
import flash.events.*;
import flash.display.*;
import flash.filters.*;
import flash.utils.*;
import org.si.sion.*;
import org.si.sion.utils.soundloader.*;
import org.si.cml.*;
import org.si.cml.extensions.*;
import org.si.cml.namespaces.*;
import org.si.b3.*;
import frocessing.color.*;
import net.wonderfl.utils.WonderflAPI;

// constant variables --------------------------------------------------------------------------------
const extendScore:int = 200000;
const defaultLife:int = 3;
const RANKING_WINDOW_URL:String = "http://swf.wonderfl.net/swf/usercode/7/7a/7af0/7af0cf8f5b74242e7eb21337bfd241a9682d2407.swf";
const checkPolicyFile:Boolean = true;
/* // local
const EXPLOSION_URL :String = "explosion.swf";
const BALL_URL :String = "ball.png";
const CHARA_MAP_URL :String = "charactor.png";
const SOUND_FONT_URL:String = "sound.png";
/*/ // wonderfl
const EXPLOSION_URL :String = "http://swf.wonderfl.net/swf/usercode/7/71/7139/71392303d34b641ce53a4768c89007ebe31181c5.swf"; // keim_at_Si's Explosion Rendering - http://wonderfl.net/c/g9Cm
const BALL_URL :String = "http://assets.wonderfl.net/images/related_images/8/8d/8d27/8d27f29ab81436f237eff1edc5efb90d38e5289e";
const CHARA_MAP_URL :String = "http://assets.wonderfl.net/images/related_images/e/e4/e488/e4889d7b98486f0edf339bb45c0a4114255988c2";
const SOUND_FONT_URL:String = "http://assets.wonderfl.net/images/related_images/a/aa/aa9a/aa9a00df008e71a100500b5c90da9b71734af5e8";
//*/

// global instance --------------------------------------------------------------------------------
var mc:CMLMovieClipEx;
var resManager:ResourceManager;
var scrManager:ScoreManager;
var actManager:ActorManager;
var painter:Painter;
var occ:Sprite;
var bg:Background;
var fx:VolumetricPointLight;

// Scenes -------------------------------------------------------------------------------- 
class TitleScene {
    private var menuIndex:int, startLevel:int;
    private var menu:Array = ["Press [Z] Key To Start", "Show Net Ranking", "Clear Cookie", "Debug Mode"];
    private var anim:Array = [0, 4, 7, 9, 10, 9, 7, 4];
    public function enter() : void {
        mc.fps.reset();
        menuIndex = 0;
        startLevel = 0;
        scrManager.reset(true);
        actManager.reset();
        scrManager.debugMode = false;
    }
    
    public function update() : void {
        if (!mc.pause) {
            if (mc.control.isHitted(CMLMovieClip.KEY_BUTTON0)) {
                if (menuIndex == 2) {
                    scrManager.clearCookie();
                    scrManager.reset(true);
                } else if (menuIndex == 1) {
                    mc.pause = true;
                    resManager.showRanking(function(e:Event) : void { mc.pause = false; });
                } else mc.scene.id = "main";
            } else if (mc.control.isHitted(CMLMovieClip.KEY_RIGHT)) {
                if (--menuIndex == -1) menuIndex = menu.length-1;
                resManager.sionDriver.noteOn(67, resManager.beep, 1);
            } else if (mc.control.isHitted(CMLMovieClip.KEY_LEFT)) {
                if (++menuIndex == menu.length) menuIndex = 0;
                resManager.sionDriver.noteOn(67, resManager.beep, 1);
            } else {
                startLevel -= mc.control.y * 5;
                if (startLevel < 0) startLevel = 0;
                else if (startLevel > 250) startLevel = 250;
            }
        }
    }
    
    public function draw() : void {
        var menuString:String = menu[menuIndex], width:int = menuString.length * 8, 
            frameCount:int = mc.fps.totalFrame;
        bg.draw();
        scrManager.draw();
        resManager.print(-166, -56, "NOMLBIRD FL", resManager.lfonttex, 32);
        resManager.print(-width, 160, menuString, resManager.fonttex, 16);
        resManager.print(-196-anim[frameCount&7], 160, "{", resManager.fonttex, 16);
        resManager.print( 180+anim[frameCount&7], 160, "}", resManager.fonttex, 16);
        resManager.print(-128, 90, "START LEVEL : " + startLevel.toString(), resManager.fonttex, 16); 
        fx.render();
    }
    
    public function exit() : void {
        scrManager.startLevel = startLevel;
        scrManager.debugMode = (menuIndex == 3);
    }
}

class MainScene {
    public function enter() : void {
        mc.fps.reset();
        scrManager.reset(false);
        actManager.reset();
        actManager.start();
        resManager.sionDriver.play(resManager.bgm);
        resManager.sionDriver.playSound(6,1,0,0,2);
    }
    
    public function update() : void {
        scrManager.update();
        actManager.update();
        if (mc.control.getPressedFrame(CMLMovieClip.KEY_ESCAPE) > 15) {
            resManager.sionDriver.play();
            mc.scene.id = "title";
        }
    }
    
    public function draw() : void {
        var f:int, t:Number;
        bg.draw();
        scrManager.draw();
        actManager.draw();
        if (mc.fps.totalFrame < 90) {
            f = mc.fps.totalFrame;
            if (f < 70) {
                t = (f<50) ? ((40-f) * (40-f) - 100) * 0.2 : 0;
                resManager.print(-102-t, -32, "ARE YOU", resManager.lfonttex, 32);
                resManager.print(-102+t,   0, "READY ?", resManager.lfonttex, 32);
            } else {
                resManager.print(-70, -16, "GO !!", resManager.lfonttex, 32);
            }
        }
        fx.render();
    }

    public function exit() : void {}
}

class GameoverScene {
    private var referenceRecord:Boolean;
    public function enter() : void {
        referenceRecord = (scrManager.delayedFrames > mc.fps.totalFrame*0.1);
        mc.fps.reset();
        resManager.sionDriver.play(resManager.gameover);
        resManager.sionDriver.playSound(2,1,0,0,2);
    }
    
    public function update() : void {
        if (!mc.pause) {
            scrManager.update();
            actManager.update();
            if (mc.fps.totalFrame > 60 && mc.control.isHitted(CMLMovieClip.KEY_BUTTON0)) {
                if (!referenceRecord && scrManager.checkResult()) {
                    mc.pause = true;
                    resManager.registerRanking(function(e:Event) : void {
                        mc.pause = false;
                        mc.scene.id = "title";
                    });
                } else mc.scene.id = "title";
            }
        }
    }
    
    public function draw() : void {
        bg.draw();
        scrManager.draw();
        actManager.draw();
        resManager.print(-136, -32, "GAME OVER", resManager.lfonttex, 32);
        if (referenceRecord) resManager.print(-96, 16, "REFERENCE=RECORD", resManager.numtex, 12, 48);
        fx.render();
    }
    
    public function exit() : void {}
}

// Managers --------------------------------------------------------------------------------
class ResourceManager {
    public var bgangle:Number = -90;
    public var sionDriver:SiONDriver = new SiONDriver();
    public var damageColt:ColorTransform = new ColorTransform(1,1,1,1,-128,-128,-128,0);
    public var playerTexture:CMLMovieClipTexture, shotTexture:CMLMovieClipTexture;
    public var fonttex:CMLMovieClipTexture, lfonttex:CMLMovieClipTexture;
    public var numtex:CMLMovieClipTexture, lnumtex:CMLMovieClipTexture;
    public var scoreTextures:Array, explosionTextures:Array, enemyTextures:Array, flashingEnemyTextures:Array, bulletTextures:Array;
    public var lifeUpTexture:CMLMovieClipTexture;
    public var bgm:SiONData, gameover:SiONData, sequences:*, beep:SiONVoice;
    public var charactorMap:BitmapData, stageSequence:CMLSequence;
    public var groupID:Array = [0, 0, 0, 3, 3, 3, 6, 7, 7, 9, 10, 9, 0, 13, 0];
    public var shotSeq:Array, groupSeq:Array, enemySeq:Array;
    public var onResourceLoaded:Function, rankingMaker:*, loaderInfoParameters:*;
    function ResourceManager(onResourceLoaded:Function, loaderInfoParameters:*) {
        this.loaderInfoParameters = loaderInfoParameters;
        // Loader --------------------------------------------------
        var loader:SoundLoader = new SoundLoader();
        this.onResourceLoaded = onResourceLoaded;
        loader.setURL(new URLRequest(EXPLOSION_URL), "exp", null, checkPolicyFile);
        loader.setURL(new URLRequest(BALL_URL), "ball", "img", checkPolicyFile);
        loader.setURL(new URLRequest(CHARA_MAP_URL), "cmap", "img", checkPolicyFile);
        loader.setURL(new URLRequest(SOUND_FONT_URL), "sample", "ssfpng", checkPolicyFile);
        loader.setURL(new URLRequest(RANKING_WINDOW_URL), "ranking", "swf");
        //loader.setURL(new URLRequest("nomltest.mml"), "bgm", "txt");
        //loader.setURL(new URLRequest("gameover.mml"), "gameover", "txt");
        //loader.setURL(new URLRequest("script.cml"), "script", "txt");
        loader.addEventListener(Event.COMPLETE, _onComplete);
        loader.loadAll();
    }
    private function _onComplete(e:Event) : void {
        var data:* = SoundLoader(e.target).hash, bmp:BitmapData = new BitmapData(128, 128, true, 0xffffffff),
            red:Array=[0.7,0.4,0.4], grn:Array=[0.4,0.7,0.4], blu:Array=[0.4,0.4,0.7], 
            i:int, j:int, c:int, mat:Matrix = new Matrix(), t:CMLMovieClipTexture, bd:BitmapData, lbl:String;
        
        // CannonML --------------------------------------------------
        CMLObject.setGlobalRankRange(0, 999);
        CMLSequence.registerUserCommand("rungroup",   _onRunGroup, 1);
        CMLSequence.registerUserCommand("groupbonus", _onGroupBonus, 1);
        
        // Textures --------------------------------------------------
        painter.ball = data["ball"].bitmapData;
        painter.createSprites();
        charactorMap = data["cmap"].bitmapData;
        bmp.copyChannel(charactorMap, bmp.rect, bmp.rect.topLeft, 1, 8);
        playerTexture = painter.birdCMCT;
        shotTexture   = painter.shotCMCT;
        fonttex  = newTexture(bmp, 0, 64, 8, 8, 96, 2, [0.65, 0.6, 0.5], true, false);
        lfonttex = newTexture(bmp, 0, 64, 8, 8, 96, 4, [0.65, 0.6, 0.5], true, false);
        numtex   = newTexture(bmp, 0, 112, 6, 6, 42, 2, [0.375, 0.375, 0.375], false, false);
        lnumtex  = newTexture(bmp, 0, 112, 6, 6, 42, 4, [0.5, 0.5, 0.5],    false, false);
        // text
        scoreTextures = [];
        for (i=0; i<=50; i++) scoreTextures.push(renderText(i.toString()+"0", numtex));
        for (i=1; i<4; i++) scoreTextures[i*100] = renderText(i.toString()+"000", lnumtex);
        lifeUpTexture = renderText("1UP", lnumtex);
        enemyTextures = [];
        flashingEnemyTextures = [];
        explosionTextures = [];
        // enemies
        var rr:CMLMovieClipTexture = painter.rollerCMCT;
        var gr:CMLMovieClipTexture = painter.texColorMatrix(painter.rollerCMCT, Painter.hue110);
        var br:CMLMovieClipTexture = painter.texColorMatrix(painter.rollerCMCT, Painter.hue220);
        var fr:CMLMovieClipTexture = painter.texColorMatrix(painter.rollerCMCT, Painter.flash);
        for (i=0; i<4; i++) {
            enemyTextures.push(br, gr, rr);
            flashingEnemyTextures.push(fr, fr, fr);
        }
        var o1:CMLMovieClipTexture = painter.octopusCMCT;
        var o2:CMLMovieClipTexture = painter.texColorMatrix(painter.octopusCMCT, Painter.hue90);
        var o3:CMLMovieClipTexture = painter.texColorMatrix(painter.octopusCMCT, Painter.hue180);
        var o4:CMLMovieClipTexture = painter.texColorMatrix(painter.octopusCMCT, Painter.hue270);
        var fo:CMLMovieClipTexture = painter.texColorMatrix(painter.octopusCMCT, Painter.flash);
        enemyTextures.push(o1, o2, o3, o4);
        flashingEnemyTextures.push(fo, fo, fo, fo);
        // explosions
        var expBmds:Vector.<BitmapData> = data["exp"].explosion();
        var patterns:Vector.<CMLMovieClipTexture> = new Vector.<CMLMovieClipTexture>();
        var exp:CMLMovieClipTexture;
        var f:ColorMatrixFilter = new ColorMatrixFilter([
                1,0,0,0,0,
                0,1,0,0,0,
                0,0,1,0,0,
                0.66,0.66,0.66,0,0
            ]);
        var bmd:BitmapData;
        for(i=0; i<(expBmds.length-2); i+=2) {
            bmd = new BitmapData(expBmds[i].width, expBmds[i].height, true);
            bmd.applyFilter(expBmds[i], expBmds[i].rect, expBmds[i].rect.topLeft, f);
            patterns[i/2] = new CMLMovieClipTexture(bmd);
        }
        patterns[0].animationPattern = patterns;
        painter.autocrop(patterns[0]);
        for(i=0; i<12; i++) explosionTextures.push(patterns[0]); // small explosions
        patterns = new Vector.<CMLMovieClipTexture>();
        for(i=0; i<(expBmds.length-2); i+=2) {
            bmd = new BitmapData(expBmds[i].width*2, expBmds[i].height*2, true);
            bmd.draw(expBmds[i], new Matrix(2, 0, 0, 2, 0, 0));
            bmd.applyFilter(bmd, bmd.rect, bmd.rect.topLeft, f);
            patterns[i/2] = new CMLMovieClipTexture(bmd);
        }
        patterns[0].animationPattern = patterns;
        painter.autocrop(patterns[0]);
        explosionTextures.push(patterns[0], patterns[0], patterns[0]); // large explosions
        for(i=0; i<expBmds.length; i++) expBmds[i].dispose(); // destroy original bitmaps
        
        // bullets
        bulletTextures = [
            painter.ellipse([14541823,2640639,5911706,4210752],[1,1,1,0],[40,82,172,255]   , 16, 16),
            painter.ellipse([14541823,9699112,3447413,4210752],[1,1,1,0],[40,82,172,255]   , 16, 16),
            painter.ellipse([14541823,16728360,10105932,4210752],[1,1,1,0],[40,82,172,255] , 16, 16),
            painter.ellipse([14541823,2640639,5911706,4210752],[1,1,1,0],[40,82,172,255]   , 24, 7),
            painter.ellipse([14541823,9699112,3447413,4210752],[1,1,1,0],[40,82,172,255]   , 24, 7),
            painter.ellipse([14541823,16728360,10105932,4210752],[1,1,1,0],[40,82,172,255] , 24, 7),
            painter.ellipse([14541823,2640639,5911706,4210752],[1,1,1,0],[40,82,172,255]   , 24, 7)
        ];
            
        // Sound --------------------------------------------------
        beep = new SiONVoice(0, 0, 63, 36, 4, 0, 0, -4);
        sionDriver.noteOnExceptionMode = SiONDriver.NEM_IGNORE;
        sionDriver.setSamplerTable(0, data["sample"].samplerTables[0]);
        //bgm = sionDriver.compile(data["bgm"]);
        bgm = sionDriver.compile(mmlMain);
        //gameover = sionDriver.compile(data["gameover"]);
        gameover = sionDriver.compile(mmlGameOver);
        
        // Sequences --------------------------------------------------
        //stageSequence = new CMLSequence(data["script"]);
        stageSequence = new CMLSequence(cmlScript);
        sequences = stageSequence.childSequence;
        shotSeq = [sequences["S1"],sequences["S2"],sequences["S3"]];
        groupSeq = [];
        enemySeq = [];
        for (i=0; i<32; i++) {
            if ((lbl = "G" + i.toString()) in sequences) groupSeq[i] = sequences[lbl];
            if ((lbl = "E" + i.toString()) in sequences) enemySeq[i] = sequences[lbl];
        }

        // Ranking window
        var param:* = {
            tweet:"N0MLB1RD FL [SCORE:%SCORE%/LEVEL:%SCORE2%] #wonderfl", 
            scoreTitle:"SCORE", 
            title:"N0MLB1RD FL Net Ranking"
        }
        rankingMaker = data["ranking"];
        rankingMaker.initialize(new WonderflAPI(loaderInfoParameters), param);
        rankingMaker.score2(12, "Lv.%SCORE%", "LEVEL");
        rankingMaker.score3(15, "%SCORE% eat", "EATEN");

        onResourceLoaded();
    }
    
    private var shadow:DropShadowFilter = new DropShadowFilter(3, 45, 0, 0.5, 4, 4);
    private function newTexture(bmp:BitmapData, x:Number, y:Number, w:Number, h:Number, a:int, scale:Number, c:Array, shd:Boolean, flash:Boolean) : CMLMovieClipTexture {
        var org:CMLMovieClipTexture = new CMLMovieClipTexture(bmp, x, y, w, h, false, a, int(bmp.width/w)*w-w), tex:CMLMovieClipTexture; // int(bmp.width/w)*w-w (for avoid bug)
        tex = org.cutout(scale, scale, 0, new ColorTransform(c[0],c[1],c[2],1), 0, (shd)?6:0);
        if (flash) {
            tex.animationPattern = new Vector.<CMLMovieClipTexture>(2, true);
            tex.animationPattern[0] = tex;
            tex.animationPattern[1] = org.cutout(scale, scale, 0, new ColorTransform(1-(1-c[0])*0.6,1-(1-c[1])*0.6,1-(1-c[2])*0.6,1), 0, (shd)?6:0);
        }
        if (shd) {
            var i:int, imax:int = tex.animationCount, bmd:BitmapData;
            for (i=0; i<imax; i++) {
                bmd = tex.animationPattern[i].bitmapData;
                bmd.applyFilter(bmd, bmd.rect, bmd.rect.topLeft, shadow);
            }
        }
        return tex;
    }

    public function renderText(txt:String, font:CMLMovieClipTexture, asciiOffset:int=48) : CMLMovieClipTexture {
        var pt:Point = new Point(), i:int, imax:int=txt.length, t:CMLMovieClipTexture, 
            bd:BitmapData = new BitmapData(font.width*imax, font.height, true, 0);
        for (i=0; i<imax; i++) {
            t = font.animationPattern[txt.charCodeAt(i) - asciiOffset];
            bd.copyPixels(t.bitmapData, t.rect, pt);
            pt.x += font.width;
        }
        return new CMLMovieClipTexture(bd);
    }
    
    public function print(x:Number, y:Number, txt:String, font:CMLMovieClipTexture, pitch:int, asciiOffset:int=32) : void {
        var tx:Number=x+8, ty:Number=y+8, i:int, imax:int=txt.length;
        for (i=0; i<imax; i++) {
            mc.copyTexture(font, tx, ty, txt.charCodeAt(i)-asciiOffset);
            tx += pitch;
        }
    }
    
    public function showRanking(onClose:Function) : void {
        var window:* = rankingMaker.makeRankingWindow();
        window.addEventListener(Event.CLOSE, onClose);
        mc.parent.addChild(window);
    }
    
    public function registerRanking(onClose:Function) : void {
        var window:* = rankingMaker.makeScoreWindow(scrManager.score, scrManager.level, scrManager.eaten);
        window.addEventListener(Event.CLOSE, onClose);
        mc.parent.addChild(window);
    }
    
    // called from &rungroup command in CML
    private function _onRunGroup(fbr:CMLFiber, args:Array) : void {
        var enemyType:int = (args[0]==0) ? int(Math.random()*15) : (args[0]-1);
        Group.run(groupID[enemyType], enemyType);
    }
    
    // called from &groupbonus command in CML
    private function _onGroupBonus(fbr:CMLFiber, args:Array) : void {
        var g:Group = fbr.object as Group;
        if (g) {
            g.finished = true;
            g.bonus = args[0];
        }
    }
}

//--------------------------------------------------------------------------------
class ScoreManager {
    public var score:int, level:int, eaten:int, life:int, eatBonus:int, nextExtend:int, gameoverLevel:int;
    public var bestResult:*, startLevel:int, debugMode:Boolean, delayedFrames:int;
    private var _scoreDraw:int, _levelDraw:int, _eatenDraw:int, _lifeDraw:int;
    private var _scoreText:String, _levelText:String, _eatenText:String, _lifeText:String;

    function ScoreManager() {
        _loadCookie();
        reset(false);
    }
    
    public function reset(setHighScore:Boolean) : void {
        CMLObject.globalRank = startLevel;
        _scoreDraw = score = (setHighScore) ? bestResult.score : 0;
        _levelDraw = level = (setHighScore) ? bestResult.level : startLevel;
        _eatenDraw = eaten = (setHighScore) ? bestResult.eaten : 0;
                _scoreText = String(4) +"";
        
       
        _levelText = "LEVEL:" + ("00" + _levelDraw.toString()).substr(-3);
        _eatenText = "EATEN:" + ("00000" + _eatenDraw.toString()).substr(-6);
        _lifeText  = "LIFE :";
        _lifeDraw = 0;
        life = (debugMode) ? 0 : defaultLife;
        eatBonus = 2;
        nextExtend = extendScore;
        delayedFrames = 0;
    }
    
    public function update() : void {
        var dif:int = (score - _scoreDraw + 7) >> 3;
        level = int(CMLObject.globalRank);
        _scoreDraw += dif;
        if (dif > 0) {
            _scoreText = "SCORE:" + ("000000000" + _scoreDraw.toString()).substr(-10);
        }
        if (_levelDraw != level) {
            _levelDraw = level;
            _levelText = "LEVEL:" + ("00" + _levelDraw.toString()).substr(-3);
        }
        if (_eatenDraw != eaten) {
            _eatenDraw = eaten;
            _eatenText = "EATEN:" + ("00000" + _eatenDraw.toString()).substr(-6);
        }
        if (_lifeDraw != life) {
            _lifeDraw = life;
            if (debugMode) {
                _lifeText = "MISS :" + _lifeDraw.toString();
            } else {
                _lifeText = "LIFE :";
                for (var i:int=0; i<_lifeDraw; i++) _lifeText += "|";
            }
        }
        if (mc.fps.frameSkipLevel > 3) delayedFrames++;
    }
    
    public function draw() : void {
        resManager.print(-220, -220, _scoreText, resManager.fonttex, 16);
        resManager.print(-220, -204, _levelText, resManager.fonttex, 16);
        resManager.print(-220, -188, _eatenText, resManager.fonttex, 16);
        resManager.print(-220, -172, _lifeText,  resManager.fonttex, 16);
        if (debugMode) {
            resManager.print(-223, 190, "PRESS [Q] TO EXIT", resManager.fonttex, 16);
            resManager.print(-223, 209, "FRAME DELAY :"+mc.fps.delayedFrames.toFixed(2), resManager.fonttex, 16);
        }
    }
    
    public function checkResult() : Boolean {
        return _updateCookie();
    }
    
    public function destroyAll(groupBonus:int) : void {
        eatBonus += 3;
        if (eatBonus > 50) eatBonus = 50;
        addScore(groupBonus);
    }
    
    public function destroyAllFailed() : void {
        eatBonus -= 2;
        if (eatBonus < 1) eatBonus = 1;
    }
    
    public function miss() : Boolean {
        actManager.destroyAllEnemies();
        eatBonus>>= 1;
        if (eatBonus < 1) eatBonus = 1;
        if (debugMode) {
            life++;
        } else {
            if (--life < 0) {
                gameoverLevel = level;
                mc.scene.id = "gameover"
                return true;
            }
        }
        return false;
    }
    
    public function eat() : int {
        eaten++;
        addScore(eatBonus * 10);
        return eatBonus;
    }
    
    public function addScore(gain:int) : void {
        score += gain;
        if (score >= nextExtend) {
            nextExtend += extendScore;
            life++;
            resManager.sionDriver.playSound(5,1,0,0,3);
            Particle.$(actManager.player.x, actManager.player.y, 0, -10, 0, 0.75, resManager.lifeUpTexture);
        }
    }
    
    public function clearCookie() : void {
        var so:SharedObject = SharedObject.getLocal("savedData");
        if (so) so.clear();
        bestResult = {"score":0, "level":0, "lines":0};
    }
    
    private function _loadCookie() : void {
        var so:SharedObject = SharedObject.getLocal("savedData");
        bestResult = (so && "bestResult" in so.data) ? (so.data.bestResult) : {"score":0, "level":0, "eaten":0};
    }
    
    private function _updateCookie() : Boolean {
        if (startLevel != 0 || debugMode) return false;
        var updated:Boolean = false, so:SharedObject, isHighScore:Boolean = (score > bestResult.score);
        if (score > bestResult.score) { updated = true; bestResult.score = score; }
        if (gameoverLevel > bestResult.level) { updated = true; bestResult.level = gameoverLevel; }
        if (eaten > bestResult.eaten) { updated = true; bestResult.eaten = eaten; }
        if (updated && (so = SharedObject.getLocal("savedData"))) {
            so.data.bestResult = bestResult;
            so.flush();
        }
        return (isHighScore);
    }
}

//--------------------------------------------------------------------------------
class ActorManager {
    public var shots:ActorFactory;
    public var enemies:ActorFactory;
    public var bullets:ActorFactory;
    public var player:Player;
    public var master:Group;
    public var frameCounter:int;
    public var groupID:int;
    public var pauseRoot:Boolean = false;
    
    function ActorManager() {
        shots     = new ActorFactory(Shot, 0, 0);
        enemies   = new ActorFactory(Enemy, 0, 1);
        bullets   = new ActorFactory(Bullet, 0, 2);
        player = new Player();
        player.evalIDNumber = 3;
        player.drawPriority = enemies.drawPriority;
        Particle.initialize(640);
    }
    
    public function reset() : void {
        CMLObject.destroyAll(0);
        Particle.reset();
    }
    
    public function start() : void {
        player.create(0, 180);
        master = new Group();
        master.create(0, 0);
        master.execute(resManager.stageSequence);
    }
    
    public function draw() : void {
        Particle.draw();
        Actor.draw();
    }
    
    public function update() : void {
        // Actor.update() is called from CMLMovieClip
        Particle.update();
        if (player.status != Player.STATUS_INVISIBLE) Actor.testf(bullets.evalIDNumber, player.evalIDNumber);
        Actor.testf(shots.evalIDNumber, enemies.evalIDNumber);
    }
    
    public function destroyAllEnemies() : void {
        eval(enemies.evalIDNumber, function(act:Actor) : void { 
            act.destroy(2); 
        });
        eval(bullets.evalIDNumber, function(act:Actor) : void { 
            act.destroy(0);
        });
    }
    
    static public function eval(evalID:int, func:Function) : void {
        var act:Actor, list:Actor = Actor._cml_internal::_evalLayers[evalID];
        for (act=list._cml_internal::_nextEval; act!=list; act=act._cml_internal::_nextEval) func(act);
    }
}

// Actors --------------------------------------------------------------------------------
class Player extends Actor {
    static public const STATUS_TRANSPARENT:int = 0;
    static public const STATUS_NORMAL:int = 1;
    static public const STATUS_INVISIBLE:int = 2;
    public var status:int, statuFrameCount:int, animationCount:int, hitSize2:Number;
    public var shotFlag:int, eatRangeShape:Shape, eatRangeAngle:Number;
    public var eatRangeDraw:Number;
    public var playerSpeed:Array = [10, 9, 9, 5];
    public var eatRange:Array = [60, 45, 30, 55];
    public var roll:int = 0;
    
    public function Player() {
        scopeEnabled = false;
        eatRangeAngle = 0;
        size = 60;
        hitSize2 = 16;
        eatRangeDraw = 0;
        eatRangeShape = new Shape();
    }
    
    override public function onCreate() : void {
        animationCount = 0;
        shotFlag = 0;
        setAsDefaultTarget();
        expandScope(-16-mc.scopeMargin, -16-mc.scopeMargin);
        status = STATUS_INVISIBLE;
        statuFrameCount = 30;
    }
    
    override public function onUpdate() : void {
        var ix:int = mc.control.x, iy:int = mc.control.y,
            flg:uint = (mc.control.flag >> 4) & 3,
            dx:Number = ix * ((iy==0) ? 1 : 0.707), dy:Number = iy * ((ix==0) ? 1 : 0.707),
            spd:Number = playerSpeed[flg];
        if (status != STATUS_INVISIBLE) {
            x += dx * spd;
            y += dy * spd;
            limitScope();
            if (shotFlag != flg) {
                halt();
                shotFlag = flg;
                if (shotFlag > 0) execute(resManager.shotSeq[shotFlag-1]);
                size = eatRange[shotFlag];
            }
            eatRangeAngle += eatRangeDraw * 0.002;
                 if (size > eatRangeDraw) eatRangeDraw += 2;
            else if (size < eatRangeDraw) eatRangeDraw -= 2;
        }
        
        if (--statuFrameCount == 0) {
            if (status == STATUS_TRANSPARENT) status = STATUS_NORMAL;
            else setTransparent();
        }

        if(mc.control.x) {
            roll += mc.control.x;
            if(roll>9) roll = 9;
            if(roll<-9) roll = -9;
        } else {
            if(roll) roll += (roll>0 ? -1 : 1);
        }

        if(fx.intensity > 4) fx.intensity *= 0.9;
        else fx.intensity = 4;
    }

    override public function onDraw() : void {
        var frame:int = (animationCount % 15) * 7;
        frame += 3 + Math.round(roll/3);
        animationCount++;
        if (status != STATUS_INVISIBLE) {
            if ((status == STATUS_NORMAL) || (animationCount & 1)) {
                mc.copyTexture(resManager.playerTexture, x, y, frame);
            }
            updateEatRange();
            mc.draw(eatRangeShape, x, y)
        } else {
            mc.copyTexture(resManager.playerTexture, x, y + ((statuFrameCount-6) * (statuFrameCount-6) - 36) * 0.4, frame);
        }
    }
    
    override public function onFireObject(args:Array) : CMLObject {
        return actManager.shots.newInstance();
    }
    
    override public function onHit(act:Actor) : void {
        if (status == STATUS_TRANSPARENT) return;
        var dx:Number = act.x - x, dy:Number = act.y - y, i:int;
        if (dx * dx + dy * dy < hitSize2) {
            setDestruction(scrManager.miss());
            fx.intensity = 128;
        }
    }
    
    public function setTransparent() : void {
        status = STATUS_TRANSPARENT;
        statuFrameCount = 60;
    }
    
    public function setDestruction(gameover:Boolean) : void {
        halt();
        status = STATUS_INVISIBLE;
        if (!gameover) {
            statuFrameCount = 30;
            resManager.sionDriver.playSound(2,1,0,0,2);
        } else {
            statuFrameCount = -1;
        }
    }
    
    private var cmd:Vector.<int> = Vector.<int>([1,2,2,2,2]);
    private var path:Vector.<Number> = new Vector.<Number>();
    private function updateEatRange() : void {
        var g:Graphics = eatRangeShape.graphics,
            sin:Number = Math.sin(eatRangeAngle) * eatRangeDraw,
            cos:Number = Math.cos(eatRangeAngle) * eatRangeDraw,
            asin:Number = (sin<0) ? -sin : sin,
            acos:Number = (cos<0) ? -cos : cos;
        path[0] =  cos; path[1] = sin;
        path[2] = -sin; path[3] = cos;
        path[4] = -cos; path[5] = -sin;
        path[6] =  sin; path[7] = -cos;
        path[8] =  cos; path[9] =  sin;
        g.clear();
        g.lineStyle(1, 0xffffff, 0.4);
        g.drawPath(cmd, path);
        g.drawRect(-acos, -asin, acos*2, asin*2);
    }
}

//--------------------------------------------------------------------------------
class Shot extends Actor {
    public function Shot() { size = 10; }
    override public function onCreate() : void { }//scopeYmax = 400; }
    override public function onDraw() : void { 
        mc.copyTexture(resManager.shotTexture, x, y,  31 & (angleVelocity*0.0888+16.5)); 
    }
    override public function onHit(act:Actor) : void { destroy(1); }
}

//--------------------------------------------------------------------------------
class Group extends CMLObject {
    static private var _freeList:Array = [];
    static public function run(groupType:int, enemyType:int) : void { 
        var group:Group = _freeList.pop() || new Group();
        group.create(0, 0);
        group.execute(resManager.groupSeq[groupType]);
        group.enemyType = enemyType;
        group.bonus = group.childCount = 0;
        group.destroyAll = true;
        group.finished = false;
    }
    public var enemyType:int, childCount:int, finished:Boolean, destroyAll:Boolean, bonus:int;
    function Group() { }
    public function onChildDestroy(enemy:Enemy) : int {
        if (enemy.destructionStatus == 0) destroyAll = false;
        if (--childCount == 0 && finished) {
            if (bonus>0) {
                if (destroyAll) {
                    scrManager.destroyAll(bonus);
                    Particle.$(enemy.x, enemy.y, 0, -6, 0, 0.5, resManager.scoreTextures[bonus]);
                } else {
                    scrManager.destroyAllFailed();
                }
            }
            destroy(0);
        }
        return 0;
    }
    override public function onDestroy() : void { _freeList.push(this); }
    override public function onNewObject(args:Array) : CMLObject {
        var enemy:Enemy = actManager.enemies.newInstance();
        enemy.type = enemyType;
        enemy.group = this;
        childCount++;
        return enemy;
    }
}

//--------------------------------------------------------------------------------
class Enemy extends Actor {
    private var flagDamage:int = 0, animationCount:int = 0;
    public  var type:int, group:Group, texture:CMLMovieClipTexture, flashTexture:CMLMovieClipTexture, bonus:int;
    public function Enemy() {}
    override public function onCreate() : void {
        var seq:CMLSequence = resManager.enemySeq[type];
        flagDamage = 0;
        life = seq.getParameter(0);
        bonus = seq.getParameter(1);
        texture = resManager.enemyTextures[type];
        flashTexture = resManager.flashingEnemyTextures[type];
        size = texture.center.x;
        execute(seq);
    }
    override public function onDestroy() : void {
        group.onChildDestroy(this);
        if (destructionStatus > 0) {
            Particle.$(x, y, vx, vy, -vx*0.04, -vy*0.04, resManager.explosionTextures[type]);
            if (destructionStatus == 1) {
                Particle.$(x, y, 0, -6, 0, 0.5, resManager.scoreTextures[bonus]);
            }
        }
    }
    override public function onDraw() : void { 
        --flagDamage;
        animationCount = (animationCount+1) % texture.animationCount;
        var tex:CMLMovieClipTexture = isFlashing() ? flashTexture : texture;
        if(type>=12) { // octopus
            mc.copyTexture(tex, x, y, animationCount);
        } else { // roller
            var rot:int = 31 & (angleVelocity*0.0888+16.5);
            var anim:int = (animationCount << 5) % texture.animationCount;
            var frame:int = anim | rot;
            mc.copyTexture(tex, x, y, frame);
        }
    }
    override public function onFireObject(args:Array) : CMLObject { return actManager.bullets.newInstance().init(args); }
    override public function onHit(act:Actor) : void {
        flagDamage = 6;
        if (life != 0) {
            life -= 1;
            if (life <= 0) {
                destroy(1);
                resManager.sionDriver.playSound((size>24)?1:3,1,0,0,1);
                scrManager.addScore(bonus * 10);
            }
        }
    }
    private function isFlashing() : int {
        return (flagDamage>0) ? (flagDamage&1) : 0;
    }
}

//--------------------------------------------------------------------------------
class Bullet extends Actor {
    public var texture:CMLMovieClipTexture;
    public var ac:int = 0, acmax:int = 0, acspd:int = 0;
    public var shape:int = 0;
    
    public function Bullet() { size = 4; }
    public function init(args:Array) : Bullet { 
        shape = args[0];
        texture = resManager.bulletTextures[shape];
        acspd = 1;
        life = 1;
        acmax = texture.animationCount << acspd;
        return this;
    }
    override public function onCreate() : void { ac = (shape<3) ? 0 : ((angleOnStage*0.08888888888888889+8.5)&15); }
    override public function onUpdate()       : void { if (shape<3 && ++ac==acmax) ac = 0; super.onUpdate(); }
    override public function onDraw()         : void { mc.copyTexture(texture, x, y,  31 & (angleVelocity*0.0888+16.5)/*ac>>acspd*/); }
    override public function onFireObject(args:Array) : CMLObject { return actManager.bullets.newInstance().init(args); }
    override public function onHit(act:Actor) : void {
        var dx:Number = (vx < 0) ? -vx : vx, dy:Number = (vy < 0) ? -vy : vy,
            v:Number = (dx > dy) ? (dx + dy * 0.2928932188134524) : (dy + dx * 0.2928932188134524);
        life -= v * 0.0125;
        if (life <= 0) {
            destroy(1);
            resManager.sionDriver.playSound(4,1,0,0,4);
            dx = actManager.player.x - x;
            dy = actManager.player.y - y;
            v = 3 / Math.sqrt(dx*dx+dy*dy);
            Particle.$(x, y, -dx*v, -dy*v, dx*v*0.18, dy*v*0.18, resManager.scoreTextures[scrManager.eat()]);
        }
    }
}

//--------------------------------------------------------------------------------
class Particle {
    public var x:Number, y:Number, vx:Number, vy:Number, ax:Number, ay:Number, anim:int, tex:CMLMovieClipTexture, next:Particle;
    public function Particle(next_:Particle) { next = next_; }
    static private var _freeList:Particle = null, _particles:Particle = new Particle(null);
    static private var _width:Number, _height:Number, _rc:Rectangle = new Rectangle(0, 0, 2, 2);
    static public function initialize(particleMax:int) : void {
        for (var i:int=0; i<particleMax; i++) _freeList = new Particle(_freeList);
        _width = 450;
        _height = 450;
    }
    static public function reset() : void {
        var i:int, p:Particle;
        p = _particles.next;
        if (p) {
            while (p.next != null) p = p.next;
            p.next = _freeList;
            _freeList = _particles.next;
            _particles.next = null;
        }
    }
    static public function update() : void {
        var p:Particle, prev:Particle;
        prev = _particles;
        for (p = prev.next; p != null; p = p.next) {
            p.x += (p.vx += p.ax);
            p.y += (p.vy += p.ay);
            if (p.y>_height || ++p.anim == (p.tex.animationCount>16 ? p.tex.animationCount : 16)) {
                prev.next = p.next;
                p.next = _freeList;
                _freeList = p;
                p = prev;
            } else {
                prev = p;
            }
        }
    }
    static public function draw() : void {
        var p:Particle, screen:BitmapData = mc.screen;
        for (p = _particles.next; p != null; p = p.next) {
            mc.copyTexture(p.tex, p.x, p.y, (p.tex.animationCount>1)?p.anim:0);
        }
    }
    static public function $(x:Number, y:Number, vx:Number, vy:Number, ax:Number, ay:Number, tex:CMLMovieClipTexture) : void {
        if (!_freeList) return;
        var newParticle:Particle = _freeList, list:Particle;
        _freeList = _freeList.next;
        newParticle.x = x;
        newParticle.y = y;
        newParticle.anim = 0;
        list = _particles;
        newParticle.vx = vx;
        newParticle.vy = vy;
        newParticle.ax = ax;
        newParticle.ay = ay;
        newParticle.tex = tex;
        newParticle.next = list.next;
        list.next = newParticle;
    }
}

//--------------------------------------------------------------------------------
class Painter {
    public var ball:BitmapData;

    // luminance-preserving hue rotation matrices, output from huerotatemat - http://www.graficaobscura.com/matrix/index.html
    public static const hue0:Array = [1.000000,-0.000000,-0.000000,0.000000,0,0.000000,1.000000,0.000000,0.000000,0,0.000000,0.000000,1.000000,0.000000,0,0.000000,0.000000,0.000000,1.000000,0];
    public static const hue10:Array = [0.936621,-0.068280,0.131659,0.000000,0,0.052069,1.016784,-0.068853,0.000000,0,-0.148442,0.132232,1.016210,0.000000,0,0.000000,0.000000,0.000000,1.000000,0];
    public static const hue20:Array = [0.854160,-0.115968,0.261808,0.000000,0,0.111933,1.021190,-0.133123,0.000000,0,-0.282998,0.278962,1.004035,0.000000,0,0.000000,0.000000,0.000000,1.000000,0];
    public static const hue30:Array = [0.755123,-0.141617,0.386494,0.000000,0,0.177772,1.013083,-0.190856,0.000000,0,-0.399578,0.435733,0.963845,0.000000,0,0.000000,0.000000,0.000000,1.000000,0];
    public static const hue40:Array = [0.642518,-0.144447,0.501929,0.000000,0,0.247587,0.992711,-0.240298,0.000000,0,-0.494640,0.597780,0.896860,0.000000,0,0.000000,0.000000,0.000000,1.000000,0];
    public static const hue50:Array = [0.519767,-0.124371,0.604604,0.000000,0,0.319255,0.960693,-0.279948,0.000000,0,-0.565297,0.760181,0.805116,0.000000,0,0.000000,0.000000,0.000000,1.000000,0];
    public static const hue60:Array = [0.390600,-0.082000,0.691400,0.000000,0,0.390600,0.918000,-0.308600,0.000000,0,-0.609400,0.918000,0.691400,0.000000,0,0.000000,0.000000,0.000000,1.000000,0];
    public static const hue70:Array = [0.258942,-0.018621,0.759680,0.000000,0,0.459453,0.865931,-0.325384,0.000000,0,-0.625610,1.066442,0.559168,0.000000,0,0.000000,0.000000,0.000000,1.000000,0];
    public static const hue80:Array = [0.128792,0.063840,0.807368,0.000000,0,0.523723,0.806067,-0.329790,0.000000,0,-0.613435,1.200998,0.412438,0.000000,0,0.000000,0.000000,0.000000,1.000000,0];
    public static const hue90:Array = [0.004105,0.162877,0.833017,0.000000,0,0.581456,0.740228,-0.321683,0.000000,0,-0.573245,1.317578,0.255667,0.000000,0,0.000000,0.000000,0.000000,1.000000,0];
    public static const hue100:Array = [-0.111329,0.275482,0.835847,0.000000,0,0.630898,0.670413,-0.301311,0.000000,0,-0.506260,1.412640,0.093620,0.000000,0,0.000000,0.000000,0.000000,1.000000,0];
    public static const hue110:Array = [-0.214004,0.398233,0.815771,0.000000,0,0.670548,0.598745,-0.269293,0.000000,0,-0.414516,1.483296,-0.068781,0.000000,0,0.000000,0.000000,0.000000,1.000000,0];
    public static const hue120:Array = [-0.300800,0.527400,0.773400,0.000000,0,0.699200,0.527400,-0.226600,0.000000,0,-0.300800,1.527400,-0.226600,0.000000,0,0.000000,0.000000,0.000000,1.000000,0];
    public static const hue130:Array = [-0.369080,0.659058,0.710021,0.000000,0,0.715984,0.458547,-0.174531,0.000000,0,-0.168568,1.543610,-0.375042,0.000000,0,0.000000,0.000000,0.000000,1.000000,0];
    public static const hue140:Array = [-0.416768,0.789208,0.627560,0.000000,0,0.720390,0.394277,-0.114667,0.000000,0,-0.021838,1.531435,-0.509598,0.000000,0,0.000000,0.000000,0.000000,1.000000,0];
    public static const hue150:Array = [-0.442417,0.913894,0.528523,0.000000,0,0.712283,0.336544,-0.048828,0.000000,0,0.134933,1.491245,-0.626178,0.000000,0,0.000000,0.000000,0.000000,1.000000,0];
    public static const hue160:Array = [-0.445247,1.029329,0.415918,0.000000,0,0.691911,0.287102,0.020987,0.000000,0,0.296981,1.424260,-0.721240,0.000000,0,0.000000,0.000000,0.000000,1.000000,0];
    public static const hue170:Array = [-0.425171,1.132004,0.293167,0.000000,0,0.659893,0.247452,0.092655,0.000000,0,0.459381,1.332515,-0.791897,0.000000,0,0.000000,0.000000,0.000000,1.000000,0];
    public static const hue180:Array = [-0.382800,1.218800,0.164000,0.000000,0,0.617200,0.218800,0.164000,0.000000,0,0.617200,1.218800,-0.836000,0.000000,0,0.000000,0.000000,0.000000,1.000000,0];
    public static const hue190:Array = [-0.319421,1.287080,0.032341,0.000000,0,0.565131,0.202016,0.232853,0.000000,0,0.765642,1.086568,-0.852210,0.000000,0,0.000000,0.000000,0.000000,1.000000,0];
    public static const hue200:Array = [-0.236960,1.334768,-0.097808,0.000000,0,0.505267,0.197610,0.297123,0.000000,0,0.900198,0.939838,-0.840035,0.000000,0,0.000000,0.000000,0.000000,1.000000,0];
    public static const hue210:Array = [-0.137923,1.360417,-0.222494,0.000000,0,0.439428,0.205717,0.354856,0.000000,0,1.016778,0.783067,-0.799845,0.000000,0,0.000000,0.000000,0.000000,1.000000,0];
    public static const hue220:Array = [-0.025318,1.363247,-0.337929,0.000000,0,0.369613,0.226089,0.404298,0.000000,0,1.111840,0.621019,-0.732860,0.000000,0,0.000000,0.000000,0.000000,1.000000,0];
    public static const hue230:Array = [0.097433,1.343171,-0.440604,0.000000,0,0.297945,0.258107,0.443948,0.000000,0,1.182497,0.458619,-0.641116,0.000000,0,0.000000,0.000000,0.000000,1.000000,0];
    public static const hue240:Array = [0.226600,1.300800,-0.527400,0.000000,0,0.226600,0.300800,0.472600,0.000000,0,1.226600,0.300800,-0.527400,0.000000,0,0.000000,0.000000,0.000000,1.000000,0];
    public static const hue250:Array = [0.358259,1.237421,-0.595680,0.000000,0,0.157747,0.352869,0.489384,0.000000,0,1.242810,0.152358,-0.395168,0.000000,0,0.000000,0.000000,0.000000,1.000000,0];
    public static const hue260:Array = [0.488408,1.154960,-0.643368,0.000000,0,0.093477,0.412733,0.493790,0.000000,0,1.230635,0.017802,-0.248438,0.000000,0,0.000000,0.000000,0.000000,1.000000,0];
    public static const hue270:Array = [0.613095,1.055923,-0.669017,0.000000,0,0.035744,0.478572,0.485683,0.000000,0,1.190445,-0.098778,-0.091667,0.000000,0,0.000000,0.000000,0.000000,1.000000,0];
    public static const hue280:Array = [0.728529,0.943318,-0.671847,0.000000,0,-0.013698,0.548387,0.465311,0.000000,0,1.123460,-0.193840,0.070380,0.000000,0,0.000000,0.000000,0.000000,1.000000,0];
    public static const hue290:Array = [0.831204,0.820567,-0.651771,0.000000,0,-0.053348,0.620055,0.433293,0.000000,0,1.031716,-0.264497,0.232781,0.000000,0,0.000000,0.000000,0.000000,1.000000,0];
    public static const hue300:Array = [0.918000,0.691400,-0.609400,0.000000,0,-0.082000,0.691400,0.390600,0.000000,0,0.918000,-0.308600,0.390600,0.000000,0,0.000000,0.000000,0.000000,1.000000,0];
    public static const hue310:Array = [0.986280,0.559741,-0.546021,0.000000,0,-0.098784,0.760253,0.338531,0.000000,0,0.785768,-0.324810,0.539042,0.000000,0,0.000000,0.000000,0.000000,1.000000,0];
    public static const hue320:Array = [1.033968,0.429592,-0.463560,0.000000,0,-0.103190,0.824523,0.278667,0.000000,0,0.639038,-0.312635,0.673598,0.000000,0,0.000000,0.000000,0.000000,1.000000,0];
    public static const hue330:Array = [1.059617,0.304905,-0.364523,0.000000,0,-0.095083,0.882256,0.212828,0.000000,0,0.482267,-0.272445,0.790178,0.000000,0,0.000000,0.000000,0.000000,1.000000,0];
    public static const hue340:Array = [1.062447,0.189471,-0.251918,0.000000,0,-0.074711,0.931698,0.143013,0.000000,0,0.320220,-0.205460,0.885240,0.000000,0,0.000000,0.000000,0.000000,1.000000,0];
    public static const hue350:Array = [1.042371,0.086796,-0.129167,0.000000,0,-0.042693,0.971348,0.071345,0.000000,0,0.157819,-0.113716,0.955897,0.000000,0,0.000000,0.000000,0.000000,1.000000,0];
    public static const flash:Array = [0.5,0.2,0.2,0.5,0, 0.2,0.5,0.2,0.25,0, 0.2,0.2,0.5,0.125,0, 0,0,0,1,0];

    public function hexTiling(height:Number, color:uint, alpha:Number = 1):BitmapData {
        var width:Number = height * 2 * Math.sin(Math.PI/3);
        var radius:Number = height / 2;
        var bmd:BitmapData = new BitmapData(width, height, true, 0);
        var s:Shape = new Shape;
        var g:Graphics = s.graphics;

        function drawHex(x:Number, y:Number):void {
            g.beginFill(color, alpha);
            g.moveTo(x, y + radius);
            for(var i:int=0; i<6; i++) g.lineTo(x + radius * Math.sin(Math.PI*i/3), y + radius * Math.cos(Math.PI*i/3));
            g.endFill();
        }

        drawHex(0, 0);
        for(var i:int=0; i<3; i++) drawHex(height * Math.sin(Math.PI/6 + Math.PI*i/3), height * Math.cos(Math.PI/6 + Math.PI*i/3));
        var m:Matrix = new Matrix;
        m.rotate(Math.PI/2);
        m.translate(width/2, 0);
        bmd.draw(s, m, null, null, null, true);
        return bmd;
    }

    public function octopusFrame(p:Number = 0):CMLMovieClipTexture {
        var shape:Shape = new Shape;
        var i:int, j:int;
        var tt:Turtle = new Turtle;
        var sin:Number = Math.sin(Math.PI*2*p);
        var arms:int = 9;

        tt.setColor(0x60,0x18,0,0.5).t(ball).fd(1000).pitch(-250).scale(0.5).push().a(0.3).v(0.2).scale(4).p().pop().scale(1.25).h(120).yaw(p*360/arms);
        function arm(parts:int, angle:Number):void {
            while(parts--) {
                tt.p().fd(50).pitch(angle).scale(0.95).h(4).s(-0.02).roll(sin/2);
            }
        }

        for(i=0;i<arms;i++) {
            tt.push().fd(300).pitch(45+15*Math.cos(6*(p+i)/arms*Math.PI*2));
            arm(50, 9+3*Math.cos(3*(p+i)/arms*Math.PI*2));
            tt.pop().yaw(360/arms);
        }

        return tt.renderCMLTexture(200);
    }

    public function rollerFrame(p:Number, rotAngle:Number):CMLMovieClipTexture {
        var shape:Shape = new Shape;
        var i:int, ii:int=3, j:int, jj:int = 3;
        var tt:Turtle = new Turtle;
        function arm(parts:int, angle:Number, len:Number):void {
            while(parts--) {
                tt.p().fd(len).pitch(angle).scaleXYZ(0.8, 1.2, 0.8).ps(0.9).h(-7).s(-0.04).v(-0.05);
            }
        }
        tt.fd(1000).setColor(100,50,25,1).setSize(200).t(ball).scale(0.14).roll(rotAngle).s(0.6).v(0.3).pitch(p*360/ii);
        for(j=0;j<jj;j++) {
            tt.yaw(360/ii/2);
            for(i=0;i<ii;i++) {
                tt.yaw(360/ii).push().fd(450);
                arm(12, -20, 100);
                tt.pop();
            }
            tt.yaw(-360/ii/2);
            tt.pitch(360/jj);
        }
        return tt.renderCMLTexture(200);
    }

    public function bird(p:Number, roll:Number):CMLMovieClipTexture {
        var shape:Shape = new Shape;
        var tt:Turtle = new Turtle;
        function line(parts:int, len:Number, ac:Number = -0.05, hc:Number = 8, p:Number = -5, vc:Number = -0.01):void {
            while(parts--) {
                tt.p().fd(len).s(0.05).v(vc).scale(0.95).pitch(p).a(ac).h(hc);
            }
        }
        function span(parts:int, len:Number, ya:Number):void {
            var cnt:int = parts;
            while(cnt--) {
                tt.p().fd(len).push().yaw(90 + (1-cnt/parts)*ya)
                line(10, 50);
                tt.pop().scale(0.98);
            }
        }
        function wing():void {
            tt.yaw(10).pitch(-15+50*sp);
            span(10,50,20);
            tt.yaw(10).pitch(-15+25*sp);
            span(10,50,20);
            tt.yaw(10).pitch(-15+30*sp);
            span(10,50,20);
        }

        var sp:Number = Math.sin(Math.pow(p,1.5)*Math.PI*2);
        tt.fd(950).setColor(25,75,100,0.9).s(-0.3).setSize(50).t(ball).scale(0.22);
        tt.yaw(roll); // player roll
        tt.fd(150*sp).push();
        // wings
        tt.push().pitch(80).yaw(65);
        wing();
        tt.pop().scaleXYZ(-1,1,1).pitch(80).yaw(65);
        wing();
        // body
        tt.pop().scale(3).fd(-3).pitch(95).push().a(-0.25);
        line(30, -20, -0.035, 6, 0, 0);
        tt.pop().pitch(-90).fd(-10).pitch(90);
        tt.h(-60).a(-0.75);
        line(30, 6, 0.01, -8, -2, -0.015);

        var cmct:CMLMovieClipTexture = tt.renderCMLTexture(200);
        var r:Rectangle = new Rectangle(cmct.center.x-5, cmct.center.y-5, 10, 10);
        return cmct;
    }

    public function ellipse(colors:Array, alphas:Array, ratios:Array, w:Number, h:Number):CMLMovieClipTexture {
        var s:Shape = new Shape;
        var container:Sprite = new Sprite;
        var m:Matrix = new Matrix;
        m.createGradientBox(w, h, 0, -w/4, -h/2);
        s.graphics.beginGradientFill("radial", colors, alphas, ratios, m, "pad", "rgb", -0.75);
        s.graphics.drawRect(-w/4, -h/2, w, h);
        s.graphics.endFill();
        s.rotation = -90;
        container.addChild(s);

        var patterns:Vector.<CMLMovieClipTexture> = new Vector.<CMLMovieClipTexture>(32, true);
        for(var i:int=0; i<32; i++) {
            var r:Rectangle = container.getBounds(container);
            r.x = Math.floor(r.x);
            r.y = Math.floor(r.y);
            r.width = Math.ceil(r.width);
            r.height = Math.ceil(r.height);
            var bmd:BitmapData = new BitmapData(r.width, r.height, true, 0);
            bmd.draw(container, new Matrix(1, 0, 0, 1, -r.x, -r.y), null, null, null, true);
            var cmct:CMLMovieClipTexture  = new CMLMovieClipTexture(bmd);
            cmct.center.x = -r.x;
            cmct.center.y = -r.y;
            patterns[i] = cmct;
            s.rotation += 360/32;
        }
        patterns[0].animationPattern = patterns;
        return patterns[0];
    }

    public var octopusCMCT:CMLMovieClipTexture;
    public var rollerCMCT:CMLMovieClipTexture;
    public var shotCMCT:CMLMovieClipTexture;
    public var birdCMCT:CMLMovieClipTexture;

    public function createSprites():void {
        var i:int, frames:int;
        var patterns:Vector.<CMLMovieClipTexture>;

        // create octopus
        frames = 15;
        patterns = new Vector.<CMLMovieClipTexture>(frames, true);
        for(i=0; i<frames; i++) {
            patterns[i] = octopusFrame(i/frames);
        }
        patterns[0].animationPattern = patterns;
        octopusCMCT = patterns[0];

        // create rollerthing
        frames = 8;
        var frame:int = 0;
        patterns = new Vector.<CMLMovieClipTexture>(frames*32, true);
        for(i=0; i<frames; i++) {
            for(var rot:int=0; rot<32; rot++) {
                patterns[frame++] = rollerFrame(i/frames, rot/32*360);
            }
        }
        patterns[0].animationPattern = patterns;
        rollerCMCT = patterns[0];

        // create shots
        shotCMCT = ellipse([16777215,15723829,15828736,14685754],[1,0.9607843137254902,1,0],[48,95,151,255], 18, 6);

        // create player
        frames = 15;
        patterns = new Vector.<CMLMovieClipTexture>(frames*7, true);
        var patIdx:int = 0;
        for(i=0; i<frames; i++) {
            for(var r:int=-3; r<4; r++) {
                patterns[patIdx++] = bird(i/frames, -r*15);
            }
        }
        patterns[0].animationPattern = patterns;
        birdCMCT = patterns[0];
    }

    public function texColorMatrix(tex:CMLMovieClipTexture, mtx:Array):CMLMovieClipTexture {
        var f:ColorMatrixFilter = new ColorMatrixFilter(mtx);
        var ret:CMLMovieClipTexture = cloneTexture(tex);
        for(var i:int=0; i<ret.animationPattern.length; i++) {
            var tbmd:BitmapData = tex.animationPattern[i].bitmapData;
            ret.animationPattern[i].bitmapData = new BitmapData(tbmd.width, tbmd.height, true);
            var bmd:BitmapData = ret.animationPattern[i].bitmapData;
            bmd.applyFilter(tbmd, tbmd.rect, tbmd.rect.topLeft, f);
        }
        return ret;
    }

    public function autocrop(tex:CMLMovieClipTexture):void {
        for each(var t:CMLMovieClipTexture in tex.animationPattern) {
            var r:Rectangle = t.bitmapData.getColorBoundsRect(0xff000000, 0, false);
            var bmd:BitmapData = new BitmapData(r.width, r.height, true);
            bmd.copyPixels(t.bitmapData, r, new Point());
            t.bitmapData.dispose();
            t.bitmapData = bmd;
            t.center.x -= r.x;
            t.center.y -= r.y;
            t.rect.x = t.rect.y = 0;
            t.rect.width = r.width;
            t.rect.height = r.height;
        }
    }

    private function cloneTexture(tex:CMLMovieClipTexture) : CMLMovieClipTexture 
    {
        var newTexture:CMLMovieClipTexture = new CMLMovieClipTexture(tex.bitmapData, tex.rect.x, tex.rect.y, tex.rect.width, tex.rect.height),
        i:int, imax:int = tex.animationPattern.length;
        newTexture.cutoutBitmapData = tex.cutoutBitmapData;
        newTexture.alphaMap = tex.alphaMap;
        newTexture.animationPattern = new Vector.<CMLMovieClipTexture>(imax, true);
        newTexture.animationPattern[0] = newTexture;
        newTexture.center.x = tex.center.x;
        newTexture.center.y = tex.center.y;
        for (i=1; i<imax; i++) {
            newTexture.animationPattern[i] = cloneTexture(tex.animationPattern[i]);
        }
        return newTexture;
    }
}

import flash.display.*;
import flash.geom.*;
import org.si.b3.*;

class Turtle {
    private var _stack:Array = []; // state stack
    private var _particles:Array = [];
    public var matrix:Matrix3D = new Matrix3D;
    public var color:FColor = new FColor;
    public var pSize:Number = 100;
    public var texture:BitmapData;
    public function clear():Turtle { _stack.length = _particles.length = 0; return setColor(1).id(); }
    public function push():Turtle { _stack.push({m: matrix.clone(), c:cloneColor(color), s:pSize}); return this; }
    public function pop():Turtle {
        if(!_stack.length) throw(new Error("Can't pop empty stack."));
        var state:Object = _stack.pop();
        matrix = state.m;
        color = state.c;
        pSize = state.s;
        return this;
    }
    public function id():Turtle { matrix.identity(); return this; }
    public function fd(n:Number):Turtle        { matrix.prependTranslation(0, 0, n); return this; }
    public function pitch(angle:Number):Turtle { matrix.prependRotation(angle, Vector3D.X_AXIS); return this; }
    public function yaw(angle:Number):Turtle   { matrix.prependRotation(angle, Vector3D.Y_AXIS); return this; }
    public function roll(angle:Number):Turtle  { matrix.prependRotation(angle, Vector3D.Z_AXIS); return this; }
    public function h(n:Number):Turtle { color.h+=n; return this; }
    public function s(n:Number):Turtle { color.s+=n; return this; }
    public function v(n:Number):Turtle { color.v+=n; return this; }
    public function a(n:Number):Turtle { color.a+=n; return this; }
    public function setSize(n:Number):Turtle { pSize=n; return this; }
    public function setColor(...args):Turtle { color.color.apply(color, args); return this; }
    public function t(t:BitmapData):Turtle { texture = t; return this; }
    public function ps(s:Number):Turtle { pSize *= s; return this; }
    public function scaleXYZ(x:Number, y:Number, z:Number):Turtle { matrix.prependScale(x, y, z); return this; } // doesn't touch pSize!
    public function scale(s:Number):Turtle { return ps(s).scaleXYZ(s, s, s); }
    public function cloneColor(c:FColor):FColor { return new FColor(c.r, c.g, c.b, c.a); }

    public function p():Turtle {
        var p:PParticle = new PParticle;
        p.x = matrix.position.x;
        p.y = matrix.position.y;
        p.z = matrix.position.z;
        p.r = color.r;
        p.g = color.g;
        p.b = color.b;
        p.a = color.a;
        p.size = pSize;
        p.texture = texture;
        _particles.push(p);
        return this;
    }

    private var _mtx:Matrix = new Matrix;
    private var _buffer:BitmapData = new BitmapData(513, 513, true, 0);
    private var _ct:ColorTransform = new ColorTransform;
    public function renderCMLTexture(focus:Number):CMLMovieClipTexture {
        var p:PParticle;
        var z:Number;
        var top:Number = 512, left:Number = 512, bottom:Number = 0, right:Number = 0;
        _particles.sortOn("z", Array.NUMERIC | Array.DESCENDING);
        _buffer.fillRect(_buffer.rect, 0);
        for each(p in _particles) {
            var scale:Number = focus/(focus+p.z);
            var radius:Number = p.size*scale;
            var htw:Number = p.texture.width/2;
            var hth:Number = p.texture.height/2;
            var x:Number = 256+p.x*scale;
            var y:Number = 256+p.y*scale;
            if(x-radius < left) left = x-radius;
            if(y-radius < top) top = y-radius;
            if(x+radius > right) right = x+radius;
            if(y+radius > bottom) bottom = y+radius;
            _mtx.identity();
            _mtx.translate(-htw, -hth);
            _mtx.scale(radius/htw, radius/hth);
            _mtx.translate(x, y);
            _ct.redMultiplier   = p.r/64;
            _ct.greenMultiplier = p.g/64;
            _ct.blueMultiplier  = p.b/64;
            _ct.alphaMultiplier = p.a;
            _ct.redOffset   = p.r/16;
            _ct.greenOffset = p.g/16;
            _ct.blueOffset  = p.b/16;
            _buffer.draw(p.texture, _mtx, _ct, null, null, true);
        }
        var bmd:BitmapData = new BitmapData(right-left+1, bottom-top+1, true, 0);
        var cmct:CMLMovieClipTexture  = new CMLMovieClipTexture(bmd);
        cmct.center.x = 256 - left;
        cmct.center.y = 256 - top;
        left = Math.floor(left);
        top = Math.floor(top);
        right = Math.ceil(right);
        bottom = Math.ceil(bottom);
        bmd.copyPixels(_buffer, new Rectangle(left, top, right-left+1, bottom-top+1), bmd.rect.topLeft);
        return cmct;
    }
}

class PParticle {
    public var x:Number;
    public var y:Number;
    public var z:Number;
    public var r:uint;
    public var g:uint;
    public var b:uint;
    public var a:Number;
    public var size:Number;
    public var texture:BitmapData;
}

class Background extends Shape {
    protected var _tilings:Array = [];
    protected var _layers:uint;
    public var smallScale:Number = 0.25;
    public var scaleFactor:Number = 2;

    public function Background(layers:uint) {
        _layers = layers;
        var color:uint = 0x404040;
        var alpha:Number = 1.05 - layers*0.2;
        for(var i:int=0; i<_layers; i++) {
            _tilings.push(painter.hexTiling(450/2, 0, alpha));
            color -= 0x202020;
            alpha += 0.2;
        }
    }

    public function draw():void {
        var x:Number = -actManager.player.x;
        var y:Number = mc.fps.totalFrame*7.5 - actManager.player.y/2;
        var m:Matrix = new Matrix;
        var s:Number = smallScale;
        fx.srcX = 225 + actManager.player.x;
        fx.srcY = 225 + actManager.player.y;
        mc.clearScreen();
        graphics.clear();
        for(var i:int=0; i<_layers; i++) {
            m.identity();
            m.translate(x, y % _tilings[i].height);
            m.scale(s, s);
            s *= scaleFactor;
            m.translate(450/2, 450/2);
            graphics.beginBitmapFill(_tilings[i], m);
            graphics.drawRect(0, 0, 450, 450);
            graphics.endFill();
        }
    }
}

// overrides --------------------------------------------------------------------------------
class CMLMovieClipEx extends CMLMovieClip {
    public var __pt:Point = new Point();
    public var __offsetX:Number;
    public var __offsetY:Number;

    function CMLMovieClipEx(parent:DisplayObjectContainer, xpos:int, ypos:int, width:int, height:int, clearColor:int=0x000000,
        addEnterFrameListener:Boolean=true, onFirstEnterFrame:Function=null, scopeMargin:Number=32, scrollDirecition:String="vertical") : void
    {
        __offsetX = width/2;
        __offsetY = height/2;
        super(parent, xpos, ypos, width, height, clearColor, addEnterFrameListener, onFirstEnterFrame, scopeMargin, scrollDirecition);
    }

    override public function copyTexture(texture:CMLMovieClipTexture, x:int, y:int, animIndex:int=0) : CMLMovieClip
    {
        var tex:CMLMovieClipTexture = texture.animationPattern[animIndex];
        __pt.x = x - tex.center.x + __offsetX;
        __pt.y = y - tex.center.y + __offsetY;
        screen.copyPixels(tex.bitmapData, tex.rect, __pt, null, null, tex.bitmapData.transparent);
        return this;
    }
}

// volumetrics ------------------------------------------------------------------------------
class EffectContainer extends Sprite {
    public var blur:Boolean = false;
    public var colorIntegrity:Boolean = false;
    public var intensity:Number = 4;
    public var passes:uint = 6;
    public var rasterQuality:String = null;
    public var scale:Number = 2;
    public var smoothing:Boolean = true;
    public var srcX:Number;
    public var srcY:Number;

    protected var _blurFilter:BlurFilter = new BlurFilter(2, 2);
    protected var _emission:DisplayObject;
    protected var _occlusion:DisplayObject;
    protected var _ct:ColorTransform = new ColorTransform;
    protected var _halve:ColorTransform = new ColorTransform(0.5, 0.5, 0.5);
    protected var _occlusionLoResBmd:BitmapData;
    protected var _occlusionLoResBmp:Bitmap;
    protected var _baseBmd:BitmapData;
    protected var _bufferBmd:BitmapData;
    protected var _lightBmp:Bitmap = new Bitmap;
    protected var _bufferSize:uint = 0x8000;
    protected var _bufferWidth:uint;
    protected var _bufferHeight:uint;
    protected var _viewportWidth:uint;
    protected var _viewportHeight:uint;
    protected var _mtx:Matrix = new Matrix;

    public function EffectContainer(width:uint, height:uint, emission:DisplayObject, occlusion:DisplayObject = null) {
        if(!emission) throw(new Error("emission DisplayObject must not be null."));
        addChild(_emission = emission);
        if(occlusion) addChild(_occlusion = occlusion);
        setViewportSize(width, height);
        _lightBmp.blendMode = BlendMode.ADD;
        addChild(_lightBmp);
        srcX = width / 2;
        srcY = height / 2;
    }

    public function setViewportSize(width:uint, height:uint):void {
        _viewportWidth = width;
        _viewportHeight = height;
        scrollRect = new Rectangle(0, 0, width, height);
        _updateBuffers();
    }

    public function setBufferSize(size:uint):void {
        _bufferSize = size;
        _updateBuffers();
    }

    protected function _updateBuffers():void {
        var aspect:Number = _viewportWidth / _viewportHeight;
        _bufferHeight = Math.max(1, Math.sqrt(_bufferSize / aspect));
        _bufferWidth  = Math.max(1, _bufferHeight * aspect);
        dispose();
        _baseBmd           = new BitmapData(_bufferWidth, _bufferHeight, false, 0);
        _bufferBmd         = new BitmapData(_bufferWidth, _bufferHeight, false, 0);
        _occlusionLoResBmd = new BitmapData(_bufferWidth, _bufferHeight, true, 0);
        _occlusionLoResBmp = new Bitmap(_occlusionLoResBmd);
    }

    public function render(e:Event = null):void {
        if(!(_lightBmp.visible = intensity > 0)) return;
        var savedQuality:String = stage.quality;
        if(rasterQuality) stage.quality = rasterQuality;
        var mul:Number = colorIntegrity ? intensity : intensity/(1<<passes);
        _ct.redMultiplier = _ct.greenMultiplier = _ct.blueMultiplier = mul;
        _drawLoResEmission();
        if(_occlusion) _eraseLoResOcclusion();
        if(rasterQuality) stage.quality = savedQuality;
        var s:Number = 1 + (scale-1) / (1 << passes);
        var tx:Number = srcX/_viewportWidth*_bufferWidth;
        var ty:Number = srcY/_viewportHeight*_bufferHeight;
        _mtx.identity();
        _mtx.translate(-tx, -ty);
        _mtx.scale(s, s);
        _mtx.translate(tx, ty);
        _lightBmp.bitmapData = _applyEffect(_baseBmd, _bufferBmd, _mtx, passes);
        _lightBmp.width = _viewportWidth;
        _lightBmp.height = _viewportHeight;
        _lightBmp.smoothing = smoothing;
    }

    protected function _drawLoResEmission():void {
        _copyMatrix(_emission.transform.matrix, _mtx);
        _mtx.scale(_bufferWidth / _viewportWidth, _bufferHeight / _viewportHeight);
        _baseBmd.fillRect(_baseBmd.rect, 0);
        _baseBmd.draw(_emission, _mtx, colorIntegrity ? null : _ct);
    }

    protected function _eraseLoResOcclusion():void {
        _occlusionLoResBmd.fillRect(_occlusionLoResBmd.rect, 0);
        _copyMatrix(_occlusion.transform.matrix, _mtx);
        _mtx.scale(_bufferWidth / _viewportWidth, _bufferHeight / _viewportHeight);
        _occlusionLoResBmd.draw(_occlusion, _mtx);
        _baseBmd.draw(_occlusionLoResBmp, null, null, BlendMode.ERASE);
    }

    public function startRendering():void {
        addEventListener(Event.ENTER_FRAME, render);
    }

    public function stopRendering():void {
        removeEventListener(Event.ENTER_FRAME, render);
    }

    protected function _applyEffect(src:BitmapData, buffer:BitmapData, mtx:Matrix, passes:uint):BitmapData {
        var tmp:BitmapData;
        while(passes--) {
            if(colorIntegrity) src.colorTransform(src.rect, _halve);
            buffer.copyPixels(src, src.rect, src.rect.topLeft);
            buffer.draw(src, mtx, null, BlendMode.ADD, null, true);
            mtx.concat(mtx);
            tmp = src; src = buffer; buffer = tmp;
        }
        if(colorIntegrity) src.colorTransform(src.rect, _ct);
        if(blur) src.applyFilter(src, src.rect, src.rect.topLeft, _blurFilter);
        return src;
    }

    public function dispose():void {
        if(_baseBmd) _baseBmd.dispose();
        if(_occlusionLoResBmd) _occlusionLoResBmd.dispose();
        if(_bufferBmd) _bufferBmd.dispose();
        _baseBmd = _occlusionLoResBmd = _bufferBmd = _lightBmp.bitmapData = null;
    }

    protected function _copyMatrix(src:Matrix, dst:Matrix):void {
        dst.a = src.a;
        dst.b = src.b;
        dst.c = src.c;
        dst.d = src.d;
        dst.tx = src.tx;
        dst.ty = src.ty;
    }
}

class VolumetricPointLight extends EffectContainer {
    protected var _colors:Array;
    protected var _alphas:Array;
    protected var _ratios:Array;
    protected var _gradient:Shape = new Shape;
    protected var _gradientMtx:Matrix = new Matrix;
    protected var _gradientBmp:Bitmap = new Bitmap;
    protected var _lastSrcX:Number;
    protected var _lastSrcY:Number;
    protected var _lastIntensity:Number;
    protected var _lastColorIntegrity:Boolean = false;
    protected var _gradientLoResBmd:BitmapData;
    protected var _gradientLoResDirty:Boolean = true;

    public function VolumetricPointLight(width:uint, height:uint, occlusion:DisplayObject, colorOrGradient:*, alphas:Array = null, ratios:Array = null) {
        if(colorOrGradient is Array) {
            _colors = colorOrGradient.concat();
            _ratios = ratios || _colors.map(function(item:*, i:int, arr:Array):int { return 0x100*i/(colorOrGradient.length+i-1) });
            _alphas = alphas || _colors.map(function(..._):Number { return 1 });
        } else {
            _colors = [colorOrGradient, 0];
            _ratios = [0, 255];
        }
        super(width, height, _gradientBmp, occlusion);
        if(!occlusion) throw(new Error("An occlusion DisplayObject must be provided."));
        if(!(colorOrGradient is Array || colorOrGradient is uint)) throw(new Error("colorOrGradient must be either an Array or a uint."));
    }

    protected function _drawGradient():void {
        var size:Number = 2 * Math.sqrt(_viewportWidth*_viewportWidth + _viewportHeight*_viewportHeight);
        _gradientMtx.createGradientBox(size, size, 0, -size/2 + srcX, -size/2 + srcY);
        _gradient.graphics.clear();
        _gradient.graphics.beginGradientFill(GradientType.RADIAL, _colors, _alphas, _ratios, _gradientMtx);
        _gradient.graphics.drawRect(0, 0, _viewportWidth, _viewportHeight);
        _gradient.graphics.endFill();
        if(_gradientBmp.bitmapData) _gradientBmp.bitmapData.dispose();
        _gradientBmp.bitmapData = new BitmapData(_viewportWidth, _viewportHeight, true, 0);
        _gradientBmp.bitmapData.draw(_gradient);
    }

    override protected function _drawLoResEmission():void {
        if(_gradientLoResDirty) {
            super._drawLoResEmission();
            _gradientLoResBmd.copyPixels(_baseBmd, _baseBmd.rect, _baseBmd.rect.topLeft);
            _gradientLoResDirty = false;
        } else {
            _baseBmd.copyPixels(_gradientLoResBmd, _baseBmd.rect, _baseBmd.rect.topLeft);
        }
    }

    override protected function _updateBuffers():void {
        super._updateBuffers();
        _gradientLoResBmd = new BitmapData(_bufferWidth, _bufferHeight, false, 0);
        _gradientLoResDirty = true;
    }

    override public function setViewportSize(width:uint, height:uint):void {
        super.setViewportSize(width, height);
        _drawGradient();
        _gradientLoResDirty = true;
    }

    override public function render(e:Event = null):void {
        var srcChanged:Boolean = _lastSrcX != srcX || _lastSrcY != srcY;
        if(srcChanged) _drawGradient();
        _gradientLoResDirty ||= srcChanged;
        _gradientLoResDirty ||= (!colorIntegrity && (_lastIntensity != intensity));
        _gradientLoResDirty ||= (_lastColorIntegrity != colorIntegrity);
        _lastSrcX = srcX;
        _lastSrcY = srcY;
        _lastIntensity = intensity;
        _lastColorIntegrity = colorIntegrity;
        super.render(e);
    }

    override public function dispose():void {
        super.dispose();
        if(_gradientLoResBmd) _gradientLoResBmd.dispose();
        _gradientLoResBmd = null;
    }
}

// scripts --------------------------------------------------------------------------------
var cmlScript:String = String(<cml><![CDATA[
w60[[w60-$l*5&rungroup0l$r+=0.5]10w70];
#S1{@{^f-12[q-14f{vx-6a0.3,-1}q14f{vx8a-0.4,-1}w2q-14f{vx-2a0.1,-1}q14f{vx4a-0.2,-1}w2q-14f{vx-8a0.4,-1}q14f{vx6a-0.3,-1}w2q-14f{vx-4a0.2,-1}q14f{vx2a-0.1,-1}w2]}
^f12{ay-1}[ha0q-12fq0fq12fw2q-14fq0fq14fw2ha0q-12fq0fq12fw2q-10fq0fq10fw2]};
#S2{ha0[[bm4,30+$l*10f30w2]3[bm4,60-$l*10f30w2]3]};
#S3{ha0[[qx$l*10-30f30]7w2]};
#G0{qy-225[?$?<0.4qx-$?*150-25n{}qx$?*150+25n{}:qx$??*175n{}]&groupbonus0};
#G3{l$1=$i?(3)[?$1==2{qx-225[qy-160n{}w5]5}{qx225[qy-160n{}w5]5}:qx$1*450-225[qy-160n{}w5]10]&groupbonus200};
#G6{l$1=$i?(3)[?$1==2{qx-225[qy-$l*20n{}w5]5}{qx225[qy-$l*20n{}w5]5}:qx$1*450-225[qy-$l*15n{}w5]10]&groupbonus200};
#G7{l$1=$i?(3)[?$1==2{qx-225[qy-120+$l*20n{}w5]5}{qx225[qy-120+$l*20n{}w5]5}:qx$1*450-225[qy-120+$l*15n{}w5]10]&groupbonus200};
#G9{qy-225[s?$i?(5)[qx$??*200n{}w3]10:1[qx$l*40-180n{}w3]10:2[qx180-$l*40n{}w3]10:3[qx180-$l*40n{}qx$l*40-180n{}w3]5:4[qx20+$l*40n{}qx-$l*40-20n{}w3]5]&groupbonus200};
#G10{qy-225[s?$i?(3)[qx$l*40-180n{}w3]10:1[qx180-$l*40n{}w3]10:2[qx180-$l*40n{}qx$l*40-180n{}]10]&groupbonus200};
#G13{q175,-225[?$?<0.4n{}w15n{}:n{}]&groupbonus0};
#E0{20,100#T{[bm$2,$1,,2bm2,2f$3{3}w$2*2bm$2,-$1,,2bm2,2f$3{3}w]3}i20py-120~ha180[s?$r&T 160,8,6:35&T 160,8,7:70&T 160,9,8:105&T 160,10,8:140&T 140,10,10:180&T 140,10,12]ay0.1};
#E1{16,100#B{bm$1,360bm$2,15,5,1}i20py-120~ha$?*360[s?$r&B 12,12:40&B 12,14:70&B 14,14:110&B 16,14:140&B 18,14:190&B 20,14]f7{1}w60ay0.1};
#E2{20,100i20py-120~bs37,10,,1bm2,,0.2^f{2}ha90[s?$rf4:30[hs180f4.5]2f:60[hs120f5]3:90[hs90f5]4:130[hs88f5.5]4]w90ay0.1};
#E3{9,5{l$1=$sx*-12l$2=$sx*6[v$1,0w6v$2,10w9v$1,0w6v$2,-10w9]} br1,40[s?$rw70-$r*0.4f3{0}:110w25f3{0}:250w22br1,40bm2,3f4{0}]};
#E4{9,5^f{4}{[s?$r[w45-$r*0.3f6+$r*0.08]:70[w15f12]:100[w15f13.5]:130[w15f15]:190[w12ht$??*10f16]]}{[csa10w]}l$1=$sx*270l$2=$sx*-90[i0ha$1rcw10i45ha$2rc~]};
#E5{9,5vx$sx*-5ha180[s?$rbr1,30,2^f4{2}:30br1,40,2^f4{2}:55br1,30,2^f4{2}:80br2,30,2^f4{2}:105br2,30,3^f6{2}:140br2,60,4bm2,20^f7{2}:220br2,30,3bm2,,0.8^f10{5}][vy-8ay0.8w22f4]};
#E6{7,3{[s?$rw30bm2,80-$r*0.4f6{0}:100w30bm4,160-$r*0.4f6{0}:200:w8bm4,90f6{0}w22f12{3}]}v$sx*-24,0i45v$sx*8,4~ay-0.5};
#E7{7,3{w$?*40+10ha[s?$r[f4{1ay0.2+$r*0.003}w80-$r*0.4]:120ha$??*30[f4{1ay0.5}w25]:220bm2,30,2ha$??*20[f4{1ay0.6}w20]]}v,-4i60px$sx*-200v$sx*6,4~htad-0.3};
#E8{7,3{w20[s?$rbr$r*0.02+1,120,2,2f4+$r*0.03{2}:210br4,120,2,2bm2,,2f14{5}]}v$sx*-12,-8i60p$sx*120,0v$sx*-12,4~ay-0.5};
#E9{3,1^f{0}{ht$??*5ad1.2w14ad-1.2}[s?$rbm2,,1{w25-$r*0.3f6+$r*0.12}w25[w18-$r*0.1f6+$r*0.12+$l]:80w10bm3,,2f14{3}w15[w10f16+$l]:140w10bm4,,3f16{3}w15[w10f18+$l*2]]};
#E10{3,1^f{1}vy48i12vy0~ha$?*360[s?$rbm2+$r*0.03,360:230bm10,360]f$?*1.5+3w5ay-0.12[w20ha$?*360f]};
#E11{3,1[s?$rvy24:85vy32:175vy40]i8vy0~[s?$rbm3,,1:30bm3,,1.5:60bm2,360bm3,,1.5:125bm3,240bm3,,1.5:260bm3,40bm3,,1.5]f-8{5hoad0.6}w5ad1};
#E12{35,300i20py-120~[s?$rl$1=75l$2=6:25l$1=60l$2=7:50l$1=45l$2=8:70l$1=30l$2=9:90l$1=25l$2=10:150l$1=15l$2=10][i0vx$i?(2)*8-4i12vx~bm12,360f40{wbm2,$1f$2{0}ko}$1,$2w12]5ay1};
#E13{35,300i20py-120~{w25[s?$rbm4,270:40bm5,360:80bm5,288:120bm7,309][ht-24bv0.5f4{4}hs4[f]5bv-0.5[f]7w50]6}i50[px-175v~px175v~]3ay1};
#E14{35,300i20py-120~vy-0.25[ht-90f8{5hoad-0.5ht-30*$1f6{2}hs$r*0.04*$1[w4f]7ko}1w30ht90f8{.}-1w]3ay1};
]]></cml>);

// mml --------------------------------------------------------------------------------
var mmlMain:String = String(<mml><![CDATA[#TITLE{Nomltest main theme};t152;
#A=v12c4v6c8;#C=e.f.dc4d;#B=C>a1rg4.<c4.;#T=s28dd8ff8gg8aa8<crd8s24[>d<c>d<cd8]20>d<c>d<c;
#S=s20[dfgagf]8s28aa8<cc8dd8ff8s25grars20[[a8gfd8]s25a8a8]a8gfdefgfedc>a<c>agfgfededc>a<c>agfaf<c;
%1r1r1$[@4q1s24o6l8[r4A(5)A(4)A(0)A(2)v12>a<rr4A(5)A(4)A(7)A(5)v12cr]|
@2q2s22l4[d2r8ef2rgf2rgfedc|d2ref2rga2rgagfer8]d2ref2rga2r<cdc>agf8
@3q3s20l8B>f4.e2.r<Ca1rg4.a4.<c1rr>Bf4.e2.re.f.dc.d.>ag.<c.>ag.a.fd.f.ab-.<d.f(c+.e.g ar4.
@2q2s22f2.ef2.rgf4e4d4c4>a2.rfg2.fag2.fecde4f4g4a4<c+4d4e4.f2.ef2.rga4g4f4e4c2.r>fg2.fal4gfec.d.a.<d.q5a2.r8]4
@3q0s20l16S[>a<c>a<cd8>a<c>a<cd8erf8]3Td8d8;
#A=v8a4v4a8;#B=degfgea4degf<c4d4>degf;#C=l8a.a.fe4f;#D=v10q0s24a8a8r4v8q3s20;#E=d>a<cd4>a<cd4>a<cdfecl4dc>ag;
%1r1r1$[@4q1s24v8l8[r4AAAAv8gr|r4AAA(2)A(3)v8gr]
v4q0o4l16a<(cdega<(cdl32s28edgeag<(c>a<dcedgeag<(cd>(a<c>gaegdecd>))a<c>gal16s24edc>))agedc|
v8s20o5l8[aB<c4>a4gaegde|f4B<c4d4cd>a<c>gec]f4B<e4f4egdecd>a
q3o5Cd1rl4c.f.d.c.DC<d1rl4c.d.e2.>DCd1rl4c.f.a.g.Dl8a.a.fe.f.dc.e.ec.e.c>a.<c.df.a.<d>(g.a.<d(er4.
v8o6Efedcl8d4ced4ced4cfe4c>ga b-<l4cdefab-<c+l8c+Eagfel8d4ced4cfl4edc>a.b-.<g.b-.<q5g2.l8r]4
@1v6q0l16r8.S[>a<c>a<cd8>a<c>a<v12crdrv6erf]3Td;
#A=v8d4v4d8;#B=[aaeeffdd];
%1l8r1r1$[@4q1s24v8l8[r4AA(-2)AA(3)v8err4AA(-2)AA(7)v8er]|
@1q1s32o7v8l16B32[B4[ggddeecc]3v10q0s24>c8c8<v8q1s32aacc]3B[ggddeecc]
@4q0s20o4v6l8f.g.a<d.g.ad.g.b<d.e.g
@4q1s24o5v8[a4A(3)A(2)AA(-2)v8drr4A(3)A(2)A(-2)A(-5)v8>a<rr4A(-7)A(-9)A(-5)A(-7)|
>v8efgl4ab-<c+defgl8g]v8grs20>l4f.<d.g.q5<d2.l8r]4
@4q1s24o5v8l8[r4AA(-2)AA(3)v8err4AA(-2)AA(7)v8gr]5;
#A=dd<d>d<d>d<cd>;#B=>b-b-<b->b-<b->b-<ab-;#C=>g8.g<g8>g8rrgg<g8g8;#D=dd<d8>dr<d8>;#M=[A]3d<cd>a<cd>a<c>;
%1@2v10q0s30o3l16M$[[M]4|[[B]3>b-<ab->b-<ab-ab-[B(2)]3c-b<c>cb-<c>c<c>[M]]
q1s24[[C(3)][C(2)]]3CC(2)l8>b-.b-.<b->e-.e-.<e-l8e.e.eq0s26(a.((a.((l16aa
v10[[D]4[D(-4)]4[D(-7)][D(-5)]|[D(-4)][D(-5)]][D(1)]4]4
[[>b-b-<b->b-<b->b-<ab-][cc<c>c<c>cb-<c>][dd<d>d<d>d<cd>]4]5;
#B=o2v10c;#W=o2v6c;#S=o4v12c;#H=o6v6g;#F=o4v10c))c(c(c(c(c(c;
#A=BHHWSHHBr))c)cHSHHB;#C=BS))cWSr))ccBF;#D=BHHWSHBrHrB))cSHBr;
%2@0q0s29l16AC$[[A]7C|[[D]7C][BrHHo2v8crHHSrv6cHrrHHo2v10rrcHrro2v7crSr|v10cHrrgg]4
o4v6crc((c((c((c[BrHHSrHHrHB))cSo2v10rcHBrHo2v8cSrHHrHB))cSrHH]3
BrHo2v12crHo4v10cro2v12crHo2v12crHSv9cs28(c8.s28(c8.s27(c8 s26c8 s29o2v12rc o4v10c(c(c(c
[[A]6|S8c8Brs27S4c8s29Brs27o4v13c4c8s29Brs27o4v15c4c8s29Bro4v13c((c]AC]4
[D]7C[BHHWSHBrHrB))cSrcH]3S))cWS))cWS))cWS))cWSrv14s27c8s29[[D]3C];
]]></mml>);

var mmlGameOver:String = String(<mml><![CDATA[#TITLE{Nomltest gameover};t152;
%1@3v12o6q6s26l8r2e.f.dc.d.>ag.<c.>ag.a.fe1;%1@4v8o5q6s26l8r2b-.b-.b-g.g.gf.f.fe.e.e >a1;
%1@4v6 o5q6s26l2r2fed>b-<c1;%1@2v10o3q0s26l8r2g.g.ga.a.ab-.b-.b-<c.c.c q6s18d1;
%2@0q0s28l16r4o4v14cv12c((c((co2v12co6v8rgg o2v10co6v8rgg o4v14crv8c o6v8g o6v8rrgg o2v12rrc o6v8grr o2v10cr o4v14cr v12c o6v8grrggq2s20 o6v5g1;
]]></mml>);