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: Moja Grand Prix

[Arrow Keys] : Steer / Move Menu Cursor
[Z] : Accelerate / Select
[X] : Brake, Reverse / Cancel
[C] : Change Camera
[CTRL] : Pause Menu

最初だけマウスでクリックして、あとはキーボードで操作します。
ドリフトはスピードつけてカーブ中にアクセルを離してまたすぐ押す感じ。
Z、Xの代わりにメニューではEnter、SHIFT、運転は上下キーでも代用できるけど、ドリフトがしにくくなるかもしれないです。
タイムトライアルモードではゴーストが10台まで一時的に残ります。(一時的なのでSWFをリセットすると消えちゃう)
セーブデータはSharedObjectで保存してるけど、Fileのエクスポート/インポートを使うとデータが移せます。
ちなみに全部クリアしても特別な事は起こりません・・・気が向いたら何か追加するかも。
/**
 * Copyright h_sakurai ( http://wonderfl.net/user/h_sakurai )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/icMM
 */

// forked from tencho's Moja Grand Prix
/**
 * [Arrow Keys] : Steer / Move Menu Cursor
 * [Z] : Accelerate / Select
 * [X] : Brake, Reverse / Cancel
 * [C] : Change Camera
 * [CTRL] : Pause Menu
 * 
 * 最初だけマウスでクリックして、あとはキーボードで操作します。
 * ドリフトはスピードつけてカーブ中にアクセルを離してまたすぐ押す感じ。
 * Z、Xの代わりにメニューではEnter、SHIFT、運転は上下キーでも代用できるけど、ドリフトがしにくくなるかもしれないです。
 * タイムトライアルモードではゴーストが10台まで一時的に残ります。(一時的なのでSWFをリセットすると消えちゃう)
 * セーブデータはSharedObjectで保存してるけど、Fileのエクスポート/インポートを使うとデータが移せます。
 * ちなみに全部クリアしても特別な事は起こりません・・・気が向いたら何か追加するかも。
 */
package {
    import com.bit101.components.ProgressBar;
    import com.bit101.components.Style;
    import flash.display.Sprite;
    import flash.display.StageQuality;
    import flash.events.Event;
    import flash.events.KeyboardEvent;
    import flash.ui.Keyboard;
    
    public class MojaGrandPrix extends Sprite {
        
        private var progress:ProgressBar;
        public var keyEnabled:Boolean = true;
        
        public function MojaGrandPrix() {
            if (stage) init();
            else addEventListener(Event.ADDED_TO_STAGE, init);
        }
        
        private function init(e:Event = null):void {
            addChild(Create.box((Display.width - 4000) / 2, (Display.height - 4000) / 2, 4000, 4000, 0, 1));
            
            Display.stage = stage;
            Display.setSize(465, 465);
            transform.perspectiveProjection.fieldOfView = 100;
            transform.perspectiveProjection.projectionCenter = Display.center;
            stage.frameRate = 30;
            stage.quality = StageQuality.LOW;
            
            $.moja = this;
            $.howto = new HowTo();
            $.replay = new ReplayManager();
            $.thumb = new Thumb();
            $.race = new Race();
            $.game = new Game();
            $.user = new UserData();
            $.selector = new TrackSelector();
            $.world = new World();
            $.top = new TopMenu();
            $.setting = new SettingMenu();
            $.transition = new FadeTransition();
            $.tape = new TickerTape();
            $.complete = new Complete();
            $.label = new KeyLabel();
            
            $.user.setDefaultData();
            $.user.load();
            
            Dot.init();
            $.label.init();
            $.complete.init();
            $.transition.init();
            $.game.init();
            $.setting.init();
            $.top.init();
            $.selector.init();
            
            ScoreWindow.title = "Moja Grand Prix";
            ScoreWindow.init(loaderInfo, start, onErrorScore);
        }
        
        private function onErrorScore():void {
            $.top.setNoConnection();
            start();
        }
        
        private function start():void {
            $.world.checkRankIn($.user);
            $.world.checkComplete($.user);
            $.game.sprite.scrollRect = Display.size;
            
            addChild($.game.sprite);
            addChild($.selector.sprite);
            addChild($.top.sprite);
            addChild($.setting.sprite);
            addChild($.complete.sprite);
            addChild($.howto.sprite);
            addChild($.tape.sprite);
            addChild($.label.sprite);
            addChild(ScoreWindow.sprite);
            addChild($.transition.sprite);
            
            Style.PROGRESS_BAR = 0xDE472D;
            progress = new ProgressBar(this, (Display.width - 80) / 2, (Display.height - 4) / 2);
            progress.setSize(80, 4);
            SE.init();
            SE.mute = $.user.mute;
            $.thumb.create(onComplete, onProgress);
        }
        
        private function onProgress(per:Number):void {
            progress.value = per;
        }
        
        private function onComplete():void {
            removeChild(progress);
            $.game.createStage($.world.demo);
            $.game.isDemo = true;
            $.game.startRace();
            $.transition.fadeIn();
            stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyUpDown);
            stage.addEventListener(KeyboardEvent.KEY_UP, onKeyUpDown);
        }
        
        private function onKeyUpDown(e:KeyboardEvent):void {
            if (!keyEnabled) return;
            $.game.keyUpDown(e);
            $.howto.keyUpDown(e);
            if (e.type == KeyboardEvent.KEY_DOWN) return;
            switch(e.keyCode) {
                case 90: case Keyboard.ENTER://Z,ENTER
                    if ($.top.scene == TopMenu.SCENE_CLICK) $.top.gotoTop();
                    break;
                case 81://Q
                    $.user.groundQuality = ++$.user.groundQuality % 2;
                    $.game.updateGround();
                    $.setting.update();
                    $.user.save();
                    break;
                case 80://P
                    $.user.showParticles = !$.user.showParticles;
                    $.setting.update();
                    $.user.save();
                    break;
                case 86://V
                    $.user.viewDistance = ++$.user.viewDistance % 5;
                    $.game.updateDisplay();
                    $.setting.update();
                    $.user.save();
                    break;
                case 76://L
                    $.user.showLight = !$.user.showLight;
                    $.game.updateDisplay();
                    $.setting.update();
                    $.user.save();
                    break;
                case 77://M
                    $.user.showMirror = !$.user.showMirror;
                    $.game.updateDisplay();
                    $.setting.update();
                    $.user.save();
                    break;
                case 83://S
                    $.user.mute = SE.mute = !SE.mute;
                    $.setting.update();
                    $.user.save();
                    break;
            }
        }
        
    }
    
}

import Box2D.Collision.Shapes.b2FilterData;
import Box2D.Common.Math.b2Vec2;
import Box2D.Dynamics.b2Body;
import com.actionsnippet.qbox.objects.GroupObject;
import com.actionsnippet.qbox.QuickBox2D;
import com.actionsnippet.qbox.QuickContacts;
import com.actionsnippet.qbox.QuickObject;
import com.bit101.components.Label;
import com.bit101.components.Style;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.BitmapDataChannel;
import flash.display.BlendMode;
import flash.display.DisplayObject;
import flash.display.DisplayObjectContainer;
import flash.display.GradientType;
import flash.display.Graphics;
import flash.display.Loader;
import flash.display.LoaderInfo;
import flash.display.MovieClip;
import flash.display.Shape;
import flash.display.Sprite;
import flash.display.Stage;
import flash.display.StageQuality;
import flash.events.Event;
import flash.events.IOErrorEvent;
import flash.events.KeyboardEvent;
import flash.events.MouseEvent;
import flash.events.SecurityErrorEvent;
import flash.filters.DropShadowFilter;
import flash.filters.GlowFilter;
import flash.geom.ColorTransform;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.geom.Vector3D;
import flash.net.FileReference;
import flash.net.SharedObject;
import flash.net.URLRequest;
import flash.system.LoaderContext;
import flash.text.TextFormat;
import flash.ui.Keyboard;
import flash.utils.ByteArray;
import flash.utils.describeType;
import net.wonderfl.utils.WonderflAPI;
import org.libspark.betweenas3.BetweenAS3;
import org.libspark.betweenas3.easing.Cubic;
import org.libspark.betweenas3.tweens.ITween;
import org.si.sion.SiONDriver;
import org.si.sion.utils.SiONPresetVoice;

/**
 * 全データ
 */
class $ {
    
    static public var moja:MojaGrandPrix;
    static public var label:KeyLabel;
    static public var tape:TickerTape;
    static public var howto:HowTo;
    static public var replay:ReplayManager;
    static public var thumb:Thumb;
    static public var race:Race;
    static public var world:World;
    static public var game:Game;
    static public var user:UserData;
    static public var selector:TrackSelector;
    static public var top:TopMenu;
    static public var setting:SettingMenu;
    static public var transition:FadeTransition;
    static public var complete:Complete;
    
}

/**
 * おめでとう
 */
class Complete {
    
    public var sprite:Sprite = new Sprite();
    private var _message:Label;
    private var _congra:Bitmap;
    
    public function Complete() {
    }
    
    public function init():void {
        var sp:Sprite = new Sprite();
        UI.createLabel(sp, 0, 0, "CONGRATULATIONS!", 32);
        _congra = Create.gradientLabel(sp, 0xF8F044, 0xE98A0A);
        _congra.x = 50;
        _congra.y = 100;
        sprite.addChild(_congra);
        _message = UI.createLabel(sprite, 60, 150, "", 16, 0xF8F044);
        _message.filters = UI.TEXT_FILTERS;
        hide();
    }
    
    public function show(lv:int):void {
        $.game.destroyStage();
        switch(lv) {
            case Race.NORMAL:
                $.world.complete.light = Light.DAY;
                $.world.complete.setPlace(Track.FOREST);
                break;
            case Race.HARD:
                $.world.complete.light = Light.DAWN;
                $.world.complete.setPlace(Track.CIRCUIT);
                break;
            case Race.EXTREME:
                $.world.complete.light = Light.NIGHT;
                $.world.complete.setPlace(Track.SNOWFIELD);
                break;
        }
        $.game.createStage($.world.complete);
        SE.stopAll();
        SE.setVolume(1);
        SE.effect(SE.SND_FINISH);
        $.game.startRace();
        var f:Point = $.game.player.front.clone();
        f.normalize(300);
        $.game.player.addForce(f);
        _message.text = "You Completed All '" + Text.LEVELS[lv] + "' Tracks!";
        sprite.visible = true;
    }
    
    public function hide():void {
        sprite.visible = false;
    }
    
}

/**
 * コース情報
 */
class Track {
    
    static public const CIRCUIT:int = 0;
    static public const FOREST:int = 1;
    static public const SNOWFIELD:int = 2;
    static public const WINTERCIRCUIT:int = 3;
    
    public var name:String = "Demo Course";
    public var id:int = -1;
    public var level:int = 0;
    public var unlock:int = 0;
    public var handicap:Number = 1;
    public var light:int = 0;
    public var place:int = CIRCUIT;
    public var lapsNum:int = 3;
    public var enemiesNum:int = 7;
    public var fenceHeight:Number = 0.8;
    public var fenceMapSize:int = 6;
    public var fenceMap:BitmapData = null;
    public var dirtFriction:Number = 0.02;
    
    public var lakeColor:Number = 0x2D488C;
    public var edgeColor:uint = 0xD9C364;
    public var roadColor:uint = 0x4A4A4A;
    public var lineColor:uint = 0xFFFFFF;
    public var pathColor:uint = roadColor;
    public var dirtColor:uint = 0x73900B;
    public var lakeEdge:uint = 0x8598A6;
    
    public var roadSize:Number = 25;
    public var pathSize:Number = 20;
    public var lakes:Array = [];
    public var points:Array = [];
    public var path:Array = [];
    public var objectS:Array = [];
    public var objectL:Array = [];
    public var lamps:Array = [];
    public var trees:Array = [];
    public var fences:Array = [];
    public var planes:Array = [];
    public var plants:Array = [];
    public var seatsXY:Array = [75, 11];
    public var wancoXY:Array = [0, 0];
    public var checkPoints:Array = [[0, 0, 100, 0], [0, 0, -100, 0]];
    
    public var particleColors:Array = [];
    public var gazeXY:Array = [];
    public var cameraXY:Array = [];
    
    public function Track(param:Object = null) {
        if (!param) return;
        if (param.course != null) setCourse(param.course);
        if (param.lv != null) level = param.lv;
        if (param.light != null) light = param.light;
        if (param.place != null) setPlace(param.place);
        if (param.unlock != null) unlock = param.unlock;
        if (param.reverse) reverse();
    }
    
    public function reverse():void {
        name += "(R)";
        points.reverse();
        for each (var p:Array in points) {
            if (p.length < 6) continue;
            var tmp:Number = p[4];
            p[4] = p[2];
            p[2] = tmp;
            tmp = p[5]
            p[5] = p[3];
            p[3] = tmp;
        }
        var start:Array = points.splice(-2, 2);
        points = start.concat(points);
        for (var i:int = 0; i < checkPoints.length; i++)
            checkPoints[i] = [checkPoints[i][2], checkPoints[i][3], checkPoints[i][0], checkPoints[i][1]];
        checkPoints.reverse();
        checkPoints.unshift(checkPoints.pop());
    }
    
    /**
     * 指定の場所にコース図を描画する
     */
    public function draw(g:Graphics, thickness:Number = 1, color:uint = 0xFFFFFF, alpha:Number = 1, scale:Number = 1):void {
        g.lineStyle(thickness, color, alpha);
        for (var i:int = 0; i < points.length; i++) {
            var a:Array = points[i];
            var b:Array = points[(i+1) % points.length];
            CurveUtil.drawBezierXY(g, a[0], a[1], b[0], b[1], a[2], a[3], b[4], b[5], 4, scale);
        }
    }
    
    /**
     * コース形状を決める
     */
    public function setCourse(num:int):void {
        switch(num) {
            case -1:
                name = "How To Drift";
                roadSize = 60;
                pathSize = 130;
                cameraXY = [-14.42,-25.85];
                gazeXY = [-7.52,-36.52];
                seatsXY = [-46.18,-1.77];
                trees = [[3.49,26.99],[10.74,24.49],[18.37,27.24],[-0.37,34.49],[-51.13,52.99],[-46.75,58.62],[-37.25,56.24],[-32.5,47.99],[-46.5,47.99],[-50.25,40.74],[-75.37,39.12],[-74.75,55.49],[-73.37,68.24],[-21.37,71.49],[-14.88,62.62],[-9,52.61],[-26.5,36.25],[-20.75,27.24],[43.99,31.99],[47.99,39.24],[59.24,38.75],[62.5,28.49],[60.5,-33.75],[63.62,-38.12],[77.37,-35.12],[71.24,-14.37],[64.62,-12.75],[71.62,-8.88],[42.99,-19],[40.49,-27.5],[41.99,-38],[43.37,-43.75],[22.24,-53.25],[5.5,-55.63],[7.75,-79.12],[26.62,-80.25],[57.5,-83.62],[74.74,-65.37],[74.74,-52.75],[48.24,-88.25],[86.37,-31.5],[88.5,15],[76.49,60.5],[72.25,71.25],[58.75,82.5],[43.5,79.25],[31.25,63.24],[-69.5,-73],[-75,-66.75],[-77.25,-54],[-72.87,-42],[-57,-76],[-57.63,-54.12],[-47.62,-54.87],[-53.62,-47.75],[-50.62,-37.75],[-36.13,-54.63],[-71.37,-32.38],[67.87,-34.75],[45.61,-49.63],[52.62,-13.25],[68.24,-78.5],[-48.37,-25.25],[-70.25,81],[-60,90.24],[-45.75,91],[-27.25,82.62],[54,42.49]];
                checkPoints = [[-78.25,-20.75,-43.25,-20.25],[86.99,4.25,55.73,3.75]];
                path = [[[35.49,44.73],[56.98,71.48],[68.73,43.73]],[[-4.25,23.24],[15.49,-1.25],[37.75,23.49]],[[-61,56.25],[-60,76.75],[-32.25,73.24],[-28,61.49]],[[54.48,-36.25],[65,-65.01],[54.75,-68.25],[21.23,-64.53]]];
                points = [[-63.2,-45.35,8.38,10.58,-25.26,-31.89],[-62.7,29.09,-1.25,52.24,0.24,-10.4],[-41.25,72.74,9.09,-2.72,-14.26,4.27],[-15.27,41.48,6.68,-14.52,-6.77,14.72],[10.25,7.99,15.7,-6.79,-12.4,5.36],[39.23,51.23,5.93,16.51,-6.67,-18.58],[65.47,52.98,13.59,-40.57,-4.08,12.17],[80.99,-20.25,-2.24,-11.43,2.53,12.93],[50.23,-25,-4.52,-8.34,4.18,7.72],[43.98,-68.26,-58.25,3.92,29.46,-1.98]];
                wancoXY = [-36.25,-0.77];
                break;
            case 11:
                name = "Moja Circuit+";
                roadSize = 37;
                pathSize = 33;
                cameraXY = [-30.91,59.05];
                gazeXY = [-39.18,10.74];
                seatsXY = [-65.81,-14.73];
                lakes = [[-50.27,-52.14,18],[52.22,29.49,14],[63.05,-58.25,12]];
                objectS = [[63.74,12.45],[66.54,13.76],[63.09,9.89],[8.35,-63.66],[10.94,-64],[13.7,-64.84],[15.95,-65.71],[18.51,-66.4],[21.32,-67.27],[60.78,0.7],[61.82,3.75],[62.47,6.69],[-54.36,-71.8],[-60.69,-69.58],[-65.74,-64.94],[-69.22,-59.53],[-70.27,-52.69]];
                objectL = [[-5.26,67.43],[-6.95,66.03],[-7.7,67.86],[-69.83,20.94],[-71.65,21.69],[-71.44,20.01]];
                lamps = [[-53.58,22.1],[-48.53,25.33],[-44.5,28.15],[-40.25,30.98],[-36.02,34.4],[-31.58,37.44],[-27.34,40.26],[-23.3,42.68],[-88.29,-36.22],[-88.7,-30.37],[-88.29,-23.7],[-88.29,-18.66],[-88.7,-13.82],[-88.29,-9.18],[-17.75,-19],[-10.25,-13.75],[-0.25,-8.5],[7.24,-4.25],[15,0.74],[-39,9.74],[-34,13],[68.99,-44.75],[-13.25,26.99],[-18.75,23.49],[53.24,-69.26],[13,-75],[27.75,-68.25],[28.5,-87],[36.5,-87],[43.49,-87.25]];
                trees = [[-88.29,-53.38],[-88.09,-60.85],[-86.27,-71.95],[-71.73,-89.7],[-62.86,-91.51],[-53.77,-91.52],[-43.69,-91.11],[-35.61,-90.71],[-27.14,-89.5],[-19.67,-87.89],[-12,-86.68],[-1.12,-83.25],[14.87,-84.24],[5.38,-81.02],[35.83,-68.58],[39.61,-65.7],[30.78,-68.72],[46.72,-60.44],[10.19,-79],[8.77,-84.65],[3.13,-86.47],[81.04,10.52],[79.17,6.15],[82.91,14.21],[59.23,-16.45],[55.8,-21.49],[91.78,26.02],[92.53,30.91],[93.86,36.53],[67.09,61.65],[61.65,66.29],[56.2,71.34],[54.19,76.39],[51.56,81.23],[47.14,89.21],[15.44,59.23],[23.51,61.45],[28.96,61.25],[35.82,59.84],[41.88,56.2],[45.48,52.38],[25.99,28.24],[25.25,35.99],[17.24,39.49],[7.75,38.75],[-44.75,4.49],[-44.5,-5.25],[-39.75,-13.25],[-33.25,-18.75],[-24.5,-21.25],[87.24,-79],[82.24,-83.75],[39.75,4.74],[26.99,-15.5],[17.75,-20.25],[22.75,-23.75],[9.49,-27.75],[-57,-20.5],[-54.25,-27.25],[-60,-27],[-62.25,2.75],[91.49,-48.75],[94.49,-38.5],[79.25,-27.25],[72.5,-23.75],[75.99,-25],[31.74,90.5],[18,89.25],[14.49,83.75],[-83.75,31.5],[-77.5,41.74],[-66.25,52.75],[-53.75,61],[-40.5,71.25],[-25,78],[-7,83.75],[24.24,-91.75],[41.25,-92.5],[53.5,-94],[65.24,-92],[74.75,-91]];
                checkPoints = [[-37,30.74,-56.5,58.24],[-46.25,25,-65.75,51.25],[-66.75,2.5,-89.5,6.75],[-64.75,-53.25,-92.25,-55.25],[-43.75,-66,-38,-94.5],[7.5,-53.75,5.99,-94],[23.75,-28.25,93.5,-82.5],[25.74,-22.5,98,-37.25],[44.24,6.99,95.5,10.25],[43.24,53.75,95,92.74],[34.24,61.99,38.5,95.24],[17.75,59,-8.5,93.99],[-26.25,38.75,-46.25,67.99]];
                planes = [[[[-45.75,62.74],[-29,36.5]],3,3,".title",true,1,1],[[[-0.25,0.24],[7.74,5.74]],4,2,".wonderfl",true,1,1],[[[50.74,-87.25],[59.48,-87.5]],5,0,".flash",false,1,1],[[[28.5,-65.75],[10,-59]],3,0,".title",false,1,1]];
                fences = [[[-67.34,-46.98],[-72.14,-33.6],[-72.34,-5.35],[-67.9,7.97],[-57.4,22.48],[-21.29,47.32],[-6.54,52.98],[7.94,58.83],[31.59,67.98],[39.79,65.26],[52.49,51.2],[56.4,42.08]],[[41.03,11.06],[49.1,9.88],[48.3,4.88],[34.04,-7.35],[29.78,-24.33],[31.79,-40.28],[25.51,-52.16],[8,-60.64],[1.87,-60.91],[-18.7,-67.95],[-30.23,-75.39],[-42,-73.7],[-49,-69.65]],[[-70.12,22.9],[-64.88,31.38],[-27.14,58.22],[-9.55,66.28]],[[10.17,73.56],[17.62,76.37],[24.09,82.84],[33.61,87.9],[45.71,85.47],[51.75,71.54],[62.43,61.66],[76.59,52.95],[83.65,42.66],[88.86,32.83],[87.18,18.63]],[[57.98,-5.68],[57.28,-10.69],[52.96,-17.04],[50.74,-28.52],[53.35,-41.25],[51.35,-51.57],[42.06,-59.22],[25.74,-66.93]],[[10,-75.39],[-12.2,-83.46],[-32.79,-87.3],[-54.38,-88.5],[-69.33,-86.67],[-79.8,-79.4],[-84.46,-66.65],[-86.07,-43.69],[-85.85,-6.15],[-84.66,16.84],[-74.56,38.43],[-53.36,55.39],[-32.79,69.92],[-14.23,77.98],[-1.11,77.79],[10.17,73.56]],[[87.1,17.73],[88.79,13.12],[85.67,-17.05],[83.72,-29.37],[87.07,-41.21]],[[87.07,-41.21],[88.75,-64.26],[82.99,-78],[65.98,-86],[21.75,-83.75],[10,-75.39]],[[44,39.75],[40.25,49.24],[24.25,55.74],[4.74,54.24],[-57.5,14.25],[-58.25,-10.75],[-42.75,-32.5],[-19.25,-38],[31.25,-6.25],[41.03,11.06]]];
                path = [[[-78.65,-49.77],[-75.53,-69.25],[-66.5,-79.09],[-52.88,-82.85],[-34,-82.05],[-21.14,-79.03]],[[9.65,68.22],[-3.29,70.98],[-12.54,70.39],[-28.68,63.7],[-47.81,50.02],[-69.51,34.02],[-78.22,15.45],[-78.4,-5.35]],[[39.85,79.16],[32.79,79.2],[24.54,74.79],[15.66,69.75]],[[50,13.49],[41,17.24],[33.75,24.25],[34.49,32.99],[32.25,44],[18,50.25],[5.5,50],[-53,12.5],[-53.25,-9.5],[-39.75,-27.25],[-21.25,-32.5],[26.75,-2.25],[33.75,24.25]],[[-31,23.24],[-15,1.99],[4.25,14.74]],[[5.99,-68.25],[30.49,-57.25],[41.24,-45.75],[41.74,-30.75],[38.5,-20.75],[43.75,-8.75],[75.11,17.24]]];
                points = [[-60.69,27.1,-10.37,-8.62,36.65,30.48],[-78.79,-24.8,-0.33,-10.75,-1.05,35.78],[-62.69,-75.34,22.51,-9.25,-19.3,8.3],[-9.88,-75.56,5.16,2.27,-15.09,-6.65],[8.28,-68.15,8.85,3.67,-6.22,-2.58],[31.23,-76.25,19.5,-0.75,-10.2,0.39],[78.98,-68,7.61,10.94,-8.67,-12.46],[67.48,-32.75,-10.54,3.76,13.46,-4.8],[67.66,7.73,2.54,6.44,-3.41,-8.66],[81.9,31.17,-0.38,9.03,0.56,-13.3],[51.4,61.04,-9.33,10.61,14.75,-16.78],[18.75,70.85,-12.37,-6.37,27.98,14.42]];
                wancoXY = [25.74,-59.01];
                break;
            case 0:
                name = "Clear";
                roadSize = 36;
                pathSize = 15;
                cameraXY = [-24.25,52.23];
                gazeXY = [10.24,4.74];
                seatsXY = [34.23,45.94];
                lamps = [[-42.5,40.98],[-37.75,36.74],[-32.25,32.99],[-26.5,27.5],[-21.25,23.49],[-16.5,19.49],[-10.5,15],[-5.5,10.5],[-0.75,6.5],[3.74,2.74],[8.24,-1.25],[-32.75,54],[-28.25,50.49],[-22.75,45.74],[-18.5,41.99],[-13.25,38.5],[-8.5,34.24],[-3.25,29.75],[1.75,25.99],[6.75,22.5],[11.25,18.24],[15,15],[18.75,12.24]];
                trees = [[43.75,-32.5],[36.99,-27.25],[31,-22.5],[24.74,-17.5],[18.24,-13],[29.74,4.25],[35.99,-1],[42.99,-7],[48.24,-12],[55.25,-17.75]];
                checkPoints = [[46.74,-46,96.48,6.49],[-67.5,45.49,-97.5,71.99],[-5,-10.5,-81.25,-92.5],[59.24,-59.25,92.25,-89.25]];
                fences = [[[63,-56.5],[-65.75,50.49],[-71.25,43.5],[57.5,-63.75],[63,-56.5]]];
                path = [[[25,0.5],[25,16.99],[-29.25,62.49],[-54,63.49]],[[25,13.49],[24.25,59.99],[28.75,66.75],[64.23,66.72]]];
                points = [[79.04,-49.37,0,0,0,0],[-60.19,67.59,0,0,0,0],[-83.5,60.22,0,0,0,0],[-85.25,33.5,0,0,0,0],[50.74,-80.5,0,0,0,0],[79.49,-77.25,0,0,0,0]];
                wancoXY = [80,-17.5];
                break;
            case 1:
                name = "Moja Circuit";
                handicap = 0.85;
                roadSize = 37;
                pathSize = 33;
                cameraXY = [-30.91,59.05];
                gazeXY = [-39.18,10.74];
                seatsXY = [-65.81,-14.73];
                lakes = [[-50.27,-52.14,18],[52.22,29.49,14],[63.07,-60,12]];
                objectS = [[53.11,11.58],[54.04,14.01],[61.49,-6.35],[11.6,-74.16],[13.32,-73.25],[15.09,-72.45],[16.95,-71.34],[18.77,-70.53],[20.32,-69.52],[52.28,9.83],[65.58,-1.24],[63.47,-3.92],[-54.36,-71.8],[-60.69,-69.58],[-65.74,-64.94],[-69.22,-59.53],[-70.27,-52.69]];
                objectL = [[-8.01,67.7],[-7.45,65.79],[-6.2,67.38],[-69.83,20.94],[-71.65,21.69],[-71.44,20.01]];
                lamps = [[-53.58,22.1],[-48.53,25.33],[-44.5,28.15],[-40.25,30.98],[-36.02,34.4],[-31.58,37.44],[-27.34,40.26],[-23.3,42.68],[-88.29,-36.22],[-88.7,-30.37],[-88.29,-23.7],[-88.29,-18.66],[-88.7,-13.82],[-88.29,-9.18],[-17.75,-19],[-10.25,-13.75],[-0.25,-8.5],[7.24,-4.25],[15,0.74],[-39,9.74],[-34,13],[68.5,-46.25],[-13.25,26.99],[-18.75,23.49],[52.49,-69.75],[53.24,6.5],[57.75,11.75],[62.5,16.75],[66.25,21.25],[67.5,27.75],[80,12.5],[83.24,16.5],[49.24,-49],[49.49,-40.5],[48.5,-32.5],[48.24,-24.75],[-70,46.74],[-62.25,52.75],[-53,60],[-44,66.49],[-36.5,71.75],[-27.5,75.74],[-20.5,78.75],[37.99,89.25],[43.49,89.25],[-3.75,-34.5],[4.25,-29.75],[66.99,-18.5],[26.5,-87.75],[33.99,-87.25]];
                trees = [[-88.29,-53.38],[-88.09,-60.85],[-86.27,-71.95],[-71.73,-89.7],[-62.86,-91.51],[-53.77,-91.52],[-43.69,-91.11],[-35.61,-90.71],[-27.14,-89.5],[-19.67,-87.89],[-12,-86.68],[-1.12,-83.25],[14.87,-84.24],[5.38,-81.02],[26.34,-70.33],[37.61,-65.7],[30.78,-68.72],[46.72,-60.44],[11.18,-81.25],[19.02,-88.9],[-4.12,-90.47],[74.54,4.27],[71.42,0.9],[77.18,7.71],[59.23,-16.45],[55.8,-21.49],[87.29,25.53],[87.29,35.42],[85.87,43.29],[67.09,61.65],[61.65,66.29],[56.2,71.34],[54.19,76.39],[51.56,81.23],[50.15,85.47],[15.44,59.23],[23.51,61.45],[28.96,61.25],[35.82,59.84],[41.88,56.2],[45.48,52.38],[25.99,28.24],[25.25,35.99],[17.24,39.49],[7.75,38.75],[-44.75,4.49],[-44.5,-5.25],[-39.75,-13.25],[-33.25,-18.75],[-24.5,-21.25],[82.74,-79],[77.74,-82],[39.75,4.74],[26.99,-15.5],[17.75,-20.25],[22.75,-23.75],[9.49,-27.75],[-57,-20.5],[-54.25,-27.25],[-60,-27],[-62.25,2.75],[5.25,21.75],[-1,20.99],[11.99,18.75],[10.74,9.49],[-31.75,-70.75],[-23.5,-65.5],[-11,-60],[1.75,-56.25],[13,-55],[19.74,-49.75],[-75.75,44],[-67.25,51.5],[-59.25,56.74],[-49,66.99],[-39,73.24],[-27.5,78.75],[-15.25,81.75],[-5.25,30.74],[-8.75,-40.25],[-23.5,-43.25],[-31.75,-40],[-89,6.5],[-88.5,22.75],[-94.5,-29.5],[-94,-18],[88.75,-29.5],[90,-20.25],[91.49,-4],[92.74,-42.5],[10.74,83.5],[23.24,88.75]];
                checkPoints = [[-52.5,21.25,-91.75,75.24],[-61.25,11.99,-98,59.75],[-65.75,-1.5,-92.75,-1.75],[-66.75,-40.25,-94.25,-40.25],[-60.25,-60.75,-83.75,-83.5],[-44.25,-65.75,-37.5,-96],[4.74,-45.75,36.74,-95.25],[23.24,-29.25,94.49,-64.75],[44.24,6.99,93.5,-15],[57.75,31.25,94.74,40],[45,54.75,89.25,92.25],[32.75,62.5,34,94.49],[10.99,57.75,-9,93.75],[-38,31.99,-77,91.99]];
                planes = [[[[-45.75,62.74],[-29,36.5]],3,3,".title",true,1,1],[[[-0.25,0.24],[7.74,5.74]],4,2,".wonderfl",true,1,1],[[[53.24,-86],[62.24,-85.25]],5,0,".flash",false,1,1]];
                fences = [[[-67.34,-46.98],[-72.14,-33.6],[-72.34,-5.35],[-67.9,7.97],[-57.4,22.48],[-21.29,47.32],[-6.54,52.98],[7.94,58.83],[31.59,67.98],[39.79,65.26],[52.49,51.2],[56.4,42.08]],[[41.03,11.06],[49.1,9.88],[48.3,4.88],[34.04,-7.35],[29.78,-24.33],[31.79,-40.28],[25.51,-52.16],[13,-61.64],[1.87,-60.91],[-18.7,-67.95],[-30.23,-75.39],[-42,-73.7],[-49,-69.65]],[[-70.12,22.9],[-64.88,31.38],[-27.14,58.22],[-9.55,66.28]],[[10.17,73.56],[17.62,76.37],[24.09,82.84],[33.61,87.9],[45.71,85.47],[51.75,71.54],[62.43,61.66],[76.59,52.95],[83.65,42.66],[84.87,33.34],[84.44,22.89]],[[69.75,-13.68],[60.03,-10.69],[52.96,-17.04],[50.74,-28.52],[53.35,-41.25],[51.35,-51.57],[42.06,-59.22],[22.75,-68.91]],[[10,-75.39],[-12.2,-83.46],[-32.79,-87.3],[-54.38,-88.5],[-69.33,-86.67],[-79.8,-79.4],[-84.46,-66.65],[-86.07,-43.69],[-85.85,-6.15],[-84.66,16.84],[-74.56,38.43],[-53.36,55.39],[-32.79,69.92],[-14.23,77.98],[-1.11,77.79],[10.17,73.56]],[[84.36,22.48],[88.79,13.12],[85.67,-17.05],[81.23,-30.37],[87.07,-41.21]],[[22.76,-68.31],[22.77,-72.36]],[[87.07,-41.21],[85.5,-60],[79.49,-76.25],[67.24,-82.5],[21.75,-83.75],[10,-75.39]],[[44,39.75],[40.25,49.24],[24.25,55.74],[4.74,54.24],[-57.5,14.25],[-58.25,-10.75],[-42.75,-32.5],[-19.25,-38],[31.25,-6.25],[41.03,11.06]]];
                path = [[[72.02,14.85],[79.83,27.56],[78.61,37.86],[73.37,45.93],[58.05,54.79]],[[-78.65,-49.77],[-75.53,-69.25],[-66.5,-79.09],[-52.88,-82.85],[-34,-82.05],[-21.14,-79.03]],[[9.65,68.22],[-3.29,70.98],[-12.54,70.39],[-28.68,63.7],[-47.81,50.02],[-69.51,34.02],[-78.22,15.45],[-78.4,-5.35]],[[39.85,79.16],[32.79,79.2],[24.54,74.79],[15.66,69.75]],[[10.43,-69.95],[23.99,-78.78],[41.15,-78.36],[66.06,-76.7],[76.09,-72.36],[80.73,-59.71],[75.81,-48.21],[72.95,-29.93],[77.45,-18.95],[78.79,-8.72],[59.01,-2.82]],[[50,13.49],[41,17.24],[33.75,24.25],[34.49,32.99],[32.25,44],[18,50.25],[5.5,50],[-53,12.5],[-53.25,-9.5],[-39.75,-27.25],[-21.25,-32.5],[26.75,-2.25],[33.75,24.25]],[[-31,23.24],[-15,1.99],[4.25,14.74]]];
                points = [[-60.69,27.1,-10.37,-8.62,36.65,30.48],[-78.79,-24.8,-0.33,-10.75,-1.05,35.78],[-62.69,-75.34,22.51,-9.25,-19.3,8.3],[-9.88,-75.29,12.33,4.38,-19.56,-6.95],[36.04,-54.4,14.02,10.34,-12.26,-9.04],[44.93,-11.76,4.62,4.14,-16.48,-14.77],[76.65,27.43,-0.39,9.03,0.57,-13.3],[51.4,61.04,-9.7,10.28,16.76,-17.77],[18.75,70.85,-12.37,-6.37,27.98,14.42]];
                plants = [[9.49,14.74,0,4,1],[11.25,14.49,0,4,1],[10.5,16.99,0,4,1],[11.99,15.74,0,4,1]];
                wancoXY = [10.61,15.73];
                break;
            case 2:
                name = "Outlying Hoge";
                roadSize = 35;
                pathSize = 89;
                cameraXY = [-14.99,-71.83];
                gazeXY = [-44.63,-54.42];
                seatsXY = [85.52,5.98];
                lakes = [[-41.64,50.02,15],[72.25,72.24,15],[45.74,10,15],[-28.75,14.25,6]];
                objectS = [[-9,-63.75],[-5.25,-63.5],[-1.25,-63.5],[2.75,-64.25],[-46.25,-29.5],[-46.25,-25.75],[-46.25,-21.5]];
                objectL = [[-13.25,-30.25],[-13.5,-34.5],[-10.25,-32.25]];
                lamps = [[-36.25,5.25],[-30.25,1.49],[-22.5,3.24],[-17.25,10.25],[-17.25,19],[-23.75,24.25],[-32.5,24.74],[-38.25,21.5],[-40.25,10.25],[4,-45.25],[4.25,-38.5],[1.99,-30.25],[-15.75,-14.75],[-23.5,-14.5],[-32.5,-16.5]];
                trees = [[79.49,26.75],[77.74,34],[75.5,40],[73,47.5],[68,53.5],[-40.5,70.74],[-35.25,73],[-26.75,72.5],[-17.25,72.5],[-12.25,69.49],[-2.25,70],[6.75,68.99],[12.24,65.24],[19.49,62.5],[30.25,60.5],[35.74,56],[43.75,53.75],[46,47.99],[52.99,43.5],[55,36.99],[59.75,32.25],[59.24,24.74],[60.5,20.99],[55.25,-6.25],[59.49,-3],[-67.25,-27],[-67.25,-21.25],[-65.75,-16],[-67.75,-9.75],[-66,-4.75],[-65,3.24],[-65,9.74],[-47.5,-15.01],[-45.5,-8.25],[-43.75,0.25],[-43.5,10],[-46.5,20],[-49.75,24.25],[-52,31.99],[-56.25,36.74],[-68.5,-33.75],[-48.25,-33.75],[-52.25,-41],[-58.5,-48.75],[-57.25,-56.5],[-55.25,-62.75],[-48.5,-66.5],[-42.5,-69.75],[-62.75,-88.25],[-70,-85],[-82.25,-66.5],[-16,-82.75],[-2.5,-81.5],[11.5,-83.5],[65.5,-84.25],[71,-79.75],[73.75,-71.75],[75,-63.5],[74.49,-54.5],[66.25,-48.75],[6.5,-65],[13.24,-64.25],[-12.5,-64.25],[-19.25,-64.5],[-26.75,-67],[-4.25,-18],[-1.5,-21.75],[-15.25,-39],[-9.25,-15],[-18.75,-33.25],[-12.25,-55],[-12.5,-50],[-24.75,-30.25]];
                checkPoints = [[5.5,49,18.49,95],[-9.5,52.75,-2.5,97.99],[-41.5,55.25,-61.25,91],[-46.75,45.74,-86.25,40.74],[5.5,18.24,-95,-0.75],[5.74,4.25,-95.25,-53.75],[4.25,-18.5,-56.25,-95],[8.24,-51.25,-3.5,-95],[30.49,-65.5,32.25,-95.5],[38.5,-67,93.24,-72.75],[16.99,-26.5,93.23,-34],[36.99,10.5,96.75,9.49],[39.49,32.25,81.73,57.23],[23,44.49,39.74,87.99]];
                planes = [[[[-10.75,5.5],[-10.75,20.74]],5,1,".moja",true,1,1],[[[-69.75,84.24],[-78.75,73.98]],5,1,".wonderfl",true,1,1],[[[63,-45.25],[52.5,-44]],5,1,".wonderfl",true,1,1],[[[-83.25,-71.5],[-75.75,-82]],5,1,".flash",true,1,1],[[[-39.5,-41.5],[-27.75,-40.25],[-22,-48.25],[-24.75,-56.75],[-35.5,-58.5],[-42.5,-51.5],[-39.5,-41.5]],8,0,"bldg",false,4,3]];
                fences = [[[58.75,67.73],[32.99,80],[-3.25,88.99],[-34.5,89.49],[-61.25,82.5],[-75.5,68.73],[-79.25,50.74],[-76.5,31.5],[-69,18.49],[-72,-40.75],[-79.25,-54],[-78.5,-67.5],[-69,-81.25],[-52.25,-88.75],[-5.75,-84.25],[42.25,-88.5],[59.74,-86.75],[70.74,-74.75],[72.25,-57.75],[61.24,-49.5],[49.74,-47]],[[-50.75,-49.5],[-47.75,-59.25],[-35.5,-65.75],[-17.5,-60],[-15.75,-45],[-26,-31.75],[-43.5,-33],[-50.5,-49.5]],[[-32.5,-13.27],[-17.25,-10.5],[-0.75,-12],[8.75,-39.25],[4.25,-51.75],[11.5,-61],[24.74,-66],[46,-68.25],[27.24,-63],[21.99,-57],[16.99,-40],[21.75,-18.5],[38.24,-3]],[[56.25,20.25],[43.74,44.24],[17.24,56.5],[-8.5,62.5],[-29.5,57.25]],[[73.75,57.75],[82.49,34.23],[76.98,28.24],[76.98,-15.75],[66.25,-28],[78.99,-37.5],[86.49,-53.5],[85,-71.25],[74.49,-88],[59.49,-86.75]],[[-32.5,-13.27],[-40.75,-10.5],[-24,-2.75],[2.99,2.24],[3.99,25.48],[-24.75,29],[-42,32.25],[-48.75,37.24]]];
                path = [[[-44.5,-25.75],[-29.75,-20.75],[-7.5,-24.25],[-3.5,-36.75],[-4.5,-55.5],[-1,-60.75]],[[51.74,-77.75],[63.73,-68.5],[55,-56.5],[33.75,-45.5],[30.24,-34.5],[61.25,-12.75],[72.5,11.99]],[[-41.5,-79.75],[-67.25,-70.25],[-67.5,-49.5]],[[-29,5],[-23,16.75],[-40,14.74]]];
                points = [[33.8,68.64,-27.96,11.55,17.58,-7.26],[-34.5,80.73,-21.25,-3.73,17.08,3],[-69.71,52.34,-0.42,-23.9,0.29,16.42],[-52.5,12.24,3.75,-5.99,-4.08,6.53],[-55.75,-23.75,4.9,-10.44,-4.52,9.64],[-64.01,-65.76,20.77,-23.18,-15.19,16.96],[-10.25,-72.01,28.16,3.18,-25.6,-2.89],[61.23,-72.76,9.29,12.52,-14.71,-19.83],[39.23,-54.25,-9.38,0.64,8.3,-0.57],[31.73,-27.5,0.22,6.51,-0.55,-16.42],[54.73,-16,9.99,-4.97,-8.57,4.26],[70.74,17.5,-1.62,24.32,1.73,-25.88]];
                wancoXY = [-1.5,13.98];
                break;
            case 3:
                name = "Mojaspeed";
                roadSize = 30;
                pathSize = 120;
                cameraXY = [-63,-22.64];
                gazeXY = [-50.8,10.99];
                seatsXY = [-35.93,30.99];
                lakes = [[3.63,2.03,19],[-44.55,-49.79,11],[76.15,-75.94,13],[-78.84,1.7,18],[30.25,-68,5]];
                objectS = [[-6.16,35.63],[-13.78,27.96],[-10.25,32.63],[29.9,73.9],[24.25,74.44],[35.25,72.94],[-64.37,45.2],[-62.43,41.91],[-60.69,37.65],[-58.95,33.98]];
                lamps = [[-40.36,15.97],[-39.97,9.77],[-39.59,1.64],[-39.97,-5.9],[-22.93,-37.08],[-24.1,-32.26],[-26.04,-26.66],[-28.16,-21.06],[44.04,-1.84],[41.92,-7.45],[39.2,-14.61],[36.11,-21.97],[87.8,39.59],[86.84,44.43],[86.06,48.5],[78.7,-57.01],[81.22,-51.01],[82.38,-44.43],[83.35,-36.88],[-63.01,-13.84],[-60.5,-6.67],[-51.01,-63.21],[-56.43,-59.53],[47.94,-86.03],[53.04,-83.96]];
                trees = [[7.24,29.49],[11.5,-22.5],[3.75,-24.75],[-3.75,-24.25],[-9,-23.75],[-6.25,-29.5],[-2.5,25.5],[-12,22.75],[-10.25,-19.25],[-14.75,17.73],[1.49,31.25],[-6.5,-35.75],[-7,24.24],[-73,-88],[-80.25,-84.25],[-87.75,-74.25],[-87,-59.5],[-85.75,-44.5],[-83.25,-36.25],[-56.75,15.25],[-55.75,7.24],[-55.5,0],[-54.75,-8],[-57.75,-17.5],[-67.25,-23],[-61.5,-64.75],[-61.5,-58.25],[-57.5,-68.25],[16.98,-42.5],[31.99,-67.25],[37.99,-63.25],[44,-59.5],[49.24,-56],[53.5,-48.25],[54.49,-42],[71.25,-38.25],[71.75,-34.25],[75.23,-30.5],[72.25,-43.75],[61.99,-77],[57.98,-79.76],[65.29,59.84],[69.36,53.69],[54,65.23],[48.24,66.25],[39.75,67.73],[32.5,68.75],[24,70],[16.25,70],[8.24,70.5],[1.25,70.5],[-10.75,70.73],[-18,71.98],[-23.79,69.85],[-33.56,71.58],[-43.1,69.8],[-30.7,67.5],[-33.64,63.1],[-44.75,59.49],[-43,52.99],[-42.25,44.24],[-47.55,64.91],[-38.5,67.73],[-42.34,64.07],[10.25,35.74],[21.75,-37.5],[24.49,28.74],[32.99,28],[45.25,21.75],[46.49,7.75],[34.49,15.74],[70.41,45.6],[59.49,46.25],[47.75,50.25],[34.49,55.74],[20.25,62.25],[62.25,-82.25],[75.74,-59.75],[72.5,-62],[78.5,76.48],[71.98,82.25],[63.24,85],[-1.25,-80],[0.25,-67.75],[0.74,-55.75],[-33.75,-86.5],[-26.75,-84.25],[-21.25,-81.25],[-75,69.75],[-74.5,62.25],[-74.5,82.25],[-69.9,21.29],[-69.59,27.2],[-68.24,32.62],[-39.78,92.44],[-23.9,92.44],[-8.8,93.61],[-1.05,93.41],[8.61,93.61],[25.07,93.99],[37.85,93.22]];
                checkPoints = [[-35.5,26.73,-80.25,24.49],[-30.5,9,-81.5,5.25],[-52.5,-42.25,-86,-39.75],[-57.75,-70.25,-69,-94],[-28.38,-56.37,-3,-88.62],[-26.62,-45,7.75,-47.75],[-35.75,0.5,29.49,-4.25],[6.24,64.24,15.99,30],[44.24,51.74,36.5,28.24],[75,20.99,50.74,18.24],[56.25,-23.25,22.5,-12.25],[28,-67,2.24,-66.75],[29.49,-70.75,24.25,-93],[33.5,-69.25,56.5,-91.25],[50,-31,90.24,-41.75],[71.75,14.25,92.23,12.75],[50.49,44,84.25,69.49],[26.99,53.75,30.49,91.25],[-16,54.75,-20.5,94.75],[-23.5,47.99,-69.25,87.25],[-35.75,39.49,-81.25,37.74]];
                planes = [[[[10.6,-83.8],[22.45,-86.55]],5,0,".wonderfl",false,1,1],[[[34.23,-58],[41.48,-50.5]],5,0,".moja",false,1,1],[[[-64.76,-10.5],[-34.75,-19.75]],3,3,".title",true,1,1],[[[-67.5,85.48],[-72.25,75.72]],5,1,".flash",true,1,1],[[[-32,-23.75],[-27.75,-36]],5,1,".moja",true,2,1]];
                fences = [[[-32,21.5],[-32.5,0],[-29.25,-15.25],[-35.25,-25.75],[-53.25,-35.5],[-63.25,-48.5],[-67,-62.5],[-61,-72.5],[-45,-73],[-25,-57],[-22.75,-44.75],[-31.98,-34.82]],[[30.12,-64.19],[30.25,-57.75],[46.25,-41.75],[60,-13.5],[71.75,8.24],[74.48,24.25],[70,38.74],[10,63.24],[-17.25,57.99],[-32,40.49]],[[2.24,-85.5],[4.49,-52.25],[8.49,-32.75],[21.74,-19.75],[32.75,10.25],[19,22.75],[14,29.98],[18.48,35.24],[47.75,27.24],[52.23,22.75],[52.5,15.25],[32.75,10.25]],[[-31.75,-88.75],[-67.5,-88.25],[-81,-80],[-83.5,-54.75],[-80.25,-16]],[[88.5,-34],[83.99,-22.25],[85.5,-9.5],[88,25.74],[82.25,56.25],[80.5,69.75],[68.5,79.75],[48.24,83.5],[24.25,89.75],[-2.25,88.24],[-27.25,88.24],[-55.25,88.24],[-66,80],[-70.5,66.25],[-67.5,51.5],[-70,36.74],[-77,19]],[[69.28,-86.5],[26.99,-90],[2.24,-85.5]],[[81.99,-64.75],[88.5,-34]],[[-31.75,-88.75],[2.23,-85.5]],[[-31.98,-34.82],[-29,-53.25],[-45,-73]]];
                path = [[[-51.27,32.47],[-47,6.75],[-44.75,-8.75],[-69.75,-36],[-71.5,-79],[-38,-70.75],[-15,-61.75],[-16.25,-26.5],[-27.25,9.25],[-15.75,42.5],[6.75,53.5],[66.75,28.75],[31.99,-49.25],[8.8,-74.11],[30.71,-83.3],[60.5,-66.75],[64.54,-30.78],[78.23,-16.5],[82.31,20.99],[71.09,69.14],[17.49,79.06],[-60.25,78.5],[-63.25,60.5],[-52,34.99]]];
                points = [[-51.51,49.67,2.56,-7.5,-2.22,6.51],[-48.88,16.42,2.37,-13.04,-2.54,13.96],[-56.75,-27,-6.02,-4.63,14.14,10.88],[-73.82,-66.58,1.5,-9.21,-2.77,16.97],[-36.47,-77.08,13.71,4.8,-24.89,-8.71],[-15.75,-36.14,-1.53,9.79,2.56,-16.38],[-23.76,17.75,0.17,14.2,0.52,-19.29],[14,51.25,13.89,-1.02,-17.51,1.29],[55.54,34.14,9.36,-6.37,-12.78,8.69],[49.44,-15.86,-4.23,-11.53,7.22,19.68],[20.19,-59.73,-10.87,-14.25,15.24,19.97],[49.69,-69.51,11.58,9.16,-21,-16.62],[72.63,-22.21,6.59,9.02,-10.99,-15.05],[80,24.82,-0.67,10.23,0.97,-14.85],[63.99,71.25,-7.47,3.59,14.84,-7.13],[9.66,79.03,-12.1,-1.5,19.47,2.42],[-52.51,75.06,-9.81,-5.4,16.94,9.31]];
                wancoXY = [-56.25,-63.26];
                break;
            case 5:
                name = "Hoge Town";
                handicap = 0.95;
                roadSize = 33;
                pathSize = 15;
                cameraXY = [-2.5,49.06];
                gazeXY = [22.96,22.34];
                seatsXY = [35.56,14.49];
                lakes = [[0.73,-0.3,15]];
                objectS = [[-29.56,-57.61],[-25.12,-59.03],[-20.28,-60.85],[-34.4,-56.4],[-69.83,29.92],[-65.08,31.53],[-73.98,27.24]];
                objectL = [[-66.9,17.64],[-62.86,19.27],[-63.66,14.83],[-34.01,-50.43],[49.79,68.64],[48.36,67.89],[-1.1,-69.32],[0.89,-69.79],[-21.13,44.85],[-23.49,44.93],[-19,45.02]];
                lamps = [[14,20.56],[13.64,26.69],[12.08,33.82],[3.32,42.54],[-3.54,42.84],[-10.07,43.1],[-21.5,83.99],[-10,84.25],[0,84.49],[-72.75,-62.25],[-68.5,-70.5],[-59.5,-75.75],[70.73,-24.25],[70,-17.5],[69.25,-10.25],[42.6,-6.38],[45.24,-17],[-32,-19],[-43.32,-17.5],[-34.5,-30.5],[-28.75,64.37],[-50.2,-77.65],[-5.72,-52.33],[58.45,31.68],[-82.55,21.62],[-78.49,28.38],[-83.49,11.84],[-22.73,6.48],[-26.88,10.8],[-30.85,14.95],[-35,18.93],[-39.33,23.59],[-34.15,-4.92],[-38.11,-0.6],[-41.57,4.07],[-45.72,8.21],[-50.91,13.23],[-72.69,33.1],[-65.94,35.85],[-59.02,36.21],[58.84,17.54],[8.36,38.99],[7.69,-40.01],[17.71,-37.6],[25.5,-35],[32.75,-32.58],[30.64,-26.35],[42.99,-22.95],[-43.25,-53],[9.49,-55.25],[28.5,-64],[36.5,-63.25],[44.49,-61.75],[21.5,84.49],[-2,69.25],[9,68.99],[20,68.5],[31,68.5],[-67.25,-19.5],[-72,-11.5],[-76.75,-3.75]];
                trees = [[-3.32,-76.38],[1.27,-76.63],[-16.79,-61.28],[18.35,-64.75],[12.37,-60.45],[-13.09,-59.17],[42.23,-1.3],[24.92,-64.48],[32.39,-63.26],[40.06,-62.25],[47.5,-60.61],[53.04,-57.07],[56.09,-51.15],[65.02,-73.79],[71.74,-66.75],[73.95,-60.44],[75.38,-52.77],[29.97,-14.83],[27.14,-21.9],[21.5,-31.58],[13.01,-35.61],[1.91,-38.04],[-6.55,-37.43],[-16.04,-36.22],[12.73,12.55],[6.71,16.76],[-1.5,17.55],[-17.18,3.43],[-14.83,8.64],[-16.15,-3.46],[-51.35,63.06],[-51.15,55.8],[-47.09,50.55],[-42.65,45.91],[-51.55,74.77],[-44.7,81.03],[-43.49,85.47],[-34.6,86.88],[-25.93,88.5],[-15.64,86.68],[-4.33,88.09],[8.36,84.66],[17.25,86.68],[27.48,85.47],[-52.98,83.57],[73.06,86.25],[34.65,85.17],[58.93,81.18],[59.64,7.76],[58.43,24.31],[58.45,38.88],[63.1,52.14],[66.44,61.18],[63.47,-7.89],[67.09,-20.67],[70.74,-33],[-57.61,3.33],[-57.41,13.21],[-64.27,7.56],[-83.35,-5.8],[-79.62,-13.25],[-75.35,-19.9],[-70.85,-25.73],[-47.04,-51.14],[-39.45,-53.93],[-67.55,-32.04],[-64.27,-68.45],[-55.64,-72.28],[-47.52,-74.36],[-40.45,-77.19],[-33.19,-78],[-66.11,-49.27],[-65.34,-43.85],[35.34,29.64],[35,36.21],[34.31,43.99],[-68.75,55.25],[-79,50.49],[-80.25,44],[-88,37.75],[-92.25,22.75],[-92.25,7.5],[-89.25,-54.75],[-85,-67.75],[-81,-82.75],[-69,-91.25],[-57,-92.25],[-38.25,-93.75],[-24.75,-95.5],[-5.5,-94.5],[35,-93.75],[45,-94.25],[62.25,-92],[70,-86.25],[88.5,-79.25],[92.25,-68],[95.24,-50]];
                checkPoints = [[35.23,9.25,7.49,8.75],[35.98,0.24,7.49,0.24],[8.24,-34,3.24,-11.75],[-28.5,-23,-8.75,-5.5],[-53.25,5,-27.75,28.5],[-58.75,6.75,-91.5,45.74],[-57.5,-1,-92.5,-26.75],[3,-47.25,-1.25,-98],[41.37,-42.81,65.5,-95.25],[48.87,-35.38,92.36,-33.5],[37.25,40.49,91.75,42.25],[42.25,61.99,78.99,96],[25.74,64.25,26.5,93.5],[-19.25,65,-20,91.99],[-20.75,65.74,-91.5,63.99],[-20.25,64.24,-37.75,32.75],[17.75,59,-27.25,20.99],[32.5,18.49,-20.5,17.75]];
                planes = [[[[13,-30.57],[21.08,-26.95]],4,1,".moja",true,1,1],[[[99.23,-42.25],[80.13,-29.81],[78.86,-0.75],[70.04,5.89],[68.64,41.16],[76.66,51.35],[75.19,97.32]],8,0,"bldg",false,15,3]];
                fences = [[[31,24.09],[30.44,39.61],[19.43,56.38],[4.25,63.24],[-22.5,63.99],[-22.75,67.25],[46.58,66.47],[39.49,45.74],[39.59,24.09]],[[30.97,4.73],[29.36,-8.38],[20.89,-23.72],[2.94,-31.8],[-18.86,-27.37],[-30.16,-15.23],[-50.7,8.38],[-60.64,9.48],[-60.64,-0.5],[-46.1,-23.7],[-47.32,-39.25],[6.35,-51.76],[45.74,-43.68],[52.98,-39.85],[46.32,-21.29]],[[-10.11,9.78],[-58.62,56.4],[-59.63,89.45]],[[-68.31,-63.51],[-21.24,-78.38],[-10.59,-77.19],[-2.27,-71.5],[1.11,-71.73],[7.77,-79.28],[20,-81.67],[51.86,-77.61],[67.7,-68.31],[72.14,-50.55],[70.94,-41.47],[62.26,-17.64],[55.6,8.57],[54.79,40.46],[66.7,72.34],[62.66,80.62]],[[-38.6,37.32],[-16.62,37.6]],[[-64.95,-37],[-63.84,-30.95],[-87.25,6.75]],[[89.22,-63.25],[73.5,-81],[54.49,-89],[21.99,-90.5],[-27,-90],[-64.25,-85.25],[-76.25,-79.25],[-83,-63.75],[-84.55,-42.85]],[[46.31,-21.29],[43.34,-36.73],[37.67,-45.32]],[[89.22,-63.25],[88.99,-35.25]],[[-59.64,89.44],[75.49,90]],[[-84.55,-42.85],[-64.96,-37]],[[-87.25,6.75],[-86.25,31.5],[-58.62,56.39]]];
                path = [[[-46.08,-44.87],[-28.21,-50.06],[-12.95,-53.95]],[[-69.56,-56.44],[-77.69,-56.6],[-78.25,-64.25],[-73,-74.5],[-62.25,-81.5],[-25.25,-85.75],[21.5,-87],[53.75,-84],[69.25,-78.5],[77.73,-67],[81,-57.5],[79.73,-47.25],[76.48,-33.5],[74.98,-4.25],[66,3.49],[63.99,43.5],[72.23,54.24],[71.75,74.48],[66.25,85],[57.5,87.25],[49.75,85],[34.99,77.73]],[[50.74,-10],[38.75,-13],[35.25,-18],[39.24,-31.5],[36.74,-37],[7.75,-45.75],[-40,-35],[-36.75,-16.5]],[[-33.75,50.74],[-41.25,54.49],[-45,61.49],[-44.75,69.48],[-39.5,75.73],[-28.5,78.75]]];
                points = [[22.41,-0.8,-0.58,-19.41,0.2,6.7],[-18.59,-11.47,-5.72,7.89,14.61,-20.17],[-53.81,25.67,-11.71,8.03,12.44,-8.53],[-73.97,5.21,7.46,-10.17,-8.66,11.81],[-54.1,-25.48,2.26,-3.25,-2.85,4.08],[-57,-57.18,5.54,-2.69,-3.42,1.66],[-21.1,-69.39,9.65,-2.87,-5.94,1.76],[0.56,-61.2,4.21,-0.19,-4.83,0.22],[19.77,-74.61,7.66,1.5,-8.32,-1.62],[49.36,-69.49,15.96,3.82,-9.44,-2.26],[63.47,-42.7,-2.87,7.86,2.59,-7.1],[50.29,-1,-3.31,15.34,2.88,-13.32],[47.79,45.96,18.92,35.99,-3.39,-6.45],[41.14,75.98,-13.69,0.84,11.18,-0.69],[-23.56,76.67,-19.2,0.95,14.16,-0.7],[-28.23,52.41,8.65,0.13,-17.12,-0.27],[3.66,52.31,22.99,0,-12.8,0]];
                plants = [[-41.85,-12.97,0,4,1],[-40.55,-14.01,0,4,1],[-39.08,-13.04,0,3,1],[-38.88,-11.21,0,4,1],[-41.35,-10.86,0,4,1],[-42.26,-14.52,0,3,1],[-38,-12.22,0,3,1],[-41.18,-15.77,0,3,1],[-65.5,-53.75,0,3,1],[-66.12,-54.75,0,4,1],[-66.25,-56.12,0,3,1],[-66,-58.75,0,3,1],[-65.5,-57.13,0,4,1]];
                wancoXY = [-40.5,-12.26];
                break;
            case 4:
                name = "Hoge & Moja";
                handicap = 0.93;
                roadSize = 40;
                pathSize = 132;
                cameraXY = [69.06,29.37];
                gazeXY = [82.23,7.1];
                seatsXY = [43.13,-44.45];
                objectS = [[23.54,7.96],[24.98,10.47],[25.87,13.15],[20.86,24.08],[23.01,22.45],[25.15,21.04]];
                objectL = [[-66.87,-7.07],[-65.97,-4.38],[-65.25,-0.8],[59.16,-15.45],[57.21,-12.44],[55.94,-9.57],[66.34,-63.11],[68.67,-60.25],[62.35,49.49],[60.88,48.8],[63.81,50.22]];
                lamps = [[-17.62,49],[-6,41.87],[1.99,54.36],[-10,61.24],[-19.25,46.62],[-20.25,50.49],[-8,39.24],[-3.5,40.74],[4.49,52.74],[3.75,56.99],[-8.25,63.36],[-12.38,62.87],[-83.16,-11.9],[-81.73,-6.53],[-80.3,-1.51],[-78.86,4.03],[-78.33,10.65],[-79.05,16.74],[-80.65,23.36],[-83.16,29.09],[-84.77,35.18],[-86.39,41.27],[-66.51,-28.91],[-61.67,-34.28],[-55.4,-36.79],[-48.96,-37.33],[-43.05,-36.79],[-37.32,-33.75],[17.28,-67.95],[20.82,-73.8],[26.77,-78.69],[32.68,-82.27],[38.95,-84.78],[44.85,-86.39],[50.39,-86.57],[56.85,-85.67],[62.76,-83.88],[68.84,-81.55],[74.22,-79.05],[77.98,-74.75],[82.45,-68.3],[78.34,19.42],[80.84,27.12],[77.98,-11.36],[81.74,-18.35],[40.25,79.94],[44.51,76.59],[49.35,74.98],[62.04,39.65],[63.65,44.49],[57.21,53.8]];
                trees = [[7.61,-57.74],[5.82,-48.43],[7.79,-41.26],[5.28,-29.63],[6.71,-22.65],[6.71,-13.15],[10.65,-4.2],[12.26,5.28],[11.73,17.64],[8.5,23.9],[-5.1,31.78],[-1.87,26.59],[-9.57,22.83],[-17.09,17.45],[-19.6,10.65],[-17.99,-4.74],[-12.79,-11],[-8.31,-24.08],[-8.85,-36.97],[-13.51,-48.43],[-16.2,-54.7],[-23.71,-59.71],[-36.43,-65.8],[-47.89,-61.68],[-58.81,-65.44],[-70.27,-61.32],[-81.55,-57.74],[-86.92,-48.43],[-91.4,-35.9],[-91.94,-24.25],[-90.15,-12.79],[-92.47,-3.85],[-90.86,12.09],[-91.94,22.29],[-93.37,28.91],[-91.94,36.44],[-93.73,48.79],[-89.79,66.51],[-87.81,73.14],[-83.34,79.4],[-79.22,79.94],[-71.16,87.29],[-63.46,88.18],[-55.05,86.39],[-45.38,87.29],[-38.94,84.96],[-31.95,79.94],[-26.94,78.87],[-19.06,72.42],[-13.87,70.81],[-11.36,76.36],[-6.7,76.9],[-5.21,83.04],[2.7,87.84],[7.46,91.55],[21.34,94.26],[29.09,91.4],[34.11,89.44],[44.32,89.25],[48.61,86.03],[55.42,86.39],[65.09,84.78],[72.6,84.78],[76.01,81.74],[84.96,77.98],[88.72,69.38],[57.56,44.91],[51.65,50.25],[51.65,44.49],[56.1,38.01],[41.63,49.69],[13.7,54.69],[17.1,58.81],[21.75,53.8],[27.66,59.89],[-34.46,37.15],[-34.81,42.52],[-41.09,48.97],[-32.49,48.97],[-43.77,57.56],[-39.47,30.53],[-58.81,-22.29],[-52.72,-28.01],[-44.66,-24.44],[-48.42,-17.27],[-57.2,-14.4],[-52.9,-9.4]];
                checkPoints = [[9.4,-39.84,41.67,-39.46],[9.4,-24.61,40.73,-24.8],[13.16,14.59,49.1,21.65],[7.25,27.66,22.11,48.06],[-41.26,55.77,-25.65,79.41],[-60.75,55.25,-85,74.01],[-59.65,24.56,-90.68,19.42],[-50.93,-30.88,-50.57,-66.87],[-40.01,-24.97,-9.75,-25.15],[-48.42,20.5,-21.05,4.22],[-6.89,75.11,15.67,59.89],[57.03,84.42,37.51,54.16],[91.23,-38.89,62.32,-43.17],[58.07,-91.09,53.24,-60.84],[9.65,-55.77,39.05,-55.02]];
                planes = [[[[-10.35,29.9],[-28.81,41.69]],3,3,".title2",true,1,1],[[[-7.38,73.07],[11.9,60.45]],3,3,".title2",true,1,1],[[[17.82,49.49],[4.37,29.09]],3,3,".title",true,1,1],[[[-35.9,52.68],[-22.79,74.35]],3,3,".title",true,1,1],[[[-82.98,-37.15],[-75.34,-46.75],[-65.28,-52.18],[-54.18,-54.86],[-42.87,-54.51],[-31.45,-51.68],[-22.32,-44.89]],5,1,".moja",true,1,1],[[[81.43,-5.74],[72.64,3.26],[80.85,12.07]],5,1,".wonderfl",true,1,1]];
                fences = [[[-23.81,43.24],[-26.45,52.2],[-46.2,62.75],[-57.56,62.04],[-65.89,54.79],[-67.5,42.61],[-54.4,0.61],[-23.82,43.23]],[[-54.4,0.61],[-64.46,-15.43],[-65.7,-23.27],[-61.95,-30.08],[-53,-34.1],[-42.52,-33.12],[-38.58,-27.57],[-39.75,-18.53],[-54.4,0.61]],[[30.25,37.33],[7.07,51.29],[7.04,58.1],[16.21,69.84],[26.5,70.05],[46.19,56.05],[46.37,41.52],[56.29,28.68],[51.53,9.45],[52.25,-11.43],[65.97,-39.61],[67.74,-53.45],[59.86,-63.85],[44.49,-66],[34.83,-57.83],[35.24,-28.45],[45.74,1.88],[44.84,18.75],[39.26,30.04],[30.25,37.32]],[[86.21,56.84],[88,-64.73],[75.97,-82.06],[51.41,-89.55],[28,-84.78],[13.02,-69.17],[12.12,-11.21],[18.54,5.19],[16.03,25.34],[-1.15,37.69],[-9.03,35.89],[-26.76,10.29],[-25.15,-1.95],[-15.55,-17.87],[-13.42,-34.68],[-20.94,-49.12],[-34.33,-56.64],[-57.04,-58.95],[-75.61,-53.02],[-87.78,-34.99],[-88.15,50.57],[-83.09,71.71],[-66.84,83.29],[-43.47,82.43],[-15.97,66.2],[-7.61,67.91],[-0.61,77.98],[10.64,87.53],[18.96,90.22],[67.79,78.55],[81.35,71.32],[86.21,56.84]]];
                path = [[[73.24,43.24],[76.49,59.49],[69.25,66.49],[55.25,66.75]],[[-75.25,-12.25],[-67,3.75],[-69.75,15.99],[-75.25,30]],[[-72.5,62.74],[-56.25,71],[-46.75,68.99]],[[24.74,-14.25],[32.75,-6.25],[37.24,20.99]]];
                points = [[24.29,-58.93,-0.03,26.74,0.02,-16.85],[35.22,12.74,7.8,11.03,-17.33,-24.5],[-41.25,69.74,-22.38,12.69,60.23,-34.15],[-69.5,65.49,-5.15,-7.86,6.44,9.83],[-73,27.23,9.54,-25.74,-7.76,20.93],[-72.75,-34.5,19.94,-14.25,-13.65,9.75],[-25.5,-35.75,8.55,12.98,-7.79,-11.84],[-37.75,9.24,7.19,10.55,-8.52,-12.5],[-2.1,60.23,13.61,22,-10.39,-16.79],[31.65,75.83,27.04,-23.69,-9.52,8.34],[73.69,46.81,-7.93,-24.33,11.77,36.1],[66.97,-16.75,11.37,-14.84,-16.55,21.61],[73.23,-68,-19.82,-18.69,12.61,11.89]];
                wancoXY = [80.99,3.71];
                break;
            case 6:
                name = "Hoge Circuit";
                roadSize = 40;
                pathSize = 32;
                cameraXY = [57.07,45.64];
                gazeXY = [92.72,65.97];
                seatsXY = [84.07,-4.75];
                lakes = [[0.24,-0.25,9]];
                objectS = [[-56,14.02],[-51.96,4.34],[-47.32,0.9],[-41.46,-0.9],[-58.22,-18.66],[-57.05,-15.98]];
                objectL = [[70,51.69],[72.8,61.83],[57.08,65.5],[-28.75,68.92],[71.11,20.28],[68.18,23.07],[64.61,25.78],[-60.07,-16.45],[-61.05,-17.85],[-59.63,-14.63]];
                lamps = [[36.74,17.5],[42.25,17.5],[48.24,16.99],[54.49,15.74],[59.75,13.75],[50.49,-58.5],[50.74,-52.75],[50.25,-46.25],[49,-40.25],[49.24,-33],[50,-26.25],[-69.75,-41.5],[-88.25,-29],[-70.5,-29.5],[-54.75,44.24],[-72.25,48.24],[45.74,65.24],[45.25,82.25],[-82,14.74],[-65,11.99],[-87.25,-15],[-70.25,-17.25],[-88,-45.75],[-60,31.5],[52.75,65.24],[52.5,83],[15.5,41.99],[22.5,41.5],[29,40.74],[-53.25,29.75],[-55.75,24.24],[-49.75,35],[-53.75,-2.75],[-47.5,-6],[-41.75,-7],[-7,-9.5],[-11.25,-1.5],[-9,5.74],[-2.5,10.5],[6.5,8],[10,1.25],[8.24,-7.5],[0.99,-12]];
                trees = [[-10.95,1.9],[-9.99,-6.2],[-2.94,-11.6],[4.87,-11.09],[10.59,-3.32],[10.13,5.14],[2.12,10.19],[-6.95,7.91],[54.39,-12.4],[53.79,-5.54],[54.39,3.33],[51.97,9.99],[65.69,57.21],[-31.58,20.69],[-62.25,-28.55],[-57.81,-25.73],[-59.02,-37.43],[-54.99,-31.38],[-43.89,-17.85],[-34,-14.02],[-67.9,-27.14],[-67.7,-5.75],[-65.89,4.13],[-63.66,15.64],[36.43,-64.27],[38.84,-67.5],[42.69,-61.65],[47.93,-62.66],[63.07,-80.42],[69.83,-65.4],[70.09,-56.05],[68.59,-46.96],[68.72,-37.64],[-31.17,62.86],[-36.82,63.87],[-38.85,59.43],[-45.5,59.03],[-48.53,50.75],[-66.7,67.91],[-70.73,57.62],[-75.56,42.48],[-79,25.73],[-84.65,9.59],[-87.08,-6.95],[-87.68,-20.48],[-88.67,-37.21],[-86.45,-63.45],[-78.2,-68.2],[-68.62,-69.76],[-57.45,-64.33],[-48.7,-56.36],[-41.32,-46.76],[-33.4,-37.03],[-24.71,-36.42],[-17.25,-38.85],[-9.77,-40.46],[26.25,-87.25],[16.99,-81],[13.49,-71.75],[8.49,-67.5],[5,-59.75],[2.5,-53],[-8.5,81.99],[2.24,83.5],[10.25,85],[16.99,85],[26.25,85.74],[84.49,32.5],[83.99,76.25],[-22.5,31.74],[-17,34]];
                checkPoints = [[16.75,38.73,21.99,9.49],[48.24,35.99,51.74,11.5],[80.24,7.5,56.5,4.74],[73.24,-51.25,41.99,-53.5],[44.24,-91.75,39.24,-54.5],[12.75,-67.26,32.5,-54.5],[-9.25,-50.25,0.49,-16.25],[-76,-85.5,-55.75,-12.5],[-92.5,6.75,-58.25,0.99],[-86,62.25,-43.75,45.74],[-45.5,86,-35.25,51.5],[70,88.75,66.49,59.24],[92.25,52.75,68.5,56.25],[61.25,29.24,63.75,56.74],[-33,21.99,-51.25,43.5],[-33.5,19,-67.25,-3.25],[-29,22.75,-21.5,-14.75]];
                planes = [[[[-16.01,-12.5],[-20.77,-38.54]],3,3,".title",true,1,1],[[[-74.5,35.73],[-82.5,33.99]],5,0,".moja",false,1,1],[[[-6.75,42.5],[-3,35.97]],5,1,".hoge",true,1,1],[[[80.73,34.23],[88,46.25],[88.73,60.74],[84.75,73.5]],4,1,".wonderfl",true,1,1],[[[-48.1,-17.27],[-51.28,-23.29]],4,1,".wonderfl",true,1,1]];
                fences = [[[71.75,56.81],[66.7,63.47],[59.44,58.63],[64.68,51.96],[71.75,56.81]],[[59.44,58.62],[30.55,64.88],[-21.69,58.02],[-42.88,54.59],[-59.43,25.12],[-65.26,-10.39],[-48.72,-16.84],[-30.77,-9.18],[3.53,-20.88],[31.99,-58.82],[44.09,-56.6],[44.29,15.03],[32.99,14.83],[13.62,10.99],[17.45,-4.34],[3.53,-20.88]],[[-66.09,-17.05],[-51.98,-22.52],[-65.34,-43.52],[-66.09,-17.05]],[[87.01,30.93],[9.25,37.64],[-25.12,25.12],[-34.36,24.96],[-35.93,17.24],[-28.75,17.23],[-25.12,25.12]],[[76.91,7.03],[77.19,-25.06],[65.77,-33.83],[67.56,-69.81],[55.87,-84.97],[36.85,-87.64],[22.28,-78.84],[4.03,-46.19]],[[43.87,-26.37],[58.82,-15.25],[58.39,10.83],[44.29,15.03]],[[89.33,77.6],[57.01,85.87],[-14.22,77.4],[-26.26,86.41],[-61.09,78.57],[-78.94,67.92]],[[4.03,-46.19],[-36.25,-43.5],[-55.25,-69.25],[-77.25,-73.75],[-89.5,-65.5],[-89.25,13.75],[-86.5,34.75],[-79,67.48]],[[76.9,7.02],[87.01,30.93],[89.73,42.73],[89.32,77.6]]];
                path = [[[58.5,-40.5],[61.25,-71.25],[52.74,-81.75],[37.74,-83.75],[26.24,-76.5],[12.75,-50.25]],[[-47.75,24.98],[-52.5,12.5],[-48.75,3.75],[-37.75,0.74],[-28.5,2.75],[-18.25,11.25]],[[-53,-47.75],[-65.76,-60],[-73.75,-58.25],[-79.75,-55],[-81,-45.25],[-78.5,-8.75]],[[83,63],[81.25,47.75],[76.48,41],[66,36.99],[52.25,40.25],[38.5,48.5]]];
                points = [[-7.5,18.22,38.5,7.74,-7.38,-1.48],[67.91,13.33,0.98,-6.75,-2.69,18.46],[67.54,-20.75,-2.5,-1,3.62,1.45],[57.68,-30.15,0.75,-4.79,-0.5,3.26],[59.8,-63.46,0,-12,0,6.56],[23.69,-67.48,-3.45,5.52,13.84,-22.11],[1.48,-33.36,-7.5,6.24,5.16,-4.29],[-38.79,-26.07,-4,-4,7.19,7.19],[-62.16,-57,-5.42,-4.9,13.82,12.51],[-78,-11,1.14,6.18,-9.25,-49.8],[-53.98,70.33,1.16,2.59,-17.08,-37.87],[-27.79,77.53,4.4,0.72,-5.76,-0.95],[-13.89,67.44,4.22,0.86,-3.95,-0.8],[60.54,73.86,4.47,-0.56,-20.53,2.58],[81.49,62.6,2.24,-11.25,-1.2,6.05],[67.54,42.04,-12.92,-1.8,8.65,1.2],[30.49,51.32,-13.75,1.99,6.3,-0.91],[-23.7,44.43,-26.49,-16,13.23,7.99],[-43.94,6.03,13.74,-8.25,-12.26,7.36]];
                wancoXY = [-81,38.97];
                break;
            case 7:
                name = "Hoge Canyon";
                handicap = 1.1;
                roadSize = 40;
                pathSize = 113;
                cameraXY = [-72.53,15.29];
                gazeXY = [-34.73,-0.71];
                seatsXY = [32.99,17.22];
                lakes = [[42.99,54,10],[83.99,21.5,9],[88.24,-37,10],[38.75,-11.75,12],[40.24,-50.5,10]];
                objectL = [[18.24,-71],[18.24,-72.75],[18.24,-74.25],[38.75,-78.5],[38.5,-76.5],[-60.75,-74.25],[-60,-76.75]];
                lamps = [[-43.5,66.25],[-38,67.24],[-32.75,66.75],[-26,64.25],[-18.25,62.5],[-10.75,61.99],[-4.25,61.49],[3.49,61.49],[12.5,61.99],[17.5,63],[-47.25,62.74],[-50.5,59.24],[-55.75,78],[-51.25,80.24],[-45.75,82.74],[-39,84.25],[-32.5,83.5],[-26,82.25],[-20.5,80.5],[-13.25,79.75],[-7.25,78],[-0.5,78.24],[5,78.75]];
                trees = [[56.25,49],[79.23,78.75],[61,50.25],[63.49,55.25],[60.24,63.5],[54,62.74],[56.74,68.24],[47.99,68.5],[52.25,71],[41.99,72.25],[80.74,49.75],[81,58.24],[77.74,66.75],[77.74,72.25],[71,80.5],[65.74,85],[57.99,87.74],[73.24,86.25],[25.74,90.24],[9.74,89.25],[76.99,38.75],[71.99,29.24],[66.25,23.75],[67.74,16.99],[72.24,21.99],[73.75,16.5],[72.74,10.5],[76,6.25],[79.25,10.99],[47.25,10.25],[53.75,8],[48.5,19.25],[45.48,28.5],[49.99,29.24],[78.99,30],[51.5,37.99],[55,41.74],[82.25,44],[83.99,65.5],[59.99,-6.75],[31,64.75],[36.99,69.74],[63.49,-10.25],[57.74,-18],[61.49,-14],[57.24,-63.5],[58.5,-0.25],[83,-18.25],[83.75,-9.5],[81.25,-0.75],[80.74,-25],[73.24,-31.75],[67.99,-40.75],[69.25,-45],[71.25,-36],[73.24,-43],[76.25,-34.5],[48.75,-36.5],[49.75,-45.25],[84.49,-64.25],[41.99,-35],[48.5,-29.75],[48.5,0.99],[52.5,-21.75],[54,-12.75],[83.74,-73.75],[52.49,-55.75],[76,-48.5],[80.24,-58.5],[80.74,-48.5],[83.24,-53],[81.25,-63.25],[80.49,-74.25],[33.75,94.25],[46.74,95],[56.5,93.5],[15.74,86.75],[7.5,83.99],[21.99,62.74],[-14.5,46],[-6.25,46.74],[-3.5,42.25],[7.75,39],[13.49,33.75],[19.49,33.75],[24,29.24],[26.75,-2.5],[24,-10.25],[23.75,-20.75],[17.5,-23],[16.75,-29.75],[7.5,-30],[11.25,-34.75],[-18.25,23.24],[-24,19.25],[-35,51.74],[-91.75,-70.5],[-26.75,53.75],[-23.5,46],[-84.5,62.74],[-82.75,54.75],[-78.75,59.49],[-73.5,62.5],[-68.5,67.25],[-62.5,-85.25],[-68.5,-82.75],[-75,-77.25],[-78.5,-69.25],[-78.75,-90.26],[-83.25,-65.5],[-86.5,-82.75]];
                checkPoints = [[4.49,88.24,7.24,43.5],[32.73,98.24,36.25,52.99],[92.25,59,49.74,55],[98.5,-44.25,43.75,-44.25],[37.25,-92.5,36.5,-53.25],[-49.75,-94.5,-48.5,-59],[-99.75,-47.25,-55.25,-47.75],[-60.25,-18.5,-50.75,-46.5],[0.5,3.75,29.75,-10.5],[-8.5,10.5,-0.75,52.5],[-38.5,-6.25,-55.5,19],[-88.25,14.25,-63.75,21.5],[-79.25,66.99,-60,41.74],[-12.75,92.74,-9.5,45.24]];
                planes = [[[[-63.17,-107.33],[-55.84,-87.89],[-36.91,-79.09],[-19.35,-76.31],[-2.97,-81.31],[9.27,-86.22],[25.15,-78.78],[37.98,-81],[52.98,-84.75],[74.25,-83.48],[90.31,-101.07]],6,0,"rock",false,10,1],[[[-99,61.25],[-89.75,50.99],[-88.25,32.75],[-84,13.49],[-77.25,-6.5],[-58,-13.5],[-41.75,-5.5],[-29.75,7.24],[-21.75,16.75],[-5.75,20.5],[5.25,10.74],[3,-2.75],[-11.75,-13],[-32,-14.5],[-51.75,-19],[-69,-25.25],[-81,-37.75],[-84.25,-54.75],[-97.5,-64.75],[-115,-63.5]],6,0,"rock",false,10,1],[[[-63,12.24],[-68,26.99],[-65.75,42.5],[-51.5,51.49],[-38,45.73],[-47.25,33.24],[-49.5,18.24],[-63,12.24]],6,0,"rock",false,10,1],[[[-3.25,-35.5],[10,-46.75],[1.75,-60.76],[-11.5,-56.01],[-33.5,-55.75],[-48.75,-64.25],[-61.01,-58.25],[-57,-44.5],[-40.75,-39.75],[-23,-37],[-3.25,-35.5]],4,0,"rock",false,10,1]];
                fences = [[[-90.75,52.99],[-60.25,79.75],[-37.5,88],[3,82.25],[34,90],[78.49,88.75],[88.75,64.75],[85.74,29.24]],[[85.24,13.24],[86.49,-28]],[[88.5,-47],[88.75,-71.5],[78.5,-88.5]],[[-57,-90.25],[-74,-86.25],[-85.5,-73.75],[-88.5,-58.25]],[[3.75,-41.5],[17.24,-39],[27.5,-31.5],[31.49,-21]],[[33.5,55.74],[13.24,48.24],[-13.25,51.99],[-38,45.74]],[[53.99,-61.25],[48.49,-56.25]],[[32.25,-45],[17.24,-39]],[[40.49,0],[40,45.49]]];
                path = [[[68.5,-59.25],[53.99,-39.75],[75.24,-10.25],[53.49,22.24],[71.49,49.49]],[[-62,-75.25],[-73.5,-68],[-72.25,-51.25],[-68.25,-38],[-57,-33.25]],[[6.99,-15],[17.75,6.25],[13.24,25.25],[0.99,33.24]],[[-49.5,2.24],[-65.5,1.75],[-77.5,14]],[[-13.25,-73.25],[10.74,-63.76],[24,-62.25],[41.5,-67.51]],[[52.99,-72.5],[65.49,-69.75]]];
                points = [[3.55,69.84,17.21,0.29,-23.41,-0.4],[38.74,81.99,6.93,3.15,-8.93,-4.05],[71.99,56.5,-0.5,-20.25,0.5,20.25],[57.24,22.24,-0.06,-11.6,0.04,7.95],[72.98,-11.75,0,-8.02,-0.01,11.41],[58.48,-40,-0.41,-10.25,0.43,10.65],[61.72,-75.39,-9.33,-3.15,18.71,6.33],[17.17,-72.28,-12.43,-6.29,22.64,11.47],[-43.91,-71.53,-15.17,-9.54,24.55,15.44],[-71.5,-57.25,-7.87,27.59,5.16,-18.09],[-27,-26.25,26.49,6.77,-15.42,-3.94],[13.24,-5.5,14.51,22.31,-14.86,-22.84],[-17,34.24,-28.65,9.67,39.92,-13.47],[-44.77,5.49,-27,-18.01,17.02,11.36],[-77.75,23.49,-3.71,11.45,7.73,-23.82],[-65.75,55.73,16.58,9.21,-10.96,-6.09],[-34.75,75.24,6.04,0.44,-25.99,-1.9]];
                plants = [[72.98,-87.25,6.1,30,0],[65.49,-87.5,6.1,30,0],[55.73,-88.25,6.1,30,0],[77.73,-93.5,6.1,30,0],[46.49,-87.25,6.1,30,0],[37.99,-85.5,6.1,30,0],[29.49,-84,6.1,30,0],[20.5,-86.75,6.1,30,0],[12.75,-90.5,6.1,30,0],[4,-89.25,6.1,30,0],[-4.5,-86,6.1,30,0],[-13,-83,6.1,30,0],[-22.25,-81.75,6.1,30,0],[-31,-84,6.1,30,0],[-40,-86,6.1,30,0],[-48.25,-88.5,6.1,30,0],[-54,-94.25,6.1,30,0],[-1.25,-0.25,6.1,30,0],[0.5,9.74,6.1,30,0],[-6.75,15.5,6.1,30,0],[-20.75,11.99,6.1,30,0],[-22.25,-0.75,6.29,30,0],[-12,-8.25,6.1,30,0],[-21,-10,6.1,30,0],[-28,2.24,6.1,30,0],[-42.75,-10.5,6.1,30,0],[-52.25,-16.75,6.1,30,0],[-63.5,-15.5,6.1,30,0],[-70,-22.5,6.1,30,0],[-73.75,-15.25,6.1,30,0],[-79,-20.5,6.29,30,0],[-76,-28.25,6.1,30,0],[-85,-33.5,6.1,30,0],[-85,-43.25,6.1,30,0],[-87.5,-53.25,6.1,30,0],[-82.75,-8,6.1,30,0],[-85.25,1.5,6.1,30,0],[-84.75,-17.75,6.29,30,0],[-87.5,11.75,6.1,30,0],[-90,23.24,6.1,30,0],[-93,37.99,6.1,30,0],[-94.25,49,6.1,30,0],[-12.5,10.5,6.29,30,0],[-30,-8,6.1,30,0],[-89.5,-6,6.1,30,0],[-85.75,-26,6.1,30,0],[-96.75,-59.25,6.1,30,0],[-92.25,-49.25,6.29,30,0],[-91.25,5.74,6.29,30,0],[-95.75,28.5,6.29,30,0],[-97.75,42.25,6.29,30,0],[-9.5,0,6.29,30,0],[-89.75,-37.25,6.29,30,0],[-61,17.24,6.1,30,0],[-52.5,21.25,6.1,30,0],[-42.75,44.24,6.1,30,0],[-49.25,35.49,6.1,30,0],[-64,25.99,6.1,30,0],[-63,36.25,6.1,30,0],[-52.75,47.24,5.09,30,0],[-57.25,25.99,6.29,30,0],[-55.25,39.24,6.29,30,0],[-0.75,-53.75,4.09,30,0],[-4.5,-41.5,4.09,30,0],[4.49,-47.75,4.09,30,0],[-10.25,-51,4.09,30,0],[-14.75,-40.75,4.09,30,0],[-21,-50.5,4.09,30,0],[-26,-41.25,4.09,30,0],[-30.5,-50.25,4.09,30,0],[-38,-42.5,4.09,30,0],[-42.75,-56.25,4.09,30,0],[-56.75,-57,4.09,30,0],[-49.25,-51.75,4.09,30,0],[-53.5,-47,4.09,30,0],[-44,-46,4.09,30,0]];
                wancoXY = [9.74,-42.54];
                break;
            case 8:
                name = "Moja Racers";
                handicap = 0.92;
                enemiesNum = 14;
                roadSize = 45;
                pathSize = 18;
                cameraXY = [-59.39,3.33];
                gazeXY = [-36.05,-32.68];
                seatsXY = [-37.68,-16.25];
                lakes = [[4.74,-35.75,12],[31.25,43.24,10],[79.49,-65.5,10],[-72.25,-73,10],[-34,42.99,10]];
                objectS = [[57.75,42.25],[57.75,37.99],[57.75,32.25],[59,27.75],[61.49,23.49],[68.75,39.49],[68.5,34.49],[70,30.25],[72.5,25.99]];
                objectL = [[-29.75,-81.75],[-27,-84],[-23.75,-85],[-19.75,-84.5],[-15.5,-84.75],[44.75,-66.75],[47.99,-64.75],[52.25,-61.75],[56,-57.75],[-62.5,76],[-59.25,79.49],[-55.5,81.25]];
                lamps = [[-42,-29.75],[-42.25,-33.75],[-42,-38],[-41,-40.75],[4.74,-62.5],[-5.5,-66.75],[-0.25,-64.5],[-57.5,-42],[-58.75,-36.25],[-59.5,-30.5],[-60,-24.25],[-60.5,-17.5],[-61,-12],[-61,-7],[-61,-1.25],[10.25,-60.25],[-26.25,-65],[-23.5,-67.25],[-35.5,-80.25],[-37.5,-77.75],[-40.5,-75]];
                trees = [[-79.25,38.75],[-79.25,43.75],[-78.5,48.75],[-79,54.49],[-78.5,60.74],[-77.5,66.49],[-77.5,70.24],[-47.75,58.5],[-48.75,53.24],[-48.75,47.5],[-49,41.25],[-49.25,34],[-45.75,28],[-80.25,32.5],[-77.25,26.25],[-76,19.74],[27.5,-53],[32.75,-50.5],[38.24,-48],[42.99,-44.5],[53.75,-31.5],[55.74,-27.5],[60.5,-19.5],[61.24,-14],[63.5,-62.5],[67.74,-57.5],[72.74,-51.75],[76.99,-45],[78.99,-38.25],[80.5,-32],[81,-25.75],[81.49,-20.25],[83.5,-16.25],[41.25,-39.25],[39,-36.5],[45.25,-28.75],[49,-30.5],[-8.25,-43.25],[-3.25,-49],[5.74,-51],[-7.5,-24.5],[0,-20.75],[39.75,32.5],[43.75,37.25],[44.24,45.25],[16.75,46.74],[20.74,52.75],[-27.5,30.25],[-21.25,33.75],[-19.75,40.74],[-56.25,-78.25],[-55,-70.5],[-59.25,-61.75],[-67.75,-58],[-76.25,-58]];
                checkPoints = [[-17.5,-12.25,-73,-12.5],[-15.25,-34.5,-73.25,-35.5],[-13.5,-46.75,-54.5,-91.75],[-0.5,-56.75,8.75,-92.25],[26.25,-43.5,58.75,-76.25],[43.5,-14.25,91,-26.75],[41.5,23.75,93,39.24],[34.24,46.25,59.49,90.74],[11.99,47.99,6.99,85.74],[-32,53.5,-23,86],[-44,54.49,-66.75,88.75],[-46,41.5,-85.25,41.5],[-15,5.25,-79.25,6.75]];
                planes = [[[[86.49,8.75],[56.99,8.24]],3,3,".title2",true,1,1],[[[-33.75,-29.75],[-68.25,-30.5]],3,3,".title",true,1,1],[[[-68,-3.5],[-33.5,-3.5]],3,3,".title",true,1,1],[[[-34.25,82.74],[-45,86.49],[-56.5,84.49],[-66.25,80.5],[-71.25,71.25],[-72.75,61.75]],5,1,".wonderfl",true,1,1],[[[63,68.49],[57.23,75.74],[51,80.5],[44.49,84.25],[35.98,85.74]],5,1,".flash",true,1,1]];
                fences = [[[-18.75,-37],[-28.25,-58],[-18.5,-66.25],[-11.5,-66],[8,-57.5],[32.99,-46.5]],[[55.73,-22],[60.5,-7.25],[61.98,12.5],[56.24,17.5],[51.74,28.49],[52.98,50.48],[34.74,64.97],[-6.5,53.74],[-20,53.99],[-44.5,64.72],[-52,59.23],[-53,31.74],[-47,23.24],[-19.25,4.99],[-18.76,-37]],[[-62.01,-38.5],[-56,-60.75],[-31.75,-88.25],[-13,-89],[12.24,-79.75],[53.74,-70.25],[73.99,-42.75],[84.75,-0.5],[81.75,25],[73.99,35.23],[75.24,57.99],[57.49,85.24],[34.73,89.74],[-5,79.25],[-17.5,79.49],[-44.75,90.49],[-68.26,85.24],[-75.25,73.48],[-76.5,30.99],[-71.5,15],[-63.5,2.24],[-62.01,-38.5]],[[32.99,-46.5],[29.24,-35.75],[43.75,-21.75],[55.73,-22]]];
                path = [[[-54.75,17.75],[-27.25,-0.75],[-27,-32.75],[-37,-43.75],[-37,-65.25]],[[56.24,-43],[43.24,-33.75]]];
                points = [[-51.44,-10.86,-0.15,-31.51,0.24,49.95],[-35,-68.5,9.94,-12.81,-15.43,19.89],[-2.75,-75,26.26,14.81,-11.65,-6.57],[55.73,-50,20.97,27.13,-17,-22],[69.73,22.24,-20.05,19.14,7.15,-6.83],[45.98,73.98,-12.54,11.65,37.75,-35.06],[-17.75,66.25,-11.24,3.26,18.38,-5.33],[-64.25,57.5,-7.25,-60.25,4.53,37.68]];
                wancoXY = [39.74,-30.02];
                break;
            case 9:
                name = "Moja Forest";
                handicap = 0.94;
                roadSize = 45;
                pathSize = 30;
                cameraXY = [67.85,24.59];
                gazeXY = [83.6,5.83];
                seatsXY = [85.72,19.47];
                objectS = [[65.37,14.87],[65.24,16.99],[65.12,19.37],[65.12,21.62],[76.62,15],[76.62,17.12],[76.25,19.37],[76.12,21.75],[-75.09,17.85],[-73.34,15.53],[-71.59,13.13],[-10.05,-56.08],[-10.28,-52.63],[-9.42,-59.1]];
                lamps = [[-37.25,64.49],[-30.75,64.25],[-22,65.24],[-15.75,65.5],[-8.25,65],[0.5,65],[8,65],[14.25,63.99],[-41.25,85],[-34.25,85.74],[-28.75,85.74],[-22,86.25],[-16.5,86.49],[-10.25,85.74],[-4.5,85.5],[3,85.24],[10.5,85.74],[15.99,85]];
                trees = [[-78,17.5],[-80.5,21.75],[-83,26.25],[-84.75,31.5],[-87,36.74],[-88,41.74],[-66,1.99],[-60.25,-2.5],[-53.75,-5.25],[-48.25,-6.25],[-1.5,4.49],[-5.5,4.99],[-84.25,-42.75],[-81.5,-32.75],[-82.75,-27.75],[-83.25,-21.25],[-13.25,-61],[-11.5,-69],[-9,-74],[-5.5,-77.25],[-1.75,-81],[5,-82],[11.5,-82.5],[-10.25,-39.25],[-10.25,-35.25],[-7.25,-31.25],[-3.75,-27.75],[22.75,61.99],[30.25,58.5],[36.74,55.25],[42.75,51.5],[48.49,45.99],[54.49,42.5],[25.25,82.74],[32.24,78.75],[40.74,75],[47.75,70.24],[54.75,66.25],[61.25,63.99],[67.74,59.75],[72.5,54.24],[58.24,35.74],[-42.25,61.99],[-47.5,61],[-52.75,61],[-58,58.75],[-64,55.49],[-65.75,50.74],[-50.5,85],[-58.25,84.25],[-64.25,82.25],[-69.75,81.25],[-72,78.24],[-75.25,76.75],[-80,71],[-84.75,68.5],[-86,64.75],[-87.75,60.74],[55.74,-87.5],[65.24,-86.25],[68.74,-71.25],[72.49,-68],[76,-60.75],[79.24,-56.25],[82.5,-51.75],[88,-48.5],[-25.75,-70],[-31.5,-80.5],[-37,-83],[-40,-87.75],[-49.75,-87.75],[-77.25,-88],[-84.5,-84.75],[-90.25,-75.5],[-92,-64.5],[-94,-56.75],[-93.75,-44.5],[60.74,-26],[56.74,-29.25],[64.49,-23.75],[45.74,-42.75],[44.24,-48.5],[-31,19.25],[-22.5,24],[-17.5,28.75],[-7.75,29.75],[-1.75,32.75],[6.25,32.75],[11.99,25.5],[23.75,19.74],[19,16.5],[27.5,14.49],[30,7.5],[32.75,0.25],[36.25,-8],[33.24,13.49],[34,-20.5],[30.25,-26.75],[19,25.5],[-58.75,-50.5],[-55.75,-44.5],[-50,-40],[-43,-35.75],[-15.5,-28],[-12.5,-19],[-18.25,-8],[-26.75,-11.75],[-35.25,-13.5],[-44.25,-13.5],[-57.5,-13.5],[-49.75,-16],[1.75,-13],[-45.5,22.75],[-52.75,24.74],[-59.5,33.75],[-61.5,41.25],[-52,32.25],[-33.75,-66],[-15.25,-39.5],[-81.5,18.24],[-61.76,2.24],[-20.26,-60],[67.74,-78.75],[68.99,-54],[-26,-60.75],[59.49,-3.5],[62.74,-13],[65.24,-18.75],[37.99,-62],[40.25,-58.25],[32.25,-62.25],[23.24,-59.75],[17.24,-58.75],[15,-55.75],[13.24,-47.5],[18.49,-42.75],[-20.25,-37.25],[-13.25,-46],[-78.12,12.24],[-69.5,5.99],[-85.75,-50.75],[-85.75,-57.25],[-85.5,-62.75],[-43,-74.25],[-39.5,-68.5],[-66,-43.25],[-64.75,-39],[-64.5,-31.25],[-65,-24],[-66,-18],[-81.5,-14.5],[-83.5,-8.25],[-83.25,0]];
                checkPoints = [[-6.75,57.25,-7.5,93.73],[-29.25,57.74,-30.75,93.24],[-57,48.49,-92.75,88.5],[-61.75,37.75,-94.75,32.25],[-46.75,19,-97.5,-62.25],[26.99,38.24,-90.5,-91.5],[11.25,-55.75,-17.25,-68.5],[29.49,-58.75,24.25,-96],[40.74,-36.25,96.75,-80],[51.5,-12.5,94.75,-6.75],[39.49,30.74,70.24,78.75],[15,54.99,15,93.75]];
                planes = [[[[-26.5,88.49],[-26,63.23]],3,3,".title",true,1,1],[[[59.23,66.49],[44.74,45.74]],3,3,".title2",true,1,1],[[[-82.25,-70.75],[-78,-76],[-70.5,-79.25],[-63.75,-80.25],[-57.25,-79.5],[-50.75,-76.75]],5,1,".wonderfl",true,1,1],[[[-87.5,57.74],[-87.5,46]],6,1,".moja",true,1,1],[[[1.5,0.25],[19.25,19]],3,3,".flash",true,5,1],[[[83.99,-1.25],[60.5,-9]],3,3,".hoge",true,4,1],[[[34.24,-85.5],[41.74,-85.5],[47.5,-83.5],[53.5,-80.25],[59.49,-76.75],[63.99,-71.5]],5,1,".moja",true,1,1],[[[-78.45,8.84],[-69.9,2.99]],5,1,".moja",true,1,1],[[[-15.83,-47.3],[-15.57,-57.55]],5,1,".moja",true,1,1]];
                fences = [[[-64.75,34.75],[-65.25,44.49],[-57.75,52.99],[-35.5,59.75],[-10,60.74],[18,57.25],[50,28.99],[54.49,-3.25],[60.49,-19.25],[51.25,-30.75],[39.49,-44.75],[44.24,-59.75],[39,-65.75],[15.5,-61],[6.99,-57.5],[10,-51.5],[9.24,-44],[20.25,-37],[29.49,-37.5],[37.75,-23],[39.49,-6.75],[35.74,21.25],[23.24,38.74],[3.24,44.99],[-17,41],[-35.75,30.99],[-39.25,18.24],[-47,14.25],[-54.75,19.25],[-64.76,34.75]],[[41.5,-88.75],[61.75,-83],[72.49,-57.5],[88.5,-38.75],[88.24,-4.5],[80,7.24],[80.24,41.5],[72.5,62.74],[50.74,78],[23.75,89.75],[-36.75,89.73],[-62,87.25],[-77,82.5],[-89.75,68.99],[-89.5,38.5],[-84.75,20.25],[-89.75,3.24],[-90,-20.75],[-89,-58.25],[-84.25,-80.25],[-63.75,-88.5],[-47.25,-83],[-34.75,-76],[-26.75,-64.75],[-16.51,-64.01],[-13.25,-79.25],[-2.75,-89.25],[20.5,-86],[41.5,-88.75]],[[-64,-3.75],[-48,-9.5],[-26,-6.75],[-9.75,3],[-1,0.99],[6.5,-8.75],[6.5,-16.02],[-9,-28],[-14.5,-34],[-17.75,-32],[-34.5,-34],[-49.25,-44.25],[-55.75,-56],[-61,-56.25],[-63.75,-51.5],[-60.5,-40],[-42,-30.75],[-20.25,-25.5],[-17.25,-14.5]],[[-17.25,-14.5],[-45,-22.25],[-63,-19.75],[-64,-3.75]]];
                path = [[[-73.76,0.74],[-73.75,-24.5],[-72.76,-36.75],[-77.25,-50.25],[-77.75,-59],[-75.75,-67.75],[-67,-73],[-55,-72],[-48,-67.25],[-44.75,-58.5],[-37.75,-53.25],[-29,-48.25],[-20,-50.25]],[[-22.25,-20.5],[-42,-25.5],[-57.75,-27.25]],[[-45.5,-60.75],[-60.25,-68.5],[-71.5,-64],[-74.25,-44.5]],[[68.75,3.75],[61.25,9.49],[60.49,26.99],[68.24,31.98]]];
                points = [[-76.56,33.73,2.84,-7.71,-6.73,18.29],[-53.07,5.85,15.5,-5.45,-12.1,4.25],[-22.25,10.24,16.24,10.25,-9.9,-6.24],[10.74,8.24,2.8,-4.58,-10.55,17.25],[17.75,-10.75,2.56,-14.88,-0.94,5.47],[-1.75,-40.75,-3.55,-10.01,3.74,10.56],[-0.5,-66.03,8.3,-9.5,-5.44,6.23],[26.5,-73.25,6.06,-3.58,-10.85,6.4],[50.48,-72.25,14.18,9.37,-11.35,-7.5],[54.23,-54.26,0.41,19.77,-0.12,-5.73],[77.23,-34.5,8.81,19.86,-5.16,-11.64],[71.73,2.73,0.23,10.27,-0.2,-8.93],[71.23,37.23,0.31,11.1,-0.23,-8.12],[18.49,75.49,-5.96,0.83,7.1,-0.99],[-35,74.98,-44.65,-0.81,23.2,0.42]];
                wancoXY = [-25.26,-21.53];
                break;
            case 10:
                name = "Moja Moja Road";
                lapsNum = 2;
                handicap = 1.1;
                roadSize = 45;
                pathSize = 106;
                cameraXY = [29.16,-21];
                gazeXY = [7.19,1.19];
                seatsXY = [73.73,4.48];
                objectS = [[84.49,51],[83.99,46.99],[83,42.75],[84.25,54.49],[27.62,49.87],[30.87,50.99],[31.74,68.12],[26.36,65.37],[29,66.37],[23.86,64.37],[33.75,52.36],[36.62,53.75],[-44.62,70.62],[-40.75,69.25],[-36.75,67.74]];
                lamps = [[-42.75,-57],[-38.25,-52.75],[-35.5,-47.5],[-33.25,-41.5],[-31.25,-34.5],[-30,-28],[-29.75,-21],[-29.75,-14],[-31.25,-8],[-33.25,-2.25],[-35.25,4],[-36.5,10.5],[-37.75,15.99],[-40.75,20],[52.75,-12.75],[50.74,-7],[49.49,6.5],[49.49,10.99],[-72.75,43.5],[-72.25,49],[-70,53.75],[-65.75,55.74],[-61.25,56.74],[-57.5,56.25],[-84.25,-3],[-83.5,2.75],[-85,19.25],[-86.25,24.49],[69.75,-56.25],[69.49,-51.5],[68.24,-46.5]];
                trees = [[-4.5,-52],[0,-55.25],[0.74,-50.5],[9.25,-44.25],[28.75,-46.25],[32.5,-51.5],[38.24,-49.5],[43.24,-46.5],[4.49,-48],[45,-42],[-6,40.74],[0.99,42.5],[8.49,41.25],[14,43.75],[19.74,43.5],[22.75,33.5],[40.49,50.49],[42.25,55.25],[34.75,73.99],[24.74,46],[31,77.49],[27.24,82.24],[12.5,64.25],[5.74,63.24],[-2.25,62.5],[-7.25,62.5],[-13.5,61.75],[-17.75,63.24],[-22.5,65.74],[-29.5,66.49],[-10.75,41.49],[35.74,80.24],[63.24,86.25],[74.75,85],[78.5,81.25],[82.74,77.74],[89.75,69.25],[49.49,-3],[49.24,1.99],[70.74,-9],[73.75,-15.25],[77.5,-21],[81,-27.75],[84.25,-32.75],[70.74,18.24],[73.75,21.25],[76,25.5],[79.25,28.5],[83,32.75],[88.24,-40.5],[-8.75,-92.5],[-16,-93.75],[-26.25,-91],[-38,-91.75],[-48,-91.25],[-57.75,-86.25],[-65.5,-82.75],[-71.75,-76.75],[-73.75,-66.75],[88.5,-45.75],[89.75,-54.5]];
                checkPoints = [[91.75,5.5,33.48,1.99],[93.49,-38,61.49,-40.25],[67.25,-95.5,56.5,-60],[-31.5,-94.25,-30,-71.25],[-73.5,-69.5,-46.25,-65.25],[-52.75,3.24,-30.5,8.49],[-46,36.74,-33,13.24],[-17.75,29.49,-28.75,10.25],[9.49,-13.25,-14.75,-12.5],[-9.25,-50.5,-35.75,-57.75],[16.25,-38.25,22.5,-75],[35.99,-42,63.75,-62],[30.25,-38,47.5,-16],[-33.25,30,-28.75,49.24],[-50,30.74,-66.5,51],[-51.25,18,-70.75,13],[-47.25,-39.25,-65,-27.5],[-87,-46.5,-71.25,-28],[-89.75,-16.75,-69.25,-19.5],[-88.25,66,-62.25,50],[-41.25,92.25,-42.25,49.49],[4.25,91.99,39,3.24],[23.49,91,49,61.25],[78.99,92.74,60.24,56.99],[92.74,65.74,65,52.99],[91.73,36.48,49.23,34]];
                planes = [[[[20.99,-29.5],[16.5,-9.5],[6.5,9.25],[-7.75,22.24],[-27.5,33.49],[-50.5,35.49]],6,1,".wonderfl",true,1,1],[[[-53.25,74.23],[-66.26,73.99],[-77,71],[-84.25,64.49],[-89.25,54.75]],6,1,".moja",true,1,1],[[[47.48,-84.75],[50.23,-91.5]],5,1,".flash",true,1,1],[[[26.75,-87.25],[29.24,-93.75]],5,1,".wonderfl",true,1,1],[[[5,-90.75],[7.24,-96.75]],5,1,".moja",true,1,1],[[[-63.75,-17.75],[-26.25,-8.5]],3,3,".title",true,1,1],[[[-86.75,5.74],[-92.5,0.25]],5,1,".moja",true,1,1]];
                fences = [[[-26.25,-73],[-4.5,-74],[46.49,-67],[63.75,-58.25],[65.24,-40.5],[54.24,-19.25],[39,-15],[32.48,-0.75],[50.49,14.74],[53.24,29.24],[68.5,48.5],[70,56.5],[55,67.74],[50.74,63.99],[43.75,46.25],[47.74,31.49],[53.24,29.24]],[[32.48,-0.75],[5.99,25.74],[-21,45.74],[-45.25,53.75],[-59.5,53.74],[-69,46.24],[-71.25,31.74],[-66.5,3],[-71.5,-17.75],[-72.5,-29.75],[-62.5,-30.5],[-60.5,-19.75],[-66.5,3]],[[4,9.74],[8.75,-8.25],[3.49,-29.75],[-12.5,-47.5],[-10,-53.75],[6.5,-40.25],[22.24,-39.75],[36.74,-43.75],[41.25,-38.25],[23,-28.75],[16.25,-5.25],[4,9.74],[-16.25,27.5],[-41.5,36],[-52.5,32.5],[-56.25,21.5],[-46,-10.5],[-45,-31.75],[-56,-47.25],[-81,-68.25],[-77.5,-84],[-59.5,-90.75],[0.99,-89.25],[72.5,-78.5],[87.23,-67.5],[86,-42.25],[66.99,-9],[66.25,18.24],[85.74,39.24],[89.49,55.49],[81.25,74.24],[51.24,88.99],[37.98,82.49],[30.49,89.24]],[[-26.25,-73],[-35,-54.75],[-28.25,-32.5],[-11.25,-15.5],[-15.25,2.75],[-35.5,17.5],[-26.75,-13.75],[-28.25,-32.5]],[[-26.25,-73],[-47.5,-68.75],[-49.75,-63.25],[-35,-54.75]],[[-56,-47.25],[-68,-52.5],[-84,-48],[-90.75,-33],[-84.5,5.99],[-91,43.24],[-89.5,67.5],[-71.51,84.24],[-45.75,84.49],[-35.25,88.99]],[[-35.25,88.99],[30.49,89.24]]];
                path = [[[-47.25,72.24],[-25.25,80.74],[-5,77.48],[13.24,80.24],[30,70.74],[34.24,40.74],[26.99,18.49],[34.49,13.49],[42.25,24.25]],[[4.49,-59.5],[16.75,-47.25],[32.5,-58]],[[-38.75,-82.25],[-69.25,-74.25],[-66.5,-60.5]]];
                points = [[52.75,-35.25,-5.51,6.01,20.95,-22.85],[26.99,-18.25,-2.11,25.75,0.77,-9.47],[-22.75,38.48,-30.05,13.84,14.97,-6.89],[-59.76,11.75,11.92,-25.35,-10.99,23.36],[-70,-42.75,-17.84,1.6,25.64,-2.31],[-75.5,-2.5,6.5,19.25,-6.5,-19.25],[-70,64.23,13.61,6.27,-27.53,-12.68],[-11.25,50.98,5.55,-1.1,-11.84,2.34],[22.99,56,21.06,4.37,-6.9,-1.43],[51.74,80.49,20.04,-10.91,-5.5,3],[78.99,46.74,-5.24,-11.74,5.24,11.74],[60,18.75,-1,-5.99,1,5.99],[60.5,-6.5,0.5,-17.42,-0.26,8.91],[66.25,-70.25,-44.49,-10.79,33.99,8.24],[-48.25,-78.5,-30.62,15.34,16.25,-8.14],[-45,-46.75,24.99,34.99,-10.26,-14.37],[-45,29.48,30.31,1.94,-19.21,-1.23],[-11.25,-31.5,-14.97,-9.09,33.74,20.49],[-10,-65.25,42.49,0.75,-26.02,-0.45]];
                wancoXY = [15.74,28.72];
                break;
        }
    }
    
    public function setPlace(type:int):void {
        place = type;
        switch(type) {
            case CIRCUIT:
                fenceMap = Dot.moja;
                dirtFriction = 0.02;
                roadColor = pathColor = 0x505050;
                lineColor = 0xffffff;
                dirtColor = 0x73900b;
                edgeColor = 0xd9c364;
                particleColors = [0x6d8a15, 0xb6e034];
                break;
            case FOREST:
                dirtFriction = 0.025;
                fenceMapSize = 4;
                fenceHeight = 2;
                fenceMap = Dot.grass;
                edgeColor = 0xA47B4E;
                dirtColor = 0x5A6130;
                roadColor = pathColor = lineColor = 0xC0AE57;
                particleColors = [0x6d8a15, 0xb6e034];
                break;
            case SNOWFIELD:
                dirtFriction = 0.03;
                fenceMapSize = 2;
                fenceHeight = 1;
                fenceMap = Dot.brick;
                dirtColor = 0xeaeaea;
                edgeColor = 0xd9c364;
                roadColor = pathColor = lineColor = 0x877B49;
                particleColors = [0xF2F2F2, 0xFFFFFF];
                break;
            case WINTERCIRCUIT:
                dirtFriction = 0.03;
                fenceMapSize = 2;
                fenceHeight = 1;
                fenceMap = Dot.brick;
                dirtColor = 0xeaeaea;
                edgeColor = 0xd9c364;
                roadColor = pathColor = 0x505050;
                lineColor = 0xffffff;
                particleColors = [0xF2F2F2, 0xFFFFFF];
                break;
        }
    }
    
    public function random(place:int = 0):void {
        var hasInFence:Boolean = true;
        var hasOutFence:Boolean = true;
        var hasCone:Boolean = true;
        var hasDrum:Boolean = true;
        var treeInRange:Array = [0.88, 0.88];
        var treeOutRange:Array = [1.12, 1.12];
        var treeNum:int = 3;
        var lampNum:int = 2;
        this.place = place;
        roadSize = 30;
        switch(place) {
            case CIRCUIT:
                roadSize = 40;
                treeNum = 3;
                treeInRange = [0.76, 0.79];
                treeOutRange = [1.22, 1.26];
                break;
            case FOREST:
                roadSize = 30;
                hasDrum = false;
                treeInRange = [0.5, 0.9];
                treeOutRange = [1.1, 1.4];
                treeNum = 6;
                fenceMapSize = 4;
                fenceHeight = 2;
                fenceMap = Dot.grass;
                break;
            case SNOWFIELD:
                roadSize = 35;
                treeNum = 1;
                break;
            case WINTERCIRCUIT:
                roadSize = 40;
                treeNum = 3;
                treeInRange = [0.76, 0.79];
                treeOutRange = [1.22, 1.26];
                break;
        }
        var i:int, n:Number, tx:Number, ty:Number, angle:Number, p1:Point, p2:Point, tp:Point, rad:Number;
        checkPoints.length = points.length = objectS.length = objectL.length = trees.length = 0;
        
        for (i = 0; i < 20; i ++) {
            rad = (i <= 2 || i == 19)? 55 : (Math.random() * 25 + 55);
            angle = Angle.RAD360 * i / 20;
            tx = Math.cos(angle + Angle.RAD90) * 5;
            ty = Math.sin(angle + Angle.RAD90) * 5;
            points.push([Math.cos(angle) * rad, Math.sin(angle) * rad, tx,ty,-tx,-ty]);
        }
        
        for (i = 0; i < 20; i ++) {
            rad = Angle.toRAD * (i / 20 * 360 + 10);
            checkPoints.push([0, 0, Math.cos(rad) * 200, Math.sin(rad) * 200]);
        }
        
        var leng:int = points.length;
        fences = [[], []];
        for (i = 0; i <= leng; i++) {
            p1 = new Point(points[i % leng][0], points[i % leng][1]);
            p2 = new Point(points[(i+1)%leng][0], points[(i+1)%leng][1]);
            fences[0].push([p1.x * 0.8, p1.y * 0.8]);
            fences[1].push([p1.x * 1.2, p1.y * 1.2]);
        }
        fences[0].reverse();
        if (!hasInFence) fences.splice(0, 1);
        if (!hasOutFence) fences.splice(-1, 1);
        
        for (i = 1; i < leng; i++) {
            p1 = new Point(points[i][0], points[i][1]);
            p2 = new Point(points[i - 1][0], points[i - 1][1]);
            
            for (n = 1; n <= lampNum; n++) {
                tp = Point.interpolate(p1, p2, n / lampNum);
                lamps.push([tp.x * 0.86, tp.y * 0.86]);
                lamps.push([tp.x * 1.14, tp.y * 1.14]);
            }
            
            if (i % 4 && i != 1) {
                for (n = 1; n <= treeNum; n++) {
                    tp = Point.interpolate(p1, p2, n / treeNum);
                    if (treeInRange.length) {
                        var perIn:Number = treeInRange[0] + (treeInRange[1] - treeInRange[0]) * Math.random();
                        trees.push([tp.x * perIn, tp.y * perIn]);
                    }
                    if (treeOutRange.length) {
                        var perOut:Number = treeOutRange[0] + (treeOutRange[1] - treeOutRange[0]) * Math.random();
                        trees.push([tp.x * perOut, tp.y * perOut]);
                    }
                }
            } else {
                tp = Point.interpolate(p1, p2, 0.4);
                if (hasDrum) for (n = 1; n <= 1; n++)
                    objectL.push([tp.x * 1.08 + Math.random() * 1.2, tp.y * 1.08 + Math.random() * 1.2]);
                if (hasCone) for (n = 0.8; n <= 1; n += 0.1) {
                    tp = Point.interpolate(p1, p2, n);
                    objectS.push([tp.x * 0.93, tp.y * 0.93]);
                }
            }
        }
    }
    
}

/**
 * トラックデータとか
 */
class World {
    public var wonderfl:Track;
    public var demo:Track;
    public var complete:Track;
    public var howto:Track;
    public var tracks:Vector.<Track> = new Vector.<Track>();
    
    public function World() {
        tracks.push( new Track({ lv:0    ,course:1        ,unlock:0    ,light:Light.DAY            ,place:Track.CIRCUIT                ,reverse:false    }));
        tracks.push( new Track({ lv:0    ,course:2        ,unlock:1    ,light:Light.DAY            ,place:Track.SNOWFIELD        ,reverse:false    }));
        tracks.push( new Track({ lv:0    ,course:3        ,unlock:2    ,light:Light.DAWN        ,place:Track.CIRCUIT                ,reverse:false    }));
        tracks.push( new Track({ lv:0    ,course:4        ,unlock:3    ,light:Light.SUNSET    ,place:Track.FOREST                ,reverse:false    }));
        tracks.push( new Track({ lv:0    ,course:5        ,unlock:4    ,light:Light.NIGHT        ,place:Track.CIRCUIT                ,reverse:false    }));
        
        tracks.push( new Track({ lv:1    ,course:6        ,unlock:0    ,light:Light.DAY            ,place:Track.WINTERCIRCUIT    ,reverse:false    }));
        tracks.push( new Track({ lv:1    ,course:7        ,unlock:1    ,light:Light.SUNSET    ,place:Track.FOREST                ,reverse:false    }));
        tracks.push( new Track({ lv:1    ,course:8        ,unlock:2    ,light:Light.DAY            ,place:Track.CIRCUIT                ,reverse:false    }));
        tracks.push( new Track({ lv:1    ,course:9        ,unlock:3    ,light:Light.DAY            ,place:Track.FOREST                ,reverse:false    }));
        tracks.push( new Track({ lv:1    ,course:10        ,unlock:4    ,light:Light.NIGHT        ,place:Track.CIRCUIT                ,reverse:false    }));
        
        tracks.push( new Track({ lv:2    ,course:1        ,unlock:0    ,light:Light.NIGHT        ,place:Track.WINTERCIRCUIT    ,reverse:true    }));
        tracks.push( new Track({ lv:2    ,course:2        ,unlock:0    ,light:Light.DAWN        ,place:Track.CIRCUIT                ,reverse:true    }));
        tracks.push( new Track({ lv:2    ,course:3        ,unlock:0    ,light:Light.DAY            ,place:Track.FOREST                ,reverse:true    }));
        tracks.push( new Track({ lv:2    ,course:4        ,unlock:0    ,light:Light.DAY            ,place:Track.CIRCUIT                ,reverse:true    }));
        tracks.push( new Track({ lv:2    ,course:5        ,unlock:2    ,light:Light.SUNSET    ,place:Track.WINTERCIRCUIT    ,reverse:true    }));
        
        tracks.push( new Track({ lv:2    ,course:6        ,unlock:3    ,light:Light.NIGHT        ,place:Track.CIRCUIT                ,reverse:true    }));
        tracks.push( new Track({ lv:2    ,course:7        ,unlock:4    ,light:Light.DAY            ,place:Track.CIRCUIT                ,reverse:true    }));
        tracks.push( new Track({ lv:2    ,course:8        ,unlock:5    ,light:Light.SUNSET    ,place:Track.CIRCUIT                ,reverse:true    }));
        tracks.push( new Track({ lv:2    ,course:9        ,unlock:6    ,light:Light.DAWN        ,place:Track.CIRCUIT                ,reverse:true    }));
        tracks.push( new Track({ lv:2    ,course:10        ,unlock:7    ,light:Light.DAY            ,place:Track.SNOWFIELD        ,reverse:true    }));
        
        tracks.push( new Track({ lv:3    ,course:11        ,unlock:0    ,light:Light.DAY            ,place:Track.WINTERCIRCUIT    ,reverse:false    }));
        
        demo = new Track({                 course:6                        ,light:Light.DAWN        ,place:Track.WINTERCIRCUIT    ,reverse:false    });
        howto = new Track({                 course:-1                        ,light:Light.DAY            ,place:Track.CIRCUIT                                    });
        complete = new Track({             course:0                        ,light:Light.NIGHT        ,place:Track.WINTERCIRCUIT                        });
        wonderfl = getTracksByLevel(3)[0];
        demo.enemiesNum = 4;
        howto.enemiesNum = 0;
        complete.enemiesNum = 0;
        wonderfl.enemiesNum = 0;
        
        for (var i:int = 0; i < tracks.length; i++) tracks[i].id = i;
    }
    
    /**
     * アンロック済みの難易度の全トラックデータを配列で取得する
     */
    public function getUnlockedTracks(user:UserData):Vector.<Track> {
        var items:Vector.<Track> = getTracksByLevel(Race.NORMAL);
        if (user.rankIn[Race.NORMAL]) items = items.concat(getTracksByLevel(Race.HARD));
        if (user.rankIn[Race.HARD]) items = items.concat(getTracksByLevel(Race.EXTREME));
        return items;
    }
    
    /**
     * 指定レベルのトラックデータを配列で取得する
     */
    public function getTracksByLevel(lv:int):Vector.<Track> {
        var items:Vector.<Track> = new Vector.<Track>();
        for each (var tk:Track in tracks) if (tk.level == lv) items.push(tk);
        return items;
    }
    
    /**
     * ユーザーデータを渡してアンロック済みのトラック数を調べる
     */
    public function getUnlockedNum(user:UserData):int {
        var cnt:int = 0;
        for each (var lv:int in Race.LEVELS) {
            if (lv >= 1 && !user.rankIn[lv-1]) continue;
            var items:Vector.<Track> = getTracksByLevel(lv);
            var num:int = user.getClearNum(items, 3);
            items.forEach(function(...arg):void { cnt += int(arg[0].unlock <= num) } );
        }
        return cnt;
    }
    
    /**
     * ユーザーデータを渡して全コース1位になったかチェックする
     */
    public function checkComplete(user:UserData):int {
        var complete:int = -1;
        for each (var lv:int in Race.LEVELS) {
            var items:Vector.<Track> = getTracksByLevel(lv);
            if (!user.complete[lv] && user.getClearNum(items, 1) == items.length) {
                user.complete[lv] = true;
                complete = lv;
            }
        }
        return complete;
    }
    
    /**
     * ユーザーデータを渡して全コース入賞したかチェックする
     */
    public function checkRankIn(user:UserData):int {
        var rankIn:int = -1;
        for each (var lv:int in Race.LEVELS) {
            var items:Vector.<Track> = getTracksByLevel(lv);
            if (!user.rankIn[lv] && user.getClearNum(items, 3) == items.length) {
                user.rankIn[lv] = true;
                rankIn = lv;
            }
        }
        return rankIn;
    }
    
}

/**
 * 戻るアイコン
 */
class KeyLabel {
    
    public var sprite:Sprite = new Sprite();
    private var _label:Label;
    
    public function KeyLabel() {
    }
    
    public function init():void {
        sprite.addChild(Create.spriteBmp(Dot.X, 2));
        sprite.x = Display.width - 80;
        sprite.y = 9;
        _label = UI.createLabel(sprite, 26, -6, Text.LABEL_BACK);
        hide();
    }
    
    public function hide():void {
        sprite.visible = false;
    }
    
    public function show(text:String = ""):void {
        if (text) _label.text = text;
        sprite.visible = true;
    }
    
}

/**
 * 設定メニュー
 */
class SettingMenu {
    
    public var sprite:Sprite = new Sprite();
    private var _menu:LabelMenu;
    private var _cancelFunc:Function;
    
    public function SettingMenu() {
    }
    
    public function init():void {
        var texts:Array = ["Mirror(M): {OFF,ON}", "Headlight(L): {OFF,ON}", "Sound(S): {OFF,ON}", "View Distance(V): {1,2,3,4,5}", "Particles(P): {OFF,ON}", "Quality(Q): {LOW,HIGH}"];
        _menu = new LabelMenu(sprite, UI.menuX, UI.menuY, texts, null, onCancel, onChange, onSwitch);
        setEnabled(false);
    }
    
    public function setEnabled(enabled:Boolean, cancel:Function = null):void {
        _cancelFunc = cancel;
        _menu.enabled = _menu.visible = enabled;
        if (enabled) {
            update();
            _menu.select(0);
        }
    }
    
    private function onChange(index:int):void {
        if (!$.game.isDemo) return;
        if (index <= 3) $.game.setCamera(SceneCamera.DRIVERS);
        else $.game.setCamera(SceneCamera.AROUND);
    }
    
    private function onSwitch(index:int, option:int):void {
        $.user.showMirror = !!_menu.options[0];
        $.user.showLight = !!_menu.options[1];
        $.user.mute = SE.mute = !_menu.options[2];
        $.user.viewDistance = _menu.options[3];
        $.user.showParticles = !!_menu.options[4];
        $.user.groundQuality = _menu.options[5];
        $.user.save();
        $.game.updateDisplay();
        if (index == 5) $.game.updateGround();
        $.game.scene.render();
    }
    
    private function onCancel():void {
        _cancelFunc();
        setEnabled(false);
    }
    
    public function update():void {
        _menu.setOption(0, int($.user.showMirror));
        _menu.setOption(1, int($.user.showLight));
        _menu.setOption(2, int(!$.user.mute));
        _menu.setOption(3, $.user.viewDistance);
        _menu.setOption(4, int($.user.showParticles));
        _menu.setOption(5, $.user.groundQuality);
    }
    
}

/**
 * タイトル画面
 */
class TopMenu {
    
    static public const SCENE_HIDE:int = 0;
    static public const SCENE_TOP:int = 1;
    static public const SCENE_LEVEL:int = 2;
    static public const SCENE_SETTING:int = 3;
    static public const SCENE_CLICK:int = 4;
    static public const SCENE_FILE:int = 5;
    
    public var sprite:Sprite = new Sprite();
    private var _noConnection:Boolean = false;
    private var _scene:int = SCENE_CLICK;
    private var _logo:Bitmap;
    private var _click:Sprite;
    private var _titleMenu:LabelMenu;
    private var _levelMenu:LabelMenu;
    private var _fileMenu:LabelMenu;
    
    public function get scene():int { return _scene; }
    
    public function TopMenu() {
    }
    
    public function init():void {
        var sp:Sprite = new Sprite();
        UI.createLabel(sp, 0, 0, Text.TITLE_MAIN, 8 * 7);
        UI.createLabel(sp, 6, 75, Text.TITLE_SUB, 8 * 3);
        
        _logo = Create.gradientLabel(sp, 0xFFFA19, 0xFF9411, 0, 0xA0);
        _logo.x = (Display.width - _logo.width)/2 | 0;
        _logo.y = 70;
        sprite.addChild(_logo);
        var items:Array = [Text.RACE_SINGLE, Text.RACE_TIMETRIAL, Text.RACE_WONDERFL, Text.RACE_RANDOM, Text.MENU_SETTINGS, Text.HOWTO, Text.FILE];
        _titleMenu = new LabelMenu(sprite, UI.menuX, UI.menuY, items, onSelectTitle);
        _levelMenu = new LabelMenu(sprite, UI.menuX, UI.menuY, Text.LEVELS, onSelectLevel, onCancelLevel);
        _fileMenu = new LabelMenu(sprite, UI.menuX, UI.menuY, [Text.EXPORT, Text.IMPORT], onSelectFile, onCancelLevel);
        
        _click = Create.box(0, 0, Display.width, Display.height, 0, 0);
        UI.createLabel(_click, (Display.width - 140)/2|0, 300, Text.MSG_CLICK, 8 * 2, UI.HIGHLIGHT).filters = UI.TEXT_FILTERS;
        _click.buttonMode = true;
        _click.mouseChildren = false;
        _click.addEventListener(MouseEvent.CLICK, gotoTop);
        sprite.addChild(_click);
        setScene(SCENE_CLICK);
    }
    
    public function setNoConnection():void {
        _noConnection = true;
        _titleMenu.items[2].setText("--------");
        _titleMenu.items[2].enabled = false;
    }
    
    private function onSelectFile(index:int):void{
        if (index == 0) $.user.exportFile();
        if (index == 1) $.user.importFile(onCompleteImport);
    }
    
    private function onCompleteImport():void{
        setScene(SCENE_TOP);
    }
    
    public function gotoTop(e:MouseEvent = null):void {
        SE.note(SE.OMP_SELECT);
        setScene(SCENE_TOP);
    }
    
    public function updateLevel():void {
        _levelMenu.items[1].setText($.user.rankIn[Race.NORMAL]? Text.LEVELS[Race.HARD] : "-----");
        _levelMenu.items[2].setText($.user.rankIn[Race.HARD]? Text.LEVELS[Race.EXTREME] : "-----");
        _levelMenu.items[1].setEnabled($.user.rankIn[Race.NORMAL]);
        _levelMenu.items[2].setEnabled($.user.rankIn[Race.HARD]);
    }
    
    public function setScene(value:int):void {
        _scene = value;
        switch(_scene) {
            case SCENE_FILE: case SCENE_LEVEL: case SCENE_SETTING: $.label.show(Text.LABEL_BACK); break;
            default: $.label.hide();
        }
        sprite.visible = (_scene != SCENE_HIDE);
        _click.visible = (_scene == SCENE_CLICK);
        _fileMenu.enabled = _fileMenu.visible = (_scene == SCENE_FILE);
        _titleMenu.enabled = _titleMenu.visible = (_scene == SCENE_TOP);
        _levelMenu.enabled = _levelMenu.visible = (_scene == SCENE_LEVEL);
        if (_scene == SCENE_SETTING) $.setting.setEnabled(true, onCancelSettings);
        $.game.updateDisplay();
        updateLevel();
    }
    
    private function onCancelLevel():void {
        setScene(SCENE_TOP);
    }
    
    private function onCancelSettings():void {
        $.game.setCamera(SceneCamera.DEMO);
        setScene(SCENE_TOP);
    }
    
    private function onSelectLevel(index:int):void {
        if (index > 0 && !_levelMenu.items[index].enabled) return;
        startSelectTrack(Race.SINGLE, index);
    }
    
    private function onSelectTitle(index:int):void {
        if (index == 0) {
            setScene(SCENE_LEVEL);
            return;
        }
        if (index <= 3) startSelectTrack(index);
        if (index == 4) setScene(SCENE_SETTING);
        if (index == 5) $.transition.fadeOutIn($.game.howto);
        if (index == 6) {
            _fileMenu.select(0);
            setScene(SCENE_FILE);
        }
    }
    
    private function startSelectTrack(mode:int, level:int = 0):void {
        setScene(SCENE_HIDE);
        $.selector.create(mode, level);
    }
    
}

/**
 * 表示テキスト
 */
class Text {
    
    static public const TITLE_MAIN:String = "MOJA";
    static public const TITLE_SUB:String = "GRAND PRIX";
    static public const LEVEL:String = "Difficulty";
    static public const RESULT:String = "Race Result";
    static public const HOWTO:String = "How To Drift";
    static public const FILE:String = "File";
    static public const LABEL_BACK:String = "Back";
    static public const LABEL_QUIT:String = "Quit";
    static public const EXPORT:String = "Export Save Data";
    static public const IMPORT:String = "Import Save Data";
    static public const DEMO:String = "DEMONSTRATION";
    static public const FINISH:String = "FINISH";
    static public const WINNER:String = "WINNER";
    static public const RACE_SINGLE:String = "Single Race";
    static public const RACE_TIMETRIAL:String = "Time Trial";
    static public const RACE_RANDOM:String = "Random Race";
    static public const RACE_WONDERFL:String = "Wonderfl Challenge";
    static public const MENU_SETTINGS:String = "Settings";
    static public const MENU_RETRY:String = "Try Again";
    static public const MENU_QUIT:String = "Quit";
    static public const MENU_RESUME:String = "Resume";
    static public const MENU_REPLAY:String = "Replay Video";
    static public const RACES:Array = [RACE_SINGLE, RACE_TIMETRIAL, RACE_WONDERFL, RACE_RANDOM];
    static public const LEVELS:Array = ["NORMAL", "HARD", "MOJA"];
    static public const PLACES:Array = ["CIRCUIT", "FOREST", "SNOWFIELD", "WINTER CIRCUIT"];
    static public const UNLOCK_TRACK:String = "New Track is Unlocked!";
    static public const MSG_CLICK:String = "CLICK TO PLAY";
    
    static public function getRankString(rank:int):String {
        if (rank >= 4) return rank + "th";
        return ["1st", "2nd", "3rd"][rank - 1];
    }
    
    static public function getTimeString(time:int):String {
        var sec:Number = time / 1000;
        if (time < 0) return "--:--:--"; 
        return String(sec / 60 | 0) + ":" + String("00").substr(String(sec % 60 | 0).length) + (sec % 60).toFixed(3).replace(".", ":");
    }
    
}

/**
 * UIの色とか
 */
class UI {
    
    static public const menuX:Number = 155;
    static public const menuY:Number = 230;
    static public const PALE:uint = 0xDDDDD0;
    static public const HIGHLIGHT:uint = 0xFFE01A;
    static public const WINNER:uint = 0xFFCC00;
    static public const BASE:uint = 0x857E56;
    static public const DARK:uint = 0x302E1F;
    static public const TEXT_FILTERS:Array = [new DropShadowFilter(1, 45, 0, 1, 3, 3, 200, 1)];
    
    static public function createLabel(parent:DisplayObjectContainer = null, x:Number = 0, y:Number = 0, text:String = "", size:Number = 16, rgb:uint = 0xFFFFFF):Label {
        var saveSize:Number = Style.fontSize;
        var saveRgb:uint = Style.LABEL_TEXT;
        Style.fontSize = size;
        Style.LABEL_TEXT = rgb;
        var label:Label = new Label(parent, x, y, text);
        Style.fontSize = saveSize;
        Style.LABEL_TEXT = saveRgb;
        return label;
    }
    
    static public function createDarkBox(alpha:Number = 0.4):Sprite {
        return Create.box(0, 0, Display.width, Display.height, DARK, alpha);
    }
    
    static public function createHeader(title:String = ""):Sprite {
        var sprite:Sprite = Create.gradientBox(0, 0, Display.width, 40, true, 90, [0x333333, 0x000000], [1, 1], [0, 0xFF]);
        createLabel(sprite, 5, 3, title);
        return sprite;
    }
    
}

/**
 * 保存するデータ
 */
class UserData {
    
    public var viewDistance:int;
    public var groundQuality:int;
    public var randomEnemiesNum:int;
    public var randomPlace:int;
    public var randomTime:int;
    public var randomLevel:int;
    public var unlock:int;
    public var showParticles:Boolean;
    public var showMirror:Boolean;
    public var showLight:Boolean;
    public var mute:Boolean;
    public var bestRank:Array = [];
    public var bestTotals:Array = [];
    public var bestLaps:Array = [];
    public var rankIn:Array = [];
    public var complete:Array = [];
    private var _so:SharedObject = SharedObject.getLocal("mojagp");
    private var _fr:FileReference = new FileReference();
    private var _error:Function;
    private var _complete:Function;
    
    public function UserData() {
    }
    
    /**
     * 指定ランク以上に入賞したトラック数を調べる
     */
    public function getClearNum(tracks:Vector.<Track>, unlockRank:int = 3):int {
        var unlock:int = 0;
        for each (var track:Track in tracks) {
            var rank:int = bestRank[track.id];
            if (rank > 0 && rank <= unlockRank) unlock++;
        }
        return unlock;
    }
    
    /**
     * アンロックしたトラック数を設定しつつ数が増えたらtrueを返す
     */
    public function setUnlockedNum(num:int):Boolean {
        var isUpdate:Boolean = (unlock < num);
        unlock = num;
        return isUpdate;
    }
    
    public function setDefaultData():void {
        var i:int, n:int;
        for (n = 0; n < $.world.tracks.length; n++) {
            bestRank[n] = -1;
            bestTotals[n] = -1;
            bestLaps[n] = -1;
        }
        mute = false;
        showParticles = true;
        showLight = true;
        showMirror = false;
        viewDistance = 2;
        groundQuality = 1;
        randomEnemiesNum = 6;
        unlock = randomPlace = randomLevel = randomTime = 0;
        setUnlockedNum($.world.getUnlockedNum(this));
        complete = [false, false, false];
        rankIn = [false, false, false];
    }
    
    public function importFile(complete:Function = null, error:Function = null):void {
        _complete = complete;
        _error = error;
        _fr.addEventListener(Event.SELECT, onSelectFile);
        _fr.addEventListener(Event.CANCEL, removeFileListeners);
        _fr.browse();
    }
    
    private function removeFileListeners(...arg):void {
        _fr.removeEventListener(Event.SELECT, onSelectFile);
        _fr.removeEventListener(Event.CANCEL, removeFileListeners);
        _fr.removeEventListener(Event.COMPLETE, onLoadFile);
    }
    
    private function onSelectFile(e:Event):void {
        removeFileListeners();
        _fr.addEventListener(Event.COMPLETE, onLoadFile);
        _fr.load();
    }
    
    private function onLoadFile(e:Event):void {
        removeFileListeners();
        try {
            _fr.data.uncompress();
            var obj:Object = _fr.data.readObject();
            for each(var node:XML in describeType(this).variable) {
                var name:String = node.@name;
                if(obj[name] != null) this[name] = obj[name];
            }
            save();
            SE.effect(SE.SND_NEWRECORD);
            if (_complete != null)_complete();
        } catch (e:Error) {
            SE.note(SE.OMP_BOO);
            if (_error != null) _error();
        }
    }
    
    public function exportFile():void {
        var ba:ByteArray = new ByteArray();
        ba.writeObject(_so.data);
        ba.compress();
        _fr.save(ba, "mojadata.dat");
    }
    
    public function save():void {
        for each(var node:XML in describeType(this).variable) {
            var name:String = node.@name;
            _so.data[name] = this[name];
        }
    }
    
    public function load():void {
        for each(var node:XML in describeType(this).variable) {
            var name:String = node.@name;
            if (_so.data[name] != null) this[name] = _so.data[name];
        }
    }
    
    public function destroy():void {
        _so.clear();
        setDefaultData();
        save();
    }
    
}

/**
 * 画面サイズ
 */
class Display {
    
    static public var stage:Stage;
    static public var width:int;
    static public var height:int;
    static public var size:Rectangle = new Rectangle();
    static public var center:Point = new Point();
    static private var _lastQuality:String = "";
    
    static public function setSize(width:Number, height:Number):void {
        center.x = (Display.width = size.width = width) / 2;
        center.y = (Display.height = size.height = height) / 2;
    }
    
    static public function cacheQuality(quality:String = ""):void {
        _lastQuality = Display.stage.quality;
        if (quality) Display.stage.quality = quality;
    }
    
    static public function restoreQuality():void {
        Display.stage.quality = _lastQuality;
    }
    
}

/**
 * レース結果画面
 */
class ResultMenu {
    
    private var _menu:LabelMenu;
    private var _labels:Label;
    private var _times:Label;
    private var _message:Label;
    private var _starts:Vector.<Bitmap> = new Vector.<Bitmap>();
    private var _trophy:Bitmap = new Bitmap();
    public var onSelect:Function;
    public var sprite:Sprite = new Sprite();
    
    public function ResultMenu() {
    }
    
    public function init():void {
        sprite.addChild(UI.createDarkBox());
        sprite.addChild(UI.createHeader(Text.RESULT));
        sprite.addChild(_trophy);
        _trophy.scaleX = _trophy.scaleY = 5;
        
        for (var i:int = 0; i < 3; i++) {
            var star:Bitmap = sprite.addChild(new Bitmap(Dot.star)) as Bitmap;
            star.scaleX = star.scaleY = 2;
            star.x = UI.menuX + 60;
            star.y = 87 + i * 26;
            _starts.push(star);
        }
        
        _labels = UI.createLabel(sprite, UI.menuX, 80);
        _times = UI.createLabel(sprite, UI.menuX + 80, 80);
        _message = UI.createLabel(sprite, UI.menuX, 180, "", 16, 0xFFFF00);
        _message.filters = _times.filters = _labels.filters = UI.TEXT_FILTERS;
        _labels.text = "RANK\rRACE\rLAP";
        _menu = new LabelMenu(sprite, UI.menuX, UI.menuY, [Text.MENU_REPLAY, Text.MENU_RETRY, Text.MENU_QUIT], onSelectMenu);
        sprite.visible = _menu.enabled = _menu.visible = false;
    }
    
    private function onSelectMenu(index:int):void {
        sprite.visible = _menu.enabled = _menu.visible = false;
        if (onSelect != null) onSelect(index);
    }
    
    public function setMessgae(str:String):void {
        _message.text = str;
    }
    
    public function setResult(race:Race):void {
        sprite.visible = true;
        var noRank:Boolean = !race.track.enemiesNum || race.mode == Race.TIMETRIAL;
        if (race.rank <= 3 && !noRank) {
            _trophy.bitmapData = Dot["trophy" + race.rank];
            _trophy.x = 96 - _trophy.bitmapData.width * _trophy.scaleX / 2 | 0;
            _trophy.y = 122 - _trophy.bitmapData.height * _trophy.scaleY / 2 | 0;
        } else _trophy.bitmapData = null;
        
        race.lapsTime.sort(Array.NUMERIC);
        var str:String = "";
        str += (noRank? "---" : Text.getRankString(race.rank)) + "\r";
        str += Text.getTimeString(race.raceTime) + "\r";
        str += Text.getTimeString(race.lapsTime[0]) + "\r";
        _times.text = str;
        _starts[0].visible = race.newRankRecord;
        _starts[1].visible = race.newRaceRecord;
        _starts[2].visible = race.newLapRecord;
        
        if (!race.isWonderfl) {
            showMenu();
            return;
        }
        $.moja.keyEnabled = false;
        ScoreWindow.show(race.raceTime, showMenu, "Time", "sec", 3, 99, true, true);
    }
    private function showMenu():void {
        $.moja.keyEnabled = true;
        _menu.select(0);
        _menu.enabled = _menu.visible = true;
    }
    
}

/**
 * 一時停止メニュー
 */
class PauseMenu {
    
    public var sprite:Sprite = new Sprite();
    public var onSelect:Function;
    private var _menu:LabelMenu;
    
    public function PauseMenu() {
    }
    
    public function init():void {
        sprite.addChild(Create.box(0, 0, Display.width, Display.height, UI.DARK, 0.8));
        sprite.addChild(Create.gradientBox(0, 0, Display.width, 40, true, 90, [0x333333, 0x000000], [1, 1], [0, 0xFF]));
        UI.createLabel(sprite, 5, 3, "PAUSE");
        _menu = new LabelMenu(sprite, UI.menuX, UI.menuY, [Text.MENU_RESUME, Text.MENU_RETRY, Text.MENU_SETTINGS, Text.MENU_QUIT], onSelectMenu);
        sprite.visible = _menu.enabled = false;
    }
    
    public function active():void {
        sprite.visible = _menu.enabled = _menu.visible = true;
        $.setting.setEnabled(false);
        _menu.select(0);
    }
    
    private function onSelectMenu(index:int):void {
        _menu.enabled = false;
        sprite.visible = !(_menu.visible = onSelect(index));
    }
    
}

/**
 * サムネイル生成
 */
class Thumb {
    
    public var images:Vector.<BitmapData> = new Vector.<BitmapData>();
    private var _complete:Function;
    private var _progress:Function;
    private var _sprite:Sprite = new Sprite();
    private var _cnt:int = 0;
    private var _save:int;
    
    public function Thumb():void {
    }
    
    public function create(complete:Function, progress:Function):void {
        _complete = complete;
        _progress = progress;
        _save = $.user.viewDistance;
        $.user.viewDistance = 0;
        $.game.updateDisplay();
        $.game.setGroundScale(5, false);
        _sprite.addEventListener(Event.ENTER_FRAME, onTick);
    }
    
    private function onTick(e:Event):void {
        var track:Track = $.world.tracks[_cnt++];
        $.game.createStage(track, true);
        $.game.scene.camera.setGazeXYZ(track.gazeXY[0], track.gazeXY[1], 0);
        $.game.scene.camera.setPositionXYZ(track.cameraXY[0], track.cameraXY[1], 1.5);
        $.game.scene.render();
        var bmd:BitmapData = new BitmapData(90, 90, false, 0);
        bmd.draw($.game.scene.display, new Matrix(bmd.width / Display.width, 0, 0, bmd.height / Display.height, 0, 0), null, null, null, true);
        var shape:Shape = new Shape();
        track.draw(shape.graphics, 3, 0xFFFFFF, 1, 0.5);
        shape.filters = [new GlowFilter(0x000000, 1.5, 1.5, 1, 10, 3)];
        bmd.draw(shape, new Matrix(1, 0, 0, 1, bmd.width/2, bmd.height/2));
        images.push(bmd);
        _progress(_cnt / $.world.tracks.length);
        if (_cnt >= $.world.tracks.length) {
            $.user.viewDistance = _save;
            $.game.updateGround(false);
            _sprite.removeEventListener(Event.ENTER_FRAME, onTick);
            _complete();
        }
    }
    
}

/**
 * コース選択画面
 */
class TrackSelector {
    
    public var sprite:Sprite = new Sprite();
    private var _bg:Sprite;
    private var _items:Vector.<SelectorItem> = new Vector.<SelectorItem>();
    private var _menu:LabelMenu;
    private var _header:Sprite;
    private var _itemContainer:Sprite = new Sprite();
    private var _itemH:Number = 100;
    private var _headerH:Number = 40;
    private var _title:Label;
    private var _level:int;
    private var _tracks:Vector.<Track>;
    
    public function TrackSelector() {
        sprite.visible = false;
    }
    
    public function init():void {
        _header = Create.gradientBox(0, 0, Display.width, _headerH, true, 90, [0x333333, 0x000000], [1, 1], [0, 0xFF]);
        _bg = UI.createDarkBox()
        sprite.addChild(_bg);
        sprite.addChild(_itemContainer);
        sprite.addChild(_header);
        _title = UI.createLabel(_header, 5, 3, "");
    }
    
    public function create(mode:int, level:int):void {
        $.label.show(Text.LABEL_BACK);
        $.race.mode = mode;
        var i:int;
        if ($.race.isWonderfl) _tracks = Vector.<Track>([$.world.wonderfl]);
        else if ($.race.isTimeTrial) _tracks = $.world.getUnlockedTracks($.user);
        else _tracks = $.world.getTracksByLevel(level);
        _level = level;
        _itemContainer.y = 0;
        _title.text = Text.RACES[mode];
        if ($.race.isSingle) _title.text += ": " + Text.LEVELS[level];
        if (_menu) _menu.dispose();
        
        var labels:Array = [];
        for (i = 0; i < _tracks.length; i++) labels.push("");
        if (!$.race.isRandom) {
            _menu = new LabelMenu(null, 0, 0, labels, onSelect, onCancel, onChange);
        } else {
            var enemiesNum:Array = [];
            for (i = 1; i <= 30; i++) enemiesNum.push(i);
            _menu = new LabelMenu(sprite, UI.menuX, UI.menuY, ["{#,#}", "Place: {" + Text.PLACES.join(",") + "}", "Time : {"+Light.LABELS.join(",")+"}", "Enemies: {" + enemiesNum.join(",") + "}", "Start"], onSelectRandom, onCancel, null, null, 30);
            _menu.setOption(0, $.user.randomLevel);
            _menu.setOption(1, $.user.randomPlace);
            _menu.setOption(2, $.user.randomTime);
            _menu.setOption(3, $.user.randomEnemiesNum);
            var list:Array = [];
            var lv:int = !$.user.rankIn[0] ? 1 : !$.user.rankIn[1]? 2 : 3;
            for (i = 0; i < lv; i++) list.push(Text.LEVEL + ": " + Text.LEVELS[i]);
            _menu.setLabelsAt(0, list);
        }
        
        while (_itemContainer.numChildren) _itemContainer.removeChildAt(0);
        _items.length = 0;
        
        var unlock:int = $.user.getClearNum(_tracks, 3);
        
        var leng:int = $.race.isRandom? 1 : _tracks.length;
        for (i = 0; i < leng; i++) {
            var item:SelectorItem = new SelectorItem();
            if (!$.race.isRandom) {
                var thumb:BitmapData = (_tracks[i].id >=0 && $.thumb.images[_tracks[i].id])? $.thumb.images[_tracks[i].id] : null;
                item.initNormal(mode, _tracks[i], thumb);
                var isUnlock:Boolean = ($.race.isTimeTrial)? $.user.bestRank[_tracks[i].id] > 0 : unlock >= _tracks[i].unlock;
                item.setUnlock(isUnlock);
                if ($.race.isTimeTrial) {
                    item.setGhostNum($.replay.getDataByID(_tracks[i].id).length);
                }
            } else {
                item.initRandom();
            }
            item.setRecord($.user);
            _items.push(item);
            _itemContainer.addChild(item);
            item.y = _itemH * i;
        }
        _bg.visible = $.race.isRandom;
        sprite.visible = true;
        onChange(0);
    }
    
    private function onSelectRandom(index:int):void {
        if (index != 4) return;
        sprite.visible = _menu.enabled = false;
        
        $.user.randomLevel = _menu.options[0];
        $.user.randomPlace = _menu.options[1];
        $.user.randomTime = _menu.options[2];
        $.user.randomEnemiesNum = _menu.options[3];
        $.user.save();
        
        var track:Track = new Track();
        track.random(_menu.options[1]);
        track.level = _menu.options[0];
        track.lapsNum = 4;
        track.enemiesNum = _menu.options[3] + 1;
        track.light = Light.LIST[_menu.options[2]];
        track.setPlace(_menu.options[1]);
        $.transition.fadeOutIn(function():void {
            $.game.isDemo = false;
            $.game.destroyStage();
            $.game.createStage(track, false);
            $.game.startRace();
        });
    }
    
    private function onChange(index:int):void {
        for (var i:int = 0; i < _items.length; i++) _items[i].setSelect(i == index);
        var min:Number = (index) * _itemH + _itemContainer.y;
        var max:Number = (index + 1) * _itemH + _itemContainer.y;
        if (max > Display.height) BetweenAS3.to(_itemContainer, { y:Display.height - _itemH*(index+1) }, 0.3, Cubic.easeOut).play();
        if (min < _headerH) BetweenAS3.to(_itemContainer, { y:_headerH - index * _itemH }, 0.3, Cubic.easeOut).play();
    }
    
    private function onCancel():void{
        sprite.visible = _menu.enabled = false;
        $.top.setScene($.race.isSingle? TopMenu.SCENE_LEVEL : TopMenu.SCENE_TOP);
    }
    
    private function onSelect(index:int):void {
        if (!_items[index].isUnlocked) return;
        sprite.visible = _menu.enabled = false;
        $.transition.fadeOutIn(function():void {
            $.game.isDemo = false;
            $.game.destroyStage();
            $.game.createStage(_tracks[index]);
            $.game.startRace();
        });
    }
    
}

/**
 * コースセレクタ用アイテム
 */
class SelectorItem extends Sprite {
    
    public var isUnlocked:Boolean = false;
    private var _mode:int = 0;
    private var _base:Sprite;
    private var _thumb:Sprite;
    private var _lock:Sprite;
    private var _bestLap:Label;
    private var _bestRace:Label;
    private var _courseName:Label;
    private var _track:Track;
    
    public function SelectorItem() {
        _base = Create.gradientBox(0, 0, Display.width, 100, true, 90, [UI.BASE, UI.DARK], [1, 1], [0, 0xFF]);
        _thumb = Create.box(0, 0, 90, 90, 0, 0.5, 5, 5);
        _lock = Create.box(0, 0, 90, 90, 0x111111, 1, 5, 5);
        _lock.addChild(Create.spriteBmp(Dot.lock, 3, -Dot.lock.width/2, -Dot.lock.height/2, 45, 45));
        _base.alpha = 0;
        addChild(_base);
        addChild(_thumb);
        addChild(_lock);
        _courseName = UI.createLabel(this, 105, 1, "");
        UI.createLabel(this, 105, 40, "BEST RACE", 16, UI.PALE);
        UI.createLabel(this, 105, 62, "BEST LAP", 16, UI.PALE);
        _bestRace = UI.createLabel(this, 220, 40, "??:??:??", 16, UI.PALE);
        _bestLap = UI.createLabel(this, 220, 62, "??:??:??", 16, UI.PALE);
    }
    
    public function initRandom():void {
        _mode = Race.RANDOM;
        _courseName.text = "??????";
        _bestRace.text = _bestLap.text = "??:??:??";
        setUnlock(true);
    }
    
    public function initNormal(mode:int, track:Track, bmd:BitmapData):void {
        _track = track;
        _mode = mode;
        _thumb.addChild(new Bitmap(bmd));
        _courseName.text = ((mode == Race.WONDERFL)? "" : String(_track.id+1) + ". ") + track.name;
    }
    
    public function setGhostNum(value:int):void {
        if (!value) return;
        addChild(Create.spriteBmp(Dot.ghost, 4, 0, 0, 355, 25));
        UI.createLabel(this, 394, 30, "x" + value, 8*3);
    }
    
    public function setUnlock(unlock:Boolean):void {
        isUnlocked = unlock
        _lock.visible = !isUnlocked;
    }
    
    public function setRecord(user:UserData):void {
        if (_mode == Race.RANDOM) return;
        if (user.bestRank[_track.id] == null) return;
        _bestRace.text = Text.getTimeString(user.bestTotals[_track.id] || -1);
        _bestLap.text = Text.getTimeString(user.bestLaps[_track.id] || -1);
        if (_mode > Race.SINGLE) return;
        if (user.bestRank[_track.id] > 3 || user.bestRank[_track.id] == -1) return;
        var bmd:BitmapData = Dot["trophy" + user.bestRank[_track.id]];
        var icon:Sprite = addChild(Create.spriteBmp(bmd, 3, -bmd.width / 2 | 0, -bmd.height / 2 | 0)) as Sprite;
        icon.x = 400;
        icon.y = 50;
    }
    
    public function setSelect(select:Boolean):void {
        if(select) BetweenAS3.to(_base, { alpha:1 }, 0.2, Cubic.easeOut).play();
        else BetweenAS3.to(_base, { alpha:0.4 }, 0.2, Cubic.easeOut).play();
    }
    
}

/**
 * ゲーム画面に表示する数値やマップ
 */
class DisplayInfo extends Sprite {
    
    public var replay:Label;
    public var map:Sprite = new Sprite();
    public var display:Sprite = new Sprite();
    public var markContainer:Sprite = new Sprite();
    
    private var _labelContainer:Sprite = new Sprite();
    private var _messageContainer:Sprite = new Sprite();
    private var _rank:Label;
    private var _speed:Label;
    private var _lapNum:Label;
    private var _messageL:Label;
    private var _messageS:Label;
    private var _enemyNum:Label;
    private var _totalTime:Label;
    private var _lapTimes:Label;
    private var _currentLapTime:Label;
    private var _laps:Vector.<int> = new Vector.<int>();
    private var _mapImage:BitmapData;
    private var _lapMax:int = 1;
    
    public function DisplayInfo() {
    }
    
    public function init():void {
        replay = UI.createLabel(null, Display.width - 85, 7, "REPLAY");
        replay.visible = false;
        addChild(display);
        addChild(replay);
        display.addChild(markContainer);
        display.addChild(_labelContainer);
        display.addChild(_messageContainer);
        display.addChild(map);
        _lapNum = UI.createLabel(_labelContainer, Display.width - 95, 10);
        _rank = UI.createLabel(_labelContainer, 370, 370);
        _speed = UI.createLabel(_labelContainer, 200, 10);
        _messageL = UI.createLabel(_messageContainer, 0, 120);
        _messageS = UI.createLabel(_messageContainer, 0, 230);
        _messageContainer.filters = UI.TEXT_FILTERS;
        
        _rank.scaleX = _rank.scaleY = 3;
        _enemyNum = UI.createLabel(_labelContainer, 420, 430);
        
        
        UI.createLabel(_labelContainer, 10, 10, "RACE");
        _totalTime = UI.createLabel(_labelContainer, 10, 30);
        UI.createLabel(_labelContainer, 10, 50, "LAP");
        _lapTimes = UI.createLabel(_labelContainer, 10, 70);
        var tfm:TextFormat = new TextFormat();
        tfm.leading = -6;
        _lapTimes.textField.defaultTextFormat = tfm;
        _currentLapTime = UI.createLabel(_labelContainer, 10, 70);
        
        _mapImage = new BitmapData(S.QtoM(S.QRECT.right), S.QtoM(S.QRECT.bottom), true, 0);
        map.addChild(new Bitmap(_mapImage)).alpha = 0.8;
        map.x = 0;
        map.y = Display.height - map.height;
    }
    
    public function setCourseData(data:Track):void {
        _lapMax = data.lapsNum;
        setLapNum(1);
        _rank.text = "";
        _enemyNum.text = "/ " + ($.race.mode == Race.TIMETRIAL? 1 : data.enemiesNum + 1);
        _laps.length = 0;
        _lapTimes.text = "";
        _currentLapTime.y = 70;
        setTotalTime(0);
        setLapTime(0);
        //マップにコースを描画する
        _mapImage.fillRect(_mapImage.rect, 0);
        var shape:Shape = new Shape();
        data.draw(shape.graphics, 13 * S.MAP, 0x444444, 1, S.MAP);
        shape.filters = [new GlowFilter(0xFFFFFF, 1, 2, 2, 5, 3)];
        Display.cacheQuality(StageQuality.BEST);
        _mapImage.draw(shape, new Matrix(1, 0, 0, 1, S.QtoM(0), S.QtoM(0)));
        Display.restoreQuality();
    }
    
    public function addLapTime(time:int, finish:Boolean = false):void {
        _currentLapTime.y += 20;
        _laps.push(time);
        _lapTimes.text = "";
        _laps.forEach(function(...arg):void { _lapTimes.text += Text.getTimeString(arg[0]) + "\r"; }, null);
        if (finish) _currentLapTime.text = "";
    }
    
    public function setLapTime(time:int):void {
        _currentLapTime.text = Text.getTimeString(time);
    }
    
    public function setTotalTime(time:int):void {
        _totalTime.text = Text.getTimeString(time);
    }
    
    public function setSpeed(speed:Number):void {
        _speed.text = String(speed * S.QtoKPH | 0) + " kph";
        _speed.x = 260 - _speed.width;
    }
    
    public function setLapNum(lap:int):void {
        _lapNum.text = "LAP: " + Math.min(_lapMax, lap) + "/" + _lapMax;
        if (lap >= _lapMax) _lapNum.transform.colorTransform = Palette.getColorTransform(UI.HIGHLIGHT);
        else _lapNum.transform.colorTransform = Palette.getColorTransform(0xFFFFFF);
    }
    
    public function setRank(rank:int):void {
        _rank.text = String(rank);
    }
    
    public function setMessage(isLarge:Boolean, str:String, scale:Number = 1, interval:int = 4000, color:uint = 0xFFFFFF):void {
        var msg:Label = (isLarge)? _messageL : _messageS;
        msg.transform.colorTransform = Palette.getColorTransform(color);
        msg.scaleX = msg.scaleY = scale;
        msg.text = str;
        msg.visible = false;
        FrameTimer.setTimer(2, onShow, [msg]);
        if (interval > 0) FrameTimer.setTimer(Math.max(3, interval / 1000 * 30), onHide, [msg]);
    }
    
    private function onHide(target:Label):void {
        target.visible = false;
    }
    
    private function onShow(target:Label):void {
        target.visible = true;
        target.x = ((Display.width - target.getRect(target.parent).width) / 2) | 0;
    }
    
}

/**
 * レース部分のメインクラス
 */
class Game {
    
    public var sprite:Sprite = new Sprite();
    private var _sceneTime:int = 0;
    private var _isDemo:Boolean = false;
    private var _isPlaying:Boolean = false;
    private var _inResultView:Boolean = false;
    private var _isRunning:Boolean = false;
    private var _isFinish:Boolean = false;
    private var _isRecording:Boolean = false;
    private var _isReplay:Boolean = false;
    private var _isComplete:Boolean = false;
    private var _isHowTo:Boolean = false;
    
    private var _mirror:Bitmap;
    private var _boxContainer:MovieClip = new MovieClip();
    private var _display:DisplayInfo = new DisplayInfo();
    private var _pause:PauseMenu;
    private var _result:ResultMenu;
    
    private var _sceneCam:SceneCamera = new SceneCamera();
    private var _scene:Scene3D = new Scene3D();
    private var _sim:QuickBox2D;
    private var _contacts:QuickContacts;
    private var _race:Race;
    private var _player:Car;
    
    private var _keyUD:uint = 0x0000;
    private var _lap:Number = 1;
    private var _time:Number = 0;
    private var _collideTime:int = 0;
    private var _mirrorCnt:int = -1;
    private var _startTime:int = 0;
    private var _countDown:int = 0;
    private var _countNum:int = 3;
    private var _countInterval:int = 30;
    private var _complete:int = -1;
    private var _completeTimer:int;
    
    private var _lines:Sprite;
    private var _lines2:Shape;
    private var _lines1:Shape;
    private var _circles:Shape;
    private var _lakes:Shape;
    private var _shadowContainer:Sprite = new Sprite();
    private var _lightContainer:Sprite = new Sprite();
    private var _noise:BitmapData = new BitmapData(50, 50, false, 0);
    private var _wanco:Wanco;
    
    /**シーンの経過フレーム時間*/
    public function get sceneTime():int { return _sceneTime; }
    public function get display():DisplayInfo { return _display; }
    public function get scene():Scene3D { return _scene; }
    public function get player():Car { return _player; }
    public function get isFinish():Boolean { return _isFinish; }
    public function get isReplay():Boolean { return _isReplay; }
    public function get isDemo():Boolean { return _isDemo; }
    public function set isDemo(value:Boolean):void { _isDemo = value; }
    
    public function Game() {
        _lines = new Sprite();
        _lines2 = _lines.addChild(new Shape()) as Shape;
        _lines1 = _lines.addChild(new Shape()) as Shape;
        _circles = _lines.addChild(new Shape()) as Shape;
        _lakes = new Shape();
        _noise.noise(123, 0x4, 0x24, 7, true);
    }
    /*
    public function saveReplay():void {
        if (!$.replay.data.length) return;
        var fr:FileReference = new FileReference();
        var ba:ByteArray = new ByteArray();
        ba.writeObject($.replay.data[0].toObject());
        ba.compress();
        fr.save(ba, "replay" + new Date().time + ".dat");
    }
    */
    public function init():void {
        _race = $.race;
        _display.init();
        _mirror = new Bitmap(new BitmapData(465/2, 75, false, 0));
        _mirror.x = (Display.width - _mirror.width)/2;
        _mirror.y = 35;
        _pause = new PauseMenu();
        _pause.init();
        _pause.onSelect = onSelectPauseMenu;
        _scene.setSize(Display.width, Display.height);
        _result = new ResultMenu();
        _result.init();
        _result.onSelect = onSelectResultMenu;
        
        sprite.addChild(_boxContainer);
        sprite.addChild(_scene.display);
        sprite.addChild(_mirror);
        sprite.addChild(_display);
        sprite.addChild(_pause.sprite);
        sprite.addChild(_result.sprite);
        
        _display.init();
        setGroundScale(1);
    }
    
    public function updateGround(draw:Boolean = true):void {
        setGroundScale([2, 1][$.user.groundQuality], draw);
    }
    
    public function setGroundScale(scale:Number = 1, draw:Boolean = false):void {
        S.setScale(scale);
        if (_scene.groundMap) _scene.groundMap.dispose();
        if (_race.dirtMap) _race.dirtMap.dispose();
        _scene.groundMap = new BitmapData(S.QtoG(S.QRECT.right), S.QtoG(S.QRECT.bottom), true, 0);
        _race.dirtMap = new BitmapData(S.QtoD(S.QRECT.right), S.QtoD(S.QRECT.bottom), false);
        if (!draw) return;
        drawGround(_race.track, false);
    }
    
    private function onSelectPauseMenu(index:int):Boolean {
        switch (index) {
            case 0: play(); break;
            case 1: $.transition.fadeOutIn(reset); break;
            case 2: $.setting.setEnabled(true, function():void { _pause.active(); } ); break;
            case 3: $.transition.fadeOutIn(gotoTop); break;
        }
        return index != 2;
    }
    
    /**
     * 操作説明デモ
     */
    public function howto():void {
        _isHowTo = true;
        _isDemo = true;
        destroyStage();
        createStage($.world.howto);
        SE.stopAll();
        SE.setVolume(1);
        startRace();
        $.howto.onFinish = function():void {
            $.transition.fadeOutIn(function():void {
                _isHowTo = false;
                $.howto.sprite.visible = false;
                quitAndReset();
            });
        }
        $.top.setScene(TopMenu.SCENE_HIDE);
        $.howto.start();
    }
    
    /**
     * 現在の難易度での全コースを1位でクリア
     */
    public function complete():void {
        _isComplete = true;
        _isDemo = true;
        _completeTimer = 30 * 7;
        $.tape.start();
        $.complete.show(_race.track.level);
        $.top.setScene(TopMenu.SCENE_HIDE);
    }
    
    /**
     * TOPへ戻る
     */
    private function quit():void {
        isDemo = true;
        destroyCars();
        _scene.destroyParticles();
        _scene.destroyGhosts();
        SE.stopAll();
        SE.setVolume(1);
        startRace();
        $.top.setScene(TopMenu.SCENE_TOP);
    }
    
    /**
     * バックミラーレンダリング
     */
    public function drawMirror():void {
        _scene.camera.horizon >> 1;
        _scene.camera.view >> 1;
        _scene.camera.setGazeXYZ(_player.position.x - _player.front.x, _player.position.y - _player.front.y, _player.position.z + 0.1);
        _scene.camera.setPositionXYZ(_player.position.x, _player.position.y, _player.position.z + 0.5);
        _scene.render();
        _scene.camera.horizon << 1;
        _scene.camera.view << 1;
        _mirror.bitmapData.draw(_scene.display, new Matrix(-0.5, 0, 0, 0.5, _mirror.width, -20), null, null);
    }
    
    private function pause():void {
        _isPlaying = false;
        _sim.stop();
        sprite.removeEventListener(Event.EXIT_FRAME, onTickSimurate);
    }
    
    private function play():void {
        _isPlaying = true;
        _sim.start();
        sprite.addEventListener(Event.EXIT_FRAME, onTickSimurate);
    }
    
    private function reset():void {
        destroyStage();
        createStage(_race.track);
        startRace();
    }
    
    private function destroyCars():void {
        _player = null;
        _scene.destroyCars();
    }
    
    public function destroyStage():void {
        if (!_sim) return;
        pause();
        _race.reset();
        _sim.destroy();
        _contacts.removeEventListener(QuickContacts.ADD, onCollide);
        while (_boxContainer.numChildren) _boxContainer.removeChildAt(0);
        _scene.destroyAll();
        _player = null;
    }
    
    /**
     * シーン生成
     * @param    course    コースデータ
     * @param    captureMode    サムネイル生成モード(Box2Dは生成しない)
     */
    public function createStage(track:Track, captureMode:Boolean = false):void {
        var i:int, n:Number, p:Point, tp:Point, list:Array, cnt:int, leng:int, num:Number, obj:Sprite3D, qobj:QuickObject;
        //コースデータを元に必要データを計算する
        _race.calculate(track, captureMode);
        var light:Light = new Light(track.light);
        _scene.setLight(light);
        _scene.destroyAll();
        
        if (!captureMode) {
            Dot.setParticleColor.apply(null, track.particleColors);
            _sim = new QuickBox2D(_boxContainer, { frim:false, timeStep:1 / 30, gravityY:0, bounds:[S.QRECT.x, S.QRECT.y, S.QRECT.right, S.QRECT.bottom] } );
            _contacts = _sim.addContactListener();
            _contacts.addEventListener(QuickContacts.ADD, onCollide);
            _sim.setDefault( { lineAlpha:0 } );
        }
        
        //ワールドの壁
        for (i = 0; i < 4; i++)
            addBox( { density:0, x:[ -95, 95, 0, 0][i], y:[0, 0, -95, 95][i], width:[10, 10, 180, 180][i], height:[200, 200, 10, 10][i], fillAlpha:0.5, fillColor:0x000000 }, captureMode);
        
        //観客席
        createSeats(track.seatsXY[0], track.seatsXY[1], 20, 4, captureMode);
        
        //フェンス
        var fp1:Point, fp2:Point, distance:Number;
        for each(list in track.fences) {
            for (i = 0;  i < list.length - 1; i++) {
                fp1 = new Point(list[i][0], list[i][1]);
                fp2 = new Point(list[i + 1][0], list[i + 1][1]);
                distance = Point.distance(fp1, fp2);
                createPlaneXY(fp1.x, fp1.y, fp2.x, fp2.y, 0, track.fenceHeight, Math.ceil(distance / 8), track.fenceMap, Math.ceil(distance / track.fenceMapSize), 1, true, false, captureMode);
            }
        }
        
        //板ポリゴン
        for each(list in track.planes) {
            for (i = 0;  i < list[0].length - 1; i++) {
                fp1 = new Point(list[0][i][0], list[0][i][1]);
                fp2 = new Point(list[0][i + 1][0], list[0][i + 1][1]);
                distance = Point.distance(fp1, fp2);
                var map:BitmapData = (list[3].indexOf(".") != -1)? Dot.ads[list[3].substr(1)] : Dot[list[3]];
                createPlaneXY(fp1.x, fp1.y, fp2.x, fp2.y, list[2], list[1], Math.ceil(distance / 8), map, list[5], list[6], true, false, captureMode || list[2] > 0);
                if (list[4]) {
                    //両サイドに柱をつける
                    var pp:Point = fp2.subtract(fp1);
                    pp.normalize(0.2);
                    createPlaneXY(fp1.x, fp1.y, fp1.x + pp.x, fp1.y + pp.y, 0, list[2], 1, 0x404040, 1, 1, true, false, true);
                    pp.normalize(-0.2);
                    createPlaneXY(fp2.x, fp2.y, fp2.x + pp.x, fp2.y + pp.y, 0, list[2], 1, 0x404040, 1, 1, true, false, true);
                }
            }
        }
        
        //木・ドラム缶・コーンetc
        cnt = -1;
        var trees:Array = Dot.trees;
        switch(track.place) {
            case Track.SNOWFIELD: case Track.WINTERCIRCUIT: trees = Dot.treesSnow; break;
        }
        for each(list in track.lamps) createObject(list[0], list[1], 0, light.isLightUp? Dot.lampOn : Dot.lampOff, 3, true, true, true, 0.3, 0, 0.2).isEmit = light.isLightUp;
        for each(list in track.trees) createObject(list[0], list[1], 0, trees[++cnt % trees.length], 10, true, true, captureMode, 0.5, 0, 0.2);
        for each(list in track.objectL) createObject(list[0], list[1], 0, Dot.drum, 3, true, true, captureMode, 0.3, 0.7, 0.2);
        for each(list in track.objectS) createObject(list[0], list[1], 0, Dot.cone, 3, true, true, captureMode, 0.2, 0.05, 0.2);
        for each(list in track.plants) createObject(list[0], list[1], list[2], Dot.plantL, list[3], !!list[4], !!list[4], true);
        for each(list in track.lakes) addCircle( { density:0, x:list[0], y:list[1], radius:list[2], fillColor:track.lakeColor }, captureMode);
        
        if (!captureMode) {
            qobj = _sim.addCircle( { density:0.02, restitution:1, radius:0.5, skin:"none" } );
            _wanco = new Wanco(qobj, Create.spriteBmp(Dot.wanco, 2, -Dot.wanco.width / 2, -Dot.wanco.height), true, true, track.wancoXY[0], track.wancoXY[1], 0)
            _scene.addSprite(_wanco);
        }
        drawGround(track, captureMode);
    }
    
    /**
     * 地面テクスチャ描画
     * @param    track
     * @param    captureMode
     */
    public function drawGround(track:Track, capture:Boolean):void {
        var i:int, p:Point, list:Array, leng:int, num:int
        _scene.groundMap.lock();
        Display.cacheQuality(StageQuality.HIGH);
        _race.dirtMap.fillRect(_race.dirtMap.rect, 0xFF808080);
        _scene.groundMap.fillRect(_scene.groundMap.rect, 0xFF<<24 | track.dirtColor);
        
        //コースを描画
        drawLine(track, true);
        //破線
        if (!capture) {
            leng = (_race.totalDistance / 10) | 0;
            num = Math.min(track.roadSize - 6, 10) * S.DRAW;
            for (i = 0; i < leng; i++) {
                p = _race.getStartPos(_race.totalDistance / leng * i)[0];
                Draw.circle(_circles.graphics, S.QtoG(p.x), S.QtoG(p.y), num, num, track.roadColor);
            }
        }
        //テクスチャにコースを貼りつけ
        _scene.groundMap.draw(_lines, null, null, null, null, true);
        _circles.graphics.clear();
        
        //コースアウト判定マップ
        if (!capture) {
            drawLine(track, false);
            _race.dirtMap.draw(_lines, null, Palette.getColorTransform(0xFFFFFF), null, null, true);
        }
        
        //スタートライン
        var mtx:Matrix = new Matrix();
        mtx.rotate(Math.atan2(_race.checkPoints[0][0].y - _race.checkPoints[0][1].y, _race.checkPoints[0][0].x - _race.checkPoints[0][1].x));
        mtx.scale(track.roadSize / 23 * S.DRAW, track.roadSize / 23 * S.DRAW);
        mtx.translate(S.QtoG(_race.startingPoint.x), S.QtoG(_race.startingPoint.y));
        _scene.groundMap.draw(Create.spriteBmp(Dot.startingLine, 2.8, -7, -2), mtx, null, null, null, true);
        
        //テクスチャにノイズを焼き込む
        if (!capture) _scene.groundMap.draw(Create.bitmapFillBox(0, 0, _scene.groundMap.width, _scene.groundMap.height, _noise, 1, 1), null, null, BlendMode.SUBTRACT, null, true);
        
        _lakes.filters = [new GlowFilter(track.lakeEdge, 1, 10 * S.DRAW, 10 * S.DRAW, 2, 2, true), new GlowFilter(track.edgeColor, 1, 30 * S.DRAW, 30 * S.DRAW, 3, 1)];
        for each(list in track.lakes) Draw.circle(_lakes.graphics, S.QtoG(list[0]), S.QtoG(list[1]), list[2]*S.QtoF*S.TEXTURE, list[2]*S.QtoF*S.TEXTURE, track.lakeColor);
        _scene.groundMap.draw(_lakes, null, null, null, null, true);
        _scene.groundMap.colorTransform(_scene.groundMap.rect, _scene.camera.light.ambientColor);
        
        if (!capture) {
            //光と影を地面に描画する
            for each(list in track.trees) Draw.gradientCircle(_shadowContainer.graphics, S.QtoG(list[0]), S.QtoG(list[1]), 20 * S.DRAW, 20 * S.DRAW, [0, 0], [0.2, 0], [100, 255]);
            _scene.groundMap.draw(_shadowContainer, null, null, BlendMode.NORMAL, null, true);
            if (_scene.camera.light.isLightUp) {
                for each(list in track.lamps) Draw.gradientCircle(_lightContainer.graphics, S.QtoG(list[0]), S.QtoG(list[1]), 25 * S.DRAW, 25 * S.DRAW, [0xffffff, 0xffffff], [1, 0], [0, 255]);
                _scene.groundMap.draw(_lightContainer, null, null, BlendMode.OVERLAY, null, true);
            }
            //地面の元画像をストックしておく
            if (_scene.rawMap) _scene.rawMap.dispose();
            _scene.rawMap = _scene.groundMap.clone();
            _scene.rawMap.colorTransform(_scene.rawMap.rect, Palette.getColorTransform(0, 0, 0.1));
            _scene.splitRawImage();
        }
        
        _lines1.graphics.clear();
        _lines2.graphics.clear();
        _lakes.graphics.clear();
        _lightContainer.graphics.clear();
        _shadowContainer.graphics.clear();
        
        Display.restoreQuality();
        _scene.groundMap.unlock();
    }
    
    private function drawLine(track:Track, texture:Boolean = true):void {
        var scale:Number = texture? S.DRAW : S.DRAW_DIRT;
        var i:int, px:Number, py:Number, p:Point, list:Array;
        _lines1.graphics.clear();
        _lines2.graphics.clear();
        _lines1.graphics.lineStyle(2 * scale, track.lineColor, 1);
        _lines2.graphics.lineStyle(2 * scale, track.pathColor, 1);
        for (i = 0; i <= _race.course.length; i++) {
            p = _race.course[i % _race.course.length];
            px = texture? S.QtoG(p.x) : S.QtoD(p.x);
            py = texture? S.QtoG(p.y) : S.QtoD(p.y);
            if (!i) _lines1.graphics.moveTo(px, py);
            else _lines1.graphics.lineTo(px, py);
        }
        for each(list in track.path) {
            for (i = 0; i < list.length; i++) {
                px = texture? S.QtoG(list[i][0]) : S.QtoD(list[i][0]);
                py = texture? S.QtoG(list[i][1]) : S.QtoD(list[i][1]);
                if (!i) _lines2.graphics.moveTo(px, py);
                else _lines2.graphics.lineTo(px, py);
            }
        }
        _lines1.filters = [new GlowFilter(track.roadColor, 1, track.roadSize * scale, track.roadSize * scale, 100, 2)];
        _lines2.filters = [new GlowFilter(track.pathColor, 1, track.pathSize * scale, track.pathSize * scale, 100, 2)];
        _lines.filters = texture? [new GlowFilter(track.edgeColor, 1, 15 * scale, 15 * scale, 3, 2)] : [];
    }
    
    /**
     * 車生成
     */
    public function createCars():void {
        _player = createCar(true, 0xFF4400, 2.3, 0.05);
        var data:Array = _race.getStartPos(0.1);
        _player.setStartPosition(data[0].x, data[0].y, data[1], _race);
        createGhost();
        if (_race.mode == Race.TIMETRIAL) return;
        var accel:Number = [3.4 * 0.9, 4.0 * 0.9, 4.1 * 0.9][_race.track.level]*1.5;
        var densities:Array = [2, 0.6, 0.45, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3, 0.3];
        for (var i:int = 0; i < _race.track.enemiesNum; i++) {
            var per:Number = (i + 1) / _race.track.enemiesNum;
            var index:int = Math.min(densities.length - 1, _race.track.enemiesNum - i - 1 + 2 - _race.track.level);
            var enemy:Car = createCar(false, getCarColor(index), densities[index], 0.8);
            enemy.steerSpeed = (1 - Math.random() * (1 - per)) * 0.5 + 0.2;//0.2/0.5
            enemy.accelFirst = enemy.accel = (accel - (1 - per)) * _race.track.handicap;
            data = _race.getStartPos(200 * per);
            enemy.setStartPosition(data[0].x + (Math.random() - 0.5) * 0 , data[0].y + (Math.random() - 0.5) * 0, data[1], _race);
            enemy.proactiveRate = Math.random() * 0.7 + 0.6;
        }
    }
    
    private function getCarColor(index:int):uint {
        var colors:Array = [0x222222, 0xFFFFFF, 0x8F27BC, 0x0088FF, 0x2FE6CF, 0x33CC33, 0xFFE31A, 0xFF9411, 0x936300, 0];
        var rgb:uint = colors[Math.min(index, colors.length - 1)];
        if (!rgb) rgb = Math.random() * 0xFFFFFF;
        return rgb;
    }
    
    public function createGhost(skipLast:Boolean = false, offset:int = 0):void {
        var cars:Vector.<Car> = new Vector.<Car>();
        for (var i:int = 0; i < $.replay.data.length; i++) {
            var data:ReplayData = $.replay.data[i];
            if (data.trackID != _race.track.id || (skipLast && $.replay.last === data)) continue;
            var car:Car = createCar(false, getCarColor(i));
            car.setGhost(data.clone());
            car.replay.startReplay(true, offset);
        }
    }
    
    private function addCircle(param:Object, noBody:Boolean = false):Sprite {
        if (noBody) return null;
        var qobj:QuickObject = _sim.addCircle(param);
        return qobj.userData;
    }
    
    private function addBox(param:Object, noBody:Boolean = false):Sprite {
        if (noBody) return null;
        var qobj:QuickObject = _sim.addBox(param);
        return qobj.userData;
    }
    
    //観客席を作る
    private function createSeats(x:Number, y:Number, height:Number = 20, segments:int = 3, noBody:Boolean = false):void {
        var width:Number = 10;
        var p:Point = new Point(x - width / 2, y - height / 2);
        createPlaneXY(p.x, p.y, p.x + width, p.y, 0, 5, 1, Dot.audienceSide, -1, 1, true, false, true);
        createPlaneXY(p.x, p.y + height, p.x + width, p.y + height, 0, 5, 1, Dot.audienceSide, -1, 1, true, false, true);
        createPlaneXY(p.x + width - 0.1, p.y, p.x + width - 0.1, p.y + height, 4.2, 7, segments, Dot.audienceRoof, -20, 1, true, false, true).forEach(function(...arg):void { arg[0].sprite.rotationX = 172; }, null);
        createPlaneXY(p.x + 0.1, p.y, p.x + 0.1, p.y + height, 0.5, 9, segments, Dot.audienceSeats, -10, 4, true, false, true).forEach(function(...arg):void { arg[0].sprite.rotationX = 15; }, null);
        createPlaneXY(p.x + width, p.y, p.x + width, p.y + height, 0, 3.5, segments, 0x808080, 1, 1, false, true, true);
        createPlaneXY(p.x + width, p.y, p.x + width, p.y + height, 2.9, 0.6, segments, 0x666666, 1, 1, false, false, true);
        createPlaneXY(p.x, p.y, p.x, p.y + height, 0, 0.5, segments, 0xDDDDDD, 1, 1, false, false, true);
        addBox( { density:0, x:x, y:y, width:width, height:height, fillColor:0x444444 }, noBody)
    }
    
    private function onCollide(e:Event):void {
        if (_isDemo || !_contacts.inCurrentContact(_player.qobject)) return;
        if (!_wanco.active && _contacts.inCurrentContact(_wanco.qobject)) _wanco.love(_player);
        if (_sceneTime - _collideTime <= 1) return;
        _collideTime = _sceneTime;
        var v:Number = _contacts.currentPoint.velocity.Length();
        if (v<1.5) return;
        var i:int = v < 4? 0 : v < 8? 1 : v < 15? 2 : 3;
        SE.note(SE.OMP_COLLISIONS[i]);
    }
    
    private function createCar(isPlayer:Boolean = true, rgb:uint = 0xC80000, density:Number = 0.3, restitution:Number = 0.8):Car {
        var car:Car = new Car(_sim, isPlayer, rgb, density, restitution);
        car.qobject.angle = Math.PI;
        car.sprite.graphics.clear();
        var p:Point = new Point( -Dot.carBase.width / 2, -Dot.carBase.height / 2);
        var scale:Number = 1.6;
        var carFrame:BitmapData = Dot.carFrame(Palette.mix(rgb, 0xFFFFFF, 0.5), rgb, Palette.mix(rgb, 0, 0.2));
        var carWing:BitmapData = Dot.carWing(Palette.mix(rgb, 0xFFFFFF, 0.5), rgb, Palette.mix(rgb, 0, 0.2));
        car.sprite.addChild(Create.bitmap(Dot.carBase, scale, p.x, p.y)).z = -1;
        car.sprite.addChild(Create.bitmap(carFrame, scale, p.x + 1, p.y)).z = -2.5;
        car.sprite.addChild(Create.bitmap(carWing, scale, p.x + 1, p.y + 21)).z = -6;
        car.shredder.addData(carFrame, carWing);
        _scene.addSprite(car);
        _scene.addLight(car.light3D);
        _display.map.addChild(car.mapMark);
        _display.markContainer.addChild(car.rankMark);
        return car;
    }
    
    private function createPlaneXY(x1:Number, y1:Number, x2:Number, y2:Number, z:Number, height:Number, segments:int = 1, material:* = 0x808080, loopW:Number = 1, loopH:Number = 1, two:Boolean = true, reverse:Boolean = false, noBody:Boolean = false):Vector.<Sprite3D> {
        return createPlane(new Point(x1, y1), new Point(x2, y2), z, height, segments, material, loopW, loopH, two, reverse, noBody);
    }
    
    private function createPlane(p1:Point, p2:Point, z:Number, height:Number, segments:int = 1, material:* = 0x808080, loopW:Number = 1, loopH:Number = 1, two:Boolean = true, reverse:Boolean = false, noBody:Boolean = false):Vector.<Sprite3D> {
        var i:int, pts:Array = [], width:Number = Point.distance(p1, p2), angle:Number = Math.atan2(p2.y - p1.y, p2.x - p1.x);
        var list:Vector.<Sprite3D> = new Vector.<Sprite3D>();
        for (i = 0; i < segments; i++) {
            var dp1:Point = Point.interpolate(p1, p2, i / segments);
            var dp2:Point = Point.interpolate(p1, p2, (i + 1) / segments);
            var w:Number = width / segments;
            var sp:Sprite = new Sprite();
            if (material is uint) sp.graphics.beginFill(material, 1);
            if (material is BitmapData) sp.graphics.beginBitmapFill(material, new Matrix(w / loopW * segments * S.QtoF / material.width, 0, 0, height * S.QtoF / material.height / loopH, w * S.QtoF * (0.5 + i), 0));
            if(height) sp.graphics.drawRect( -w / 2 * S.QtoF, -height * S.QtoF, w * S.QtoF, height * S.QtoF);
            sp.graphics.endFill();
            list.push(_scene.addSprite(new Sprite3D(sp, false, true, (dp1.x + dp2.x) / 2, (dp1.y + dp2.y) / 2, z, angle * Angle.toROT, two, reverse)));
        }
        addBox( { density:0, restitution:0.1, x:(p1.x + p2.x) / 2, y:(p1.y + p2.y) / 2, width:width, height:0.5, angle:angle, skin:"none", bullet:false }, noBody);
        return list;
    }
    
    private function createObject(xp:Number, yp:Number, zp:Number, bmd:BitmapData, scale:Number = 1, lookAt:Boolean = true, stand:Boolean = true, noBody:Boolean = false, radius:Number = 0.3, density:Number = 1, restitution:Number = 0.2):Sprite3D {
        var sp:Sprite3D;
        if (noBody) {
            var slideH:Number = (!lookAt && !stand)? -bmd.height / 2 : -bmd.height;
            sp = _scene.addSprite(new Sprite3D(Create.spriteBmp(bmd, scale, -bmd.width / 2, slideH), lookAt, stand, xp, yp, zp));
        } else {
            var qobj:QuickObject = _sim.addCircle( { density:density, restitution:restitution, x:xp, y:yp, radius:radius, skin:"none" } );
            sp = _scene.addSprite(new QuickBox3D(qobj, Create.spriteBmp(bmd, scale, -bmd.width / 2, -bmd.height), lookAt, stand, xp, yp, zp));
        }
        return sp;
    }
    
    /**
     * カウントダウン開始
     */
    public function startRace():void {
        $.label.hide();
        createCars();
        _sceneCam.setStartingPos();
        _countDown = _countInterval * (_countNum + 1);
        _isRecording = _isRunning = _isFinish = false;
        _lap = 1;
        _sim.start();
        if (_isHowTo) {
            setCamera(SceneCamera.BEHIND);
            _player.autoSteer = false;
            startRun();
        } else if (_isComplete) {
            setCamera(SceneCamera.COMPLETE);
            _player.autoSteer = true;
            startRun();
        } else if (_isDemo) {
            setCamera(SceneCamera.DEMO);
            _player.autoSteer = true;
            startRun();
        } else {
            setCamera(SceneCamera.BEHIND);
            _display.setCourseData(_race.track);
            _display.setMessage(true, "");
            _display.setMessage(false, "");
        }
        updateDisplay();
        _scene.render();
        play();
    }
    
    public function setCamera(type:int):void {
        _sceneCam.cameraType = type;
        updateDisplay();
    }
    
    public function startRun():void {
        _sceneTime = _collideTime = 0;
        _time = _startTime = getSceneTime();
        _isReplay = _inResultView = false;
        _isRecording = _isRunning = true;
        if(!_isDemo) _scene.startRecord();
    }
    
    /**
     * キー入力
     */
    public function keyUpDown(e:KeyboardEvent):void {
        if (_isDemo) return;
        var isDown:Boolean = (e.type == KeyboardEvent.KEY_DOWN);
        switch(e.keyCode) {
            case 38: _keyUD = (isDown)? _keyUD | 0xF000 : _keyUD & 0x0FFF; break;
            case 90: _keyUD = (isDown)? _keyUD | 0x0F00 : _keyUD & 0xF0FF; break;
            case 40: _keyUD = (isDown)? _keyUD | 0x00F0 : _keyUD & 0xFF0F; break;
            case 88: _keyUD = (isDown)? _keyUD | 0x000F : _keyUD & 0xFFF0; break;
            case Keyboard.LEFT: _race.key = (isDown)? _race.key | 0x00F00 : _race.key & 0xFF0FF; break;
            case Keyboard.RIGHT: _race.key = (isDown)? _race.key | 0x000F0 : _race.key & 0xFFF0F; break;
        }
        _race.key = (_keyUD & 0xFF00)? _race.key | 0xF0000 : _race.key & 0x0FFFF;
        _race.key = (_keyUD & 0x00FF)? _race.key | 0x0F000 : _race.key & 0xF0FFF;
        if (!isDown) switch(e.keyCode) {
            case 88: case Keyboard.SHIFT: case 90: case Keyboard.ENTER://Z,Enter,X,SHIFT
                if (_isReplay && !$.transition.isFading) endReplay();
                break;
            case 67://C
                changeCamera();
                updateDisplay();
                _scene.render();
                break;
            case Keyboard.CONTROL:
                if (!_isFinish && !$.transition.isFading) {
                    pause();
                    _pause.active();
                    SE.stopAll();
                }
                break;
        }
    }
    
    private function changeCamera():void {
        if (_isReplay || _isDemo) return;
        _sceneCam.swap();
        updateDisplay();
    }
    
    private function endReplay():void {
        $.transition.fadeOutIn(function():void {
            _isReplay = _isRecording = false;
            _display.replay.visible = false;
            _scene.destroyGhosts();
            gotoTop();
        });
    }
    
    public function startReplay():void {
        updateGround();
        _sceneTime = 0;
        _sceneCam.startReplay();
        _display.replay.visible = true;
        _isReplay = _isRecording = true;
        _sim.stop();
        setCamera(SceneCamera.REPLAY);
        _scene.startReplay();
        _scene.destroyGhosts();
        if(_race.isTimeTrial) createGhost(true, -$.replay.last.timeCount);
    }
    
    /**
     * シーンやカメラが変化した時に画面の表示を更新する
     */
    public function updateDisplay():void {
        if (_player) {
            _player.isAlwaysLight = _sceneCam.cameraType == SceneCamera.DRIVERS;
            _player.visible = (_isDemo || _isReplay || _sceneCam.cameraType == SceneCamera.BEHIND);
        }
        _scene.camera.ground = 900 + 300 * $.user.viewDistance;
        _scene.camera.view = 1100 + 300 * $.user.viewDistance;
        _scene.updateLight();
        _scene.updateFog();
        _mirrorCnt = -1;
        _mirror.visible = _sceneCam.cameraType == SceneCamera.DRIVERS && $.user.showMirror;
        _display.display.visible = !isDemo && !_inResultView && !_isReplay;
    }
    
    ///===========================================================
    ///    ゲームのメイン処理
    ///===========================================================
    private function onTickSimurate(e:Event = null):void {
        _scene.groundMap.lock();
        var i:int, qobj:QuickObject, car:Car;
        _sceneTime++;
        
        if ((!_isDemo || _isHowTo) && (!_isFinish || _isReplay)) SE.update(_player);
        if (!_isDemo && _countDown > 0) tickCountDown();
        
        if (_isHowTo) {
            $.howto.tick();
            _race.key = $.howto.key;
        }
        
        if (_isComplete && --_completeTimer<=0) {
            _isComplete = false;
            $.transition.fadeOutIn(onFinishCompleteScene);
        }
        
        if (_wanco.active && _sceneTime % 10 == 0) _scene.addParticle(Dot.heart, 1, 1.5, 2, 30, _wanco.point.x + (Math.random() - 0.5), _wanco.point.y + (Math.random() - 0.5), 1, 2);
        
        for each (car in _scene.cars) car.stockPrev();
        if (_mirror.visible && ++_mirrorCnt % 2 == 0) drawMirror();
        
        //リプレイ処理
        if (_isRecording && _isReplay) {
            _scene.tickRecord();
            if (_player.replay.isFinish) repeatReplay();
        }
        
        _sceneCam.update(_sceneTime);
        _scene.camera.setPosition(_sceneCam.vector.position);
        _scene.camera.setGaze(_sceneCam.vector.gaze);
        
        //レンダリング
        _scene.render();
        _scene.fadeTireTrack();
        for each (car in _scene.cars) car.updateMark();
        if (_isRecording && !_isReplay && !_inResultView) _scene.tickRecord();
        
        if (_isRunning) {
            var layRubber:Number = $.user.groundQuality? 0.7 : 0.75;
            //車の処理
            for each (car in _scene.cars) {
                car.simulate(_race);
                if (car.speedRate > 0.2) {
                    //パーティクル
                    if ($.user.showParticles && (car.visible || car.isAlwaysLight) && _sceneTime % 3 == 0) {
                        if (car.slip > 0.8) _scene.addParticle(Math.random() > 0.5? Dot.smoke1 : Dot.smoke2, 0.6, 3, 8, Math.random()*30+15, car.position.x, car.position.y, car.position.z, car.position.z + 1);
                        if (car.dirtRate > 0.2) _scene.addParticle(Dot.grassParticle, 1, 2.5, 2.5, 20, car.position.x + (Math.random() - 0.5), car.position.y + (Math.random() - 0.5), car.position.z, car.position.z + 0.5);
                    }
                    //タイヤの跡
                    if (car.slip > 0.8 || car.dirtRate > 0.2) {
                        Pixel.drawDarkLine(_scene.groundMap, S.QtoG(car.tireL.x), S.QtoG(car.tireL.y), S.QtoG(car.tireLprev.x), S.QtoG(car.tireLprev.y), layRubber, true);
                        Pixel.drawDarkLine(_scene.groundMap, S.QtoG(car.tireR.x), S.QtoG(car.tireR.y), S.QtoG(car.tireRprev.x), S.QtoG(car.tireRprev.y), layRubber, true);
                    }
                }
            }
            
            if (!_isFinish && !_isDemo && !_isReplay) {
                var lapTime:int = getSceneTime() - _time;
                _race.raceTime = getSceneTime() - _startTime;
                _display.setTotalTime(_race.raceTime);
                _display.setLapTime(lapTime);
                
                //順位チェック
                _scene.cars.sort(function(a:Car, b:Car):int { return int(a.lap < b.lap) - int(a.lap > b.lap) } );
                for (i = 0; i < _scene.cars.length; i++) {
                    _scene.cars[i].setRank(i + 1);
                    if (_race.track.level == Race.EXTREME && !_scene.cars[i].isPlayer && _scene.cars[i].lap < _player.lap && _scene.cars[i].nitro == 0) _scene.cars[i].chargeNitro();
                }
                _display.setRank(_player.rank);
                
                //周回チェック
                if (_player.lap >= _lap) {
                    _race.lapsTime.push(lapTime);
                    _display.setLapNum((_player.lap | 0)+1);
                    var newLapRecord:Boolean = checkLapRecord(lapTime);
                    if (newLapRecord) _race.newLapRecord = true;
                    _display.setMessage(false, Text.getTimeString(lapTime), 1, 3000, (newLapRecord)? UI.HIGHLIGHT : 0xFFFFFF);
                    if (_race.track.lapsNum - _lap == 1) _display.setMessage(true, "FINAL LAP", 1, 3000, UI.HIGHLIGHT);
                    //FINISH
                    if (_isFinish = (_lap++ == _race.track.lapsNum)) {
                        _race.rank = _player.rank;
                        _player.replay.setFinish(_race);
                        checkTotalRecord();
                        onFinish();
                    }
                    _display.addLapTime(lapTime, _isFinish);
                    SE.effect((_isFinish)? SE.SND_FINISH : (_lap == _race.track.lapsNum)? SE.SND_FINAL : (newLapRecord)?SE.SND_NEWRECORD : SE.SND_LAP);
                    _time = getSceneTime();
                }
            }
            
            _display.setSpeed(_player.speed);
        }
        
        //BOX2D摩擦
        if (!_isReplay) _scene.updateQuickBox();
        
        //画面の揺れ
        if (!_isDemo && !_isReplay) {
            _scene.display.x = _player.dirtRate * _player.speedRate * Math.random() * 8;
            _scene.display.y = _player.dirtRate * _player.speedRate * Math.random() * 8;
        }
        
        _scene.groundMap.unlock();
    }
    
    private function onFinishCompleteScene():void {
        $.tape.stop();
        $.complete.hide();
        quitAndReset();
    }
    
    public function quitAndReset():void {
        destroyStage();
        createStage($.world.demo);
        quit();
    }
    
    public function getSceneTime():int {
        return _sceneTime * 33;
    }
    
    private function repeatReplay():void {
        $.transition.fadeOutIn(startReplay);
    }
    
    //ハイスコアチェック
    private function checkTotalRecord():void {
        if (_race.isRandom) return;
        var bestRace:int = $.user.bestTotals[_race.track.id];
        var bestRank:int = $.user.bestRank[_race.track.id];
        if (!_race.isTimeTrial && !_race.isWonderfl && (_race.rank < bestRank || bestRank < 0)) {
            _race.newRankRecord = true;
            $.user.bestRank[_race.track.id] = _race.rank;
        }
        if (_race.raceTime < bestRace || bestRace < 0) {
            _race.newRaceRecord = true;
            $.user.bestTotals[_race.track.id] = _race.raceTime;
        }
        $.user.save();
    }
    
    //LAPタイムチェック
    private function checkLapRecord(time:int):Boolean {
        if (_race.isRandom) return false;
        var record:int = $.user.bestLaps[_race.track.id];
        if (time < record || record < 0) {
            _race.newLapRecord = true;
            _race.fastLapTime = time;
            $.user.bestLaps[_race.track.id] = time;
            $.user.save();
            return true;
        } else return false;
    }
    
    //カウントダウン
    private function tickCountDown():void {
        if (--_countDown / _countInterval <= _countNum && _countDown % _countInterval == 0) {
            if (!_countDown) {
                _display.setMessage(true, "GO", 3, 1500, UI.HIGHLIGHT);
                startRun();
            } else _display.setMessage(true, String(_countDown / _countInterval), 3, 0);
            SE.effect((_countDown)? SE.SND_COUNT : SE.SND_GO);
        }
    }
    
    //ゴール
    private function onFinish():void {
        if (_player.rank == 1 && !_race.isTimeTrial && _race.track.enemiesNum > 0) _display.setMessage(true, Text.WINNER, 3, 4000, UI.WINNER);
        else _display.setMessage(true, Text.FINISH, 3, 4000, UI.HIGHLIGHT);
        _player.autoSteer = true;
        FrameTimer.setTimer(30 * 3, showResult, null);
    }
    
    //結果画面
    private function showResult():void {
        _isRecording = false;
        SE.stopAll();
        _scene.stopRecord();
        if(_race.mode == Race.TIMETRIAL) $.replay.add(_player.replay.clone());
        _inResultView = true;
        _result.setResult(_race);
        updateDisplay();
        
        //アンロック確認
        $.world.checkRankIn($.user);
        _result.setMessgae($.user.setUnlockedNum($.world.getUnlockedNum($.user))? Text.UNLOCK_TRACK : "");
        _complete = $.world.checkComplete($.user);
        $.user.save();
    }
    
    private function onSelectResultMenu(index:int):void {
        _inResultView = false;
        switch(index) {
            case 0: $.transition.fadeOutIn(startReplay); break;
            case 1: $.transition.fadeOutIn(reset); break;
            case 2: $.transition.fadeOutIn(gotoTop); break;
        }
    }
    
    private function gotoTop():void {
        if (_complete == -1) {
            quit();
            return;
        }
        complete();
    }
    
}

/**
 * 紙吹雪エフェクト
 */
class TickerTape {
    
    public var sprite:Sprite = new Sprite();
    public var tapes:Vector.<Tape> = new Vector.<Tape>();
    
    public function TickerTape() {
        for (var i:int = 0; i < 30; i++) addTape();
    }
    
    public function start():void {
        sprite.visible = true;
        for each(var tape:Tape in tapes) tape.reset();
        sprite.addEventListener(Event.ENTER_FRAME, onTick);
    }
    
    public function addTape():Tape {
        var tape:Tape = new Tape();
        tape.reset();
        sprite.addChild(tape);
        tapes.push(tape);
        return tape;
    }
    
    public function stop():void {
        sprite.visible = false;
        sprite.removeEventListener(Event.ENTER_FRAME, onTick);
    }
    
    private function onTick(e:Event):void {
        for each(var tape:Tape in tapes) {
            tape.tick();
            tape.x += tape.vx;
            tape.y += tape.vy;
            if (tape.x > Display.width || tape.y > Display.height) tape.reset();
        }
    }
    
}

/**
 * 紙吹雪用テープ
 */
class Tape extends Bitmap {
    
    public var vx:Number = 1;
    public var vy:Number = 1;
    private var _frame:int = 0;
    private var _images:Array = [];
    private var _limit:int = 20;
    
    public function Tape() {
        super();
        vx = Math.random() * 1 + 1;
        vy = Math.random() * 2 + 1;
        _frame = Math.random() * 20;
        _images = [Dot.papers[0], Dot.papers[1], Dot.papers[2], Dot.papers[1]];
    }
    
    public function tick():void {
        _frame = ++_frame % _limit;
        bitmapData = _images[_frame / 5 | 0];
    }
    
    public function reset():void {
        x = Math.random() * 465 - 100;
        y = Math.random() * 30 - 50;
    }
    
}

/**
 * ドリフト解説
 */
class HowTo {
    
    public var sprite:Sprite = new Sprite();
    public var key:uint = 0x000000;
    private var _active:Boolean = false;
    private var _lastKey:uint = 0;
    private var _time:int = 0;
    private var _step:int = 0;
    private var _isFinish:Boolean = false;
    private var _message:Label;
    private var _press:Object = {};
    private var _keys:Array = [
        [72, 0xf0000], [233, 0xf0f00], [255, 0xf00], [259, 0xf0f00, true], [293, 0xf0000, false], [388, 0xf00f0], [412, 0xf0],
        [421, 0xf00f0, true], [447, 0xf0000, false], [456, 0xf0f00], [464, 0xf0000], [501, 0xf0f00], [530, 0xf00], [536, 0xf0f00, true],
        [554, 0xf0000, false], [577, 0xf00f0], [580, 0xf0000], [636, 0xf0f00], [660, 0xf00], [667, 0xf0f00, true], [683, 0xf0000],
        [695, 0xf00f0], [731, 0xf0000, false], [745, 0xf0f00], [755, 0xf00], [760, 0xf0f00, true], [779, 0xf0000, false], [789, 0xf00f0],
        [793, 0xf0000], [906, 0xf0f00], [925, 0xf00], [932, 0xf0f00, true], [957, 0xf0000, false], [987, 0xf00f0], [995, 0xff000], [1100, 0xff000]
    ];
    
    public var onFinish:Function;
    
    public function HowTo() {
        _press.Z = sprite.addChild(Create.spriteBmp(Dot.Z, 6, 0, 0, 60, 100));
        _press.X = sprite.addChild(Create.spriteBmp(Dot.X, 6, 0, 0, 130, 100));
        _press.L = sprite.addChild(Create.spriteBmp(Dot.L, 6, 0, 0, 260, 100));
        _press.R = sprite.addChild(Create.spriteBmp(Dot.R, 6, 0, 0, 330, 100));
        _message = UI.createLabel(sprite, 190, 200, "DRIFT", 8 * 3, UI.HIGHLIGHT);
        _message.filters = UI.TEXT_FILTERS;
        UI.createLabel(sprite, 150, 45, Text.DEMO).filters = UI.TEXT_FILTERS;
        sprite.visible = false;
    }
    /*
    public function record(key:uint):void {
        _time++;
        if (_lastKey == key) return;
        _lastKey = key;
        trace("[" + _time + ", " + "0x"+key.toString(16) + "],");
    }
    */
    public function keyUpDown(e:KeyboardEvent):void {
        if (!_active || $.transition.isFading || e.type == KeyboardEvent.KEY_DOWN || _time < 30) return;
        switch(e.keyCode) {
            case Keyboard.SHIFT: case 88:
                end();
                break;
        }
    }
    
    public function start():void {
        $.label.show(Text.LABEL_QUIT);
        _time = _step = key = 0;
        sprite.visible = true;
        _isFinish = false;
        _message.visible = false;
        _active = true;
        for (var k:String in _press) _press[k].transform.colorTransform = Palette.getColorTransform(0, 0.6, 1);
    }
    
    public function end():void {
        if (_isFinish) return;
        _isFinish = true;
        _active = false;
        if (onFinish != null) onFinish();
    }
    
    public function tick():void {
        if (_step >= _keys.length) return;
        _time++;
        if (_keys[_step][0] == _time) {
            key = _keys[_step][1];
            if(_keys[_step][2] != null) _message.visible = _keys[_step][2];
            _press.Z.transform.colorTransform = Palette.getColorTransform(0, 0.6 * int(!(key & 0xF0000)), 1);
            _press.X.transform.colorTransform = Palette.getColorTransform(0, 0.6 * int(!(key & 0x0F000)), 1);
            _press.L.transform.colorTransform = Palette.getColorTransform(0, 0.6 * int(!(key & 0x00F00)), 1);
            _press.R.transform.colorTransform = Palette.getColorTransform(0, 0.6 * int(!(key & 0x000F0)), 1);
            _step++;
            if (_step >= _keys.length) end();
        }
    }
    
}

class SceneCamera {
    
    static public const DRIVERS:int = 0;
    static public const BEHIND:int = 1;
    static public const REPLAY:int = 2;
    static public const DEMO:int = 3;
    static public const AROUND:int = 4;
    static public const COMPLETE:int = 5;
    
    public var vector:CameraVector = new CameraVector();
    public var cameraType:int = DEMO;
    private var _replayCameraTime:int = 0;
    private var _replayCameraCount:int = 0;
    private var _replayCameraType:int = 0;
    private var _replayCameraPos:Vector3D = new Vector3D();
    private var _replayTargetCar:Car;
    private var _trackingSpeed:Number = 0.25;
    private var _cameraSpeed:Number = 0;
    
    public function SceneCamera() {
    }
    
    public function swap():void {
        cameraType = cameraType == BEHIND? DRIVERS : BEHIND;
    }
    
    public function startReplay():void {
        _replayCameraTime = 0;
        _replayCameraCount = -1;
    }
    
    public function setStartingPos():void {
        _cameraSpeed = 0;
        vector.moveOffsetXYZ(1, 15, -0.4, 1);
    }
    
    public function update(time:int):void {
        var t:Number;
        var player:Car = $.game.player;
        
        _cameraSpeed += (_trackingSpeed - _cameraSpeed) * 0.01;
        switch(cameraType) {
            case AROUND:
                t = time / 100;
                vector.movePosition(player.position.add(new Vector3D(Math.cos(t) * 5, Math.sin(t) * 5, 2)), 1);
                vector.moveGaze(player.position, 1);
                break;
            case DEMO:
                t = time / 180;
                var per:Number = (Math.sin(t / 1.5) + 1) * 0.5;
                vector.movePosition(Vector3DUtil.mix(player.position.add(new Vector3D(Math.cos(t * 2.5) * 4, Math.sin(t * 2.5) * 4, 3)), new Vector3D(Math.cos(t) * 70, Math.sin(t) * 70, 8), per), 1);
                vector.moveGaze(player.position, 1);
                break;
            case COMPLETE:
                t = -time / 50;
                vector.movePosition(player.position.add(new Vector3D(Math.cos(t) * 4, Math.sin(t) * 4, 2)), 1);
                vector.moveGaze(player.position.add(new Vector3D(0, 0, 2)), 1);
                break;
            case BEHIND:
                vector.moveGazeXYZ(player.qobject.x, player.qobject.y, 1.2, 1);
                if (!$.game.isFinish || $.game.isReplay) vector.moveOffsetXYZ(player.front.x * -2.6, player.front.y * -2.6, 0.4, _cameraSpeed);
                break;
            case DRIVERS:
                vector.moveGazeXYZ(player.qobject.x + player.front.x * 2.5, player.qobject.y + player.front.y * 2.5, 0.8, 1);
                vector.moveOffsetXYZ(player.front.x * -2.5, player.front.y * -2.5, 0.05, 1);
                break;
            case REPLAY:
                updateReplayCamera();
                break;
        }
        vector.tick();
    }
    
    //リプレイ用カメラ
    private function updateReplayCamera():void {
        var rnd:int, c:Car, min:Number, d:Number;
        var player:Car = $.game.player;
        var cars:Vector.<Car> = $.game.scene.cars;
        
        if (_replayCameraCount > 0) _replayCameraCount--;
        if (_replayCameraCount <= 0) {
            _replayCameraTime = 0;
            rnd = Math.random() * 3;
            if (_replayCameraCount == -1) rnd = 1;
            if (rnd == 2) {
                _replayCameraType = 2;
                _replayCameraCount = 30 * 7;
                min = Number.MAX_VALUE;
                for each (c in cars) {
                    if (!c.isPlayer) {
                        d = Point.distance(c.point, player.point)
                        if (min > d) {
                            min = d;
                            _replayTargetCar = c;
                        }
                    }
                }
                if (min > 25) rnd = Math.random() * 2;
            }
            if (rnd ==0) {
                if (player.speed > 10) {
                    _replayCameraPos.x = player.point.x + player.velocity.x * 30 * 1 + (Math.random() - 0.5) * 10;
                    _replayCameraPos.y = player.point.y + player.velocity.y * 30 * 1 + (Math.random() - 0.5) * 10;
                    _replayCameraPos.z = Math.random() * 3 + 1;
                    _replayCameraType = 0;
                    _replayCameraCount = 30 * 2.5;
                } else rnd = 1;
            }
            if (rnd == 1) {
                _replayCameraType = 1;
                _replayCameraCount = 30 * 14;
            }
        }
        
        _replayCameraTime++;
        if (_replayCameraType == 1) {
            var time:Number = _replayCameraTime / 100;
            var per:Number = (Math.cos(time*1.2) + 1) * 0.5;
            var rad:Number = Math.atan2(player.point.y, player.point.x) + Angle.toRAD * 38 - _replayCameraTime / 300;
            var pos:Vector3D = new Vector3D(Math.cos(rad) * 70, Math.sin(rad) * 70, 12);
            _replayCameraPos = Vector3DUtil.mix(player.position.add(new Vector3D(Math.cos(time) * 7, Math.sin(time) * 7, 3)), pos, per);
        }
        
        var speed:Number = 1;
        if (_replayCameraType == 2) {
            var v:Point = player.point.subtract(_replayTargetCar.point);
            v.normalize(4 + _replayCameraTime/100);
            _replayCameraPos = player.position.add(new Vector3D(v.x, v.y, 1.5 + _replayCameraTime / 70));
            if (_replayCameraTime == 1) speed = 1;
            else speed = 0.1;
        }
        vector.movePosition(_replayCameraPos, speed);
        vector.moveGaze(player.position, 1);
        SE.setVolume(Math.max(0, 1 - player.position.subtract($.game.scene.camera.position).length / 30));
    }
    
}

/**
 * 背景画像
 */
class BackGround extends Sprite {
    
    private var _zoom:int = 2;
    private var _mtx:Matrix = new Matrix(_zoom, 0, 0, _zoom);
    private var _bmd:BitmapData = new BitmapData(1900 / _zoom, 170 / _zoom);
    private var _clouds:Vector.<BitmapData> = new Vector.<BitmapData>();
    private var _fogBox:Sprite;
    
    public function BackGround() {
    }
    
    public function init():void {
        _fogBox = Create.box(0, 0, Display.width, 100, 0, 1, 0, -5);
        addChild(_fogBox);
        var i:int, n:int, h:Number, c:BitmapData;
        for (n = 0; n < 3; n++) {
            _clouds[n] = new BitmapData(_bmd.width, 55 / _zoom, true, 0);
            for (i = 0; i < 13; i++) {
                h = (i % 2 * 15 + 40);
                c = Create.smoke(250 / _zoom, h / _zoom, i + 50, 0.7 - n * 0.2, 0);
                _clouds[n].copyPixels(c, c.rect, new Point((i * 140 + i % 3 * 20) / _zoom, _clouds[n].height - h / _zoom), null, null, true);
                c.dispose();
            }
        }
    }
    
    public function draw(light:Light):void {
        _bmd.draw(Create.gradientBox(0, 0, _bmd.width, _bmd.height, true, 90, [light.sky, light.horizon, light.fog], [1, 1, 1], [0, light.horizonPer* 200, 200]));
        if (light.hasStars) for (i = 0; i < 100; i++) _bmd.setPixel(Math.random() * _bmd.width, Math.random() * _bmd.height / 2, Palette.mix(light.sky, 0xFFE033, i / 100 * 0.7 + 0.3));
        var i:int, rgb:uint, cloudDark:uint = Palette.mix(light.horizon, light.sky, 0.2);
        for (i = 0; i < 3; i++) {
            rgb = Palette.mix(light.cloud, cloudDark, 1 - i / 2);
            _clouds[i].colorTransform(_clouds[i].rect, Palette.getColorTransform(rgb));
            _bmd.copyPixels(_clouds[i], _clouds[i].rect, new Point(0, _bmd.height - 55 / _zoom - _clouds[i].height), null, null, true);
        }
        slide(0);
        _fogBox.transform.colorTransform = Palette.getColorTransform(light.fog, 1);
    }
    
    public function slide(per:Number):void {
        _mtx.tx = _bmd.width * _zoom * per;
        graphics.clear();
        graphics.beginBitmapFill(_bmd, _mtx, true, false);
        graphics.drawRect(0, -_bmd.height * _zoom, 465, _bmd.height * _zoom);
    }
    
}

/**
 * プレイ中に必要な情報色々
 */
class Race {
    
    static public var SINGLE:int = 0;
    static public var TIMETRIAL:int = 1;
    static public var WONDERFL:int = 2;
    static public var RANDOM:int = 3;
    
    static public var NORMAL:int = 0;
    static public var HARD:int = 1;
    static public var EXTREME:int = 2;
    static public var LEVELS:Array = [NORMAL, HARD, EXTREME];

    /**現在のトラックデータ*/
    public var track:Track;
    /**ゲームモード*/
    public var mode:int;
    /**キー入力データ*/
    public var key:uint = 0x00000;
    /**減速するエリア*/
    public var dirtMap:BitmapData;
    /**コース形状*/
    public var course:Vector.<Point> = new Vector.<Point>();
    /**各区画における対応チェックポイント番号*/
    public var lineID:Vector.<int> = new Vector.<int>();
    /**チェックポイントリスト*/
    public var checkPoints:Vector.<Vector.<Point>> = new Vector.<Vector.<Point>>();
    /**次のコーナーまでの長さ*/
    public var distances:Vector.<Number> = new Vector.<Number>();
    public var totalDistance:Number = 0;
    public var startingPoint:Point;
    public var startingOffsetPer:Number = 0.5;
    
    public var rank:int = 0;
    public var fastLapTime:int = 0;
    public var raceTime:int = 0;
    public var lapsTime:Array = [];
    public var newRankRecord:Boolean = false;
    public var newRaceRecord:Boolean = false;
    public var newLapRecord:Boolean = false;
    
    public function get isRandom():Boolean { return mode == RANDOM; }
    public function get isSingle():Boolean { return mode == SINGLE; }
    public function get isTimeTrial():Boolean { return mode == TIMETRIAL; }
    public function get isWonderfl():Boolean { return mode == WONDERFL; }
    
    public function Race() {
    }
    
    public function calculate(data:Track, rough:Boolean = false):void {
        var i:int, cnt:int, list:Array, p1:Array, p2:Array, bezList:Vector.<Point>, pointList:Array = [];
        reset();
        track = data;
        course.length = checkPoints.length = 0;
        
        for each (list in data.points) {
            if (list.length < 6) {
                list[2] = list[4] = 0;
                list[3] = list[5] = 0;
            }
            pointList.push([new Point(list[0], list[1]), new Point(list[0] + list[2], list[1] + list[3]), new Point(list[0] + list[4], list[1] + list[5])]);
        }
        
        var segments:int = rough? 3 : 10;
        for (i = 0; i < pointList.length; i++) {
            p1 = pointList[i % pointList.length];
            p2 = pointList[(i+1) % pointList.length];
            bezList = CurveUtil.getBezierList(p1[0], p2[0], p1[1], p2[2], segments);
            bezList.pop();
            course = course.concat(bezList);
        }
        
        //チェックポイント
        for each (list in data.checkPoints) checkPoints.push(Vector.<Point>([new Point(list[0], list[1]), new Point(list[2], list[3])]));
        
        //長さ計算
        totalDistance = distances.length = 0;
        var d:Number, leng:int = course.length;
        for (i = 0; i < leng; i++) {
            d = course[i].subtract(course[(i + 1 + leng) % leng]).length;
            distances.push(d);
            totalDistance += d;
        }
        
        //スタートライン
        startingPoint = null;
        var startIndex:int = -1;
        if (checkPoints.length > 0) {
            for (i = 0; i < course.length; i++) {
                var cross:Point = LineUtil.crossSegment(course[i], course[(i + 1) % course.length], checkPoints[0][0], checkPoints[0][1]);
                if (cross) {
                    startingPoint = cross;
                    startIndex = i;
                    break;
                }
            }
        }
        if (startIndex > 0) {
            course = course.concat(course.splice(0, startIndex));
            distances = distances.concat(distances.splice(0, startIndex));
        }
        if (!startingPoint) startingPoint = Point.interpolate(course[0], course[1], 0.5);
        
        //スタート位置のライン上の割合
        startingOffsetPer = Point.distance(startingPoint, course[0]) / distances[0];
        
        var cp:Vector.<Point>;
        cnt = 0;
        for (i = 0; i < course.length - 1 && cnt < checkPoints.length; i++) {
            cp = checkPoints[cnt];
            lineID[i] = cnt;
            if (LineUtil.isCrossXY(course[i].x, course[i].y, course[i + 1].x, course[i + 1].y, cp[0].x, cp[0].y, cp[1].x, cp[1].y)) cnt++;
        }
    }
    
    //チェックポイントのどっち側にいるか
    public function checkSide(id:int, car:Car):Boolean {
        var p0:Point = checkPoints[id][0];
        var p1:Point = checkPoints[id][1];
        return VectorUtil.crossXY(p0.x - p1.x, p0.y - p1.y, car.point.x - p1.x, car.point.y - p1.y) <= 0;
    }
    
    //チェックポイントを通過したか
    public function checkCross(id:int, car:Car):Boolean {
        var p0:Point = checkPoints[id][0];
        var p1:Point = checkPoints[id][1];
        return LineUtil.isCross(p0, p1, car.point, car.prevPoint);
    }
    
    //チェックポイントまでの距離
    public function getDistance(id:int, car:Car):Number {
        var p0:Point = checkPoints[id][0];
        var p1:Point = checkPoints[id][1];
        return LineUtil.getNearDistance(p0, p1, car.point);
    }
    
    public function getStartPos(leng:Number):Array {
        leng += distances[0] * startingOffsetPer;
        var index:int = 0, per:Number = 0;
        while (1) {
            if (leng < distances[index]) {
                per = (!distances[index])? 0 : leng / distances[index];
                break;
            } else {
                leng -= distances[index];
                index = ++index % course.length;
            }
        }
        var index2:int = (index + 1) % course.length;
        return [Point.interpolate(course[index2], course[index], per), index2];
    }
    
    public function reset():void {
        checkPoints.length = course.length = distances.length = lapsTime.length = 0;
        dirtMap.fillRect(dirtMap.rect, 0);
        rank = raceTime = 0;
        newRankRecord = newRaceRecord = newLapRecord = false;
    }
    
}

/**
 * 各種スケール
 */
class S {
    
    static public const QtoF:Number = 30;
    static public const QtoKPH:Number = 5;
    static public const FtoQ:Number = 1 / QtoF;
    static public var TEXTURE:Number = 1 / 5;
    static public var DRAW:Number = 5 * TEXTURE;
    static public const DIRT:Number = 1 / 15;
    static public const DRAW_DIRT:Number = 5 * DIRT;
    static public const MAP:Number = 0.8;
    static public const QRECT:Rectangle = new Rectangle(-100, -100, 200, 200);
    static public function QtoP(x:Number, y:Number):Point { return new Point(x * QtoF, y * QtoF); }
    static public function QtoG(num:Number):Number { return (num + QRECT.width / 2) * QtoF * TEXTURE; }
    static public function QtoD(num:Number):Number { return (num + QRECT.width / 2) * QtoF * DIRT; }
    static public function QtoM(num:Number):Number { return (num + QRECT.width / 2) * MAP }
    static public function setScale(scale:Number):void {
        TEXTURE = 1 / 5 / scale;
        DRAW = 5 * TEXTURE;
    }
    
}

/**
 * ドットグラフィック
 */
class Dot {
    
    static public var grassParticle:BitmapData;
    static public var lampOn:BitmapData;
    static public var lampOff:BitmapData = image(6, 31, "'00.21,11@',',,,1222.1233.13443113443101331001,10'.''.''.''.''.''.''.''.''.''.''.''.''.''.''.''.''.''.''.''.''.''.''.'", [0x000000, 0x333333, 0x666666, 0xcccccc]);
    static public var lamp:BitmapData = image(6, 31, "'00.21,43:34;11@';';22;234432:55,:66,:66,034430023320'22''.''.''.''.''.''.''.''.''.''.''.''.''.''.''.''.''.''.''.''.''.''.'", [0x000000,0x3f371f,0x686236,0x8c8713,0xffff00,0xffffff]);
    static public var smoke1:BitmapData = image(9, 8, "'12.00,21:11;23!20@.:'..'';!':,;01::1332,',;,'31;!02323331.0233!.", [0xcccccc,0x999999,0x666666]);
    static public var smoke2:BitmapData = image(8, 9, "'21.11,22:12;00!20@;.!;02.'10.::''..,::,3231',33',2332031323!;02!;", [0xcccccc,0x999999,0x666666]);
    static public var heart:BitmapData = image(7, 6, "'10.12,22:00@01'1101,.21.,,'.,':.'::':", [0x990033,0xff3399]);
    static public var star:BitmapData = image(9, 10, "'00.01,10:12;21!44I32|33@'',''':,'.:I11.|!|31:!!I,:!I,.23|;'13;23,.;.;'11'.,", [0x000000,0xae8300,0xffcc00,0xffffcc]);
    static public var LR:BitmapData = image(10, 8, "'00.11,10:01;22@':.,''12.21':;.;,13;.;311432.2341:43.34,'14.41'':.,'", [0x000000,0xf2f2f2,0xcccccc,0x999999]);
    
    static public var Z:BitmapData = image(11, 11, "'11.33,34:13;22!41I12|44?23-01@-''''-;;;;31I...,'?'':!I..:,'?.:.!I.:.,'?'':!I...,',|||!-''''0", [0x000000,0xf2f2f2,0xcccccc,0x999999]);
    static public var X:BitmapData = image(11, 11, "'11.33,13:34;41!22I12|31?44-23*01@*''''*!!!!|I...:'-'|,;I.',:'-.,.;I.',:'-'|,;I...:':???;*''''0", [0x000000,0xf2f2f2,0xcccccc,0x999999]);
    static public var L:BitmapData = image(11, 11, "'11.33,34:13;22!41I12|44?23-01@-''''-;;;;31I...,'?.:.!I.:.,'?'':!I.:.,'?.:.!I...,',|||!-''''0", [0x000000,0xf2f2f2,0xcccccc,0x999999]);
    static public var R:BitmapData = image(11, 11, "'11.33,34:13;22!41I12|44?23-01@-''''-;;;;31I...,'?.:.!I..:,'?'':!I..:,'?.:.!I...,',|||!-''''0", [0x000000,0xf2f2f2,0xcccccc,0x999999]);
    static public var U:BitmapData = image(11, 11, "'33.11,13:34;22!41I12|44?23-01@-....-;;;;31I''':.?','!I'.,:.?,,,!I'31':.?','!I''':.:|||!-....0", [0x000000, 0xf2f2f2, 0xcccccc, 0x999999]);
    static public var D:BitmapData = image(11, 11, "'33.11,13:34;22!41I12|44?23-01@-....-;;;;31I''':.?','!I'31':.?,,,!I'.,:.?','!I''':.:|||!-....0", [0x000000, 0xf2f2f2, 0xcccccc, 0x999999]);
    
    static public var bldg:BitmapData = image(10, 13, "'11.22,44:34;43@'''''''''''''''.....:,;,,:,;,,:,;,,:,;,,.....'''''''''''''''.....", [0xb4b4aa,0x75756f,0x131313,0x5a7189]);
    static public var carBase:BitmapData = image(15, 25, "'11.00,10:21;33!02I01|31?13@...',..I'.'',I'',!''.'?;''''?;;|'''';|'!'''I'',:'',',.'''',..''''',.!'''''..:'''',.!'''''..:'''',.!'''''..:'''',.!'''''..:'''',.!:''':...:'',.I'.:',I'',!''.'?;''''?;;|'''';|'.2222,I'',....'1", [0x000000,0x000001,0x272727]);
    static public var grass:BitmapData = image(16, 9, "'00.44,34:10;22!33I43|24?42-23*01^02@'':'''''*':':'':^:;'20*':03;32^31^*2112,!--13;;!?,,!-!?,,III.,,...|..|.|.,..,.?", [0x6c8319,0x515c24,0x434920,0x2b3205]);
    static public var brick:BitmapData = image(15, 6, "'55.44,11:45@,,,,,,,1223.322223.33'''3335'''.:.:.:.'''''''54:.:.:.:", [0xffffff,0xe0e0e0,0xbdbdbd,0x5b472c,0x3d2714]);
    static public var trophy1:BitmapData = image(15, 15, "'00.11,01:41;55!10I32|34?23-13*33^44@',....'',12I*^.',21??|:31,2112I*^.:-.??|:14!-:I|:^!'.,*:,!''',31''''',-.'''',I|:''',....'''15;;;!'',56666651'''15;;;!'',....'0", [0x000000,0xffffcc,0xffcc00,0xae8300,0x765038,0xd6cdb1]);
    static public var trophy2:BitmapData = image(9, 14, "'11.01,00:55;41!24I44@'''''23!42;1232I!'23!42;.32I21,.!21,,.;,,.14',.22I;.''''15:::'5666665115:::'''''", [0x000000,0xcccccc,0xf2f2f2,0x999999,0x765038,0xd6cdb1]);
    static public var trophy3:BitmapData = image(7, 12, "'11.55,00:41@''''2322:123224'2322:0132:,0121,,1210,132410''''..5115..''''", [0x000000,0xbd8000,0xffad02,0x845900,0x765038]);
    static public var arrow:BitmapData = image(10, 7, "'11.00,22@'''''12,,,'13,,23'0132,3110.1323'..013110...'..", [0x000000,0xffcc00,0xae8300]);
    static public var audienceRoof:BitmapData = image(4, 4, "'12@''''''''", [0x494949,0x333333]);
    static public var audienceSeats:BitmapData = image(8, 6, "'11@''''23456325''''65354562''''52465236", [0x494949,0xffcc00,0x336699,0xff9933,0xcc3300,0x006633]);
    static public var audienceSide:BitmapData = image(18, 27, "'00.14,33:10;12!23I13|32?20-01*31@''''':'''''''':'''''-'':'''''-'':''':'-'':''';'-'':'''I?-'':'''I|-'':'''I,21'':'''!,*'':''';,*?':'''.!*|':'''.;*,?:'''..21,|:'''..;,,:'''...!,;'''...;,I?''....!I|''....;I,?'.....!,|'.....;,,?......!,|......;,,.......!,.......;,........!........;", [0x666666,0x999999,0xcccccc,0x545454]);
    static public var moja:BitmapData = image(42, 6, "'22.12,11:21@,,,,,,,,,,.''''''''''.,..':.:''..'.,,',:,..:'.,:,::..,:..:'::'.....,:,:''.....:'::,..,..':'::...'.,,:,:'.,,,,,,,,,,.''''''''''", [0x333333,0xcccccc]);
    static public var drum:BitmapData = image(6, 8, "'41.11,12:21@01.10:.,3222:33'.13',31,:33'.03'10", [0x5a7a85,0x415c5d,0x91b7cc,0x7497a6]);
    static public var cone:BitmapData = image(5, 7, "'00.01@'10'02'.13'2240.33.244311333", [0xff3333,0xffff00,0x990000,0xcc9900]);
    static public var startingLine:BitmapData = image(14, 4, "'11.10,01@'''''''.......,,,,,,,'''''''", [0xffffff]);
    static public var plantS:BitmapData = image(10, 10, "'00.02,43:13;10!21I31|20@'01||''!:11'|1211I..!I,;:33341422!I,,41.::44;!1441,;.443432''01.;'", [0x515c24,0x6c8319,0x434920,0x2b3205]);
    static public var plantL:BitmapData = image(14, 14, "'00.13,31:12;21!10I32|43?20-01*23^44~34/14@'''-''''?!?:''02:;,;!'';,**?!:.;..,'-*.|,I':I~,|.!*.33~/22!03;,||41-!I^/^.?-;/41|,'02.^~II''?,I11'''':':''", [0x515c24,0x6c8319,0x434920,0x2b3205]);
    static public var rock:BitmapData = image(8, 24, "'65.56,87:55;76!88I58|68?67-75*45^34@123213322343^43^:***:::....?'''!-;.,',I,I!I;I!I'',57I;:'';.;86:|!86'!!;85!,|-78,?:?;'?::'85-|I,.!|,.,66;.;425344^", [0x6c8319,0x515c24,0x434920,0x2b3205,0x4c402c,0x7b6647,0x9c825a,0xbea467]);
    static public var lock:BitmapData = image(9, 12, "'11.24,00:12;21!33I41|01@,|',,|22;,|;';,:10:10'''''233242I:!..'233242I:!..';''I:!..'''''", [0x000000,0xcccccc,0xf2f2f2,0x999999]);
    static public var ghost:BitmapData = image(8, 11, "'22.25,42:24;23@0111111002333320'''':;31,'533612.32;61.32;61'533621:.62,:.62,07''70", [0x010101,0x000000,0xff3300,0x666666,0x990000,0xff8b6e,0x000001]);
    static public var wanco:BitmapData = image(16, 16, "'00.44,11:14;10!21I41|01?13-31*42^12~24@''',;''''''^-''''''?.;'''',~*,'''|~.43:;''?32.!34!''^....!'|.....-;|~:.:*?I?......!^.I:I..!::..I.*-|I,,:*33;|-5155?33,'',5551,,'''',;''''", [0x000000,0xb1582c,0x934824,0xcc6633,0xcc3333]);
    
    static public var pattern1:BitmapData = image(2, 2, "@1001", [0x000000]);
    static public var papers:Array = [
        image(7, 7, "'22.10,00:21;01@,;.,11'.12':;'':;''.12:.,11,0", [0x000000, 0xffcc00]),
        image(8, 7, "'22.00,12:01;21!10@.:!.:,;.,''!,'';:'';.,'!.:11.", [0x000000, 0xffaf00]),
        image(10, 6, "'00.22,10:21;01@';,'';12:,'12..:,;...:'11.:,''11,'", [0x000000,0xff9900])
    ];
    static public var trees:Array = [
        image(12, 24, "'00.12,30:43;34!11I21|13?33-50*06^14~31/01(22)41{02}03@''/10''''/I''''.!,''/.|40'''I44,''{.):''.^);''/(^;,{.I.:,'(.||'}I!I;40I!(|;?{.!~44,23(.^|:!I!~:;:.~)~?!;^;?,}|:?::04?35?,,'}*-''''*-''''*-''''*-''''*-''", [0x73900b,0x8cb10d,0x3e5a1d,0x577314,0x694e1f,0x755f2b]),
        image(10, 23, "'00.31,12:13;65!44I24|40?01-33*20^41~21/22@'','''?.*''..~'03:11,'?.11I'..,/|:.~/*?:,I!:..^*-.32,^.-,/!.-.,|?:,!I:.!^!.:I,|03~!I|'422542''04;|''';'''';'''';'''';'''';''", [0x73900b,0x577314,0x8cb10d,0x3e5a1d,0x694e1f,0x755f2b]),
        image(12, 23, "'00.12,23:32;43!21I34|22?06-50*40^11~44/30(01@''(20''''../''(.31,''.^.!''^^!|/02^|.,*'..31|/(!,|I*,|..,;::::~2003!!24:I!.:|,;.!!,24I,,:I;~;33,;:/0433;~;*';III''0445~*'''?-''''?-''''?-''''?-''''?-''", [0x8cb10d, 0x73900b, 0x577314, 0x3e5a1d, 0x694e1f, 0x755f2b])
    ];
    static public var treesSnow:Array = [
        image(12, 23, "'00.11,80:76;09!12I67|21?22-70*55^44~54/33(60)77{23}01@''}10''''.!10''}!|{''42{42/''^^~35(05^*4556-'.4564*(}..15I-|.?!56:63?/25)5006~~5762..1565*|!|.51..{32?13/?37:6326:65(076636):-':III''0778)-''';,'''';,'''';,'''';,'''';,''", [0xffffff, 0xe0e0e0, 0xbdbdbd, 0x848e23, 0x636a14, 0x465013, 0x2e3b13, 0x4c321a, 0x5b472c]),
        image(10, 23, "'00.11,98:12;44!21I24|77?70-46*22@''.'''01.10''.!23'02!3232'0342:I'5354;-103553636120.15416442....60*!!..*:::4754;*-?01;-|61:43|73.I456741I0563|;?'766876''07,?''','''','''','''','''',''", [0xffffff, 0xe0e0e0, 0x636a14, 0xbdbdbd, 0x848e23, 0x465013, 0x2e3b13, 0x4c321a, 0x5b472c]),
        image(12, 24, "'00.13,11:35;80!44I40|64?09-55*22^01~05/52(04@''^10''''^12''''..I''^,1560''03,36I''~./|''31:6243''.3721,10~33/23.50~-272134'(72*.:6072*,335114^,:-,3015.572653:--*45:-|274262/!*462646!I(24|!||06!48!II'(?;''''?;''''?;''''?;''''?;''", [0xffffff, 0x636a14, 0xe0e0e0, 0x2e3b13, 0xbdbdbd, 0x465013, 0x848e23, 0x4c321a, 0x5b472c])
    ];
    static public function carWing(color1:uint, color2:uint, color3:uint):BitmapData {
        return image(13, 4, "'22.11@12'''''31'''''2312'''''31.....13", [color1, color2, color3]);
    }
    static public function carFrame(color1:uint, color2:uint, color3:uint):BitmapData {
        return image(13, 24, "'22.33,00:11;30!23I44|01?12-42*02^24~20@,,|:,,,,,'~,,,,?';,*~|'!,':,?';|:10|'!,:',?';*!;:'!;.,:''!;,:'I-!;:?^I'.31:^II!.:?II-.31:^II!.41--'-43I14'''^34:?'''.;|'''!,,*''',*~...;':03...|:10...;:'03...*!;...;.", [color1,color2,color3,0x000000]);
    }
    static public function carImage(color1:uint, color2:uint):BitmapData {
        return image(6, 9, "'11.22,30:03;33@01'10.;.23'32:',:.,:',.'.23;32:;,", [color1, 0x333333, color2]);
    }
    static public var ads:Object = { };
    
    static public function init():void {
        var radius:int = 10;
        lampOn = new BitmapData(radius*2, lamp.height + radius - 3, true, 0);
        lampOn.draw(Create.gradientCircle(radius, radius, radius, radius, [0xFFFF00, 0xFFF7B2], [1, 0], [0, 255]));
        lampOn.copyPixels(lamp, lamp.rect, new Point((lampOn.width - lamp.width) / 2, (lampOn.height - lamp.height)), null, null, true);
        var data:Array = [
            ["title", 160, 20, 0x23416D, 0x111111, "pattern1", 5, 0x000000, 0.4, "MOJA GRAND PRIX", 40, 0, 8, 0xFFFFFF],
            ["title2", 160, 20, 0xFFFFFF, 0xBBBBBB, "pattern1", 5, 0x000000, 0.1, "MOJA GRAND PRIX", 40, 0, 8, 0x000000],
            ["moja", 60, 40, 0x444444, 0x111111, "pattern1", 10, 0x000000, 0.4, "MOJA", 16, 10, 8, 0xFFFFFF],
            ["hoge", 60, 40, 0x444444, 0x111111, "pattern1", 10, 0x000000, 0.4, "HOGE", 16, 10, 8, 0xFFFFFF],
            ["wonderfl", 60, 40, 0x6C0502, 0x111111, "pattern1", 10, 0x000000, 0.5, "wonderfl", 10, 10, 8, 0xFFFFFF],
            ["flash", 60, 40, 0xD30909, 0x680302, "pattern1", 10, 0xFF0000, 0.1, "Flash", 16, 10, 8, 0xFFFFFF]
        ];
        for each (var item:Array in data) {
            var bmd:BitmapData = new BitmapData(item[1], item[2], false);
            bmd.draw(Create.gradientBox(0, 0, bmd.width, bmd.height, true, 90, [item[3], item[4]], [1, 1], [0, 255]));
            bmd.draw(Create.bitmapFillBox(0, 0, bmd.width, bmd.height, Dot[item[5]], item[6], item[6], 0, 0), null, new ColorTransform(0, 0, 0, item[8], item[7] >> 16 & 0xFF, item[7] >> 8 & 0xFF, item[7] & 0xFF));
            bmd.draw(UI.createLabel(null, 0, 0, item[9], item[12], item[13]), new Matrix(1, 0, 0, 1, item[10], item[11]));
            ads[item[0]] = bmd;
        }
    }
    
    static public function setParticleColor(rgb1:uint = 0x6d8a15, rgb2:uint = 0xb6e034):void {
        grassParticle = image(8, 5, "'00@'01''10'2001'''20''''02''10", [rgb1, rgb2]);
    }
    
    static public function image(width:int, height:int, dot:String, colors:Array):BitmapData {
        var bmd:BitmapData = new BitmapData(width, height, true, 0), data:Array = dot.split("@");
        while (data[0].length>=3) {
            data[1] = data[1].split(data[0].substr(0,1)).join(data[0].substr(1,2));
            data[0] = data[0].substr(3);
        }
        var pixels:Array = String(data[1]).split("");
        for (var i:int = 0; i < pixels.length; i++) bmd.setPixel32(i % bmd.width, int(i / bmd.width), (pixels[i] == "0")? 0 : 0xFF << 24 | colors[parseInt(pixels[i], 36) - 1]);
        return bmd;
    }
    
}

/**
 * 球面線形補完で動くカメラ用ベクトル
 */
class CameraVector {
    
    private var _gaze:Vector3D = new Vector3D();
    private var _offset:Vector3D = new Vector3D(0, 1, 0);
    private var _position:Vector3D = new Vector3D();
    private var _distance:Number = 100;
    private var _gazeTarget:Vector3D = new Vector3D();
    private var _offsetTarget:Vector3D = new Vector3D(0,1,0);
    private var _distanceTarget:Number = 100;
    private var _gazeSpeed:Number = 1;
    private var _offsetSpeed:Number = 1;
    
    public function CameraVector() {
        tick();
    }
    
    public function tick():void {
        if (!_gaze.equals(_gazeTarget)) {
            _gaze.x += (_gazeTarget.x - _gaze.x) * _gazeSpeed;
            _gaze.y += (_gazeTarget.y - _gaze.y) * _gazeSpeed;
            _gaze.z += (_gazeTarget.z - _gaze.z) * _gazeSpeed;
        }
        if (_distanceTarget != _distance) _distance += (_distanceTarget - _distance) * _offsetSpeed;
        if (!_offset.equals(_offsetTarget)) {
            _offset = Vector3DUtil.slerpVector(_offset, _offsetTarget, _offsetSpeed);
        }
        _offset.normalize();
        _offset.scaleBy(_distance);
        _position.x = _gaze.x + _offset.x;
        _position.y = _gaze.y + _offset.y;
        _position.z = _gaze.z + _offset.z;
    }
    
    public function moveDistance(d:Number, speed:Number = 0.2):void {
        _offsetSpeed = speed;
        _distanceTarget = d;
        if (speed == 1) _distance = _distanceTarget;
    }
    
    public function movePositionXYZ(x:Number, y:Number, z:Number, speed:Number = 0.2):void {
        moveOffsetXYZ(x - _gazeTarget.x, y - _gazeTarget.y, z - _gazeTarget.z, speed);
    }
    
    public function movePosition(v:Vector3D, speed:Number = 0.2):void {
        movePositionXYZ(v.x, v.y, v.z, speed);
    }
    
    public function moveOffset(v:Vector3D, speed:Number = 0.2):void {
        moveOffsetXYZ(v.x, v.y, v.z, speed);
    }
    
    public function moveGaze(v:Vector3D, speed:Number = 0.2):void {
        moveGazeXYZ(v.x, v.y, v.z, speed);
    }
    
    public function moveOffsetXYZ(x:Number, y:Number, z:Number, speed:Number = 0.2):void {
        _offsetSpeed = speed;
        Vector3DUtil.setXYZ(_offsetTarget, x, y, z);
        _distanceTarget = _offsetTarget.length;
        if (speed == 1) {
            Vector3DUtil.setXYZ(_offset, x, y, z);
            _distance = _distanceTarget;
        }
    }
    
    public function moveGazeXYZ(x:Number, y:Number, z:Number, speed:Number = 0.2):void {
        _gazeSpeed = speed;
        Vector3DUtil.setXYZ(_gazeTarget, x, y, z);
        if(speed == 1) Vector3DUtil.setXYZ(_gaze, x, y, z);
    }
    
    public function get gaze():Vector3D { return _gaze; }
    public function get offset():Vector3D { return _offset; }
    public function get distance():Number { return _distance; }
    public function get position():Vector3D { return _position; }
    
}

/**
 * 光源
 */
class Light3D {
    
    public var point:Point = new Point();
    public var min:Number = 1;
    public var max:Number = 5;
    
    public function Light3D() {
    }
    
}

/**
 * 環境データ
 */
class Light {
    
    public var hasStars:Boolean = false;
    public var useHeadlight:Boolean = false;
    public var isLightUp:Boolean = false;
    public var fog:uint = 0x1f2f3f;
    public var horizon:uint = 0x538BDE;
    public var sky:uint = 0x1F1F1F;
    public var horizonPer:Number = 0.7;
    public var cloud:uint = 0;
    public var ambient:uint = 0x0F0F1F;
    public var ambientPer:Number = 0.8;
    public var ambientColor:ColorTransform;
    
    static public const DAY:int = 0;
    static public const SUNSET:int = 1;
    static public const NIGHT:int = 2;
    static public const DAWN:int = 3;
    static public const LIST:Array = [DAY, SUNSET, NIGHT, DAWN];
    static public const LABELS:Array = ["DAY", "SUNSET", "NIGHT", "DAWN"];
    
    public function Light(type:int = DAY) {
        switch(type) {
            case DAY: setDay(); break;
            case SUNSET: setSunset(); break;
            case NIGHT: setNight(); break;
            case DAWN: setDawn(); break;
        }
    }
    
    public function setDawn():void {
        useHeadlight = hasStars = true;
        sky = 0x7B94AA;
        horizon = 0xDBDAD8;
        fog = 0xEBD4B2;
        cloud = 0x7C7A79;
        ambient = 0x1E4068;
        ambientPer = 0.5;
        updateAmbient();
    }
    
    public function setSunset():void {
        isLightUp = true;
        sky = 0x8F8898;
        horizon = 0xD9B07F;
        fog = 0xE5D065;
        cloud = 0x604407;
        ambient = 0x74290F;
        ambientPer = 0.5;
        updateAmbient();
    }
    
    public function setNight():void {
        isLightUp = useHeadlight = hasStars = true;
        sky = 0x051549;
        horizon = 0x2950B7;
        fog = 0x317BDE;
        cloud = 0x061534;
        ambient = 0x051549;
        ambientPer = 0.6;
        updateAmbient();
    }
    
    public function setDay():void {
        sky = 0x3172D6;
        horizon = 0x5D9DF8;
        fog = 0x7BBDFF;
        cloud = 0xFFFFFF;
        ambient = 0xFFFFFF;
        ambientPer = 0;
        updateAmbient();
    }
    
    private function updateAmbient():void{
        ambientColor = Palette.getColorTransform(ambient, ambientPer);
    }
    
}

/**
 * カメラ
 */
class Camera3D {
    
    public var light:Light = new Light();
    public var horizon:Number = 2000;
    //public var ground:Number = 1200;
    public var gradientArea:Number = 400;
    public var ground:Number = 1100;
    public var view:Number = 1400;
    public var rotation:Number = 0;
    public var angle:Number = 0;
    public var center:Vector3D = new Vector3D(0, 0, -314);
    public var gaze:Vector3D = new Vector3D();
    public var position:Vector3D = new Vector3D();
    public var point:Point = new Point();
    public var offset:Point = new Point();
    public var direction:Point = new Point();
    
    public function Camera3D():void {
    }
    
    public function setCenterXY(x:Number, y:Number):void {
        center.x = Display.center.x;
        center.y = Display.center.y;
    }
    
    public function setGaze(v:Vector3D):void {
        setGazeXYZ(v.x, v.y, v.z);
    }
    
    public function setPosition(v:Vector3D):void {
        setPositionXYZ(v.x, v.y, v.z);
    }
    
    public function setGazeXYZ(x:Number, y:Number, z:Number):void {
        Vector3DUtil.setXYZ(gaze, x, y, z);
    }
    
    public function setPositionXYZ(x:Number, y:Number, z:Number):void {
        Vector3DUtil.setXYZ(position, x, y, z);
    }
    
    public function update():void {
        var dx:Number = gaze.x - position.x;
        var dy:Number = gaze.y - position.y;
        rotation = Math.atan2(dx, dy) + Math.PI;
        angle = Math.atan2(Math.sqrt(dx * dx + dy * dy), gaze.z - position.z) + Math.PI;
        point.x = position.x;
        point.y = position.y;
        direction.x = Math.cos( -rotation);
        direction.y = Math.sin( -rotation);
        offset.x = Math.cos( -rotation + Angle.RAD90);
        offset.y = Math.sin( -rotation + Angle.RAD90);
    }
    
}

/**
 * 簡易3Dシーン
 */
class Scene3D {
    
    public var camera:Camera3D = new Camera3D();
    public var display:Sprite = new Sprite();
    public var groundMap:BitmapData;
    public var rawMap:BitmapData;
    public var lights:Vector.<Light3D> = new Vector.<Light3D>();
    
    private const SCALE:Number = 4;
    private var _root:Sprite = new Sprite();
    private var _container:Sprite = new Sprite();
    private var _lightContainer:Sprite = new Sprite();
    private var _bg:Sprite = Create.box(-1000, -1000, 2465, 2465, 0);
    private var _sky:BackGround = new BackGround();
    private var _fog:Sprite = new Sprite();
    private var _wrapper:Sprite = new Sprite();
    private var _ground:Sprite = new Sprite();
    private var _horizon:Point = new Point();
    private var _drawIndex:int = 0;
    private var _splitRects:Vector.<Rectangle> = new Vector.<Rectangle>();
    
    private var _recycleParticles:Vector.<Particle3D> = new Vector.<Particle3D>();
    private var _recycleLimit:int = 40;
    private var _recycleIndex:int = -1;
    
    private var _objects:Vector.<Sprite3D> = new Vector.<Sprite3D>();
    private var _cars:Vector.<Car> = new Vector.<Car>();
    private var _qboxes:Vector.<QuickBox3D> = new Vector.<QuickBox3D>();
    private var _sorts:Vector.<Sprite3D> = new Vector.<Sprite3D>();
    
    public function get cars():Vector.<Car> { return _cars; }
    
    public function Scene3D() {
        _sky.init();
        display.addChild(_bg);
        display.addChild(_sky);
        display.addChild(_root);
        _root.scaleX = _root.scaleY = _root.scaleZ = SCALE;
        _root.addChild(_wrapper);
        _wrapper.addChild(_ground);
        _wrapper.addChild(_lightContainer);
        _lightContainer.blendMode = BlendMode.OVERLAY;
        _wrapper.addChild(_fog);
        _wrapper.addChild(_container);
    }
    
    public function setSize(width:Number, height:Number):void {
        camera.setCenterXY(width / 2, height / 2);
    }
    
    public function setLight(light:Light):void {
        camera.light = light;
        updateFog();
        _bg.transform.colorTransform = Palette.getColorTransform(camera.light.sky, 1);
        _sky.draw(camera.light);
        updateLight();
    }
    
    public function updateFog():void {
        _fog.graphics.clear();
        var gr:Number = camera.ground, ga:Number = camera.gradientArea, fog:uint = camera.light.fog;
        Draw.gradientBox(_fog.graphics, gr - ga, -gr, ga, gr * 2, true, 0, [fog, fog], [0, 1], [0, 250]);
    }
    
    public function updateLight():void {
        _lightContainer.visible = $.user.showLight && camera.light.useHeadlight;
    }
    
    public function getScreenXYZ(x:Number, y:Number, z:Number):Point {
        return _container.local3DToGlobal(new Vector3D(x, y, -z));
    }
    
    public function getScreen(v:Vector3D):Point {
        return _container.local3DToGlobal(new Vector3D(v.x, v.y, -v.z));
    }
    
    public function addLight(light:Light3D):Light3D {
        lights.push(light);
        return light;
    }
    
    public function addParticle(bmd:BitmapData, alpha:Number = 0.5, scaleMin:Number = 1, scaleMax:Number = 1.5, fade:int = 30, x:Number = 0, y:Number = 0, z:Number = 0, tz:Number = 0):Particle3D {
        var obj:Particle3D;
        if (_recycleParticles.length >= _recycleLimit) {
            _recycleIndex = ++_recycleIndex % _recycleParticles.length;
            obj = _recycleParticles[_recycleIndex];
            obj.setParam.apply(null, arguments);
        } else {
            obj = new Particle3D(bmd, alpha, scaleMin, scaleMax, fade, x, y, z, tz);
            _recycleParticles.push(obj);
            addSprite(obj);
        }
        return obj;
    }
    
    public function addSprite(obj:Sprite3D):Sprite3D {
        obj.parent = _container;
        obj.onDestroy = onDestroySprite;
        if (obj is Car) {
            _cars.push(obj);
            Car(obj).lightParent = _lightContainer;
            _lightContainer.addChild(Car(obj).light);
        }
        _container.addChild(obj.sprite);
        _objects.push(obj);
        if (obj is QuickBox3D && QuickBox3D(obj).qobject.params.density != 0) _qboxes.push(obj);
        return obj;
    }
    
    private function onDestroySprite(obj:Sprite3D):void {
        var index:int;
        index = _objects.indexOf(obj);
        if (index > -1) _objects.splice(index, 1);
        index = _cars.indexOf(obj);
        if (index > -1) _cars.splice(index, 1);
        index = _qboxes.indexOf(obj);
        if (index > -1) _qboxes.splice(index, 1);
    }
    
    public function destroyParticles():void {
        var i:int, leng:int = _objects.length - 1;
        for (i = leng; i >= 0; i--) if (_objects[i] is Particle3D) _objects[i].destroy();
        _recycleParticles.length = 0;
        _recycleIndex = -1;
    }
    
    public function destroyCars():void {
        while (_cars.length) _cars[0].destroy();
    }
    
    public function destroyGhosts():void {
        var i:int, leng:int = _cars.length - 1;
        for (i = leng; i >= 0; i--) if (_cars[i].isGhost) _cars[i].destroy();
    }
    
    public function destroyAll():void {
        _sorts.length = lights.length = 0;
        while (_objects.length) _objects[0].destroy();
        destroyParticles();
    }
    
    public function updateQuickBox():void {
        
        for each (var qbox:QuickBox3D in _qboxes) {
            if (qbox is Car) continue;
            var body:b2Body = qbox.qobject.body;
            if (!body.IsSleeping()) {
                body.GetLinearVelocity().Multiply(0.95);
                if ($.game.sceneTime % 5 == 0 && body.GetLinearVelocity().Length() < 0.05) body.PutToSleep();
            }
        }
    }
    
    public function clearRecord():void {
        for each (var qobj:QuickBox3D in _qboxes) qobj.replay.clear();
    }
    
    public function stopRecord():void {
        for each (var qobj:QuickBox3D in _qboxes) qobj.replay.stopRecord();    
    }
    
    public function startReplay():void {
        for each (var qobj:QuickBox3D in _qboxes) qobj.replay.startReplay(qobj.isGhost);
    }
    public function startRecord():void {
        for each (var qobj:QuickBox3D in _qboxes) {
            if(!qobj.isGhost) qobj.replay.startRecord();
            else qobj.replay.startReplay(qobj.isGhost);
        }
    }
    
    public function tickRecord():void {
        for each (var qobj:QuickBox3D in _qboxes) qobj.tickRecord();
    }
    
    /**
     * レンダリング
     */
    public function render():void {
        var i:int, obj:Sprite3D;
        camera.update();
        _root.x = camera.center.x;
        _root.rotationX = camera.angle * Angle.toROT;
        _root.y = -camera.position.z * SCALE * S.QtoF * Math.sin(camera.angle) + camera.center.y;
        _root.z = camera.position.z * SCALE * S.QtoF * Math.cos(camera.angle) + camera.center.z;
        
        _sorts.length = 0;
        for each (obj in _objects) obj.update(this) && _sorts.push(obj);
        _sorts.sort(zsort);
        for (i = 0; i < _sorts.length; i++) _container.setChildIndex(_sorts[i].sprite, i);
        
        _container.x = _lightContainer.x = -camera.position.x * S.QtoF;
        _container.y = _lightContainer.y = -camera.position.y * S.QtoF;
        _wrapper.rotationZ = camera.rotation * Angle.toROT;
        if (!groundMap) return;
        
        //地面描画
        _ground.graphics.clear();
        var mtx:Matrix = new Matrix();
        mtx.scale(1 / S.TEXTURE, 1 / S.TEXTURE);
        mtx.translate(_container.x - 3000, _container.y - 3000);
        mtx.rotate(camera.rotation + Angle.RAD90);
        _ground.graphics.beginBitmapFill(groundMap, mtx);
        _ground.graphics.drawTriangles(Vector.<Number>([-140, -140, -140, 140, camera.ground, camera.ground, camera.ground, -camera.ground]), Vector.<int>([0, 1, 2, 2, 3, 0]));
        _ground.rotationZ = _fog.rotationZ = -camera.rotation * Angle.toROT - 90;
        
        //地平線の高さ計算
        var vx:Number = -Math.sin(camera.rotation) * camera.horizon + camera.position.x * S.QtoF;
        var vy:Number = -Math.cos(camera.rotation) * camera.horizon + camera.position.y * S.QtoF;
        _horizon = getScreenXYZ(vx, vy, 0);
        
        //背景スライド
        _sky.slide(camera.rotation % Angle.RAD360 / Angle.RAD360);
        _sky.y = _horizon.y;
    }
    
    /**
     * 地面に付いた跡を徐々に消す
     */
    public function fadeTireTrack():void {
        if (groundMap && rawMap) {
            _drawIndex = ++_drawIndex % _splitRects.length;
            groundMap.copyPixels(rawMap, _splitRects[_drawIndex], _splitRects[_drawIndex].topLeft, null, null, true);
        }
    }
    
    /**
     * 地面の画像を細かく分割した矩形を計算
     */
    public function splitRawImage():void {
        var size:int = 100, x:int = 0, y:int = 0;
        _splitRects.length = 0;
        while (1) {
            _splitRects.push(new Rectangle(x, y, Math.min(rawMap.width - x, size), Math.min(rawMap.height - y, size)));
            if ((x += size) >= rawMap.width) {
                x = 0;
                if ((y += size) >= rawMap.height) break;
            }
        }
    }
    
    private function zsort(a:Sprite3D, b:Sprite3D):int {
        var az:Number = a.point.x * Math.sin(camera.rotation) + a.point.y * Math.cos(camera.rotation);
        var bz:Number = b.point.x * Math.sin(camera.rotation) + b.point.y * Math.cos(camera.rotation);
        return int(az > bz) - int(az < bz);
    }
    
}

/**
 * 簡易3Dスプライト
 */
class Sprite3D {
    
    public var sprite:Sprite;
    public var screen:Point;
    public var parent:DisplayObjectContainer;
    public var lookAtCamera:Boolean = true;
    public var isStand:Boolean = true;
    public var isEmit:Boolean = false;
    public var point:Point = new Point();
    public var position:Vector3D = new Vector3D();
    public var radius:Number = 0;
    public var angle:Number = 0;
    public var twoSides:Boolean = true;
    public var isReverse:Boolean = false;
    public var lightEnabled:Boolean = true;
    public var ambientEnabled:Boolean = true;
    public var shredder:Shredder = new Shredder();
    
    private var _alpha:Number = 1;
    private var _direction:Point = new Point();
    private var _visible:Boolean = true;
    private var _ct:ColorTransform = new ColorTransform();
    private var _ct2:ColorTransform = new ColorTransform();
    private var _lightPer:Number = 1;
    private var _lightUpdate:int = -1;
    private var _inScreen:Boolean;
    private var _dp:Point = new Point();
    private var _sp:Point = new Point();
    private var _blend:Point = new Point();
    private var _lastBlend:Point = new Point();
    protected var _distance:Number = 0;
    
    public var onDestroy:Function;
    
    public function get visible():Boolean { return _visible; }
    public function set visible(value:Boolean):void { if (!(_visible = value) && sprite.parent) parent.removeChild(sprite); }
    public function get alpha():Number { return _alpha; }
    public function set alpha(value:Number):void { sprite.alpha = _alpha = value; }
    
    public function Sprite3D(sprite:Sprite, lookAtCamera:Boolean = true, stand:Boolean = true, x:Number = 0, y:Number = 0, z:Number = 0, angle:Number = 0, two:Boolean = true, reverse:Boolean = false) {
        setPosition(x, y, z);
        this.sprite = sprite;
        this.lookAtCamera = lookAtCamera;
        this.angle = angle * Angle.toRAD;
        this.twoSides = two;
        this.isReverse = reverse;
        updateDirection();
        var bounds:Rectangle = sprite.getRect(sprite);
        radius = Math.max(Math.abs(bounds.left), Math.abs(bounds.right)) * sprite.scaleX * S.FtoQ;
        isStand = stand;
        if (stand) sprite.rotationX = 90;
        shredder.addData(sprite);
    }
    
    public function setPosition(x:Number, y:Number, z:Number):void {
        point.x = position.x = x;
        point.y = position.y = y;
        position.z = z;
    }
    
    protected function updateDirection():void {
        _direction.x = Math.cos(angle+Angle.RAD180);
        _direction.y = Math.sin(angle+Angle.RAD180);
    }
    
    public function setSide(two:Boolean, reverse:Boolean = false):void {
        twoSides = two;
        isReverse = reverse;
    }
    
    public function getScreen():Point {
        return (sprite.parent && _inScreen)? sprite.parent.local3DToGlobal(new Vector3D(position.x * S.QtoF, position.y * S.QtoF, -position.z * S.QtoF)) : null;
    }
    
    public function update(scene:Scene3D):Boolean {
        if (!_visible) return false;
        var camera:Camera3D = scene.camera;
        var light:Light = camera.light;
        camera.offset.normalize(5 + radius);
        
        _dp.x = point.x - camera.point.x;
        _dp.y = point.y - camera.point.y;
        _sp.x = _dp.x - camera.offset.x;
        _sp.y = _dp.y - camera.offset.y;
        _distance = Point.distance(point, camera.point) * S.QtoF;
        _inScreen = VectorUtil.cross(camera.direction, _dp) < 0;
        var cross:Number = (twoSides)? 1 : VectorUtil.cross(_direction, _dp);
        if (isReverse && !twoSides) cross *= -1;
        var show:Boolean = cross > 0 && VectorUtil.cross(camera.direction, _sp) < 0 && _distance < camera.view;
        if (show) {
            var ang:Number = VectorUtil.getAngleXY( -camera.offset.x, -camera.offset.y, _dp.x, _dp.y) * Angle.toROT;
            if (ang - (radius * 500 / _distance) > 55 && _distance > (radius * 30 + 100)) show = false;
        }
        if (show) {
            //位置更新
            sprite.x = point.x * S.QtoF;
            sprite.y = point.y * S.QtoF;
            sprite.z = -position.z * S.QtoF;
            
            if (lookAtCamera) {
                sprite.rotationZ = -camera.rotation * Angle.toROT;
                if(!isStand) sprite.rotationX = -camera.angle * Angle.toROT;
            }
            else sprite.rotationZ = angle * Angle.toROT;
            
            //ライトの影響をうけるか
            if (!isEmit && lightEnabled && $.user.showLight && light.useHeadlight && ++_lightUpdate % 2 == 0) {
                var min:Number = Number.MAX_VALUE;
                var targetLight3D:Light3D = null;
                for each (var light3D:Light3D in scene.lights) {
                    var d:Number = Point.distance(light3D.point, point) - radius;
                    if (min > d) {
                        min = d;
                        targetLight3D = light3D;
                    }
                }
                _lightPer = (targetLight3D && min < targetLight3D.max)? (min - targetLight3D.min) / (targetLight3D.max - targetLight3D.min) : 1;
                if (_lightPer < 0) _lightPer = 0;
            }
            //フォグ着色
            _blend.x = (_distance - camera.view + camera.gradientArea) / camera.gradientArea;
            if (_blend.x < 0) _blend.x = 0;
            //環境光着色
            _blend.y = (!ambientEnabled || light.ambientPer == 0 || isEmit)? 0 : (($.user.showLight && light.useHeadlight)? light.ambientPer * _lightPer : 1);
            //着色量が変化した時だけ更新
            if (!_blend.equals(_lastBlend)) {
                Palette.setColorTransform(_ct, light.fog, _blend.x, _alpha);
                if (_blend.y == 1) {
                    _ct.concat(light.ambientColor);
                } else if (_blend.y > 0) {
                    Palette.setColorTransform(_ct2, light.ambient, light.ambientPer * _lightPer);
                    _ct.concat(_ct2);
                }
                
                sprite.transform.colorTransform = _ct;
            }
            _lastBlend.x = _blend.x;
            _lastBlend.y = _blend.y;
            
            if (!sprite.parent) parent.addChild(sprite);
        } else if (sprite.parent) parent.removeChild(sprite);
        return !!sprite.parent;
    }
    
    public function destroy():void {
        if (onDestroy != null) onDestroy(this);
        shredder.destroy();
    }
    
}

/**
 * パーティクルスプライト
 */
class Particle3D extends Sprite3D {
    
    private var _alphaMax:Number = 1;
    private var _scaleMin:Number = 1;
    private var _scaleMax:Number = 2;
    private var _time:int = 0;
    private var _fadeTime:int = 30;
    private var _zMin:Number = 0;
    private var _zMax:Number = 0;
    private var _active:Boolean = true;
    
    public function Particle3D(bmd:BitmapData, alpha:Number = 1, scaleMin:Number = 1, scaleMax:Number = 2, fade:int = 30, x:Number = 0, y:Number = 0, z:Number = 0, tz:Number = 0) {
        super(Create.spriteBmp(bmd, 1, -bmd.width / 2, -bmd.height / 2), true, false, x, y, z, 0, true, false);
        setParam(bmd, alpha, scaleMin, scaleMax, fade, x, y, z, tz);
        lightEnabled = false;
    }
    
    public function setParam(bmd:BitmapData, alpha:Number = 1, scaleMin:Number = 1, scaleMax:Number = 2, fade:int = 30, x:Number = 0, y:Number = 0, z:Number = 0, tz:Number = 0):void{
        var bmp:Bitmap = sprite.getChildAt(0) as Bitmap;
        bmp.bitmapData = bmd;
        bmp.x = -bmd.width / 2;
        bmp.y = -bmd.height / 2;
        setPosition(x, y, z);
        _time = 0;
        this.alpha = 0;
        _alphaMax = alpha;
        _scaleMax = scaleMax;
        _scaleMin = scaleMin;
        _fadeTime = fade;
        _zMin = z;
        _zMax = tz;
        _active = true;
    }
    
    override public function update(scene:Scene3D):Boolean {
        if (!_active) return false;
        _time++;
        var per:Number = _time / _fadeTime;
        sprite.scaleX = sprite.scaleY = _scaleMin + (_scaleMax - _scaleMin) * per;
        if ((alpha = _alphaMax * (1 - per)) <= 0) return (_active = false);
        position.z = _zMin + (_zMax - _zMin) * per;
        updateDirection();
        return super.update(scene);
    }
    
}

/**
 * 簡易3DスプライトにBox2Dを関連付けたもの
 */
class QuickBox3D extends Sprite3D {
    
    public var qobject:QuickObject;
    public var isStatic:Boolean = false;
    public var isGhost:Boolean = false;
    public var replay:ReplayData = new ReplayData();
    
    public function QuickBox3D(qobj:QuickObject, sprite:Sprite, lookAtCamera:Boolean = true, stand:Boolean = true, x:Number = 0, y:Number = 0, z:Number = 0, angle:Number = 0) {
        super(sprite, lookAtCamera, stand, x, y, z, angle);
        qobject = qobj;
        shredder.addData(qobject);
        isStatic = (qobject.params.density == 0);
        point.x = position.x = qobject.x = x;
        point.y = position.y = qobject.y = y;
    }
    
    public function addForce(v:Point):void {
        var mass:Number = qobject.body.GetMass();
        qobject.body.ApplyForce(new b2Vec2(v.x * mass, v.y * mass), qobject.body.GetWorldCenter());
    }
    
    public function tickRecord():void {
        if (!replay.isPlaying) replay.add(point, qobject.angle);
        replay.tick();
    }
    
    override public function destroy():void {
        super.destroy();
        if(!isGhost) replay.clear();
    }
    
    override public function update(scene:Scene3D):Boolean {
        if (!replay.isPlaying) {
            qobject.angle %= Angle.RAD360;
            point.x = position.x = qobject.x;
            point.y = position.y = qobject.y;
            angle = qobject.angle;
        } else if (!isStatic && replay.isValid) {
            qobject.x = point.x = position.x = replay.x;
            qobject.y = point.y = position.y = replay.y;
            qobject.angle = angle = replay.angle;
        }
        updateDirection();
        return super.update(scene);
    }
    
}

/**
 * データをまとめて破棄する
 */
class Shredder {
    
    private var _data:Array = [];
    
    public function Shredder() {
    }
    
    public function addData(...arg):void {
        _data = _data.concat(arg);
    }
    
    public function destroy():void {
        for each (var item:* in _data) {
            if (item is QuickObject) QuickObject(item).fullDestroy();
            if (item is BitmapData) BitmapData(item).dispose();
            if (item is Array) item.length = 0;
            if (item is DisplayObject && item.parent) DisplayObject(item).parent.removeChild(item);
        }
        _data.length = 0;
    }
    
}

/**
 * ワンコ
 */
class Wanco extends QuickBox3D {
    
    private var _active:Boolean = false;
    private var _target:Sprite3D;
    
    public function get active():Boolean { return _active; }
    
    public function Wanco(qobj:QuickObject, sprite:Sprite, lookAtCamera:Boolean = true, stand:Boolean = true, x:Number = 0, y:Number = 0, z:Number = 0, angle:Number = 0) {
        super(qobj, sprite, lookAtCamera, stand, x, y, z, angle);
    }
    
    public function love(target:Sprite3D):void {
        _target = target;
        _active = true;
    }
    
    override public function update(scene:Scene3D):Boolean {
        if (_target) chase();
        return super.update(scene);
    }
    
    private function chase():void {
        var p:Point = _target.point.subtract(point);
        if (p.x * p.x + p.y * p.y < 4) return;
        p.normalize(26 * qobject.body.GetMass());
        qobject.body.ApplyForce(new b2Vec2(p.x, p.y), qobject.body.GetPosition());
        qobject.body.WakeUp();
    }
    
}

/**
 * 車オブジェクト
 */
class Car extends QuickBox3D {
    
    public var mapMark:Sprite;
    public var rankMark:Sprite;
    public var light:Sprite = new Sprite();
    public var light3D:Light3D = new Light3D();
    public var lightParent:DisplayObjectContainer;
    public var autoSteer:Boolean = true;
    public var isPlayer:Boolean = false;
    
    //加速スピード
    public var accel:Number = 6;
    public var accelFirst:Number = 8;
    public var handling:Number = 0.38*1.2;
    public var driftPower:Number = 0.75;
    
    //大きいほど自動操縦時にカーブを早めに曲がる(0~1くらい)
    public var proactiveRate:Number = 2;
    public var isAlwaysLight:Boolean = false;
    
    public var prevPoint:Point = new Point();
    public var tireLprev:Point = new Point();
    public var tireRprev:Point = new Point();
    public var tireL:Point = new Point();
    public var tireR:Point = new Point();
    public var steerSpeed:Number = 0.25;
    
    private var _isWaiting:Boolean = false;
    private var _rank:int = 0;
    private var _rankLabel:Label;
    private var _speedRate:Number = 0;
    private var _dirtRate:Number = 0;
    private var _speed:Number = 0;
    private var _front:Point = new Point();
    private var _slip:Number = 0;
    private var _torque:Number = 0;
    private var _driftPer:Number = 0;
    private var _driftStart:int;
    private var _readyDrift:Boolean = false;
    private var _tireOffsetL:Point = new Point(-0.35, 0.4);
    private var _tireOffsetR:Point = new Point(0.35, 0.4);
    //周回数+周回率
    private var _lap:Number = 0;
    
    //[COM]最高難易度専用のニトロ量
    private var _nitro:int = 0;
    //[COM]次に向かうコーナー番号
    private var _targetIndex:int = 1;
    //[COM]バックするモード
    private var _backMode:Boolean = false;
    //[COM]動けないと増えていく
    private var _stress:int = 0;
    
    //リプレイ用
    private var _velocity:Point = new Point();
    
    //周回判定用
    private var _areaID:int = 1;
    private var _roundNum:int = 0;
    private var _key:uint;
    
    public function get front():Point { return _front; }
    public function get speedRate():Number { return _speedRate; }
    public function get dirtRate():Number { return _dirtRate; }
    public function get speed():Number { return _speed; }
    public function get lap():Number { return _lap; }
    public function get velocity():Point { return _velocity; }
    public function get slip():Number { return _slip; }
    public function get nitro():int { return _nitro; }
    public function get rank():int { return _rank; }
    
    
    public function Car(sim:QuickBox2D, isPlayer:Boolean = true, rgb:uint = 0xC80000, density:Number = 0.3, restitution:Number = 0.8) {
        this.isPlayer = isPlayer;
        autoSteer = !isPlayer;
        var qobj:QuickObject;
        if (isPlayer) {
            var box:QuickObject = sim.addBox( { skin:"none", density:density, restitution:restitution, friction:0.01, x:0, y:0.2, width:0.6, height:1.0 } );
            var circle:QuickObject = sim.addCircle( { skin:"none", density:density*0.425, restitution:restitution, friction:0.01, x:0, y:-0.4, radius:0.3 } );
            qobj = sim.addGroup( { objects:[box, circle] } );
        } else {
            qobj = sim.addBox( { density:density, restitution:restitution, friction:0.01, x:0, y:0.2, width:0.6, height:1.2 } );
        }
        super(qobj, qobj.userData, false, false);
        light.graphics.clear();
        Draw.circle(light.graphics, -10, -70, 20, 50, 0xFFFFFF, 1);
        Draw.circle(light.graphics, 10, -70, 20, 50, 0xFFFFFF, 1);
        mapMark = Create.box( -2, -2, 5, 5, (isPlayer)? 0xFFFF00 : 0xFF0000);
        rankMark = Create.spriteBmp(Dot.arrow, 2, -Dot.arrow.width / 2, -Dot.arrow.height - 6);
        _rankLabel = UI.createLabel(rankMark, -6, -55, "");
        shredder.addData(mapMark, rankMark, light);
    }
    
    public function finish():void {
        _roundNum = 1000;
    }
    
    override public function tickRecord():void {
        if (!replay.isPlaying) replay.add(point, qobject.angle, ! !(_key & 0x0F000), _slip > 0.8, _dirtRate > 0.2);
        replay.tick();
        if (isGhost && replay.isFinish) destroy();
    }
    
    public function setRank(num:int):void {
        _rank = num;
        _rankLabel.text = String(num);
    }
    
    public function stockPrev():void {
        prevPoint.x = point.x;
        prevPoint.y = point.y;
        tireLprev = PointUtil.rotate(_tireOffsetL, angle).add(point);
        tireRprev = PointUtil.rotate(_tireOffsetR, angle).add(point);
    }
    
    override public function update(scene:Scene3D):Boolean {
        var showBody:Boolean = super.update(scene);
        if (isGhost && replay.isWaiting) showBody = false;
        tireL = PointUtil.rotate(_tireOffsetL, angle).add(point);
        tireR = PointUtil.rotate(_tireOffsetR, angle).add(point);
        if (isGhost && _isWaiting != replay.isWaiting) {
            _isWaiting = replay.isWaiting;
            visible = !_isWaiting;
        }
        if (replay.isPlaying) {
            updateFront();
            _velocity.x = point.x - prevPoint.x;
            _velocity.y = point.y - prevPoint.y;
            _speed = _velocity.length * 30;
            updateSpeedRate();
            if (replay.isValid) {
                _slip = int(replay.isDrift);
                _dirtRate = int(replay.inDirt);
            }
        }
        var showLight:Boolean = showBody || isAlwaysLight;
        if (showLight && !light.parent) lightParent.addChild(light);
        if (!showLight && light.parent) lightParent.removeChild(light);
        if (showLight) {
            light.x = sprite.x + Math.cos(angle - Angle.RAD90) * 0;
            light.y = sprite.y + Math.sin(angle - Angle.RAD90) * 0;
            light.rotation = sprite.rotation;
        }
        light3D.point.x = point.x + _front.x * 2.5;
        light3D.point.y = point.y + _front.y * 2.5;
        return showBody;
    }
    
    override public function destroy():void {
        super.destroy();
    }
    
    public function setGhost(replay:ReplayData):void {
        _lap = -100000;
        var f:b2FilterData, objects:Array;
        if (qobject is GroupObject) objects = qobject.params.objects;
        else objects = [qobject];
        for each(var obj:QuickObject in objects) {
            f = obj.shape.GetFilterData();
            f.maskBits = f.categoryBits = 0;
            obj.shape.SetFilterData(f);
        }
        _rankLabel.visible = false;
        isGhost = true;
        this.replay = replay;
    }
    
    /**
     * 車の初期位置を指定
     * @param    x
     * @param    y
     * @param    index    次のコーナーID
     * @param    race
     */
    public function setStartPosition(x:Number, y:Number, index:int, race:Race):void {
        point.x = position.x = qobject.x = x;
        point.y = position.y = qobject.y = y;
        var vec:Point = race.course[index].subtract(new Point(x, y));
        qobject.angle = Math.atan2(vec.y, vec.x) + Angle.RAD90;
        updateFront();
        stockPrev();
        _targetIndex = index;
        if (race.lineID.length) {
            var id:int = race.lineID[index];
            var nextID:int = (id + 1) % race.checkPoints.length;
            _roundNum = _areaID =  (race.checkSide(id, this))? nextID : id;
        }
    }
    
    /**
     * 自動操縦用のキーを取得
     */
    public function getAutoKey(race:Race):uint {
        var key:uint = 0xF0000;
        var p:Point = new Point(qobject.x, qobject.y), cp:Point = race.course[_targetIndex], ofp:Point = cp.subtract(p);
        var d:Number = Point.distance(p, cp);
        //次のコーナーに近づいたらターゲットを次に移す
        if (d < 10 + _speed / 20 * 8 * proactiveRate) _targetIndex = ++_targetIndex % race.course.length;
        //次のコーナーを向くようにハンドリング調整
        if (Math.acos((ofp.x * _front.x + ofp.y * _front.y) / (ofp.length * _front.length)) > 0.13)
            key |= (ofp.x * _front.y - ofp.y * _front.x > 0)? 0x00F00 : 0x000F0;
        //一定時間止まっていたらバックに切り替える
        if (!_backMode && _speed < 2 && ++_stress > 80) _backMode = true;
        else if (_backMode && --_stress <= 0) _backMode = false;
        //バック中は進む方向とハンドリングを逆転させる
        if (_backMode) key ^= 0xFFFF0;
        else if (_speed > 4) _stress = 0;
        return key;
    }
    
    /**
     * 車にかかる力を調整
     */
    public function simulate(race:Race):void {
        
        qobject.angle = qobject.angle % Angle.RAD360;
        updateFront();
        
        if (replay.isPlaying) return;
        
        //周回判定処理
        checkLap(race);
        
        //key: [accel][break/back][left][right][empty]
        _key = (autoSteer)? getAutoKey(race) : race.key;
        
        //ドリフト
        var steering:Number = Math.abs(qobject.body.GetAngularVelocity());
        if (!_readyDrift && !autoSteer && _key & 0x00FF0 && !(_key & 0xF0000)) {
            _driftStart = $.game.getSceneTime();
            _readyDrift = true;
        } else if (_readyDrift && (_key & 0x00FF0) && (_key & 0xF0000)) {
            _readyDrift = false;
            if ($.game.getSceneTime() - _driftStart < 1000) {
                _driftPer = Math.min(1, _speedRate * steering);
            }
        }
        
        var fv0:Point = _front.clone();
        var fv:Point = _front.clone();
        var vec:b2Vec2 = qobject.body.GetLinearVelocity();
        _speed = (vec.x * fv0.x + vec.y * fv0.y) * (1 - _driftPer) + vec.Length() * _driftPer;
        fv.normalize(_speed);
        updateSpeedRate();
        
        //摩擦とブレーキ
        _dirtRate = 1 - (race.dirtMap.getPixel(S.QtoD(qobject.x), S.QtoD(qobject.y)) & 0xFF) / 0xFF;
        var isBraking:Boolean = (_key & 0xFF000) == 0xFF000 || (_speed > 0.1 && (_key & 0x0F000));
        var brakeRate:Number = isBraking? 0.99 - (1 - _speedRate) * (1 - _speedRate) * (1 - _speedRate) * 0.15 : 0.996;
        var friction:Number = brakeRate * (1 - _dirtRate * race.track.dirtFriction);
        var tslip:Number = Math.min(0.99, steering / 2.67 * _speedRate * 2.5 * (1 + _driftPer * 0.4));
        _slip += (tslip - _slip) * ((tslip > _slip)? 0.11 : 0.07);
        vec.x = (vec.x * _slip + fv.x * (1-_slip)) * friction;
        vec.y = (vec.y * _slip + fv.y * (1-_slip)) * friction;
        qobject.body.SetLinearVelocity(vec);
        
        var a:Number = (accel * _speedRate + accelFirst * (1 - _speedRate)) * ((_nitro > 0)? 1.35 : 1);
        if (_nitro > 0) _nitro--;
        fv0.normalize(qobject.body.GetMass() * a * int(!!(_key & 0xF0000)) - qobject.body.GetMass() * a / 2 * int(!!(_key & 0x0F000)));
        if (!isBraking) qobject.body.ApplyForce(new b2Vec2(fv0.x, fv0.y) , qobject.body.GetPosition());
        
        //ハンドリング制御
        var t:Number = (autoSteer? 5 : 6 * (1 - _speedRate * (1 - handling))) * (int(!!(_key & 0x000F0)) - int(!!(_key & 0x00F00))) * ((_speed > 0)? 1 : -1);
        _torque += (t - _torque) * (autoSteer? steerSpeed : 0.35);
        var trq:Number = qobject.body.GetMass() * vec.Length() / (14 + _speedRate * 43) * _torque * (1 + _driftPer * driftPower);
        qobject.body.ApplyTorque(trq);
        qobject.body.SetAngularVelocity(qobject.body.GetAngularVelocity() * 0.8);
        
        if (_driftPer > 0 && ((steering < 0.2 && _slip <= 1.5) || _speedRate < 0.5 || _dirtRate > 0.5 || isBraking)) _driftPer *= 0.8;
        if ((_driftPer -= 0.005) < 0.2) _driftPer = 0;
    }
    
    private function updateSpeedRate():void {
        var rate:Number = Math.abs(_speed) / 20;
        _speedRate = (rate > 1)? 1 : (rate < 0)? 0 : rate;
    }
    
    /**
     * 一定時間加速
     */
    public function chargeNitro():void {
        _nitro = 50;
    }
    
    //周回チェック
    private function checkLap(race:Race):void {
        if (!race.checkPoints.length || (prevPoint.x == qobject.x && prevPoint.y == qobject.y)) return;
        
        var prevID:int = (_areaID - 1 + race.checkPoints.length) % race.checkPoints.length;
        if (race.checkCross(_areaID, this) && race.checkSide(_areaID, this)) {
            _areaID = ++_areaID % race.checkPoints.length;
            _roundNum++;
        } else if (race.checkCross(prevID, this) && !race.checkSide(prevID, this)) {
            _areaID = prevID;
            _roundNum--;
        }
        _lap = (_roundNum - 1 + (10000 - race.getDistance(_areaID, this)) / 10000) / race.checkPoints.length;
    }
    
    public function updateMark():void {
        mapMark.x = S.QtoM(qobject.x);
        mapMark.y = S.QtoM(qobject.y);
        var screen:Point = (isPlayer)? null : getScreen();
        rankMark.visible = (!!screen && _distance < 700);
        if (rankMark.visible) {
            rankMark.x = screen.x | 0;
            rankMark.y = screen.y | 0;
        }
    }
    
    private function updateFront():void {
        _front.x = Math.cos(qobject.angle - Angle.RAD90);
        _front.y = Math.sin(qobject.angle - Angle.RAD90);
    }
    
}

/**
 * リプレイ管理
 */
class ReplayManager {
    
    //ゴーストカーの上限数
    private const LIMIT:int = 10;
    private var _data:Vector.<ReplayData> = new Vector.<ReplayData>();
    private var _last:ReplayData;
    
    public function get data():Vector.<ReplayData> { return _data; }
    public function get last():ReplayData { return _last; }
    
    public function ReplayManager() {
    }
    
    public function getDataByID(id:int):Vector.<ReplayData> {
        var list:Vector.<ReplayData> = new Vector.<ReplayData>();
        for (var i:int = 0; i < _data.length; i++) {
            if (_data[i].trackID == id) list.push(_data[i]);
        }
        return list;
    }
    
    public function add(replay:ReplayData):void {
        _last = replay;
        var same:Vector.<ReplayData> = new Vector.<ReplayData>();
        var other:Vector.<ReplayData> = new Vector.<ReplayData>();
        for (var i:int = 0; i < _data.length; i++) {
            if (replay.trackID == _data[i].trackID) same.push(_data[i]);
            else other.push(_data[i]);
        }
        same.push(replay);
        same.sort(function(a:ReplayData, b:ReplayData):int { return a.raceTime - b.raceTime } );
        if (same.length > LIMIT) same.length = LIMIT;
        if (other.length + same.length > LIMIT) other.length = LIMIT - same.length;
        _data = same.concat(other);
    }
    
}

/**
 * リプレイデータ
 */
class ReplayData {
    
    public var limit:int = 21600;//4data * 30frame * 60sec * 3min;
    public var data:Vector.<int> = new Vector.<int>();
    public var index:int = 0;
    public var finish:int = 0;
    public var start:int = 0;
    public var end:int = 0;
    public var timeCount:int = 0;
    public var wait:int = 0;
    public var isPlaying:Boolean = false;
    
    public var raceTime:int = 0;
    public var trackID:int;
    
    public function get x():Number { return data[index] / 1000; }
    public function get y():Number { return data[index+1] / 1000; }
    public function get angle():Number { return data[index+2] / 100; }
    public function get inDirt():Boolean { return (data[index+3] & 4) > 0; }
    public function get isBreak():Boolean { return (data[index+3] & 2) > 0; }
    public function get isDrift():Boolean { return (data[index+3] & 1) > 0; }
    public function get isValid():Boolean { return index < data.length - 3; }
    public function get isFinish():Boolean { return index == finish; }
    public function get isWaiting():Boolean { return wait > 0; }
    
    public function ReplayData() {
    }
    
    public function clone():ReplayData {
        var r:ReplayData = new ReplayData();
        for each(var node:XML in describeType(this)..variable)
            if (["int", "Boolean", "String", "Number", "uint"].indexOf(String(node.@type)) != -1)
                r[node.@name] = this[node.@name];
        r.data = data.concat();
        return r;
    }
    
    public function clear():void {
        data.length = index = finish = start = end = timeCount = wait = 0;
        isPlaying = false;
    }
    
    public function setFinish(race:Race):void {
        finish = index;
        trackID = race.track.id;
        raceTime = race.raceTime;
    }
    
    public function startRecord():void {
        clear();
        timeCount = limit / 4;
        index = 0;
    }
    
    public function stopRecord():void {
        if (data.length < limit) {
            start = 0;
            end = data.length - 1;
        } else {
            start = index;
            end = (index - 4 + limit) % limit;
        }
    }
    
    /**
     * リプレイデータを最初から再生
     * @param    isGhost    ゴーストデータかどうか
     */
    public function startReplay(isGhost:Boolean = false, offset:int = 0):void {
        index = start;
        isPlaying = true;
        wait = (isGhost)? Math.max(0, -timeCount) : 0;
        if (offset < 0) offset = 0;
        wait -= offset;
        if (wait < 0) index = (index - wait * 4) % limit;
    }
    
    public function add(point:Point, angle:Number, isBreak:Boolean = false, isDrift:Boolean = false, isDirt:Boolean = false):void {
        data[index] = (point.x * 1000) | 0;
        data[index+1] = (point.y * 1000) | 0;
        data[index + 2] = (angle * 100) | 0;
        data[index + 3] = int(isDirt) << 2 | int(isBreak) << 1 | int(isDrift);
    }
    
    public function toObject():Object {
        return { track:trackID, data:data };
    }
    
    public function tick():void {
        if (wait-- > 0) return;
        index = (index + 4) % limit;
        if (!isPlaying) timeCount--;
    }
    
}

/**
 * SiONで効果音
 */
class SE {
    //OMP_*** = [voice1, voice2, note, length]
    static public const OMP_COLLISIONS:Array = [[14, 32, 5, 1], [1, 51, 20, 1], [1, 47, 30, 1], [6, 13, 30, 3]];
    static public const OMP_OPTION:Array = [0, 13, 90, 1];
    static public const OMP_SELECT:Array = [14, 0, 50, 1];
    static public const OMP_SWITCH:Array = [14, 0, 80, 1];
    static public const OMP_CANCEL:Array = [0, 11, 50, 1.5];
    static public const OMP_BO:Array = [0, 3, 35, 0.5];
    static public const OMP_BOO:Array = [0, 3, 40, 5];
    
    static public const SND_LAP:String = "lap";
    static public const SND_NEWRECORD:String = "highscore";
    static public const SND_FINAL:String = "final";
    static public const SND_FINISH:String = "finish";
    static public const SND_COUNT:String = "count";
    static public const SND_GO:String = "go";
    
    static private var _time:int = 0;
    static private var _driver:SiONDriver = new SiONDriver();
    static private var _voices:SiONPresetVoice = new SiONPresetVoice();
    static private var _engineData:Array = [];
    static private var _isLastDirt:Boolean = false;
    static private var _isLastDrift:Boolean = false;
    static private var _isMute:Boolean = false;
    static private var _effectData:Object = { };
    
    static public function get mute():Boolean { return _isMute; }
    static public function set mute(value:Boolean):void { if(_isMute = value) stopAll(); }
    
    public function SE() {
    }
    
    static public function init():void {
        _driver.volume = 1;
        _driver.bpm = 120;
        //[voice1, voice2, min, max, old]
        _engineData.push([0, 10, 13, 30, -1]);
        _engineData.push([13, 111, 10, 27, -1]);
        _effectData[SND_COUNT] = _driver.compile("t120; %3@1 cr2;");
        _effectData[SND_GO] = _driver.compile("t120; %3@1 <cr2;");
        _effectData[SND_LAP] = _driver.compile("t120; %3@17 l24 <1q0ccc;");
        _effectData[SND_NEWRECORD] = _driver.compile("t120 %3@1 l16v10<cg;");
        _effectData[SND_FINAL] = _driver.compile("t120; %3@17 l32 <1q0cdcd;");
        _effectData[SND_FINISH] = _driver.compile("%0@1 l32 v4c1&v3c16&v2c16; %0@0v2l8 r8<3c*>c*<c32>3r8<3c*>c*<c32;");
        _driver.play();
    }
    
    static public function effect(data:String):void {
        if (_isMute) return;
        _driver.sequenceOn(_effectData[data], null, 0, 0, 0, 4);
    }
    
    static public function note(data:Array):void {
        if (_isMute) return;
        _driver.noteOn(data[2], _voices.categolies[data[0]][data[1]], data[3], 0, 0, 3);
    }
    
    static public function setVolume(value:Number):void {
        _driver.volume = value;
    }
    
    static public function update(car:Car):void {
        if (_isMute) return;
        var per:Number = car.speed / 25;
        if (per < 0) per *= -1;
        per = per > 1.5? 1.5 : per < 0? 0 : per;
        var isDirt:Boolean = car.dirtRate > 0 && per > 0.05;
        var isDrift:Boolean = car.slip > 0.8 && per > 0.05;
        if (_isLastDrift != isDrift) {
            if (isDrift) _driver.noteOn(19, _voices.categolies[5][38], 0, 0, 0, 3);
            else _driver.noteOff(19, 3);
            _isLastDrift = isDrift;
        }
        if (_isLastDirt != isDirt) {
            if (isDirt) _driver.noteOn(35, _voices.categolies[13][73], 0, 0, 0, 2);
            else _driver.noteOff(35, 2);
            _isLastDirt = isDirt;
        }
        for (var i:int = 0; i < _engineData.length; i++) {
            var data:Array = _engineData[i];
            var note:int = data[2] + (data[3] - data[2]) * per ;
            if (data[4] == note && ++_time<150) continue;
            _time = 0;
            _driver.noteOff(data[4], i);
            _driver.noteOn(note, _voices.categolies[data[0]][data[1]], 0, 0, 0, i);
            data[4] = note;
        }
    }
    
    static public function stopAll():void {
        for (var i:int = 0; i < _engineData.length; i++) {
            var data:Array = _engineData[i];
            if (data[4] != -1) _driver.noteOff(data[4], i);
            data[4] = -1;
        }
        _isLastDirt = false;
        _driver.noteOff(35, 2);
        _driver.noteOff(19, 3);
    }
    
}

/**
 * フェードエフェクト
 */
class FadeTransition {
    
    public var sprite:Sprite = new Sprite();
    private var _tween:ITween;
    private var _func:Function;
    private var _isFading:Boolean = true;
    /**フェード中か*/
    public function get isFading():Boolean { return _isFading; }
    
    public function FadeTransition() {
        sprite.visible = true;
        sprite.alpha = 1;
    }
    
    public function init():void {
        Draw.box(sprite.graphics, 0, 0, Display.width, Display.height, 0);
    }
    
    public function fadeIn(func:Function = null):void {
        if (_tween) _tween.stop();
        _tween = BetweenAS3.to(sprite, { alpha:0 }, 0.5);
        _tween.onComplete = function():void {
            sprite.visible = false;
            _isFading = false;
            if (func != null) func();
        };
        _tween.play();
    }
    
    public function fadeOut(func:Function = null):void {
        sprite.visible = true;
        _isFading = true;
        if (_tween) _tween.stop();
        _tween = BetweenAS3.to(sprite, { alpha:1 }, 0.5);
        _tween.onComplete = func;
        _tween.play();
    }
    
    public function fadeOutIn(func:Function = null):void {
        //指定のfunc処理が重いと時間が飛ぶので前後に数フレーム間を空ける
        fadeOut(function():void {
            FrameTimer.setTimer(2, function():void {
                if (func != null) func();
                FrameTimer.setTimer(2, fadeIn);
            });
        });
    }
    
}

/**
 * 選択肢
 */
class LabelMenu extends Sprite {
    
    private var _enabled:Boolean = true;
    private var _selectFunc:Function;
    private var _cancelFunc:Function;
    private var _changeFunc:Function;
    private var _switchFunc:Function;
    private var _selected:int = 0;
    private var _items:Vector.<MenuItem> = new Vector.<MenuItem>();
    private var _labels:Vector.<Array> = new Vector.<Array>();
    private var _options:Vector.<int> = new Vector.<int>();
    
    public function get items():Vector.<MenuItem> { return _items; }
    public function get options():Vector.<int> { return _options; }
    public function get selected():int { return _selected; }
    public function get enabled():Boolean { return _enabled; }
    public function set enabled(value:Boolean):void {
        _enabled = value;
        if(_enabled) Display.stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
        else Display.stage.removeEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
    }
    
    /**
     * @param    parent    addChild()する場所
     * @param    x    X座標
     * @param    y    Y座標
     * @param    texts    選択肢をラベル名の配列で指定。XXX{A,B,C}YYYの書式で{}の中のリストが左右で切り替えできる
     * @param    selectFunc    決定キーを押したら呼ばれる
     * @param    cancelFunc    キャンセルキーを押したら呼ばれる
     * @param    changeFunc    上下移動したら呼ばれる
     * @param    switchFunc    左右に切り替えたら呼ばれる
     * @param    space    選択肢の配置間隔
     */
    public function LabelMenu(parent:DisplayObjectContainer = null, x:Number = 0, y:Number = 0, texts:Array = null, selectFunc:Function = null, cancelFunc:Function = null, changeFunc:Function = null, switchFunc:Function = null, space:Number = 30) {
        if (parent) parent.addChild(this);
        this.y = y;
        _selectFunc = selectFunc;
        _cancelFunc = cancelFunc;
        _changeFunc = changeFunc;
        _switchFunc = switchFunc;
        
        var i:int, match:Array, labels:Array, item:MenuItem;
        if (!texts) texts = [];
        for (i = 0; i < texts.length; i++) {
            match = texts[i].match(/(.*)\{(.*)\}(.*)/);
            labels = [];
            if (match == null) labels = [texts[i]];
            else for each(var str:String in match[2].split(",")) labels.push(match[1] + str + match[3]);
            item = addChild(new MenuItem(x, space)) as MenuItem;
            item.index = i;
            item.setText(labels[0]);
            item.y = i * space;
            item.icon.bitmapData = (labels.length >= 2)? Dot.LR : Dot.Z;
            item.icon.y += (labels.length >= 2)? 2 : 0;
            _items.push(item);
            _labels.push(labels);
            _options.push(0);
        }
        update();
        enabled = true;
    }
    
    public function setLabelsAt(n:int, labels:Array):void {
        _labels[n] = labels;
        _items[n].setText(_labels[n][_options[n]]);
    }
    
    public function dispose():void {
        enabled = false;
        if (parent) parent.removeChild(this);
        _selectFunc = _cancelFunc = _changeFunc = _switchFunc = null;
    }
    
    private function onKeyDown(e:KeyboardEvent):void {
        if (!_enabled) return;
        switch(e.keyCode) {
            case Keyboard.UP: updown(-1); break;
            case Keyboard.DOWN: updown(1); break;
            case Keyboard.RIGHT: switchOption(1); break;
            case Keyboard.LEFT: switchOption(-1); break;
            case Keyboard.ENTER: case 90:
                if (!_items[_selected].enabled) {
                    SE.note(SE.OMP_BO);
                } else {
                    if (_selectFunc != null) _selectFunc(_selected);
                    SE.note(SE.OMP_SELECT);
                }
                break;
            case Keyboard.SHIFT: case 88:
                if (_cancelFunc != null) _cancelFunc();
                SE.note(SE.OMP_CANCEL);
                break;
        }
    }
    
    /**
     * インデックス指定でアイテムを選択
     */
    public function select(index:int):void {
        _selected = index;
        update();
    }
    
    /**
     * 左右のオプションを切り替える
     * @param    index
     * @param    option
     * @param    dispatchEvent
     */
    public function setOption(index:int, option:int, dispatchEvent:Boolean = false):void {
        _options[index] = option;
        _items[index].setText(_labels[index][option]);
        if (dispatchEvent) _switchFunc(index, option);
    }
    
    private function switchOption(delta:int):void {
        _options[_selected] = (_options[_selected] + delta + _labels[_selected].length) % _labels[_selected].length;
        _items[_selected].setText(_labels[_selected][_options[_selected]]);
        if (_labels[_selected].length >= 2) {
            SE.note(SE.OMP_OPTION);
            if (_switchFunc != null) _switchFunc(_selected, _options[_selected]);
        }
    }
    
    private function updown(delta:int):void {
        _selected = (_selected + delta + _items.length) % _items.length;
        update();
        SE.note(SE.OMP_SWITCH);
    }
    
    private function update():void {
        for each (var item:MenuItem in _items) item.setSelect(item.index == _selected);
        if (_changeFunc != null) _changeFunc(_selected);
    }
    
}

/**
 * 選択肢用アイテム
 */
class MenuItem extends Sprite {
    
    private var _label:Label;
    private var _base:Sprite;
    public var icon:Bitmap = new Bitmap();
    public var index:int = 0;
    public var enabled:Boolean = true;
    
    public function MenuItem(x:Number = 0, height:Number = 30) {
        _label = UI.createLabel(null, x, int((height - 34) / 2), "");
        _base = Create.gradientBox(0, 0, Display.width, height, true, 90, [UI.BASE, UI.DARK], [0.9, 0.9], [0, 0xFF]);
        addChild(_base);
        addChild(_label);
        addChild(icon);
        icon.x = _label.x - 25;
        icon.y = _label.y + 7;
        icon.scaleX = icon.scaleY = 2;
        icon.visible = false;
    }
    
    public function setText(str:String):void {
        _label.text = str;
    }
    
    public function setEnabled(enabled:Boolean):void {
        this.enabled = enabled;
        var rgb:uint = enabled? 0xFFFFFF : 0xDDDDDD;
        _label.transform.colorTransform = Palette.getColorTransform(rgb);
    }
    
    public function setSelect(select:Boolean):void {
        icon.visible = _base.visible = select;
        filters = (select)? [] : UI.TEXT_FILTERS;
    }
    
}

/**
 * フレームベースのタイマー
 */
class FrameTimer {
    
    static private var _sprite:Sprite = new Sprite();
    static private var _timers:Object = { };
    static private var _count:int = 0;
    
    static public function clearTimer(id:String = ""):void {
        if (id == "") _timers = { };
        else _timers[id] && delete _timers[id];
    }
    
    static public function setTimer(interval:int, complete:Function, args:Array = null, id:String = ""):void {
        if (interval <= 0) {
            complete.apply(null, args || []);
            return;
        }
        _timers[id || "_"+(++_count)] = { time:interval, complete:complete, args:(args || []) };
        _sprite.addEventListener(Event.ENTER_FRAME, onTickTimer);
    }
    
    static private function onTickTimer(e:Event = null):void {
        for (var k:String in _timers) {
            if (--_timers[k].time <= 0) {
                _timers[k].complete.apply(null, _timers[k].args);
                clearTimer(k);
            }
        }
    }
    
}

/**
 * ラジアンと通常角の変換
 */
class Angle {
    
    static public const RAD45:Number = Math.PI / 4;
    static public const RAD90:Number = Math.PI / 2;
    static public const RAD180:Number = Math.PI;
    static public const RAD360:Number = Math.PI * 2;
    static public const toRAD:Number = Math.PI / 180;
    static public const toROT:Number = 180 / Math.PI;
    
}

/**
 * 色操作
 */
class Palette {
    
    static public function mix(rgb1:uint, rgb2:uint, per:Number = 1):uint {
        var r:uint = (rgb1 >> 16 & 0xFF) * (1 - per) + (rgb2 >> 16 & 0xFF) * per;
        var g:uint = (rgb1 >> 8 & 0xFF) * (1 - per) + (rgb2 >> 8 & 0xFF) * per;
        var b:uint = (rgb1 & 0xFF) * (1 - per) + (rgb2 & 0xFF) * per;
        return r << 16 | g << 8 | b;
    }
    
    static public function getColorTransform(rgb:uint, per:Number = 1, alpha:Number = 1):ColorTransform {
        var ct:ColorTransform = new ColorTransform();
        setColorTransform(ct, rgb, per, alpha);
        return ct;
    }
    
    static public function setColorTransform(ct:ColorTransform, rgb:uint, per:Number, alpha:Number = 1):void {
        ct.redMultiplier = ct.greenMultiplier = ct.blueMultiplier = 1 - per;
        ct.redOffset = (rgb >> 16 & 0xFF) * per;
        ct.greenOffset = (rgb >> 8 & 0xFF) * per;
        ct.blueOffset = (rgb & 0xFF) * per;
        ct.alphaMultiplier = alpha;
    }
    
}

/**
 * グラフィックを生成する
 */
class Create {
    
    static public function gradientLabel(sp:Sprite, rgb1:uint, rgb2:uint, start:uint = 0x0, end:uint = 0xFF):Bitmap {
        var mask:BitmapData = new BitmapData(sp.width, sp.height, false, 0xFF000000);
        Display.cacheQuality(StageQuality.BEST);
        mask.draw(sp, null, Palette.getColorTransform(0xFFFFFF));
        var bmd:BitmapData = new BitmapData(mask.width, mask.height, true, 0);
        bmd.draw(Create.gradientBox(0, 0, bmd.width, bmd.height, true, 90, [rgb1, rgb2], [1, 1], [start, end]));
        bmd.copyChannel(mask, mask.rect, PointUtil.ZERO, BitmapDataChannel.BLUE, BitmapDataChannel.ALPHA);
        bmd.applyFilter(bmd, bmd.rect, PointUtil.ZERO, new DropShadowFilter(1, 45, 0x0, 4, 4, 4, 60, 1));
        mask.dispose();
        Display.restoreQuality();
        return new Bitmap(bmd);
    }
    
    static public function circle(x:Number, y:Number, width:Number, height:Number, rgb:uint = 0x000000, alpha:Number = 1):Sprite {
        var sp:Sprite = new Sprite();
        Draw.circle(sp.graphics, x, y, width, height, rgb, alpha);
        return sp;
    }
    
    static public function gradientCircle(x:Number, y:Number, width:Number, height:Number, rgbs:Array, alphas:Array, ratios:Array):Sprite {
        var sp:Sprite = new Sprite();
        Draw.gradientCircle(sp.graphics, x, y, width, height, rgbs, alphas, ratios);
        return sp;
    }
    
    static public function box(x:Number, y:Number, width:Number, height:Number, rgb:uint, alpha:Number = 1, tx:Number = 0, ty:Number = 0):Sprite {
        var sp:Sprite = new Sprite();
        Draw.box(sp.graphics, x, y, width, height, rgb, alpha);
        sp.x = tx;
        sp.y = ty;
        return sp;
    }
    
    static public function gradientBox(x:Number, y:Number, width:Number, height:Number, isLinear:Boolean, rotation:Number, rgbs:Array, alphas:Array, ratios:Array):Sprite {
        var sp:Sprite = new Sprite();
        Draw.gradientBox(sp.graphics, x, y, width, height, isLinear, rotation, rgbs, alphas, ratios);
        return sp;
    }
    
    static public function spriteBmp(bmd:BitmapData, scale:Number = 1, tx:Number = 0, ty:Number = 0, x:Number = 0, y:Number = 0):Sprite {
        var sp:Sprite = new Sprite();
        sp.addChild(bitmap(bmd, scale, tx, ty));
        sp.x = x;
        sp.y = y;
        return sp;
    }
    
    static public function cloud(width:Number, height:Number, seed:int, rgb1:uint, rgb2:uint):BitmapData {
        var bmd:BitmapData = new BitmapData(width, height, true, 0);
        for (var i:int = 0; i < 3; i++) bmd.draw(smoke(width, height, seed, 0.7 - i * 0.2, Palette.mix(rgb1, rgb2, 1 - i / 2)));
        return bmd;
    }
    
    static public function smoke(width:Number, height:Number, seed:int, per:Number, rgb:uint):BitmapData {
        var bmd:BitmapData = new BitmapData(width, height, true, 0);
        bmd.perlinNoise(width/4, height/4, 3, seed, false, true, BitmapDataChannel.RED, false);
        bmd.draw(new Bitmap(bmd), null, null, BlendMode.OVERLAY);
        bmd.draw(gradientBox(0, 0, width, height, false, 0, [0x000000, 0x000000], [1, 0], [0x00, 0xFF]), null, null, BlendMode.OVERLAY);
        bmd.draw(gradientBox(0, 0, width, height, false, 0, [0xFF0000, 0xFF0000], [0, 1], [0x80, 0xFF]));
        bmd.threshold(bmd, bmd.rect, PointUtil.ZERO, ">", 0xFF000000 | per*0xFF<<16, 0);
        bmd.colorTransform(bmd.rect, new ColorTransform(0, 0, 0, 1, rgb>>16&0xFF, rgb>>8&0xFF, rgb&0xFF));
        return bmd;
    }
    
    static public function bitmapFillBox(x:Number, y:Number, width:Number, height:Number, bmd:BitmapData, scaleX:Number = 1, scaleY:Number = 1, tx:Number = 0, ty:Number = 0):Sprite {
        var sp:Sprite = new Sprite();
        Draw.bitmapFillBox(sp.graphics, x, y, width, height, bmd, scaleX, scaleY, tx, ty);
        return sp;
    }
    
    static public function bitmap(bmd:BitmapData, scale:Number = 1, x:Number = 0, y:Number = 0):Bitmap {
        var bmp:Bitmap = new Bitmap(bmd);
        bmp.scaleX = bmp.scaleY = scale;
        bmp.x = x * scale;
        bmp.y = y * scale;
        return bmp;
    }
    
}

/**
 * グラフィックを描画する
 */
class Draw {
    
    static public function bitmapFillBox(g:Graphics, x:Number, y:Number, width:Number, height:Number, bmd:BitmapData, scaleX:Number = 1, scaleY:Number = 1, tx:Number = 0, ty:Number = 0):void {
        g.beginBitmapFill(bmd, new Matrix(scaleX, 0, 0, scaleY, tx, ty), true);
        g.drawRect(x, y, width, height);
        g.endFill();
    }
    
    static public function box(g:Graphics, x:Number, y:Number, width:Number, height:Number, rgb:uint, alpha:Number = 1):void {
        g.beginFill(rgb, alpha);
        g.drawRect(x, y, width, height);
        g.endFill();
    }
    
    static public function circle(g:Graphics, x:Number, y:Number, width:Number, height:Number, rgb:uint = 0x000000, alpha:Number = 1):void {
        g.beginFill(rgb, alpha);
        g.drawEllipse(x - width, y - height, width * 2, height * 2);
        g.endFill();
    }
    
    static public function gradientBox(g:Graphics, x:Number, y:Number, width:Number, height:Number, isLinear:Boolean, rotation:Number, rgbs:Array, alphas:Array, ratios:Array):void {
        var mtx:Matrix = new Matrix();
        mtx.createGradientBox(width, height, rotation * Angle.toRAD, x, y);
        g.beginGradientFill(isLinear? GradientType.LINEAR : GradientType.RADIAL, rgbs, alphas, ratios, mtx);
        g.drawRect(x, y, width, height);
        g.endFill();
    }
    
    static public function gradientCircle(g:Graphics, x:Number, y:Number, width:Number, height:Number, rgbs:Array, alphas:Array, ratios:Array):void {
        var mtx:Matrix = new Matrix();
        mtx.createGradientBox(width*2, height*2, 0, x - width, y - height);
        g.beginGradientFill(GradientType.RADIAL, rgbs, alphas, ratios, mtx);
        g.drawEllipse(x - width, y - height, width * 2, height * 2);
        g.endFill();
    }
    
}

/**
 * 2D座標操作
 */
class PointUtil {
    
    static public const ZERO:Point = new Point();
    
    static public function rotate(p:Point, radian:Number):Point {
        var np:Point = new Point();
        np.x = p.x * Math.cos(radian) - p.y * Math.sin(radian);
        np.y = p.x * Math.sin(radian) + p.y * Math.cos(radian);
        return np;
    }
    
}

/**
 * ピクセル操作
 */
class Pixel {
    
    static public function drawDarkLine(bmd:BitmapData, x1:int, y1:int, x2:int, y2:int, per:Number, skipFirst:Boolean = false):void {
        var dx:int = (x2 > x1)? x2 - x1 : x1 - x2;
        var dy:int = (y2 > y1)? y2 - y1 : y1 - y2;
        var tx:int = (x2 > x1)? 1 : -1
        var ty:int = (y2 > y1)? 1 : -1;
        var e:int, i:int, x:int = x1, y:int = y1;
        if (dx >= dy) {
            e = 2 * dy - dx;
            for (i = 0; i <= dx; i++) {
                if (!skipFirst || i) setDark(bmd, x, y, per);
                x += tx;
                e += 2*dy;
                if (e >= 0) {
                    y += ty;
                    e -= 2 * dx;
                }
            }
        } else {
            e = 2 * dx - dy;
            for (i = 0; i <= dy; i++) {
                if(!skipFirst || i)setDark(bmd, x, y, per);
                y += ty;
                e += 2 * dx;
                if (e >= 0) {
                    x += tx;
                    e -= 2 * dy;
                }
            }
        }
    }
    
    static public function setDark(bmd:BitmapData, x:int, y:int, alpha:Number):void {
        var rgb:uint = bmd.getPixel(x, y);
        rgb = (rgb >> 16 & 0xFF) * alpha << 16 | (rgb >> 8 & 0xFF) * alpha << 8 | (rgb & 0xFF) * alpha;
        bmd.setPixel(x, y, rgb);
    }
    
}

/**
 * Vector3D操作
 */
class Vector3DUtil {
    
    static private var _fromN:Vector3D = new Vector3D;
    static private var _toN:Vector3D = new Vector3D;
    
    static public function slerpVector(from:Vector3D, to:Vector3D, t:Number):Vector3D {
        if (!from.lengthSquared || !from.lengthSquared || from.equals(to)) return from.clone();
        copy(from, _fromN);
        copy(to, _toN);
        _fromN.normalize();
        _toN.normalize();
        var angle:Number = getAngleUnit(_fromN, _toN);
        if (angle == Math.PI) {
            _fromN.x += 0.000001;
            if (!_fromN.y && !_fromN.z) _fromN.y += 0.000001;
            _fromN.normalize();
            angle = getAngleUnit(_fromN, _toN);
        }
        var sin:Number = Math.sin(angle);
        if (!sin) return _fromN.clone();
        var sinf:Number = Math.sin(angle * (1 - t));
        var sint:Number = Math.sin(angle * t);
        var v:Vector3D = new Vector3D();
        if (sin) {
            v.x = (_fromN.x * sinf + _toN.x * sint) / sin;
            v.y = (_fromN.y * sinf + _toN.y * sint) / sin;
            v.z = (_fromN.z * sinf + _toN.z * sint) / sin;
        }
        v.normalize();
        return v;
    }
    
    static public function setXYZ(v:Vector3D, x:Number, y:Number, z:Number):void {
        v.x = x;
        v.y = y;
        v.z = z;
    }
    
    static public function mix(a:Vector3D, b:Vector3D, per:Number = 0):Vector3D {
        return new Vector3D(a.x * (1 - per) + b.x * per, a.y * (1 - per) + b.y * per, a.z * (1 - per) + b.z * per);
    }
    
    static public function cross(a:Vector3D, b:Vector3D):Vector3D {
        return new Vector3D((a.y * b.z) - (a.z * b.y), (a.z * b.x) - (a.x * b.z), (a.x * b.y) - (a.y * b.x));
    }
    
    static public function copy(from:Vector3D, to:Vector3D):void {
        to.x = from.x;
        to.y = from.y;
        to.z = from.z;
        to.w = from.w;
    }
    
    static public function getAngle(a:Vector3D, b:Vector3D):Number {
        var dot:Number = (a.x * b.x + a.y * b.y + a.z * b.z) / (a.length * b.length);
        return Math.acos(dot > 1? 1 : dot < -1? -1 : dot);
    }
    
    static public function getAngleUnit(a:Vector3D, b:Vector3D):Number {
        var dot:Number = (a.x * b.x + a.y * b.y + a.z * b.z);
        return Math.acos(dot > 1? 1 : dot < -1? -1 : dot);
    }
    
}

/**
 * 2Dベクトル操作
 */
class VectorUtil {
    
    static public function dot(a:Point, b:Point):Number {
        return dotXY(a.x, a.y, b.x, b.y);
    }
    
    static public function cross(a:Point, b:Point):Number {
        return crossXY(a.x, a.y, b.x, b.y);
    }
    
    /**
     * 2つのベクトルの角度をXYで求める
     */
    static public function getAngleXY(ax:Number, ay:Number, bx:Number, by:Number):Number {
        var cos:Number = (ax * bx + ay * by) / (Math.sqrt(ax * ax + ay * ay) * Math.sqrt(bx * bx + by * by));
        return Math.acos(cos > 1? 1 : (cos < -1? -1 : cos));
    }
    
    /**
     * 2つのベクトルの内積をXYで求める
     * >1に近いほど同方向に平行
     * >-1に近いほど逆向き平行
     * >0に近いほど直角
     */
    static public function dotXY(ax:Number, ay:Number, bx:Number, by:Number):Number {
        return ax * bx + ay * by;
    }
    
    /**
     * 2つのベクトルの外積をXYで求める
     * >結果の符号でベクトルAに対してベクトルBが左右どちらにあるか判定できる
     * >結果が0なら2つのベクトルは平行
     */
    static public function crossXY(ax:Number, ay:Number, bx:Number, by:Number):Number {
        return ax * by - ay * bx;
    }
    
}

/**
 * 直線処理
 */
class LineUtil {
    
    /**
     * 2線分の交点をPointで求める
     */
    static public function crossSegment(a1:Point, a2:Point, b1:Point, b2:Point):Point {
        if (!isCross.apply(null, arguments)) return null;
        else return crossLine.apply(null, arguments);
    }
    
    /**
     * 2直線の交点をPointで求める
     */
    static public function crossLine(a1:Point, a2:Point, b1:Point, b2:Point):Point {
        return crossLineXY(a1.x, a1.y, a2.x, a2.y, b1.x, b1.y, b2.x, b2.y);
    }
    /**
     * 2直線の交点をXYで求める
     */
    static public function crossLineXY(ax1:Number, ay1:Number, ax2:Number, ay2:Number, bx1:Number, by1:Number, bx2:Number, by2:Number):Point {
        var v1x:Number = ax2 - ax1, v1y:Number = ay2 - ay1;
        var v2x:Number = bx1 - ax1, v2y:Number = by1 - ay1;
        var v3x:Number = bx2 - bx1, v3y:Number = by2 - by1;
        var cross:Number = VectorUtil.crossXY(v3x, v3y, v1x, v1y);
        if (!cross) return null;
        var scale:Number = VectorUtil.crossXY(v3x, v3y, v2x, v2y) / cross;
        return new Point(v1x * scale + ax1, v1y * scale + ay1);
    }
    
    /**
     * 2線分が交差しているかPointで調べる(先端が接触していても交差とみなす)
     */
    static public function isCross(a1:Point, a2:Point, b1:Point, b2:Point):Boolean {
        return isCrossXY(a1.x, a1.y, a2.x, a2.y, b1.x, b1.y, b2.x, b2.y);
    }
    
    /**
     * 2線分が交差しているかXYで調べる(先端が接触していても交差とみなす)
     */
    static public function isCrossXY(ax1:Number, ay1:Number, ax2:Number, ay2:Number, bx1:Number, by1:Number, bx2:Number, by2:Number):Boolean {
        if (((ax1 - ax2) * (by1 - ay1) + (ay1 - ay2) * (ax1 - bx1)) * ((ax1 - ax2) * (by2 - ay1) + (ay1 - ay2) * (ax1 - bx2)) > 0) return false;
        if (((bx1 - bx2) * (ay1 - by1) + (by1 - by2) * (bx1 - ax1)) * ((bx1 - bx2) * (ay2 - by1) + (by1 - by2) * (bx1 - ax2)) > 0) return false;
        return true;
    }
    
    /**
     * 2点間の距離をXYで求める
     */
    static public function getDistanceXY(ax:Number, ay:Number, bx:Number, by:Number):Number {
        var dx:Number = ax - bx;
        var dy:Number = ay - by;
        return Math.sqrt(dx * dx + dy * dy);
    }
    
    /**
     * 1つの点と線分の最短距離をPointで求める
     */
    static public function getNearDistance(a:Point, b:Point, p:Point):Number {
        return getNearDistanceXY(a.x, a.y, b.x, b.y, p.x, p.y);
    }
    
    /**
     * 1つの点と線分の最短距離をXYで求める
     */
    static public function getNearDistanceXY(x1:Number, y1:Number, x2:Number, y2:Number, px:Number, py:Number):Number {
        var dx:Number = x2 - x1, dy:Number = y2 - y1;
        var t:Number = -(dx * (x1 - px) + dy * (y1 - py)) / (dx * dx + dy * dy);
        t = (t < 0)? 0 : (t > 1)? 1 : t;
        var tx:Number = x1 + dx * t;
        var ty:Number = y1 + dy * t;
        return Math.sqrt((px - tx) * (px - tx) + (py - ty) * (py - ty));
    }
}

/**
 * 曲線処理
 */
class CurveUtil {
        
    public function CurveUtil() {
    }
    
    static public function getBezierListXY(ax:Number, ay:Number, bx:Number, by:Number, cax:Number, cay:Number, cbx:Number, cby:Number, segments:int = 1):Vector.<Point> {
        var points:Vector.<Point> = new Vector.<Point>();
        for (var i:int = 0; i < segments + 1; i++) points.push(getBezierXY(ax, ay, bx, by, cax, cay, cbx, cby, i / segments));
        return points;
    }
    
    static public function getBezierList(a:Point, b:Point, ca:Point, cb:Point, segments:int = 1):Vector.<Point> {
        return getBezierListXY(a.x, a.y, b.x, b.y, ca.x, ca.y, cb.x, cb.y, segments);
    }
    
    static public function drawBezierXY(graphics:Graphics, ax:Number, ay:Number, bx:Number, by:Number, cax:Number, cay:Number, cbx:Number, cby:Number, segments:int = 1, scale:Number = 1):void {
        var points:Vector.<Point> = getBezierListXY(ax, ay, bx, by, ax + cax, ay + cay, bx + cbx, by + cby, segments);
        for (var i:int = 0; i < points.length; i++) {
            if (!i) graphics.moveTo(points[i].x * scale, points[i].y * scale);
            else graphics.lineTo(points[i].x * scale, points[i].y * scale);
        }
    }
    
    static public function drawBezier(graphics:Graphics, a:Point, b:Point, ca:Point, cb:Point, segments:int = 1, scale:Number = 1):void {
        drawBezierXY(graphics, a.x, a.y, b.x, b.y, ca.x, ca.y, cb.x, cb.y, segments, scale);
    }
    
    static public function getBezierXY(ax:Number, ay:Number, bx:Number, by:Number, cax:Number, cay:Number, cbx:Number, cby:Number, t:Number):Point {
        var t2:Number = 1 - t;
        var v1:Number = t2 * t2 * t2;
        var v2:Number = 3 * t * t2 * t2;
        var v3:Number = 3 * t * t * t2;
        var v4:Number = t * t * t;
        return new Point(v1 * ax + v2 * cax + v3 * cbx + v4 * bx, v1 * ay + v2 * cay + v3 * cby + v4 * by);
    }
    
    static public function getBezier(a:Point, b:Point, ca:Point, cb:Point, t:Number):Point {
        return getBezierXY(a.x, a.y, b.x, b.y, ca.x, ca.y, cb.x, cb.y, t);
    }
    
}

/**
 * bkzenさんのをお借りしました
 * @see    http://wonderfl.net/c/cuY4
 * @see    http://wonderfl.net/c/kYyY
 */
class ScoreWindow {
    
    public static var sprite:Sprite = new Sprite();
    public static var title:String = "Test";
    private static var _api:WonderflAPI;
    private static var _content:Object;
    private static const SWF:String = "http://swf.wonderfl.net/swf/usercode/5/57/579a/579a46e1306b5770d429a3738349291f05fec4f3.swf";
    
    public static function init(loaderInfo:LoaderInfo, complete:Function = null, error:Function = null): void {
        _api = new WonderflAPI(loaderInfo.parameters);
        var loader:Loader = new Loader();
        var removeEvent:Function = function():void {
            loader.contentLoaderInfo.removeEventListener(Event.COMPLETE, compFunc);
            loader.contentLoaderInfo.removeEventListener(IOErrorEvent.IO_ERROR, errorFunc);
            loader.contentLoaderInfo.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, errorFunc);
        }
        var errorFunc:Function = function(e:Event): void {
            removeEvent();
            if(error != null) error();
        }
        var compFunc:Function = function(e:Event): void{
            removeEvent();
            _content = loader.content;
            if(complete != null) complete();
        }
        loader.contentLoaderInfo.addEventListener(Event.COMPLETE, compFunc);
        loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, errorFunc);
        loader.contentLoaderInfo.addEventListener(SecurityErrorEvent.SECURITY_ERROR, errorFunc);
        loader.load(new URLRequest(SWF), new LoaderContext(true));
    }
    
    /**
     * スコア登録ウィンドウを開く
     * @param    score    登録するスコア
     * @param    close    閉じられた時呼ばれる
     * @param    scoreTitle    スコアの種類を決める名前
     * @param    unit    スコアの後ろにつけるテキスト
     * @param    decimal    小数点を何桁まで使うか
     * @param    limit    スコア表示件数
     * @param    reverse    trueでスコアが小さい方が上位になる
     * @param    modal    モーダル処理
     */
    public static function show(score:int, close:Function = null, scoreTitle:String = "Score", unit:String = "", decimal:int = 0, limit:int = 99, reverse:Boolean = false, modal:Boolean = true):void {
        var tweet:String = "Playing " + title + " [" + scoreTitle + ": %SCORE%" + unit + "] #wonderfl";
        var win:DisplayObject = _content.makeScoreWindow(_api, score, title, Math.pow(10, decimal), tweet, scoreTitle, unit, limit, int(!reverse));
        var closeFunc:Function = function(e: Event):void {
            win.removeEventListener(Event.CLOSE, closeFunc);
            if(close != null) close();
        }
        win.addEventListener(Event.CLOSE, closeFunc);
        sprite.addChild(win);
    }
    
}