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

MojaGrandPrix(play with WebCam)

stolen from @makc3d, see http://makc3d.wordpress.com/2010/10/08/hough-transform-part-2/

/* How to play */
Get a pen, ruler, any banknote, etc. Steer it in front of webcam. see http://nullurban.appspot.com/moja-HOW-TO.swf


[T] - show estimator (for adjustment)
[W] - show/hide wheel
[UP/W] - auto acceleration
[Down/S] - auto reverse
Get Adobe Flash player
by zob 25 Feb 2011
  • Forked from tencho's Moja Grand Prix
  • Diff: 500
  • Related works: 5
  • Talk

    makc3d at 25 Feb 2011 15:42
    you could significantly reduce the code here, for example you dont need local maximas, only global one. also, could be good idea to filter vertical angles.
    makc3d at 25 Feb 2011 15:43
    also #2 you do not need second kernel, as far as you only use angle.
    zob at 28 Feb 2011 10:24
    hmm... i think it's a pity to remove codes from it(such a genius work), so we could still see most of the details while viewing the estimator

    Tags

    Embed
/**
 * Copyright zob ( http://wonderfl.net/user/zob )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/dOOl
 */

/**
 * Copyright tencho ( http://wonderfl.net/user/tencho )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/t5IZ
 */

/**
 * [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;
        private var de:DistanceEstimator;
        private var wheel:Wheel = new Wheel(stage);
        
        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 = 78;
            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(de = new DistanceEstimator(wheel));
            $.user = new UserData();
            $.selector = new TrackSelector();
            $.world = new World();
            $.top = new TopMenu();
            $.setting = new SettingMenu();
            $.transition = new FadeTransition();
            $.tape = new TickerTape();
            $.complete = new Complete();
            
            $.user.setDefaultData();
            $.user.load();
            
            Dot.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($.game.display.backSprite);
            addChild($.top.sprite);
            addChild($.setting.sprite);
            addChild($.complete.sprite);
            addChild($.howto.sprite);
            addChild($.tape.sprite);
            addChild(ScoreWindow.sprite);
            addChild($.transition.sprite);
            addChild(wheel);
            
            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 71://G
                    $.user.showGhost = !$.user.showGhost;
                    $.game.updateGhostVisible();
                    $.setting.update();
                    $.user.save();
                    break;
                case 83://S
                    $.user.mute = SE.mute = !SE.mute;
                    $.setting.update();
                    $.user.save();
                    break;
                case 84://T
                    de.visible = !de.visible;
                    if(de.visible)
                        addChild(de.show());
                    else
                        removeChild(de.hide());
                    break;
                case 87://W
                    wheel.visible = !wheel.visible;
                    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.PushButton;
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 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.NIGHT        ,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 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}", "Ghost Car(G): {OFF,ON}"];
        _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.showGhost = !!_menu.options[6];
        $.user.groundQuality = _menu.options[5];
        $.user.save();
        $.game.updateDisplay();
        if (index == 5) $.game.updateGround();
        if (index == 6) $.game.updateGhostVisible();
        $.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);
        _menu.setOption(6, int($.user.showGhost));
    }
    
}

/**
 * タイトル画面
 */
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: $.game.display.showBack(Text.LABEL_BACK); break;
            default: $.game.display.hideBack();
        }
        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 showGhost: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;
        showGhost = 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();
    private var _continue:PushButton;
    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);
        _continue = new PushButton(sprite, 182, 222, "CONTINUE", showMenu);
        _continue.visible = false;
        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;
        FrameTimer.setTimer(5, function():void {
            _continue.visible = true;
        }, null, "continue");
        ScoreWindow.show(race.raceTime, showMenu, "Time", "sec", 3, 99, true, true);
    }
    private function showMenu(...arg):void {
        FrameTimer.clearTimer("continue");
        _continue.visible = false;
        $.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 {
        $.game.display.showBack(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();
    public var backSprite:Sprite = new Sprite();
    
    private var _backLabel:Label;
    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;
        
        backSprite.addChild(Create.spriteBmp(Dot.X, 2));
        backSprite.x = Display.width - 80;
        backSprite.y = 9;
        _backLabel = UI.createLabel(backSprite, 26, -6, "");
        hideBack();
    }
    
    public function hideBack():void {
        backSprite.visible = false;
    }
    
    public function showBack(text:String = ""):void {
        if (text) _backLabel.text = text;
        backSprite.visible = true;
    }
    
    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;
    private var _distanceEstimator:DistanceEstimator;
    
    /**シーンの経過フレーム時間*/
    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(de:DistanceEstimator = null) {
        _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);
        _distanceEstimator = de;
    }
    /*
    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);
        
        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];
        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);
        }
        updateGhostVisible();
    }
    
    public function updateGhostVisible():void {
        for each(var c:Car in _scene.cars) {
            if (c.isGhost) c.ghostVisible = $.user.showGhost;
        }
    }
    
    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, _distanceEstimator);
        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 {
        $.game.display.hideBack();
        createCars();
        _race.key = _keyUD = 0;
        _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;
        }
        if(isDown)
        {
            _race.reverse = !(_keyUD & 0xFF00);
            _race.reverse = (_keyUD & 0x00FF);
        }
        _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;
        }
        _race.key = _race.reverse?(_race.key | 0x0F000):(_race.key | 0xF0000);
        /*cheat*/
        
        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 {
        $.game.display.showBack(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 var reverse: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 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 TWILIGHT:int = 4;
    static public const LIST:Array = [DAY, SUNSET, NIGHT, DAWN, TWILIGHT];
    static public const LABELS:Array = ["DAY", "SUNSET", "NIGHT", "DAWN", "TWILIGHT"];
    
    public function Light(type:int = DAY) {
        setTime(type);
    }
    
    public function setTime(type:int):void {
        switch(type) {
            case DAY:
                isLightUp = useHeadlight = hasStars = false;
                sky = 0x3172D6;
                horizon = 0x5D9DF8;
                fog = 0x7BBDFF;
                cloud = 0xFFFFFF;
                ambient = 0xFFFFFF;
                ambientPer = 0;
                break;
            case SUNSET:
                isLightUp = true;
                useHeadlight = hasStars = false;
                sky = 0x8F8898;
                horizon = 0xD9B07F;
                fog = 0xE5D065;
                cloud = 0x604407;
                ambient = 0x74290F;
                ambientPer = 0.5;
                break;
            case NIGHT:
                isLightUp = useHeadlight = hasStars = true;
                sky = 0x051549;
                horizon = 0x2950B7;
                fog = 0x317BDE;
                cloud = 0x061534;
                ambient = 0x051549;
                ambientPer = 0.6;
                break;
            case DAWN:
                isLightUp = false;
                useHeadlight = hasStars = true;
                sky = 0x7B94AA;
                horizon = 0xB7BDBB;
                fog = 0xDDCFC1;
                cloud = 0x7C7A79;
                ambient = 0x1E4068;
                ambientPer = 0.6;
                break;
            case TWILIGHT:
                isLightUp = true;
                useHeadlight = hasStars = false;
                sky = 0x856289;
                horizon = 0xC992A5;
                fog = 0xF3BEB2;
                cloud = 0x856289;
                ambient = 0x43345B;
                ambientPer = 0.5;
                break;
        }
        ambientColor = Palette.getColorTransform(ambient, ambientPer);
    }
    
}

/**
 * カメラ
 */
class Camera3D {
    
    public var light:Light = new Light();
    public var horizon:Number = 2000;
    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);
            if (obj.sprite.parent) _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 _inDisplay:Boolean = true;
    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 get inDisplay():Boolean { return _inDisplay; }
    
    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):void {
        if (!_visible) return;
        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;
        }
        _inDisplay = show;
        if (_inDisplay) {
            //位置更新
            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 (_inDisplay) {
            if (!sprite.parent) parent.addChild(sprite);
        } else if (sprite.parent) parent.removeChild(sprite);
    }
    
    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):void {
        if (!_active) return;
        _time++;
        var per:Number = _time / _fadeTime;
        sprite.scaleX = sprite.scaleY = _scaleMin + (_scaleMax - _scaleMin) * per;
        if ((alpha = _alphaMax * (1 - per)) <= 0) {
            _active = false;
            return;
        }
        position.z = _zMin + (_zMax - _zMin) * per;
        updateDirection();
        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):void {
        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();
        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):void {
        if (_target) chase();
        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 ghostVisible:Boolean = true;
    
    //加速スピード
    public var accel:Number = 4;
    public var accelFirst:Number = 6;
    public var handling:Number = 0.38;
    public var driftPower:Number = 0.75;
    
    //大きいほど自動操縦時にカーブを早めに曲がる(0~1くらい)
    public var proactiveRate:Number = 1;
    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 _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;
    
    private var _distanceEstimator:DistanceEstimator;
    
    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, de:DistanceEstimator = null) {
        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);
        _distanceEstimator = de;
    }
    
    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):void {
        super.update(scene);
        tireL = PointUtil.rotate(_tireOffsetL, angle).add(point);
        tireR = PointUtil.rotate(_tireOffsetR, angle).add(point);
        
        var v:Boolean = !(isGhost && (replay.isWaiting || !ghostVisible));
        if (visible != v) visible = v;
        
        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 = inDisplay || 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;
    }
    
    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][brake/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);
        if(autoSteer)
            _torque += (t - _torque) * (autoSteer? steerSpeed : 0.35);
        else
            _torque += ((7*(_distanceEstimator.currentAngle/Math.PI-0.5)*2) - _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 = _speed / 20;
        if (rate < 0) rate *= -1;
        _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 isBrake():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, isBrake: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(isBrake) << 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? 1 : 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);
    }
    
}


