#apply
#include "../_include.txt"
#include "../../define/_vector2.txt"
#include "../../define/_vector3.txt"


cev[CEV_HIGHWAY_ALEX] .name("mg_ハイウェイALEX") {
    def {
        NUM_OF_VAR = 256
        NUM_OF_STR = 8
        NUM_OF_PIC = 190

        BASE_LAYER = 6

        PATH_FRAME  = "ha/fr"
        PATH_SKY    = "ha/sk"
        PATH_HANDLE = "ha/ha"
        PATH_ROAD   = "ha/ro"
        PATH_SPRITE = "ha/sp"
        PATH_DIGIT  = "ha/di"
        PATH_GEAR   = "ha/ge"
        PATH_TACHO  = "ha/ta"
        PATH_SIGN   = "ha/si"
        PATH_LENGTH = "ha/le"
        PATH_CURSOR = "cm/cu"

        //ピクチャID
        PIC_HEAD      = 1
        PIC_SKY       = 2    //+0_+1
        PIC_SORTED    = 10   //+0_+15
        PIC_SPR_BACK  = 30   //+0_+15
        PIC_GROUND    = 49
        PIC_ROAD      = 50   //+0_+79
        PIC_SPR_FRONT = 130  //+0_+15
        PIC_FRAME     = 150
        PIC_TACHO     = 151
        PIC_GEAR      = 152
        PIC_VEL_METER = 153  //+0_+2
        PIC_TIMER     = 156  //+0_+4
        PIC_DAMAGE    = 161  //+0_+1
        PIC_LENGTH    = 165
        PIC_SIGN      = 166
        PIC_HANDLE    = 167
        PIC_CURSOR    = 168

        //実績
        ACHIEVE_DAMAGE = 12
        ACHIEVE_TIME   = UNIT_FPS * 60 * 4  //4分

        //定数
        OUT_RANGE        = -320
        REF_X            = 32
        REF_Y            = 24
        FOV              = UNIT_ANG * 100       //度数 * 単位
        HALF_FOV         = FOV / 2
        SCR_WIDTH        = 256
        SCR_HEIGHT       = 160
        SCR_HALF_W       = 128
        SCR_HALF_H       = 80
        SCR_DIST         = 107                  //width / 2 / tan(fov / 2)
        SCR_MIN_DEPTH    = 10 * UNIT_POS        //距離 * 単位
        SCR_MAX_DEPTH    = 900 * UNIT_POS
        SCR_OPACITY_AWAY = UNIT_POS * 100       //遠くに行くほど透明になる
        COURSE_LENGTH    = 150 * SCR_MAX_DEPTH

        //シーン
        SCENE_START = 0
        SCENE_GAME
        SCENE_GOAL
        SCENE_RESULT
        SCENE_CHOICE

        //単位
        UNIT_POS  = 10
        UNIT_ANG  = 10
        UNIT_PL_Z = 10

        //速度計
        METER_X     = 296
        METER_Y     = 208
        METER_DIGIT = 3
        METER_WIDTH = 12

        //タイマー
        TIMER_X     = 80
        TIMER_Y     = 16
        TIMER_DIGIT = 5
        TIMER_WIDTH = 14
        TIMER_MAX   = 60 * 60 * 9  //9分

        //進行度
        CURSOR_X      = 16
        CURSOR_Y      = 35
        CURSOR_LENGTH = 60

        //タコメーター
        TACHO_X     = 278
        TACHO_Y     = 214
        TACHO_ANGLE = 180 * UNIT_ANG
        TACHO_MAX   = 1000

        //ギア
        GEAR_X = 282
        GEAR_Y = 196

        //ハンドル
        HANDLE_X = 160
        HANDLE_Y = 260

        //ダメージ
        DAMAGE_DIGIT = 2
        DAMAGE_WIDTH = 14
        DAMAGE_X = 30
        DAMAGE_Y = 48

        //プレイヤー
        PL_BOOST_TIMER = 60
        PL_MAX_X       = 50 * UNIT_POS
        PL_MAX_VX      = 2 * UNIT_POS
        PL_SHOULDER_X  = 48 * UNIT_POS
        PL_SHOULDER_VZ = 1 * UNIT_PL_Z
        PL_DAMAGE_VZ   = 40 * UNIT_PL_Z
        PL_BRAKE_VZ    = 1 * UNIT_PL_Z
        PL_MIN_VZ      = 20 * UNIT_PL_Z
        PL_MAX_VZ      = 300 * UNIT_PL_Z
        PL_Z           = 24 * UNIT_POS
        PL_Y           = 12 * UNIT_POS
        PL_SCALE       = 35

        //ギアに応じた値を返す
        GEAR_JUST_MIN = 740
        GEAR_JUST_MAX = 840
        GEAR_DOWN_MIN = 50

        GEAR_MAX = 4
        GEAR_ADD = [ 0,-40, -80, -120, -160][plGear]
        GEAR_DIV = [ 60, 70, 80, 100, 140][plGear]
        GEAR_ACC = [5, 4, 3, 2, 1][plGear]

        //街灯,トンネル,ゴール
        STREET_INTERVAL = SCR_MAX_DEPTH / 3

        //スプライト
        SPR_NUM = 8
        SPR_NUM_STREET = 3
        SPR_NUM_OTHER = 4

        SPR_TYPE_NONE = 0
        SPR_TYPE_PLAYER
        SPR_TYPE_LIGHT
        SPR_TYPE_TUNNEL
        SPR_TYPE_GOAL
        SPR_TYPE_OTHER1
        SPR_TYPE_OTHER2

        SPR_STATE_NONE = 0
        SPR_STATE_DELETE
        SPR_STATE_CREATE
        SPR_STATE_ENABLE
        SPR_STATE_DAMAGE

        SPR_ID_PLAYER = 0
        SPR_ID_STREET = 1
        SPR_ID_OTHER  = 4

        //セル番号
        CELL_PL = 1
        CELL_LIGHT = 13
        CELL_TUNNEL = 16
        CELL_GOAL = 19
        CELL_ENEMY1 = 6
        CELL_ENEMY2 = 11

        //敵
        OT_LANE_X = 40 * UNIT_POS
        OT_LANE_VX = 1 * UNIT_POS

        OT1_Y = 14 * UNIT_POS
        OT1_SCALE = 35
        OT1_SIZE_X = 20 * UNIT_POS
        OT1_SIZE_Z = 20 * UNIT_POS

        OT2_Y = 22 * UNIT_POS
        OT2_SCALE = 60
        OT2_SIZE_X = 22 * UNIT_POS
        OT2_SIZE_Z = 30 * UNIT_POS

        //道
        ROAD_SEGMENT = 80//道の区間の数
        ROAD_PIC_NUM = 78//segment-2
        ROAD_PIC_WIDTH = 100//セルの横幅
        ROAD_PIC_HEIGHT = 2//セルの縦幅
        ROAD_PATTERN_NUM = 16//セルの縦パターン数
        ROAD_PATTERN_INTERVAL = 5 * UNIT_POS//セルのパターンが一巡する間隔
        ROAD_START_Y = 2//描画の開始位置
        ROAD_WIDTH = 120//道の横幅
        ROAD_SCALE_CORRECT = 10//拡大による横ブレを抑える係数
        ROAD_FINE_MUL = 10//カーブの上下による粗さの変化
        ROAD_MAX_FINE = 1000
        ROAD_CURVE_DIV = 1000000//カーブの見た目上の強さ

        //カーブパラメータ
        MC_PRO = 0//進行度
        MC_POW//カーブ強さ
        MC_LEN1//開始地点
        MC_LEN2//カーブ長さ
        MC_LEN3//カーブ終わりの長さ
        MC_SSTA//画面上のカーブ開始位置
        MC_SLEN//画面上のカーブ長さ
        MC_STAG//画面上のカーブ強さ

        //スプライト
        MS_PID = 0
        MS_CELL
        MS_TYPE
        MS_STATE
        MS_TIMER
        MS_SCALE
        MS_ANGLE
        MS_PARAM1
        MS_PARAM2
        MS_X
        MS_Y
        MS_Z
        SIZE_OF_SPR

    }

    def {
        basePicCmd = bl {
            .chromakey(1)
            .mapLayer(BASE_LAYER)
            .eraseWhenTransfer
            .affectedByTint
            .affectedByFlash
            .affectedByShake
        }

        skyPicCmd = {
            PATH_SKY
            .grid(2, 3)
            basePicCmd
        }

        digitPicCmd = {
            PATH_DIGIT
            .grid(10, 1)
            basePicCmd
        }

        tachoPicCmd = {
            PATH_TACHO
            basePicCmd
        }

        gearPicCmd = {
            PATH_GEAR
            .grid(10, 1)
            basePicCmd
        }

        signPicCmd = {
            PATH_SIGN
            .grid(1, 5)
            basePicCmd
        }

        lengthPicCmd = {
            PATH_LENGTH
            basePicCmd
        }

        cursorPicCmd = {
            PATH_CURSOR
            .topLeft
            basePicCmd
        }

        roadPicCmd = {
            PATH_ROAD
            .center
            .grid(1, ROAD_PATTERN_NUM)
            basePicCmd
        }

        spritePicCmd = {
            PATH_SPRITE
            .center
            .grid(5, 4)
            basePicCmd
        }
    }

    defv {
        varHead = TV_MINIGAME

        initHead

        idxA
        idxB
        ptrA
        ptrB

        ftA
        ftB

        ipt
        pid
        cell
        state
        type
        timer

        depth
        alpha
        beta
        gamma
        trans
        color
        lx
        ly
        lz
        dx
        dy
        dsx
        ux
        uy
        usx
        scrY

        sceneState
        sceneTimer

        courseLen
        coursePro
        courseNext
        courseTimer
        trackCount
        damageCount
        damageCountUpdate

        iptUp
        iptDown

        cursorX
        cursorY

        camX
        camY
        camZ

        plVx
        plVel
        plSide
        plTacho
        plSlip
        plGear
        plRpm
        plBoost

        handleAngle
        handleBase
        handleRotate

        scrollLock
        laneFlag
        roadStep
        roadVz
        nowBG
        nextBG//次の景観
        nextStreetObj//次に現れるオブジェクトの種類
        streetCount//使用するスプライトID
        streetStep//街灯やトンネルの出現間隔

        colPower
        colFrontRG
        colBackRG
        colB
        colS

        bgScrX
        bgScrY
        bgZ
        bg2ScrX
        grScrY
        grZ
        sharpFine

        cProX//横カーブ
        cPowX
        cLen1X
        cLen2X
        cLen3X
        cSharpStaX
        cSharpLenX
        cSharpTagX

        cProY//縦カーブ
        cPowY
        cLen1Y
        cLen2Y
        cLen3Y
        cSharpStaY
        cSharpLenY
        cSharpTagY

        initFoot

        sprHead
        sortDepth = (__id(sprHead)   + SPR_NUM * SIZE_OF_SPR )//ソート用配列
        sortSprId = (__id(sortDepth) + SPR_NUM)

        varFoot
    }

    deft {
        strHead = TS_MINIGAME
        strCourse = TS_MINIGAME
        strSource
        strLine
        strParam1
        strParam2
        strParam3
        strParam4
        strParam5
    }

    commentRangeOfVariables(varFoot, TV_MINIGAME, NUM_OF_VAR, NUM_OF_STR, NUM_OF_PIC)

    //シーンの状態を変える
    __fn _changeSceneState $state bl {
        v[__id(sceneState)..__id(sceneTimer)] = [$state, -1]
    }

    //スプライトのポインタを取得する
    __fn _getSprPtr $dvDst $idx bl {
        v[__id($dvDst)] = __id(sprHead) + SIZE_OF_SPR * ($idx)
    }

    //スプライトのメンバ変数を初期化する
    __fn _setSprMembers $ptr $arr bl {
        v[$ptr..$ptr + SIZE_OF_SPR - 1] = $arr
    }

    //ピクチャ番号以外のメンバー変数を初期化する
    __fn _initSprite $ptr bl {
        ftA = v[$ptr+MS_PID]
        v[$ptr..$ptr + SIZE_OF_SPR - 1] = [ftA, 0, SPR_TYPE_NONE, SPR_STATE_NONE, 0, 100, 0, 0, 0,  0, 0, 0]
    }

    //スプライトを有効化し、ピクチャ番号以外のメンバ変数を初期化する
    __fn _setupSprite $ptr $cell $type $scale $x $y $z bl {
        ftA = v[$ptr+MS_PID]
        v[$ptr..$ptr + SIZE_OF_SPR - 1] = [ftA, $cell, $type, SPR_STATE_CREATE, 0, $scale, 0, 0 ,0 ,  $x, $y, $z]
    }

    //スプライトのピクチャを初期化する
    //ポインタではなくインデックス番号を渡す
    __fn _initSpritePicture $idx bl {
        _getSprPtr(ftA, $idx)
        ftB  = v[ftA+MS_PID]
        @pic.show [ftB] .pos(OUT_RANGE, OUT_RANGE) .trans(100) spritePicCmd
    }

    //スプライトの状態を変える
    __fn _changeSpriteState $ptr $state bl {
        v[$ptr+MS_STATE..$ptr+MS_TIMER] = [$state, -1]
    }

    // y = p(x - s)^2
    // y = p * l^2 + (x - l) * p * 2 * l
    //カーブに合わせたX座標を得る
    __fn _addXRelativeToRoad $dst $wz {
        @if `cSharpStaX < ($wz) && cSharpTagX != 0 bl {
            v[__id($dst)] += cSharpStaX + cSharpLenX < ($wz) ?
                muldiv(cSharpTagX, pow(cSharpLenX, 2) + 2 * (($wz) - cSharpLenX) * cSharpLenX, ROAD_CURVE_DIV) :
                muldiv(cSharpTagX, pow(($wz) - cSharpStaX, 2), ROAD_CURVE_DIV)
        }
    }

    //カーブに合わせたY座標を得る
    __fn _addYRelativeToRoad $dst $wz {
        @if `cSharpStaY < ($wz) && cSharpTagY != 0 bl {
            v[__id($dst)] += cSharpStaY + cSharpLenY < ($wz) ?
                muldiv(cSharpTagY, pow(cSharpLenY, 2) + 2 * (($wz) - cSharpLenY) * cSharpLenY, ROAD_CURVE_DIV) :
                muldiv(cSharpTagY, pow(($wz) - cSharpStaY, 2), ROAD_CURVE_DIV)
        }
    }

    //カーブを設定する
    __fn _setCurve $ptr $pow $len1 $len2 $len3 {
        v[$ptr+MC_PRO..$ptr+MC_STAG] = [0, $pow, $len1, $len2, $len3, 0, 0, 0]
    }

    //数値を表示する
    //ux. dx. pid, cellを使用する
    __fn _showCounter $num $pid $dig $wid $x $y bl {
        dy = 1
        @loop $dig .dst(dx) bl {
            v[__id(ux)..__id(uy)] = [($x) - dx * $wid, ($y)]
            pid  = ($pid) + dx
            cell = (($num) < dy && dy != 1) ? (0) : (1 + ($num) % (dy * 10) / dy)
            @pic.show [pid] .pos(ux, uy) .topRight .scale(100) .cell(cell) digitPicCmd
            //次の桁
            dy *= 10
        }
    }

    //ハイスコアを更新する
    //判定なし
    __fn _updateHighScore bl {
        highwayAlexScore = courseTimer
    }

    //------------------------------------------------------------------------------------
    //開始処理
    bl {
        initPicturesAndVariables(PIC_HEAD, NUM_OF_PIC, TV_MINIGAME, NUM_OF_VAR, TS_MINIGAME, NUM_OF_STR)
        storeAndStopBGM()
        msgBottom()
        
        //ピクチャ
        @pic.show[PIC_FRAME] PATH_FRAME .pos(0, 0) .topLeft .trans(0) basePicCmd
        @pic.show[PIC_HANDLE] PATH_HANDLE .pos(OUT_RANGE, OUT_RANGE) .center basePicCmd
        //標識
        @pic.show[PIC_SIGN] .pos(OUT_RANGE, OUT_RANGE) .cell(0) signPicCmd
        //地面
        @pic.show[PIC_GROUND] .pos(OUT_RANGE, OUT_RANGE) .cell(0) skyPicCmd
        //タコメーター
        @pic.show[PIC_TACHO] .pos(OUT_RANGE, OUT_RANGE) .cell(0) tachoPicCmd
        //進行度カーソル
        @pic.show[PIC_LENGTH] .pos(OUT_RANGE, OUT_RANGE) lengthPicCmd
        //マウスカーソル
        @pic.show[PIC_CURSOR] .pos(OUT_RANGE, OUT_RANGE) cursorPicCmd
        //速度計
        @loop METER_DIGIT .dst(idxA) bl {
            pid = PIC_VEL_METER + idxA
            @pic.show[pid] .pos(OUT_RANGE, OUT_RANGE) .cell(0) digitPicCmd
        }
        //時間
        @loop TIMER_DIGIT .dst(idxA) bl {
            pid = PIC_TIMER + idxA
            @pic.show[pid] .pos(OUT_RANGE, OUT_RANGE) .cell(0) digitPicCmd
        }
        //空
        @loop 2 .dst(idxA) bl {
            pid = PIC_SKY + idxA
            @pic.show[pid] .pos(OUT_RANGE, OUT_RANGE) .cell(0) skyPicCmd
        }
        //道路ピクチャ
        @loop ROAD_PIC_NUM .dst(pid) bl {
            pid += PIC_ROAD
            @pic.show[pid] .pos(OUT_RANGE, OUT_RANGE) .cell(0) roadPicCmd
        }
        //スプライト
        @loop SPR_NUM .dst(idxA) bl {
            //ピクチャ番号を割り振る
            _getSprPtr(ptrA, idxA)
            v[ptrA+MS_PID] = PIC_SPR_FRONT + idxA
            _initSpritePicture(idxA)
            _initSprite(ptrA)
        }
        @label.jump "gameLoop"
    }

    //------------------------------------------------------------------------------------
    //ゲーム処理
    @label.set "gameLoop"
    bl {
        //変数を一括で初期化する
        assignVarUsingDeclared(initHead, initFoot, 0)
        //ピクチャを隠す
        hidePicturesForEach( [PIC_SIGN] )
        //----------------------------
        //コース生成
        //length = x * SCR_MAX_DEPTH
        //"0, length" : 待機
        //"1, power, length1, length2, length3" : Xカーブ
        //"2, power, length1, length2, length3" : Yカーブ
        //"3" : トラック
        //"4, bg" : トンネル
        //"5" : ゴール
        $UNIT = SCR_MAX_DEPTH
        $GOAL = (ipt - 2 <= depth)
        $TUNNEL = (COURSE_LENGTH / 3)
        //コース全長
        courseLen = COURSE_LENGTH

        strCourse .getLen(alpha)
        @if alpha <= 0 bl {
            strCourse .asg("")
            //現在のシーン
            nextBG = 0
            //コース全体の距離 / unit
            ipt = courseLen / $UNIT
            //生成した距離
            depth = 0
            //生成した距離の始点
            timer = 0
            //次のXカーブまでの距離(スタートが基準)
            lx = rnd(1, 5)
            //次のYカーブまでの距離(スタートが基準)
            ly = rnd(1, 5)
            //次のトラックまでの距離(スタートが基準)
            lz = rnd(1, 5)
            //次のトンネルまでの距離(スタートが基準)
            dx = ipt / 3
            //最初に直進を追加する
            strCourse .cat(__str("0,", 1, NEWLINE) .extract)
            depth += 1
            timer = depth
            @loop bl {
                //直線を追加する
                @if `depth > timer && (lx <= depth || ly <= depth || lz <= depth || dx <= depth || $GOAL) bl {
                    //timer-depth
                    alpha = depth - timer
                    strCourse .cat(__str("0,\", alpha, NEWLINE) .extract)

                    timer = depth
                }
                //ゴール前の直進を含む
                @if `$GOAL bl {
                    //最後に直進とゴールを追加する
                    strCourse .cat(__str("0,", 1, NEWLINE) .extract)
                    strCourse .cat(__str("5,0", NEWLINE) .extract)

                    depth += 1
                    
                    @break
                }

                //xカーブを生成する
                @if lx <= depth bl {
                    cPowX  = [rnd(2, 12), rnd(1, 3), rnd(8, 14)][nextBG] * [-1, 1][rnd(0,1)] * 10
                    cLen1X = rnd(1, 2)
                    cLen2X = [rnd(1, 4), rnd(1, 4), rnd(1, 2)][nextBG]
                    cLen3X = rnd(1, 2)

                    alpha = lx
                    lx += cLen1X + cLen2X + cLen3X + rnd(1, 2)

                    //ゴールか、トンネルの前後を跨いでいる場合は登録しない
                    //稀にトンネル内でカーブする時があるが直すのも面倒だし、見た目が面白いので放って置く
                    @if `cPowX != 0 &&
                         lx < ipt &&
                        (alpha * SCR_MAX_DEPTH) / $TUNNEL == (lx * SCR_MAX_DEPTH) / $TUNNEL bl {
                        strCourse .cat(__str("1,\",cPowX,",\",cLen1X,",\",cLen2X,",\",cLen3X, NEWLINE) .extract)
                    }
                }

                //yカーブを生成する
                @if ly <= depth bl {
                    cPowY  = [rnd(2, 5), rnd(1, 4), 0][nextBG] * [-1, 1][rnd(0,1)] * 4
                    cLen1Y = rnd(1, 4)
                    cLen2Y = rnd(1, 4)
                    cLen3Y = rnd(1, 4)

                    alpha = ly
                    ly += cLen1Y + cLen2Y + cLen3Y + rnd(1, 4)

                    //ゴールか、トンネルの前後を跨いでいる場合は登録しない
                    @if `cPowY != 0 &&
                         ly < ipt &&
                        (alpha * SCR_MAX_DEPTH) / $TUNNEL == (ly * SCR_MAX_DEPTH) / $TUNNEL bl {
                        strCourse .cat(__str("2,\",cPowY,",\",cLen1Y,",\",cLen2Y,",\",cLen3Y, NEWLINE) .extract)
                    }
                }

                //トラック
                //最初のシーンが一番多い
                @if lz <= depth bl {
                    strCourse .cat(__str("3,0", NEWLINE) .extract)
                    lz += [rnd(2, 4), rnd(3, 5), rnd(7, 8)][nextBG]
                }

                //トンネル
                @if dx <= depth bl {
                    @if nextBG < 2 bl {
                        nextBG += 1
                        strCourse .cat(__str("4,\",nextBG, NEWLINE) .extract)
                    }
                    dx += ipt / 3
                }
                depth += 1
            }
        }
        //db_log(strCourse)

        //course -> source
        strSource .asg(strCourse)

        //----------------------------
        //スプライトを初期化する
        @loop SPR_NUM .dst(idxA) bl {
            _getSprPtr(ptrA, idxA)
            _initSpritePicture(idxA)
            _initSprite(ptrA)
        }

        //----------------------------
        //マウスカーソル
        @if gameMode == 1 bl {
            V2_set(cursorX, 160, 120)
            @mouse.setPos(cursorX, cursorY)
        }

        //----------------------------
        //カメラ
        //道路の一番下が画面の下に来る程度
        V3_set(camX,
            0,
            25 * UNIT_POS,
            10 * UNIT_POS * SCR_DIST / SCR_HALF_H * -1
        )

        //自車
        _getSprPtr(ptrA, 0)
        _setupSprite(ptrA, 1, SPR_TYPE_PLAYER, PL_SCALE, 0, PL_Y, PL_Z)

        //初期景観
        nextStreetObj = SPR_TYPE_LIGHT
        nowBG = 0
        nextBG = 0
        colPower = 5
        colFrontRG = 100
        colBackRG = 20
        colB = 100
        colS = 100

        //衝突回数をリセットする
        damageCountUpdate = TRUE

        //----------------------------
        //初期シーン
        _changeSceneState(SCENE_START)

        //----------------------------
        //暗転
        fastFadeInTintOnly(2)

        //----------------------------
        //ループ
        @loop bl {
            @wait(0)
            
            //----------------------------
            //ポーズ
            @key.input .dst(ipt) .cancel
            @if `ipt bl {
                @se.play "Cancel2" .opt(100, 100, 50)
                @msg.choice bl {
                    .cancel(0)
                    .case "\>つづける" bl {
                        @wait(3)
                    }
                    .case "\>\c[2]やりなおす\c[0]" bl {
                        @label.jump "gameLoop"
                    }
                    .case "\>\c[2]別のコースでやりなおす\c[0]" bl {
                        strCourse .asg("")
                        @label.jump "gameLoop"
                    }
                    .case "\>やめる" bl {
                        @label.jump "gameEnd"
                    }
                }
            }

            //----------------------------
            //シーン
            sceneTimer += 1

            pseudoSwitchState(sceneState,
                [SCENE_GAME, bl {
                    courseTimer = min(courseTimer + 1, TIMER_MAX)
                },
                SCENE_START, bl {
                    $FRAME1 = 60
                    $FRAME2 = 30

                    //カウントダウン
                    @if `between(sceneTimer, $FRAME1, $FRAME1 + $FRAME2 * 2) && !(sceneTimer % $FRAME2) bl {
                        cell = (sceneTimer - $FRAME1) / $FRAME2 + 1
                        @pic.show [PIC_SIGN] .pos(REF_X+SCR_HALF_W, REF_Y+SCR_HALF_H) .scale(150) .cell(cell) signPicCmd
                        @pic.move [PIC_SIGN] .relative .pos(0, 0) .scale(-50) .trans(0) .time(1)
                        @se.play "Cursor2" .opt(80, 100, 50)

                    //スタート
                    } .elif `sceneTimer == $FRAME1 + $FRAME2 * 3 bl {
                        @pic.show [PIC_SIGN] .pos(REF_X+SCR_HALF_W, REF_Y+SCR_HALF_H) .scale(100) .cell(4) .rgbs(200,200,200,100) signPicCmd
                        @pic.move [PIC_SIGN] .relative .pos(0, 0) .scale(50) .trans(100) .time(4)
                        @se.play "Combat1" .opt(80, 100, 50)

                        //BGM
                        startBGM("broomstick", FALSE, 0, 100, 100, 50)

                        _changeSceneState(SCENE_GAME)
                    }
                },
                SCENE_GOAL, bl {
                    //ゴールイン
                    @if sceneTimer == 0 bl {
                        //スクロールを固定する
                        scrollLock = 1
                        
                        @se.play "maou_carthrough" .opt(100, 100, 50)
                        @scr.flash .once 2 .rgbv(31, 31, 31, 24)

                    //ハイスコア判定
                    } .elif sceneTimer == 80 bl {
                        //実績
                        checkSetAchievement(achievedHighway, damageCount <= ACHIEVE_DAMAGE && courseTimer <= ACHIEVE_TIME)

                        //ハイスコアが初期値でも更新する
                        @if `courseTimer < highwayAlexScore || highwayAlexScore <= 0 bl {
                            _updateHighScore()

                            @pic.show [PIC_SIGN] .pos(REF_X+SCR_HALF_W, REF_Y+SCR_HALF_H) .scale(300) .cell(5) .rgbs(200,200,200,100) signPicCmd
                            @pic.move [PIC_SIGN] .relative .pos(0, 0) .scale(-200) .trans(0) .time(4)

                            @se.play "cheers" .opt(100, 100, 50)
                            @se.play "Holy7" .opt(100, 100, 50)
                            @scr.flash .once 2 .rgbv(31, 31, 31, 24)

                        } .else bl {
                            @se.play "Holy5" .opt(100, 100, 50)
                        }
                        
                    } .elif sceneTimer >= 180 bl {
                        _changeSceneState(SCENE_CHOICE)
                    }

                    //プレイヤーは画面奥へ走り去る
                    plSide = 0
                    plVx = 0
                    
                    _getSprPtr(ptrA, SPR_ID_PLAYER)
                    @if `v[ptrA+MS_Z] < SCR_MAX_DEPTH bl {
                        v[ptrA+MS_Z] += 8 * UNIT_POS
                    }
                },
                SCENE_CHOICE, bl {
                    //キャンセルを押すと終了する
                    @msg.choice bl {
                        .cancel(2)
                        .case "やりなおす" bl {
                            @label.jump "gameLoop"
                        }
                        .case "\>\c[2]別のコースでやりなおす\c[0]" bl {
                            strCourse .asg("")
                            @label.jump "gameLoop"
                        }
                        .case "\>やめる" bl {
                            @label.jump "gameEnd"
                        }
                    }
                }],
                bl {}
            )

            //----------------------------
            // コース読み込み
            @if sceneState == SCENE_GAME bl {
                //進行
                coursePro += roadVz
                //load
                @if coursePro >= courseNext bl {
                    @loop bl {
                        strSource .getLen(alpha)
                        @if alpha <= 0 bl {
                            @break

                        } .else bl {
                            strSource .popLine(strLine)
                            strLine .split(",", strParam1, alpha)

                            strParam1 .toNum(type)
                            strParam2 .toNum(state)
                            strParam3 .toNum(alpha)
                            strParam4 .toNum(beta)
                            strParam5 .toNum(gamma)

                            pseudoSwitchState(type,
                            [0, bl {
                                courseNext += state * SCR_MAX_DEPTH
                                @break(2)
                            },
                            1, bl {
                                v[__id(alpha)..__id(gamma)] *= SCR_MAX_DEPTH
                                _setCurve(__id(cProX), state, alpha, beta, gamma)
                            },
                            2, bl {
                                v[__id(alpha)..__id(gamma)] *= SCR_MAX_DEPTH
                                _setCurve(__id(cProY), state, alpha, beta, gamma)
                            },
                            3, bl {
                                trackCount += 1
                            },
                            4, bl {
                                nextStreetObj = SPR_TYPE_TUNNEL
                                nextBG = state
                            },
                            5, bl {
                                nextStreetObj = SPR_TYPE_GOAL
                            }],
                            bl {
                                @break(2)
                            } )
                        }
                    }
                }

                //アザーカーは常に補充される
                //スタート直後とゴール直前は除く
                alpha = [90, 60, 120][nowBG]
                @if `!(courseTimer % alpha) && between(coursePro, SCR_MAX_DEPTH * 1, courseLen - SCR_MAX_DEPTH * 4) bl {
                    //空きを探す
                    @loop SPR_NUM_OTHER .dst(idxA) bl {
                        idxA += SPR_ID_OTHER
                        _getSprPtr(ptrA, idxA)

                        //空きを見つけた
                        @if `v[ptrA+MS_STATE] == SPR_STATE_NONE bl {
                            alpha = rnd(0, 2)
                            @loop 2 bl {
                                @if `!(laneFlag & (1 << alpha)) bl {
                                    //位置
                                    V3_set(lx,
                                        [-OT_LANE_X, 0, OT_LANE_X][alpha],
                                        0,
                                        SCR_MAX_DEPTH
                                    )

                                    //車種
                                    //トラック
                                    //コース読み込み中に予約される
                                    @if `trackCount > 0 bl {
                                        trackCount -= 1

                                        beta = SPR_TYPE_OTHER2

                                        //プレイヤーの速度に応じて避けづらくなる
                                        //通常よりは避けやすいが、留まりやすくもある
                                        lx = lerp(2, 4, plVel, PL_MAX_VZ) * UNIT_POS
                                        ly = lerp(4, 8, plVel, PL_MAX_VZ) *UNIT_POS

                                        gamma = roadVz - rnd(lx, ly)

                                    //通常
                                    } .else bl {
                                        beta = SPR_TYPE_OTHER1

                                        //プレイヤーの速度に応じて避けづらくなる
                                        lx = lerp(2,  6, plVel, PL_MAX_VZ) * UNIT_POS
                                        ly = lerp(10, 16, plVel, PL_MAX_VZ) * UNIT_POS

                                        gamma = roadVz - rnd(lx, ly)
                                    }

                                    //設置する
                                    _setupSprite(ptrA, 0, beta, 100, lx, ly, lz)

                                    //前進速度
                                    //スクロール速度より下で、プレイヤーの最低速度よりも上
                                    gamma = min(gamma, roadVz - 2 * UNIT_POS)
                                    gamma = max(gamma, PL_MIN_VZ / (2 * UNIT_PL_Z) + 1 * UNIT_POS)
                                    v[ptrA+MS_PARAM1] = gamma

                                    //車線
                                    v[ptrA+MS_PARAM2] = alpha

                                    @break(2)
                                }

                                //次のレーンを判定する
                                alpha = (alpha + 1) % 3
                            }
                        }
                    }
                }
            }

            //----------------------------
            // 操作
            //入力
            @if sceneState == SCENE_GAME bl {
                //縦横
                V2_getInput(lx)
                //加速
                @key.input .dst(ipt) {
                    .up
                    .decision
                    .lclick
                }
                iptUp = ipt ? iptUp + 1 : 0
                //減速
                @key.input .dst(ipt) {
                    .down
                    .shift
                    .rclick
                }
                iptDown = ipt ? iptDown + 1 : 0
            //入力を初期化する
            } .else bl {
                V2_set(lx, 0, 0)
                iptUp = 0
                iptDown = 0
            }

            //マウスカーソル
            @if gameMode == 1 bl {
                @mouse.getPos(cursorX, cursorY)
                @pic.show [PIC_CURSOR] .pos(cursorX, cursorY) cursorPicCmd
            }

            //----------------------------
            //シフトアップ
            //スリップしていないとき
            @if `iptUp == 1 && !plSlip && plGear < GEAR_MAX && between(plTacho, GEAR_JUST_MIN, 1000) bl {
                plGear += 1
                @se.play "car_gearchange1" .opt(100, 100, 50)

                //ちょうどいいとブースト状態に入る
                @if `between(plTacho, GEAR_JUST_MIN, GEAR_JUST_MAX) bl {
                    plBoost = PL_BOOST_TIMER
                    @se.play "Raise3" .opt(100, 100, 50)
                }
            }

            //----------------------------
            //シフトダウン
            //回転数が落ちると自動で作動する
            @if `plGear > 0 && between(plTacho, 0, GEAR_DOWN_MIN) bl {
                plGear -= 1
                @se.play "car_gearchange1" .opt(100, 90, 50)
            }

            //----------------------------
            //加速
            @if `!plSlip && sceneState == SCENE_GAME bl {
                //ブースト中にアクセルを踏むと3倍加速
                plVel += GEAR_ACC * (plBoost > 0 && iptUp ? 3 : 1)
            }

            //ブーストカウンター
            @if plBoost > 0 bl {
                plBoost -= 1
            }

            //----------------------------
            //ブレーキ
            @if `iptDown bl {
                //ハンドル角度が大きい状態でブレーキを踏むとスリップする
                //80%-
                @if `!plSlip && iptDown == 1 && abs(handleAngle) >= muldiv(handleBase, 8, 10) bl {
                    plSlip = handleAngle > 0 ? 1 : -1
                } .else bl {
                    plVel -= PL_BRAKE_VZ
                }
            }

            //速度制限
            //rpm = (vel / unit + acc) * tacho_max / div
            //rpm * div * unit / tacho_max - acc * unit = vel
            alpha = muldiv(UNIT_PL_Z, GEAR_DIV, 1) - GEAR_ADD * UNIT_PL_Z
            plVel = clamp(plVel, PL_MIN_VZ, alpha)

            //----------------------------
            //タコメーター
            //rpm = (vel + acc * unit) * tm / (div * unit)
            alpha = muldiv(plVel + GEAR_ADD * UNIT_PL_Z, TACHO_MAX, GEAR_DIV * UNIT_PL_Z)
            plTacho = clamp(alpha, 0, TACHO_MAX)
            //最大ハンドル角度
            handleBase = lerp(120 * UNIT_ANG, 10 * UNIT_ANG, plVel, PL_MAX_VZ)
            //ハンドル角度
            //マウスカーソル
            @if gameMode == 1 bl {
                @if sceneState == SCENE_GAME bl {
                    alpha = atan2(cursorY - HANDLE_Y, cursorX - HANDLE_X, UNIT_ANG) + 90 * UNIT_ANG
                    alpha = clamp(alpha, -handleBase, handleBase) - handleAngle
                } .else bl {
                    alpha = 0
                }
            //キーボード
            } .else bl {
                alpha = lx * handleBase - handleAngle
            }

            updateSpeedToApproach(handleRotate, alpha, handleRotate, UNIT_ANG)
            handleAngle += handleRotate

            //----------------------------
            //アクセルを踏んでおらず、横移動量が少ないとスリップが収まる
            @if `plSlip && !iptUp && abs(handleAngle) <= muldiv(handleBase, 2, 10) bl {
                plSlip = 0
            }

            //----------------------------
            //横移動

            @if `scrollLock bl {

            } .else bl {
                //ハンドル角度から移動量を算出する
                //スリップ中
                @if `plSlip bl {
                    plVx = lerp(0, PL_MAX_VX, handleAngle + handleBase * plSlip, handleBase * 2)
                //通常
                } .else bl {
                    plVx = lerp(0, PL_MAX_VX, handleAngle, handleBase)
                }

                plVx = clamp(plVx, -PL_MAX_VX, PL_MAX_VX)
            }

            //----------------------------
            //スクロール速度
            //ゴールするとスクロールが停止する
            @if `scrollLock bl {
                roadVz = 0

            } .else bl {
                //車の速度の半分
                roadVz = muldiv(plVel, 1, UNIT_PL_Z * 2)
                @if cPowX == 0 bl {
                    plSide = 0

                //カーブに入ると進行に合わせて横にずれる
                } .else bl {
                    //カーブ開始
                    alpha = cProX - PL_Z
                    @if `between(alpha, cLen1X / 2, cLen1X) bl {
                        beta = lerp(0, 1000, alpha - cLen1X / 2, cLen1X / 2)
                    //カーブ中
                    } .elif `between(alpha - cLen1X, 0, cLen2X + cLen3X / 2) bl {
                        beta = 1000
                    //カーブ終わり
                    } .elif `between(alpha - cLen1X - cLen2X - cLen3X / 2, 0, cLen3X) bl {
                        beta = lerp(1000, 0, alpha - cLen1X - cLen2X - cLen3X / 2, cLen3X / 2)
                    } .else bl {
                        beta = 0
                    }

                    //カーブの内外ではずれる速度が変わる
                    //40％
                    gamma = 1000 + muldiv(40 * 10, v[ptrA+MS_X], PL_MAX_X) * (cPowX > 0 ? 1 : -1)
                    gamma = muldiv(cPowX, beta, 1000) * muldiv(plVel, gamma, 10 * 1000)
                    //スリップ中は弱まる
                    //50%
                    gamma = muldiv(gamma, 100 + (plSlip ? -50 : 0), 100)
                    plSide -= gamma
                    //カーブの内外ではスクロール速度が変わる
                    //8％
                    gamma = muldiv(muldiv(cPowX, v[ptrA+MS_X], PL_MAX_X), beta, 1000)
                    roadVz = muldiv(roadVz, 1000 + gamma * 2, 1000)
                }
            }

            //----------------------------
            //カーブを更新する
            //x, y
            @foreach [__id(cProX), __id(cProY)] .dst(ptrA) {
                gamma = v[ptrA+MC_LEN1] + v[ptrA+MC_LEN2] + v[ptrA+MC_LEN3]
                @if `v[ptrA+MC_PRO] < gamma {
                    v[ptrA+MC_PRO] += roadVz
                    @if `v[ptrA+MC_PRO] >= gamma bl {
                        //カーブを過ぎると初期化する
                        v[ptrA+MC_PRO..ptrA+MC_STAG] = 0

                    } .else bl {
                        //カーブ開始
                        // y = h / w^2 * (x - w)^2
                        @if `v[ptrA+MC_PRO] < v[ptrA+MC_LEN1] bl {
                            alpha = muldiv(SCR_MAX_DEPTH, pow(v[ptrA+MC_PRO] - v[ptrA+MC_LEN1], 2), pow(v[ptrA+MC_LEN1], 2))
                            beta = gamma - v[ptrA+MC_PRO]
                        //カーブ中
                        } .elif `v[ptrA+MC_PRO] < v[ptrA+MC_LEN1] + v[ptrA+MC_LEN2] bl {
                            alpha = 0
                            beta = gamma - v[ptrA+MC_PRO]
                        //カーブ終わり
                        } .else bl {
                            alpha = 0
                            $b = v[ptrA+MC_PRO] - (v[ptrA+MC_LEN1] + v[ptrA+MC_LEN2]) - v[ptrA+MC_LEN3]
                            beta = muldiv(SCR_MAX_DEPTH, pow($b, 2), pow(v[ptrA+MC_LEN3], 2))
                        }

                        v[ptrA+MC_SSTA] = max(alpha, 0)
                        v[ptrA+MC_SLEN] = max(beta, 0)
                        v[ptrA+MC_STAG] = v[ptrA+MC_POW]
                    }
                }
            }

            //----------------------------
            //山と空の位置は道路のピクチャに合わせて変わる
            grScrY = 9999 * UNIT_POS
            grZ = SCR_MAX_DEPTH
            bgScrY = 9999 * UNIT_POS

            //道路が上方向に曲がると荒くなる
            _addYRelativeToRoad(ly, SCR_MAX_DEPTH)
            sharpFine = clamp( ly * ROAD_FINE_MUL, 0, ROAD_MAX_FINE)
            //db_log(__str("fine:\",sharpFine))
            //道路のピクチャを更新する
            //手前から順番に
            scrY = ROAD_START_Y + SCR_HALF_H
            @loop ROAD_SEGMENT .dst(idxA) bl {
                //Y軸のスクリーン座標をZ軸に変換する
                V3_set(lx, 0, 0, 0)
                //下に傾くとZ座標を決める曲線が移り変わる
                alpha = muldiv(SCR_DIST, camY, scrY) + camZ
                //easeInoutCubic
                /*
                @if sharpFine > 0 bl {
                    @if idxA < ROAD_SEGMENT / 2 bl {
                        beta = divmul(4 * pow(idxA, 3), ROAD_SEGMENT * ROAD_SEGMENT * ROAD_SEGMENT, SCR_MAX_DEPTH)
                    } .else bl {
                        beta = SCR_MAX_DEPTH + divmul(4 * pow(idxA - ROAD_SEGMENT, 3), ROAD_SEGMENT * ROAD_SEGMENT * ROAD_SEGMENT, SCR_MAX_DEPTH)
                    }
                }
                */
                easingInOut(beta, idxA, SCR_MAX_DEPTH, ROAD_SEGMENT)
                //後半に行くほど補正が強まる
                //重ねすぎて自分でもよく分からなくなっている
                lz = lerp(0, sharpFine, alpha, SCR_MAX_DEPTH)
                lz = lerp(alpha, beta, lz, ROAD_MAX_FINE)
                //道路に沿ったxy座標を取得する
                _addXRelativeToRoad(lx, lz)
                _addYRelativeToRoad(ly, lz)
                //カメラの位置を引く
                V3_asSub(lx, camX)
                //横拡大率
                dsx = muldiv(SCR_DIST * UNIT_POS, 100 * ROAD_SCALE_CORRECT * ROAD_WIDTH / ROAD_PIC_WIDTH, lz)
                //スクリーン座標
                V2_set(dx, SCR_HALF_W + SCR_DIST * lx / lz, SCR_HALF_H - SCR_DIST * ly / lz)
                //地面の位置
                @if grScrY > dy {
                    grScrY = dy//画面上の高さ
                    grZ = lz//3dでの奥行き
                }
                //空の位置
                @if idxA == ROAD_SEGMENT - 1 bl {
                    bgScrY = dy
                    bgZ = lz
                }
                //ピクチャを配置する
                //最奥のピクチャが妙に伸びるので、最初の2点は省く
                @if idxA > 1 bl {                    
                    //ピクチャ番号
                    pid = PIC_ROAD + ROAD_PIC_NUM - (idxA - 1)
                    //セル番号
                    cell = ((roadStep + lz) / ROAD_PATTERN_INTERVAL) % ROAD_PATTERN_NUM + 1
                    //セグメントの中間にピクチャを置く
                    V2_set(lx, REF_X + (dx + ux) / 2, REF_Y + (dy + uy) / 2)
                    //横拡大率
                    alpha = (dsx + usx) / (2 * ROAD_SCALE_CORRECT)
                    //縦拡大率
                    //細すぎると消える
                    beta = max(100 * abs(uy - dy) / ROAD_PIC_HEIGHT, 100 / ROAD_PIC_HEIGHT)
                    //地面より下にある場合は透明にする
                    trans = grScrY < dy ? 100 : 0
                    //Red, Green
                    color = min(lz, SCR_MAX_DEPTH)
                    color = lerp(colFrontRG, colBackRG, color, SCR_MAX_DEPTH)
                    color += cos(360 * (streetStep + lz) / STREET_INTERVAL, 1, colPower)
                    //ピクチャを表示する
                    @pic.show [pid] .pos(lx, ly) .center .scale2(alpha, beta) .trans(trans) .cell(cell) .rgbs(color, color, colB, colS) roadPicCmd
                }

                //座標を移す
                usx = dsx
                V2_copy(ux, dx)
                //Y座標を一つ下に
                scrY -= 1
            }

            //道路の進行度
            roadStep = (roadStep + roadVz) % (ROAD_PATTERN_NUM * ROAD_PATTERN_INTERVAL)

            //街灯、トンネル、ゴール
            @if nextStreetObj != SPR_STATE_NONE bl {
                streetStep += roadVz
                @if streetStep >= STREET_INTERVAL bl {
                    streetStep %= STREET_INTERVAL
                    //分岐
                    pseudoSwitchState(nextStreetObj,
                        //街灯
                        [SPR_TYPE_LIGHT, bl {
                            type = SPR_TYPE_LIGHT //タイプ
                            lx = SCR_MAX_DEPTH - streetStep //Z座標
                            ly = (nextBG != nowBG ? (nextBG) : nowBG)
                            cell = CELL_LIGHT + ly //セル番号
                            usx = [180, 250, 180][ly] //拡大率
                        },
                        //トンネル
                        SPR_TYPE_TUNNEL, bl {
                            type = SPR_TYPE_TUNNEL
                            lx = SCR_MAX_DEPTH - streetStep
                            cell = CELL_TUNNEL + nextBG
                            usx = 200
                        },
                        //ゴール
                        SPR_TYPE_GOAL, bl {
                            type = SPR_TYPE_GOAL
                            lx = SCR_MAX_DEPTH - streetStep
                            cell = CELL_GOAL
                            usx = 180
                        }],
                    bl {} )

                    //使用するスプライトID
                    idxA = SPR_ID_STREET + streetCount
                    streetCount = (streetCount + 1) % SPR_NUM_STREET

                    //設置する
                    _getSprPtr(ptrA, idxA)
                    _setupSprite(ptrA, cell, type, usx, 0, 40 * usx / 100 * UNIT_POS, SCR_MAX_DEPTH - streetStep)

                    //次に現れるのは街灯
                    nextStreetObj = SPR_TYPE_LIGHT
                }
            }

            //----------------------------
            // ハンドル
            @pic.move[PIC_HANDLE] .pos(HANDLE_X, HANDLE_Y) .center .angle(handleAngle, UNIT_ANG)

            //速度計
            alpha = plVel / UNIT_PL_Z
            _showCounter(alpha, PIC_VEL_METER, METER_DIGIT, METER_WIDTH, METER_X, METER_Y)

            //タコメーター
            gamma = clamp(plTacho, 0, TACHO_MAX)
            gamma = min((gamma - TACHO_MAX) * TACHO_ANGLE / TACHO_MAX, 0)
            @pic.move[PIC_TACHO] .pos(TACHO_X, TACHO_Y) .center .angle(gamma, UNIT_ANG)

            //ギア
            cell = plGear + 2
            @pic.show[PIC_GEAR] .pos(GEAR_X, GEAR_Y) .topRight .cell(cell) gearPicCmd

            //時計
            //m:ss:MM
            alpha = min(courseTimer / (60 * 60), 9) * 10000 + (courseTimer / 60 % 60) * 100 + (courseTimer % 60) * 100 / 60 
            _showCounter(alpha, PIC_TIMER, TIMER_DIGIT, TIMER_WIDTH, TIMER_X, TIMER_Y)

            //進行度
            V2_set(lx,
                CURSOR_X + muldiv(CURSOR_LENGTH, coursePro, courseLen),
                CURSOR_Y
            )
            @pic.move[PIC_LENGTH] .pos(lx, ly)

            //衝突回数
            @if `damageCountUpdate bl {
                damageCountUpdate = FALSE
                _showCounter(damageCount, PIC_DAMAGE, DAMAGE_DIGIT, DAMAGE_WIDTH, DAMAGE_X, DAMAGE_Y)
            }

            //----------------------------
            //カメラのX座標をプレイヤーのX座標に合わせる
            _getSprPtr(ptrA, SPR_ID_PLAYER)
            camX = v[ptrA+MS_X]
            _addXRelativeToRoad(camX, PL_Z)

            //----------------------------
            //アザーカーの車線移動用のフラグ
            laneFlag = 0x00
            @loop SPR_ID_OTHER .dst(idxA) {
                idxA += SPR_ID_OTHER
                _getSprPtr(ptrA, idxA)

                @if `v[ptrA+MS_STATE] == SPR_STATE_ENABLE bl {
                    alpha = v[ptrA+MS_PARAM2]
                    laneFlag |= 1 << alpha
                }
            }

            //----------------------------
            //スプライトを更新する
            @loop SPR_NUM .dst(idxA) {
                _getSprPtr(ptrA, idxA)
                //スプライトの状態
                state = v[ptrA+MS_STATE]

                @if state < SPR_STATE_CREATE bl {
                    @continue
                }

                //スプライトのタイプ
                type = v[ptrA+MS_TYPE]
                //タイマー
                v[ptrA+MS_TIMER] += 1
                timer = v[ptrA+MS_TIMER]
                //タイプごとに分岐する
                //アザーカーはもう一つ下
                pseudoSwitchState(type,
                    //自車
                    [SPR_TYPE_PLAYER, bl {
                        pseudoSwitchState(state,
                            [SPR_STATE_CREATE, bl {
                                _changeSpriteState(ptrA, SPR_STATE_ENABLE)
                            },
                            //走行時
                            SPR_STATE_ENABLE, bl {
                                //横移動
                                @if `abs(plSide) > 1000 bl {
                                    v[ptrA+MS_X] += plSide / 1000
                                    plSide %= 1000
                                }

                                v[ptrA+MS_X] = clamp(v[ptrA+MS_X] + plVx, -PL_MAX_X, PL_MAX_X)

                                //状態
                                @if `!scrollLock bl {
                                    //通常走行
                                    cell = 0
                                    @if `abs(plVx) > 1 * UNIT_POS bl { 
                                        cell = plVx < 0 ? 3 : 1
                                    }

                                    //路肩を擦る
                                    @if `abs(v[ptrA+MS_X]) > PL_SHOULDER_X bl {
                                        cell = v[ptrA+MS_X] > 0 ? (timer % 4 < 2 ? 4 : 3) : (timer % 4 < 2 ? 2 : 1)

                                        //減速
                                        plVel -= PL_SHOULDER_VZ

                                        //衝突音
                                        @if `!(timer % 6) bl {
                                            @se.play "Fire1" .opt(80, 200, 50)
                                        }
                                    }

                                    //スリップ
                                    @if `plSlip bl {
                                        cell = plSlip < 0 ? (timer % 4 < 2 ? 4 : 3) : (timer % 4 < 2 ? 2 : 1)

                                        //ブレーキ音
                                        @if `!(timer % 6) bl {
                                            alpha = lerp(80, 150, plVel, PL_MAX_VZ)
                                            @se.play "car_brake1" .opt(50, alpha, 50)
                                        }
                                    }

                                    //走行音
                                    beta = lerp(8, 4, plVel, PL_MAX_VZ)
                                    @if `!(timer % beta) bl {
                                        alpha = lerp(50, 150, plVel, PL_MAX_VZ)
                                        beta = (plBoost > 0 && iptUp > 0) ? 100 : 70
                                        @se.play "car_accel1" .opt(beta, alpha, 50)
                                    }

                                } .else bl {
                                    cell = 0
                                }

                                //セル番号
                                v[ptrA+MS_CELL] = cell + CELL_PL

                            },
                            //横に吹っ飛ぶ
                            //param1: 移動量
                            SPR_STATE_DAMAGE, bl {
                                //スリップと同じアニメーション
                                cell = plVx > 0 ? (timer % 4 < 2 ? 4 : 3) : (timer % 4 < 2 ? 2 : 1)
                                //セル番号
                                v[ptrA+MS_CELL] = cell + CELL_PL
                                //横移動
                                v[ptrA+MS_X] = clamp(v[ptrA+MS_X] + v[ptrA+MS_PARAM1], -PL_MAX_X, PL_MAX_X)
                                //復帰する
                                @if `timer > 12  bl {
                                    _changeSpriteState(ptrA, SPR_STATE_ENABLE)

                                    v[ptrA+MS_PARAM1] = 0

                                    plVx = 0
                                    plSide = 0
                                    plSlip = 0
                                }
                            }],
                            bl {}
                        )
                    },
                    //街灯
                    //手前に移動し、見えなくなると消える
                    SPR_TYPE_LIGHT, bl {
                        v[ptrA+MS_Z] -= roadVz
                        @if `v[ptrA+MS_Z] <= SCR_MIN_DEPTH bl {
                            v[ptrA+MS_STATE] = SPR_STATE_DELETE
                        }
                    },
                    //トンネル
                    //手前に移動すると景観が切り替わる
                    SPR_TYPE_TUNNEL, bl {
                        v[ptrA+MS_Z] -= roadVz
                        @if `v[ptrA+MS_Z] <= SCR_MIN_DEPTH bl {
                            v[ptrA+MS_STATE] = SPR_STATE_DELETE

                            //景観切り替え
                            nowBG = nextBG

                            //色を変える
                            colPower = [5, 20, 15][nowBG]
                            colFrontRG = [100, 80, 100][nowBG]
                            colBackRG = 20
                            colB = [100, 110, 90][nowBG]
                            colS = 100
                        }
                    },
                    //ゴール
                    //手前に移動するとゴールとなる
                    SPR_TYPE_GOAL, bl {
                        v[ptrA+MS_Z] -= roadVz
                        @if `v[ptrA+MS_Z] < SCR_MIN_DEPTH bl {
                            v[ptrA+MS_STATE] = SPR_STATE_DELETE

                            //ゴール
                            _changeSceneState(SCENE_GOAL)
                        }
                    }],
                    bl {}
                )

                //----------------------------
                //アザーカー共通処理
                //param1: Z軸移動量
                //param2: 車線(0_2)
                @if `between(type, SPR_TYPE_OTHER1, SPR_TYPE_OTHER2) bl {
                    pseudoSwitchState(state,
                        [SPR_STATE_CREATE, bl {
                            _changeSpriteState(ptrA, SPR_STATE_ENABLE)
                            v[ptrA+MS_Y] = type == SPR_TYPE_OTHER2 ? OT2_Y : OT1_Y
                            v[ptrA+MS_SCALE] = type == SPR_TYPE_OTHER2 ? OT2_SCALE : OT1_SCALE
                            v[ptrA+MS_CELL] = type == SPR_TYPE_OTHER2 ? (rnd(0, 1) + CELL_ENEMY2) : (rnd(0, 4) + CELL_ENEMY1)
                        },
                        //走行
                        //1: 前進速度
                        SPR_STATE_ENABLE, bl {
                            //移動
                            v[ptrA+MS_Z] -= roadVz - v[ptrA+MS_PARAM1]
                            //車線移動
                            alpha = v[ptrA+MS_PARAM2]
                            beta = [-OT_LANE_X, 0, OT_LANE_X][alpha] - v[ptrA+MS_X]
                            v[ptrA+MS_X] += clamp(beta, -OT_LANE_VX, OT_LANE_VX)
                            //車線変更
                            //二秒ごとに判定する
                            //プレイヤーが近いと無視する
                            @if `!(sceneTimer % 120) && v[ptrA+MS_Z] > SCR_MAX_DEPTH / 3 && rnd(0, 99) < 40 bl {
                                //現在の車線
                                alpha = v[ptrA+MS_PARAM2]
                                //変更後の車線
                                beta = alpha == 1 ? [0, 2][rnd(0,1)] : 1
                                //他のアザーカーがなければ変更する
                                @if `!(laneFlag & (1 << beta)) bl {
                                    //フラグを切り替える
                                    gamma = 1 << alpha
                                    bitOff(laneFlag, gamma)
                                    gamma = 1 << beta
                                    bitOn(laneFlag, gamma)
                                    //車線変更
                                    v[ptrA+MS_PARAM2] = beta
                                }
                            }

                            //衝突判定
                            //alpha:種類
                            //beta:横幅
                            //gamma:縦幅
                            @if type == SPR_TYPE_OTHER2 bl {
                                alpha = 1
                                V2_set(beta, OT2_SIZE_X, OT2_SIZE_Z)
                            } .else bl {
                                alpha = 0
                                V2_set(beta, OT1_SIZE_X, OT1_SIZE_Z)
                            }

                            //プレイヤーとの衝突判定
                            _getSprPtr(ptrB, SPR_ID_PLAYER)
                            @if `v[ptrB+MS_STATE] == SPR_STATE_ENABLE && !invincibleSwitch && sceneState == SCENE_GAME bl {
                                V3_set(lx,
                                    v[ptrA+MS_X] - v[ptrB+MS_X],
                                    v[ptrA+MS_Y] - v[ptrB+MS_Y],
                                    v[ptrA+MS_Z] - v[ptrB+MS_Z]
                                )

                                @if `abs(lx) < beta && abs(lz) < gamma bl {
                                    //衝突数増加
                                    damageCount += 1
                                    damageCountUpdate = TRUE

                                    //アザーカーが吹っ飛ぶ
                                    @if alpha == 0 {
                                        _changeSpriteState(ptrA, SPR_STATE_DAMAGE)
                                        v[ptrA+MS_PARAM1] = lx > 0 ? 1 : -1
                                    }

                                    //プレイヤーは吹っ飛び、減速する
                                    _changeSpriteState(ptrB, SPR_STATE_DAMAGE)
                                    v[ptrB+MS_PARAM1] = (lx > 0 ? 1 : -1) * -1 * UNIT_POS
                                    plVel -= PL_DAMAGE_VZ

                                    //効果音
                                    alpha = rnd(10, 14) * 10
                                    @se.play "Thunder7" .opt(100, alpha, 50)
                                }
                            }

                            //見えなくなると消える
                            @if `!between(v[ptrA+MS_Z], 0, SCR_MAX_DEPTH + 20 * UNIT_POS) bl {
                                v[ptrA+MS_STATE] = SPR_STATE_DELETE
                                //ある程度の速度差で追い抜くと効果音が鳴る
                                @if `v[ptrA+MS_Z] < 0 && abs(roadVz - v[ptrA+MS_PARAM1]) > 2 * UNIT_POS bl {
                                    alpha = rnd(8, 12) * 10
                                    @se.play "maou_carthrough" .opt (50, alpha, 50)
                                }
                            }
                        },
                        //吹っ飛ぶ
                        //param: 横移動の向き
                        SPR_STATE_DAMAGE, bl {
                            v[ptrA+MS_ANGLE] += 30 * UNIT_ANG
                            v[ptrA+MS_X] += v[ptrA+MS_PARAM1] * UNIT_POS
                            v[ptrA+MS_Y] += 1 * UNIT_POS

                            @if timer > 40 bl {
                                _changeSpriteState(ptrA, SPR_STATE_DELETE)
                            }
                        }],
                        bl {}
                    )
                }
            }

            //----------------------------
            //遠景ピクチャ
            $X = 100
            $UNIT = 100
            @if `cSharpTagX != 0 bl {    
                @if `between(cProX, 0, cLen1X) bl {
                    lx = lerp(0, $X, cProX, cLen1X)
                } .elif `between(cProX - cLen1X - cLen2X, 0, cLen3X) bl {
                    lx = lerp($X, 0, cProX - cLen1X - cLen2X, cLen3X)
                } .else bl {
                    lx = $X
                }

                lx = pow(lx, 2) / $X
                lx = cSharpTagX * roadVz * lx / ($X * $UNIT)

                bgScrX  = (bgScrX  - lx + SCR_WIDTH * $UNIT) % (SCR_WIDTH * $UNIT)
                bg2ScrX = (bg2ScrX - lx + SCR_WIDTH * $UNIT) % (SCR_WIDTH * $UNIT)
            }

            @loop 2 .dst(idxA) {
                pid = PIC_SKY + idxA
                cell = nowBG * 2 + 1
                lx = bgScrX / $UNIT
                V2_set(lx, REF_X + lx - idxA * SCR_WIDTH, REF_Y + bgScrY)
                @pic.show[pid] .bottomLeft .pos(lx, ly) .cell(cell) .rgbs(100, 100, 100, 100) skyPicCmd
            }

            //----------------------------
            //地面ピクチャ
            cell = nowBG * 2 + 2
            V2_set(lx, REF_X, REF_Y + grScrY)
            @pic.show[PIC_GROUND] .topLeft .pos(lx, ly) .cell(cell) .rgbs(100, 100, 100, 100) skyPicCmd

            //----------------------------
            //スプライトを更新する
            @loop SPR_NUM .dst(idxA) {
                _getSprPtr(ptrA, idxA)
                //状態
                state = v[ptrA+MS_STATE]

                //無視
                @if state == SPR_STATE_NONE bl {
                    @continue
                }

                //スプライトを初期化する
                @if `v[ptrA+MS_STATE] == SPR_STATE_DELETE bl {
                    _initSprite(ptrA)
                    _initSpritePicture(idxA)
                    @continue
                }

                //ピクチャ番号
                pid = v[ptrA+MS_PID]
                //スプライト座標
                V3_set(lx, v[ptrA+MS_X], v[ptrA+MS_Y], v[ptrA+MS_Z])
                //道路に合わせる
                _addXRelativeToRoad(lx, lz)
                _addYRelativeToRoad(ly, lz)

                //カメラ座標を引く
                V3_asSub(lx, camX)
                //範囲外ならば隠す
                @if `between(lz, SCR_MIN_DEPTH, SCR_MAX_DEPTH) bl {
                    //セル番号
                    cell = v[ptrA+MS_CELL]
                    //拡大率
                    alpha = muldiv(SCR_DIST * UNIT_POS, v[ptrA+MS_SCALE], lz)
                    //回転角度
                    beta = v[ptrA+MS_ANGLE]
                    //透明度
                    gamma = 0
                    @if `lz > SCR_MAX_DEPTH - SCR_OPACITY_AWAY bl {
                        gamma = clamp( 100 - (SCR_MAX_DEPTH - lz) * 100 / SCR_OPACITY_AWAY, 0, 100)
                    }
                    //スクリーン座標 + 調節
                    V2_set(dx, REF_X + SCR_HALF_W + SCR_DIST * lx / lz, REF_Y + SCR_HALF_H - SCR_DIST * ly / lz)
                    //Red, Green
                    color = min(lz, SCR_MAX_DEPTH)
                    color = lerp(colFrontRG, colBackRG, color, SCR_MAX_DEPTH)
                    color += cos(360 * (streetStep + lz) / STREET_INTERVAL, 1, colPower)
                    //ピクチャを更新する
                    @pic.show[pid] .pos(dx, dy) .scale(alpha) .trans(gamma) .cell(cell) .rgbs(color, color, colB, colS) .angle(beta, UNIT_ANG) spritePicCmd

                } .else bl {
                    @pic.show [pid] .pos(OUT_RANGE, OUT_RANGE) .trans(100) spritePicCmd
                }
            }

            //----------------------------
            //スプライトのピクチャIDをソートする
            //Z座標とスプライトIDを列挙し、ピクチャIDをスプライトID順に並べる
            //道路のピクチャまでソートするのは不安なので前後に振り分ける
            sortSprId.enum(0, SPR_NUM)
            @loop SPR_NUM .dst(idxA) bl {
                _getSprPtr(ptrA, idxA)
                //奥行を登録する
                v[__id(sortDepth) + idxA] = v[ptrA+MS_Z]
                //移動元ピクチャID
                lx = v[ptrA+MS_PID]
                //移動先ピクチャID
                ly = idxA + PIC_SORTED
                @pic[lx].setId .move(ly, 1)
            }

            //Z座標でスプライトIDを降順に並べ替える
            sortDepth.sortDescending(SPR_NUM) .sync(sortSprId)

            //並び替えた順にピクチャIDを移動させる
            @loop SPR_NUM .dst(idxB) bl {
                //スプライトID
                idxA = v[__id(sortSprId) + idxB]
                //奥行
                lz = v[__id(sortDepth) + idxB]
                //移動元ピクチャID
                lx = idxA + PIC_SORTED
                //移動先ピクチャID
                //道路の奥行を境に前後に分かれる
                ly = idxB + (lz < grZ ? (PIC_SPR_FRONT) : PIC_SPR_BACK)
                @pic[lx].setId .move(ly, 1)
                //メンバー変数に代入
                _getSprPtr(ptrA, idxA)
                v[ptrA+MS_PID] = ly
            }
        }
    }

    //------------------------------------------------------------------------------------
    //終了する
    @label.set "gameEnd"
    bl {
        initPicturesAndVariables(PIC_HEAD, NUM_OF_PIC, TV_MINIGAME, NUM_OF_VAR, TS_MINIGAME, NUM_OF_STR)
        restoreBGM()
        fastFadeInTintOnly(1)
    }
}