import com.bit101.components.CheckBox;
import com.bit101.components.HSlider;
import com.bit101.components.Label;
import com.bit101.components.PushButton;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.IBitmapDrawable;
import flash.display.Loader;
import flash.display.Shader;
import flash.display.Shape;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.filters.BlurFilter;
import flash.filters.ShaderFilter;
import flash.geom.Matrix;
import flash.geom.Rectangle;
import flash.media.Camera;
import flash.media.Video;
import flash.net.FileFilter;
import flash.net.FileReference;
import flash.utils.clearTimeout;
import flash.utils.getTimer;
import flash.utils.setTimeout;
//import net.hires.debug.Stats;

class DistanceEstimator extends Sprite {
    public var video:Video;
    public var source:IBitmapDrawable;
    public var file:FileReference;
    public var loader:Loader;
    public var timeout:uint = 0;
    public var resized:BitmapData;
    public var input:BitmapData;
    public var output:BitmapData;
    public var output2:BitmapData;
    public var bfilter:BlurFilter;
    public var sfilter:ShaderFilter;
    public var dfilter:ShaderFilter;
    public var kslider:HSlider;
    public var kslider2:HSlider;
    public var info:Label;
    public var info2:Label;
    public var mainAngle:CheckBox;
    public var lines:Shape;
    public var currentAngle:Number = 0;
    public var wheel:Wheel;
    public var bmp:Bitmap;
    public var ibmp:Bitmap;
    public var obmp:Bitmap;
    public var obmp2:Bitmap;
    public var panel:Sprite = new Sprite();
    public var g:Sprite = new Sprite();
    public function DistanceEstimator (w:Wheel, cw:Number = 320, ch:Number = 240, fr:Number = 30) {
        addChild(new Bitmap(new BitmapData(465,465,true,0x88FFFFFF)));
        this.visible = false;
        wheel = w;
        //stage.scaleMode = "noScale";
        // for lines to draw fast
        //stage.quality = "low";
        // set up webcam feed
        //stage.frameRate = 30;
        var cam:Camera = Camera.getCamera ();
        if(!cam) return;
        cam.setMode (cw, ch, fr);
        source = video = new Video; video.attachCamera (cam);
        video.x = cw; video.scaleX = -1;
        //addChild (video);
        // set up file feed
        //file = new FileReference;
        //file.addEventListener (Event.SELECT, onFileSelected);
        //file.addEventListener (Event.COMPLETE, onFileLoaded);
        // set up images
        bmp = new Bitmap (resized = new BitmapData (cw, ch, true, 0));
        with (bmp) {
            x = cw; scaleX = -1;
        }
        /**/
        ibmp = new Bitmap (input = new BitmapData (cw/2, ch/2, false, 0));
        ibmp.x = cw; //addChild (ibmp);
        obmp = new Bitmap (output = input.clone ());
        obmp.x = cw; obmp.y = ch/2; //addChild (obmp);
        obmp2 = new Bitmap (output2 = output.clone ());
        obmp2.x = cw; obmp2.y = ch; //addChild (obmp2);
        // set up filters
        bfilter = new BlurFilter (4, 4, 2);
        
        sfilter = new ShaderFilter (new Shader (new Base64 (SlopeEstimator3.pbj)));
        dfilter = new ShaderFilter (new Shader (new Base64 (DistanceEstimator3.pbj)));
        // lines
        lines = new Shape; lines.scrollRect = resized.rect; //addChild (lines); 
        
        // image load button
        //var button:PushButton = new PushButton (this, 0, 0, "LOAD IMAGE", onButtonClicked);
        // treshold sliders
        var tslider:HSlider = new HSlider (panel, 0, 0, onTSlider);
        tslider.minimum = 0; tslider.maximum = 9; tslider.tick = 0.01;
        tslider.value = sfilter.shader.data.threshold.value [0];
        tslider.x = 320 + (160 - tslider.width) / 2;
        tslider.y = 240 - tslider.height;
        var tslider2:HSlider = new HSlider (panel, 0, 0, onTSlider2);
        tslider2.minimum = 0.001; tslider2.maximum = 0.1; tslider2.tick = 0.001;
        tslider2.value = dfilter.shader.data.threshold.value [0];
        tslider2.x = 320 + (160 - tslider2.width) / 2;
        tslider2.y = 360 - tslider2.height;
        // blur slider
        var bslider:HSlider = new HSlider (panel, 0, 0, onBSlider);
        bslider.minimum = 1.0; bslider.maximum = 8.0; bslider.tick = 1.0;
        bslider.value = bfilter.blurX;
        bslider.x = 320 + (160 - bslider.width) / 2;
        bslider.y = 120 - bslider.height;
        // locality sliders
        kslider = new HSlider (panel, 200, 270);
        kslider.minimum = 2; kslider.maximum = 20; kslider.tick = 2.0;
        kslider.value = 10;
        kslider2 = new HSlider (panel, 200, 310);
        kslider2.minimum = 2; kslider2.maximum = 22; kslider2.tick = 2.0;
        kslider2.value = 2;
        // info labels
        info = new Label (panel, 200, 250, "");
        info2 = new Label (panel, 200, 290, "");
        // mode
        mainAngle = new CheckBox (panel, tslider2.x, tslider2.y + 30, "Dominant angle only");
        mainAngle.selected = true;
        /**/
        // processing
        addEventListener (Event.ENTER_FRAME, loop);
        // mrdoob's stats
        //var stats:Stats = new Stats; stats.alpha = 0.75; addChild (stats);
        //button.x = stats.width;
    }
    public function hide():DistanceEstimator
    {
        removeChild (video);
        removeChild (ibmp);
        removeChild (obmp);
        removeChild (obmp2);
        removeChild (bmp);
        removeChild (panel);
        removeChild (lines); 
        removeChild (g);
        return this;
    }
    public function show():DistanceEstimator
    {
        addChild (video);
        addChild (ibmp);
        addChild (obmp);
        addChild (obmp2);
        addChild (bmp);
        addChild (panel);
        addChild (lines); 
        addChild (g);
        return this;
    }
    public var h_linear:Vector.<Number> = new Vector.<Number> (768, true);
    public var h_linear3:Vector.<Number> = new Vector.<Number> (256, true);
    public var h_indices:Vector.<int> = new Vector.<int> (50, true);
    public var h_angles:Vector.<Number> = new Vector.<Number> (9, true);
    public function loop (e:Event):void {
        input.fillRect (input.rect, 0); input.draw (source,
            new Matrix (-0.5, 0, 0, 0.5, 160), null, null, null, true
        );
        input.applyFilter (input, input.rect, input.rect.topLeft, bfilter);
        output.applyFilter (input, input.rect, output.rect.topLeft, sfilter);
        // fetch results
        var r:Rectangle = output.rect; r.inflate ( -2 * bfilter.blurX, -2 * bfilter.blurY);
        var h:Vector.<Vector.<Number>> = output.histogram (r);
        // copy to linear array, and find max value
        var h_max:Number = copyToLinearArray3 (h, h_linear3);
        // find local maxima (and draw them)
        if(this.visible) g.graphics.clear ();
        /*
        if (getTimer () > 8000) {
            1 + 2;
        }
        /**/
        var j:int = findLocalMaxima (h_linear3, 0.2 * h_max, h_indices, kslider.value);
        var i_max:int = 0, n_max:Number = 0;
        for (var k:int = 0; k < h_angles.length; k++) {
            h_angles [k] = -1;
        }
        for (k = 0; k < j; k++) {
            var i:int = h_indices [k];
            var n:Number = h_linear3 [i] / h_max;
            if (n_max < n) {
                n_max = n;
                i_max = i;
            }
            
            var a:Number = Math.PI * i / 255.0;
            if(this.visible)
            {
                g.graphics.lineStyle (2, 65536 * i);
                g.graphics.moveTo (2, 360);
                g.graphics.lineTo (2 + n * 120 * Math.sin (a), 360 - n * 120 * Math.cos (a));
            }
            if (k < h_angles.length) h_angles [k] = a;
        }
        if(this.visible)
        {
            info.text = j + " local maxima (angle)";
            if (j < 1) {
                output2.fillRect (output2.rect, 0);
                lines.graphics.clear ();
                return;
            }
        }
        /**/
        // mark dominant angle
        a = Math.PI * i_max / 255.0;
        currentAngle = a;
        wheel.rotation = a/Math.PI*180;
        //graphics.lineTo (2 + n_max * 120 * Math.sin (a), 360 - n_max * 120 * Math.cos (a));
        if(this.visible) 
        {
            g.graphics.lineStyle (2, 0);
            g.graphics.moveTo (2, 360);
            g.graphics.lineTo (2 + n_max * 120 * Math.sin (a), 360 - n_max * 120 * Math.cos (a));
            g.graphics.beginFill (0xFFFFFF);
            g.graphics.drawCircle (2 + n_max * 120 * Math.sin (a), 360 - n_max * 120 * Math.cos (a), 5);
            g.graphics.endFill ();
        
            /**/
            // let's now find lines for this angle (or h_angles)
            
            info2.text = "(multiple angles)";
            lines.graphics.clear ();
            lines.graphics.lineStyle (2, 0xFF7F00);
            for (var m:int = 0; m < h_angles.length; m++) {
                if (!mainAngle.selected) {
                    a = h_angles [m];
                    if (a < 0) {
                        continue;
                    }
                }
    
                dfilter.shader.data.angle.value [0] = a; 
                output2.applyFilter (output, output.rect, output2.rect.topLeft, dfilter);
                h = output2.histogram (r);
                h_max = copyToLinearArray (h, h_linear);
                // find local maxima (and draw them)
                j = findLocalMaxima (h_linear, 0.5 * h_max, h_indices, kslider2.value);
                for (k = 0; k < j; k++) {
                    i = h_indices [k];
                    g.graphics.lineStyle (0, [65536, 256, 1][i >> 8] * (i % 256));
                    if (mainAngle.selected) {
                        g.graphics.moveTo (200 + i / 4, 470);
                        g.graphics.lineTo (200 + i / 4, 470 - 100 * h_linear [i] / h_max);
                    } else {
                        g.graphics.moveTo (200 + i / 4, 470 - 10 * m);
                        g.graphics.lineTo (200 + i / 4, 470 - 10 * m - 10 * h_linear [i] / h_max);
                    }
                    // draw lines: Ax + By + C = 0, y = - (Ax + C) / B (also scale up by 2)
                    var A:Number = - Math.cos (+a);
                    var B:Number = - Math.sin (+a);
                    var C:Number = i - 384;
                    lines.graphics.moveTo (0, -2 * C / B);
                    lines.graphics.lineTo (2 * 160, -2 * (A * 160 + C) / B);
                }
                if (mainAngle.selected) {
                    info2.text = j + " local maxima (distance)";
                    break;
                }
            }
    
            g.graphics.moveTo (200, 470);
            for (i = 0; i < 3; i++) {
                g.graphics.lineStyle (0, [0xFF0000, 0xFF00, 0xFF][i]);
                g.graphics.lineTo (200 + (768 / 12) * (i + 1), 470);
            }
        }
        /**/
    }
    public function copyToLinearArray3 (h_in:Vector.<Vector.<Number>>, h_out:Vector.<Number>):Number {
        var h_max:Number = 0;
        h_out [0] = 0;
        for (var i:int = 1; i < 256; i++) {
            var c:Number = h_in [0][i] + h_in [1][i] + h_in [2][i];
            if (c > h_max) h_max = c;
            h_out [i] = c;
        }
        return h_max;
    }
    public function copyToLinearArray (h_in:Vector.<Vector.<Number>>, h_out:Vector.<Number>):Number {
        var h_max:Number = 0;
        for (var i:int = 0; i < 256 * 3; i++) {
            if (i % 256 == 0) continue;
            var c:Number = h_in [i >> 8] [i % 256];
            if (c > h_max) h_max = c;
            h_out [i] = c;
        }
        for (i = 0; i < 3; i++) {
            h_out [256 * i] = 0.5 * (
                h_out [(256 * i - 1 + 768) % 768] +
                h_out [(256 * i + 1 + 768) % 768]
            );
        }
        return h_max;
    }
    public function findLocalMaxima (input:Vector.<Number>, threshold:Number, output:Vector.<int>, K:int):int {
        var L:int = input.length, j:int = 0;
        var n:int = input.length / output.length;
        search: {
            for (var p:int = 0; p < output.length; p++) {
                for (var q:int = 0; q < n; q++) {
                    var i:int = n * p + q;
                    if (i < L) {
                        var i_value:Number = input [i];
                        if (i_value > threshold) {
                            var i_ismax:Boolean = true;
                            for (var k:int = -K; k < K; k++) {
                                if (k < 0) {
                                    if (input [(i + L + k) % L] > i_value) {
                                        i_ismax = false; break;
                                    }
                                } else if (k > 0) {
                                    if (input [(i + L + k) % L] >= i_value) {
                                        i_ismax = false; break;
                                    }
                                }
                            }
                            if (i_ismax) {
                                // dijkstra would hate me :(
                                output [j] = i; j++; if (j >= output.length) break search;
                            }
                        }
                    }
                }
            }
        }
        for (k = j; k < output.length; k++) {
            output [k] = -1;
        }
        return j;
    }
    
    public function onTSlider (e:Event):void {
        sfilter.shader.data.threshold.value [0] = HSlider (e.target).value;
    }
    public function onTSlider2 (e:Event):void {
        dfilter.shader.data.threshold.value [0] = HSlider (e.target).value;
    }
    public function onBSlider (e:Event):void {
        bfilter.blurX = bfilter.blurY = HSlider (e.target).value;
    }
    /**/
    /*
    public function onButtonClicked (e:MouseEvent):void {
        file.browse ([new FileFilter ("Images", "*.jpe;*.jpeg;*.jpg;*.gif;*.png")]);
    }
    public function onFileSelected (e:Event):void { file.load (); }
    public function onFileLoaded (e:Event):void {
        loader = new Loader;
        loader.contentLoaderInfo.addEventListener (Event.COMPLETE, onImageReady);
        loader.loadBytes (file.data);
    }
    
    public function onImageReady(e:Event):void {
        if (loader.content) {
            loader.contentLoaderInfo.removeEventListener (Event.COMPLETE, onImageReady);
            resized.draw (loader.content,
                new Matrix (
                    -320 / loader.content.width, 0, 0,
                    +240 / loader.content.height, 320
                ), null, null, null, true
            );
            source = resized;
            if (timeout != 0) clearTimeout (timeout);
            timeout = setTimeout (backToWebcamFeed, 15000);
        }
    }
    public function backToWebcamFeed ():void {
        resized.fillRect (resized.rect, 0); source = video;
    }
    /**/
}

import flash.display.Stage;

class Wheel extends Shape
{
    public function Wheel(s:Stage)
    {
        graphics.lineStyle(3,0,0.5);
        graphics.drawCircle(0,0,10);
        graphics.moveTo(0,-10);
        graphics.lineTo(0,10);
        scaleX = 7; scaleY = 7;
        x = s.stageWidth/2; y = s.stageHeight - 20;
        //visible = false;
    }
}

// base64 code by 2ndyofyyx,
// http://wonderfl.kayac.com/code/b3a19884080f5ed34137e52e7c3032f3510ef861
import flash.utils.ByteArray; 
class Base64 extends ByteArray { 
    private static const BASE64:Array = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,62,0,0,0,63,52,53,54,55,56,57,58,59,60,61,0,0,0,0,0,0,0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,0,0,0,0,0,0,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,0,0,0,0,0]; 
    public function Base64(str:String) { 
        var n:int, j:int; 
        for (var i:int = 0; i < str.length && str.charAt(i) != "="; i++) { 
            if (str.charCodeAt(i) < 32) continue;
            j = (j << 6) | BASE64[str.charCodeAt(i)]; 
            n += 6; 
            while(n >= 8) { 
                writeByte((j >> (n -= 8)) & 0xFF); 
            } 
        } 
        position = 0; 
    } 
}

// http://www.motobit.com/util/base64-decoder-encoder.asp
class SlopeEstimator3 {
    public static var pbj:XML = <pbj>pQEAAACkDwBTbG9wZUVzdGltYXRvcjOgDG5hbWVzcGFjZQBleHBlcmltZW50YWwAoAx2ZW5kb3IA
bWFrYwCgCHZlcnNpb24AAQCgDGRlc2NyaXB0aW9uAHNsb3BlIGVzdGltYXRvcgChAQIAAAxfT3V0
Q29vcmQAoQEBAAACdGhyZXNob2xkAKIBbWluVmFsdWUAAAAAAKIBbWF4VmFsdWUAQRAAAKIBZGVm
YXVsdFZhbHVlAD6ZmZqjAARzcmMAoQIEAQAPZHN0AB0CAMEAABAAMgMAgAAAAAAyAwBAAAAAADID
ACAAAAAAMgMAED+AAAAyAAAQP4AAAB0EAIACAAAAAQQAgAAAwAAdAgAgBAAAADIAABA/gAAAHQQA
gAIAQAACBACAAADAAB0CABAEAAAAMAQA8QIAsAAdBQDzBAAbADIAABA/gAAAHQQAgAIAAAABBACA
AADAAB0CACAEAAAAMgAAED+AAAAdBACAAgBAAAEEAIAAAMAAHQIAEAQAAAAwBADxAgCwAB0GAPME
ABsAMgAAED+AAAAdBACAAgAAAAIEAIAAAMAAHQIAIAQAAAAyAAAQP4AAAB0EAIACAEAAAQQAgAAA
wAAdAgAQBAAAADAEAPECALAAHQcA8wQAGwAyAAAQP4AAAB0EAIACAAAAAgQAgAAAwAAdAgAgBAAA
ADIAABA/gAAAHQQAgAIAQAACBACAAADAAB0CABAEAAAAMAQA8QIAsAAdCADzBAAbADIAABA/gAAA
HQQAgAIAAAABBACAAADAAB0CACAEAAAAHQIAEAIAQAAwBADxAgCwADIAABA/gAAAHQkAgAIAAAAC
CQCAAADAAB0CACAJAAAAHQIAEAIAQAAwCQDxAgCwAB0KAPMEABsAAgoA8wkAGwAyAAAQPo6rNh0E
APMFABsAAQQA8wYAGwAdCQDzBAAbAAIJAPMIABsAHQQA8wkAGwACBADzBwAbAB0JAPMAAP8AAwkA
8wQAGwAdBADzCgAbAAEEAPMJABsAHQkA8wQAGwAdAgAgAgAAADIAABA/gAAAHQQAgAIAQAABBACA
AADAAB0CABAEAAAAMAQA8QIAsAAdAgAgAgAAADIAABA/gAAAHQoAgAIAQAACCgCAAADAAB0CABAK
AAAAMAoA8QIAsAAdCwDzBAAbAAILAPMKABsAMgAAED6OqzYdBADzBgAbAAEEAPMHABsAHQoA8wQA
GwACCgDzBQAbAB0EAPMKABsAAgQA8wgAGwAdCgDzAAD/AAMKAPMEABsAHQQA8wsAGwABBADzCgAb
AB0KAPMEABsAHQAAEAkAAAABAAAQCQBAAB0EAIAAAMAAAQQAgAkAgAAdAgAgBAAAAB0AABAKAAAA
AQAAEAoAQAAdBACAAADAAAEEAIAKAIAAHQIAEAQAAAAdBADBAgCwACQAABEEABAAKgAAIAAAwAAd
AYCAAIAAADQAAAABgAAAMgAAED6i+YMdAgAgBABAAAYCACAEAAAAHQIAEAAAwAADAgAQAgCAAB0A
ABACAMAAMgIAIAAAAAArAAAQAgCAAB0BgEAAgAAANAAAAAGAQAAyAgAgP4AAAAEAABACAIAANgAA
AAAAAAAdAwCAAADAADICACA7gAAAHQIAEAAAwAACAgAQAgCAAB0DAEACAMAAMgIAIDuAAAAdAgAQ
AADAAAECABACAIAAHQMAIAIAwAA2AAAAAAAAAB0BAPMDABsA</pbj>;
}

class DistanceEstimator3 {
    public static var pbj:XML = <pbj>pQEAAACkEgBEaXN0YW5jZUVzdGltYXRvcjOgDG5hbWVzcGFjZQBleHBlcmltZW50YWwAoAx2ZW5k
b3IAbWFrYwCgCHZlcnNpb24AAQCgDGRlc2NyaXB0aW9uAGRpc3RhbmNlIGVzdGltYXRvcgChAQIA
AAxfT3V0Q29vcmQAoQEBAAACYW5nbGUAogFtaW5WYWx1ZQAAAAAAogFtYXhWYWx1ZQBASQ/bogFk
ZWZhdWx0VmFsdWUAP4AAAKEBAQAAAXRocmVzaG9sZACiAW1pblZhbHVlADqDEm+iAW1heFZhbHVl
AD3MzM2iAWRlZmF1bHRWYWx1ZQA8I9cKowAEc3JjAKECBAEAD2RzdAAdAgDBAAAQADADAPECABAA
HQQA8wMAGwAyAgAgAAAAACoCACAEAAAAHQGAgACAAAA0AAAAAYAAADICACBASQ/bHQIAEAIAgAAD
AgAQBAAAAB0CACACAMAAHQIAEAIAgAACAgAQAACAABgDAIACAMAAKgMAgAAAwAAdAYBAAIAAADQA
AAABgEAADQIAEAIAgAAdAwCAAgDAAAMDAIACAAAADAIAEAIAgAAdAwBAAgDAAAMDAEACAEAAHQIA
EAMAAAABAgAQAwBAAB0DAIACAMAAMgIAEEPAAAABAwCAAgDAADICABA7gAAAAwMAgAIAwAAyAgAQ
P4AAACsDAIACAMAAHQGAIACAAAA0AAAAAYCAAB0EAIADAAAAMgQAQAAAAAAyBAAgAAAAADUAAAAA
AAAAMgIAEEAAAAArAwCAAgDAAB0BgBAAgAAANAAAAAGAwAAyBACAAAAAADICABA/gAAAHQMAQAMA
AAACAwBAAgDAAB0EAEADAEAAMgQAIAAAAAA1AAAAAAAAADIEAIAAAAAAMgQAQAAAAAAyAgAQQAAA
AB0DAEADAAAAAgMAQAIAwAAdBAAgAwBAADYAAAAAAAAANgAAAAAAAAA1AAAAAAAAADIEAIAAAAAA
MgQAQAAAAAAyBAAgAAAAADYAAAAAAAAANgAAAAAAAAAdAQDzBAAbAA==</pbj>;
}