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


cev[CEV_AIR_HOCKEY] .name("mg_えあほっけー") {

    def {
        NUM_OF_VAR = 64
        NUM_OF_STR = 0
        NUM_OF_PIC = 8

        BASE_LAYER = 6

        PATH_BACK   = "ah\ba"
        PATH_DISK   = "ah\di"
        PATH_CHAR   = "ah\ch"
        PATH_CURSOR = "cm\cu"

        PIC_HEAD   = 1
        PIC_TABLE  = 2
        PIC_CHAR   = 3
        PIC_DISK   = 4//+0_+2
        PIC_CURSOR = 7
        PIC_TEXT   = 8

        //結果
        RESULT_NONE = 0
        RESULT_WON_ROUND
        RESULT_LOST_ROUND
        RESULT_WIN
        RESULT_LOSE

        //CPU
        CPU_MAX = 3
        CPU_ROUND = 0
        CPU_ACCEL
        CPU_MIRROR
        CPU_KEEPER

        //定数
        MAX_POINT = 5
        SCREEN_WIDTH = 480
        SCREEN_HEIGHT= 1000
        GOAL_WIDTH = 190
        SLOPE_Y = 12
        COLL_CORRECT = 2
        COLL_FRICTION = 990
        COLL_REFLECTION = 600
        DISK_RAD = 32
        DISK_SPEED = 100
        MALLET_RAD = 32
        MALLET_MARGIN = 20
        MALLET_SPEED_NORMAL = 10
        MALLET_SPEED_FAST = 25
        MALLET_SPEED_MOUSE = 60

        ACHIEVE_WIN = 3

        //操作タイプ
        PLAYER_CTRL_KEY = 0
        PLAYER_CTRL_MOUSE
    }

    //変数定義
    defv {
        varHead = TV_MINIGAME
        
        ipt
        sx
        sy
        ex
        ey
        vx
        vy
        cx
        cy
        nx
        ny
        rx
        ry
        ptr
        idx
        pic
        cell
        time
        sca
        dot
        result
        cpuType
        cpuTimer
        plPoint
        enPoint
        diskCx
        diskCy
        diskAx
        diskAy
        diskVx
        diskVy
        diskOx1
        diskOy1
        diskOx2
        diskOy2
        enMalletCx
        enMalletCy
        enMalletAx
        enMalletAy
        plMalletCx
        plMalletCy
        plMalletAx
        plMalletAy

        varFoot

    }

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

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

        singlePicCmd = {
            .topLeft
            .scale(100)
            .trans(0)
            .rgbs(100, 100, 100, 100)
            basePicCmd
        }
        
        charaPicCmd = {
            PATH_CHAR
            .top
            .scale(100)
            .trans(0)
            .rgbs(100, 100, 100, 100)
            .grid(3, 1)
            basePicCmd
        }

        diskPicCmd = {
            PATH_DISK
            .center
            .grid(3, 1)
            basePicCmd
        }

        textStrPicCmd = {
            .pos(160, -120) .center
            .size(0, 0)
            .scale2(100, 100)
            .trans(0)
            .font("", 12) .bold
            .spacing(0, 4)
            .skin ""
            .rgbs(100, 100, 100, 100)
            basePicCmd
        }
    }

    //------------------------------------------------------------------------------------
    //変数を初期化
    bl {
        initPicturesAndVariables(PIC_HEAD, NUM_OF_PIC, TV_MINIGAME, NUM_OF_VAR, 0, 0)
        msgBottom()
        
        //ピクチャを準備する
        //テーブル
        @pic.show[PIC_TABLE] PATH_BACK .pos(0, 0) singlePicCmd
        //カーソル
        @pic.show[PIC_CURSOR] PATH_CURSOR .pos(-32, -32) singlePicCmd
        //ブームくん
        @pic.show[PIC_CHAR] .pos(160, 0) .cell(1) charaPicCmd
        //マレットとディスク
        //ディスク、自マレット、敵マレットの順
        @loop 3 .dst(idx) {
            pic = PIC_DISK + idx
            cell = idx + 1
            @pic.show[pic] {
                PATH_DISK
                .pos(0, 0) .center
                .scale(100)
                .trans(100)
                .rgbs(100, 100, 100, 100)
                .grid(3, 1) .cell(cell)
                .chromakey(1)
                .mapLayer(BASE_LAYER)
                .eraseWhenTransfer
                .affectedByTint
                .affectedByFlash
                .affectedByShake
            }
        }

        //暗転
        fastFadeInTintOnly(1)
        @wait(1)
    }

    //------------------------------------------------------------------------------------
    //ゲームを開始する
    @label.set "gameStart"
    bl {
        //得点
        @pic[PIC_TEXT].strpic __str("\c[2]0\c[0] - 0") textStrPicCmd
        //ポイントを初期化
        plPoint = 0
        enPoint = 0
        //CPUのタイプを設定する
        cpuType = rnd(0, CPU_MAX)
        //ラウンドの結果を初期化
        result = RESULT_NONE
        //----------------------------
        //ラウンドを開始する
        @label.set "roundStart"

        //相手の画像を初期化する
        @pic.show[PIC_CHAR] .pos(160, 0) .cell(1) charaPicCmd
        //得点表示を画面隅に移動させる
        @pic[PIC_TEXT].move {
            .pos(32, 120) .center
            .scale2(100, 100)
            .trans(0)
            .keepRgbs
            .time(2)
        }
        //マレットの位置を設定する
        V2_set(plMalletCx, 0, 250)
        V2_copy(plMalletAx, plMalletCx)
        V2_set(enMalletCx, 0, 750)
        V2_copy(enMalletAx, enMalletCx)
        //ディスクの位置と速度を設定する
        //ラウンドの結果でディスクの初期位置が変わる
        V2_set(diskCx, 0, (result == RESULT_WON_ROUND ? 650 : 350))
        V2_copy(diskAx, diskCx)
        V2_copy(diskOx1, diskCx)
        V2_copy(diskOx2, diskCx)
        V2_set(diskVx, 0, 0)
        //CPUパラメータを初期化
        cpuTimer = result == RESULT_WON_ROUND ? -60 : 0//すぐに動き出さないように
        enMalletAx = rnd(-32, 32)//初期位置を微妙にずらす
        //ラウンドの結果を初期化
        result = RESULT_NONE
        //マウスカーソル
        @if gameMode == PLAYER_CTRL_MOUSE bl {
            //カーソルピクチャを一度透明化する
            @pic.move[PIC_CURSOR] {
                .pos(-32, -32) .topLeft
                .scale(100)
                .trans(100)
                .rgbs(100, 100, 100, 100)
                .time(0)
            }
            //カーソルと移動させる
            @mouse.setPos(160, 146)
        }

        //----------------------------
        //メインループ
        @while result == RESULT_NONE bl {
            @wait(0)

            //----------------------------
            //ポーズ
            pauseProcces(TRUE, ipt, "gameEnd")

            //----------------------------
            //プレイヤーの移動

            //速度を初期化する
            V2_set(vx, 0, 0)
            //キー
            @if gameMode == PLAYER_CTRL_KEY bl {
                V2_getInput(vx)
                //Y軸のみ反転させる
                vy *= -1
                //速度
                @key.input .dst(ipt) {
                    .decision
                    .shift
                }
                ipt = ipt != 0 ? MALLET_SPEED_FAST : MALLET_SPEED_NORMAL
                V2_asMulScalar(vx, ipt)
                V2_clampMag(vx, ipt)

            //マウス
            } .else bl {
                //マレットはカーソルを追従する
                @mouse.getPos(sx, sy)
                //方程式解導出ツールを使用
                //掴む位置を少し上へずらす
                //移動量は距離の2/3
                $sy = (sy + 8)
                vx = ((800 * sx - 128000) / ($sy + 102) - plMalletAx) * 2 / 3
                vy = ((-1000 * $sy + 218000) / ($sy + 102) - plMalletAy) * 2 / 3
                V2_clampMag(vx, MALLET_SPEED_MOUSE)
                //カーソルピクチャ
                @pic.move[PIC_CURSOR] {
                    .pos(sx, sy) .topLeft
                    .scale(100)
                    .trans(0)
                    .rgbs(100, 100, 100, 100)
                    .time(0)
                }
            }

            //ディスクをshiftキーで停止させる
            //@key.input .dst(ipt) .shift
            //@if ipt != 0 bl {
            //    V2_set(diskVx, 0, 0)
            //}
            //座標更新
            V2_copy(plMalletCx, plMalletAx)
            V2_asAdd(plMalletAx, vx)
            //範囲制限
            V2_clampPointInsideRect2(plMalletAx,
            -SCREEN_WIDTH / 2 + MALLET_RAD,
            0 + MALLET_RAD,
            SCREEN_WIDTH / 2 - MALLET_RAD,
            SCREEN_HEIGHT / 2 - MALLET_RAD - MALLET_MARGIN)
            //----------------------------
            //CPU
            cpuTimer += 1
            //少しだけ待つ
            @if cpuTimer >= 0 bl {
                //速度を初期化する
                V2_set(vx, 0, 0)
                ipt = 1
                //左右に往復するだけ
                @if cpuType == CPU_ROUND bl {
                    //あまりにも遅い場合は直接当てに行く
                    @if `diskOy2 > 500 && abs(diskVy) < 10 bl {
                        ipt = 15//ちょっと遅い

                        sx = diskOx2
                        sy = diskOy2 + 40

                    //ひたすら往復する
                    } .else bl {
                        ipt = 25

                        time = cpuTimer % 30
                        time = time < 15 ? time : (15 - (time - 15))

                        sx = lerp(-80, 80, time, 15) + diskOx2 / 2
                        sy = 900
                    }

                //返すときだけ加速する
                } .elif cpuType == CPU_ACCEL bl {
                    //当てに行く
                    @if `diskOy2 > 500 && diskVy > -10 bl {
                        //近いと速い
                        //縦方向に強めに弾く
                        @if `diskOy2 + 20 < enMalletAy && abs(diskOx2 - enMalletAx) < 40 bl {
                            ipt = 30

                            sx = diskOx2
                            sy = diskOy2

                        } .else bl {
                            ipt = 10

                            sx = diskOx2
                            sy = diskOy2 + 200
                        }

                    //戻るのは極端に遅く、乗せられやすい
                    } .else bl {
                        ipt = 5

                        sx = diskOx2 / 3
                        sy = 800
                    }

                //相手に合わせて構える
                } .elif cpuType == CPU_MIRROR bl {
                    //当てに行く
                    @if `diskOy2 > 500 && diskVy > -10 bl {
                        //奥に動いている時、待ち構える
                        @if `abs(diskOx2 + diskVy * 2 - enMalletAx) > 60 || diskOy2 + 10 > enMalletAy bl {
                            ipt = 15

                            sx = diskOx2
                            sy = max(diskOy2 + 400, 800)

                        //微妙に横にずらして斜めに弾く
                        } .else bl {
                            ipt = 25

                            sx = diskOx2 - (diskOx2 - enMalletAx > 0 ? 1 : -1) * 40
                            sy = diskOy2 + 0
                        }

                    //相手の位置に合わせる
                    } .else bl {
                        ipt = 10

                        sx = plMalletAx
                        sy = 800
                    }

                //ゴール前で構える
                } .elif cpuType == CPU_KEEPER bl {
                    //当てに行く
                    @if `diskOy2 > 500 && diskVy > -10 bl {
                        //持ち場に戻る
                        @if diskVy > 10 bl {
                            ipt = 10
                            //少し予測する
                            sx = diskOx2 + diskVx * 2
                            sy = max(diskOy2 + 200, 900)
                        //十分に遅い時は押し込む
                        } .else bl {
                            ipt = 25
                            sx = diskOx2
                            sy = diskOy2 + 10
                        }

                    //相手の位置に合わせる
                    } .else bl {
                        ipt = 10
                        //ディスクとは反対側に置く
                        sx = 0 - diskOx2 * 2 / 3
                        sy = 900
                    }
                }

                //移動量は距離の2/3
                vx = (sx - enMalletAx) * 2 / 3
                vy = (sy - enMalletAy) * 2 / 3
                //速度制限
                V2_clampMag(vx, ipt)
                //座標更新
                V2_copy(enMalletCx, enMalletAx)
                V2_asAdd(enMalletAx, vx)
                //範囲制限
                V2_clampPointInsideRect2(enMalletAx,
                -SCREEN_WIDTH / 2 + MALLET_RAD,
                SCREEN_HEIGHT / 2 + MALLET_RAD + MALLET_MARGIN,
                SCREEN_WIDTH / 2 - MALLET_RAD,
                SCREEN_HEIGHT - MALLET_RAD)
            }
            //相手の体がマレットに合わせて少し動く
            sx = 160 + muldiv(enMalletAx, 20, 1000)
            sy = 0
            @pic.move[PIC_CHAR] {
                .pos(sx, sy) .top
                .scale(100)
                .trans(0)
                .rgbs(100, 100, 100, 100)
                .time(0)
            }

            //----------------------------
            // ディスクの座標を2F分記録する
            V2_copy(diskOx2, diskOx1)
            V2_copy(diskOx1, diskCx)

            //----------------------------
            //ディスクの衝突判定
            @loop 8 .dst(idx) bl {
                __fn _setSegment $sx $sy $ex $ey $vx $vy bl {
                    v[__id(sx)..__id(vy)] = [$sx, $sy, $ex, $ey, $vx, $vy]
                }

                //マレット
                @if `between(idx, 0, 1) bl {
                    //相手
                    @if idx == 0 bl {
                        _setSegment(enMalletCx, enMalletCy, enMalletAx, enMalletAy, enMalletAx - enMalletCx, enMalletAy - enMalletCy)
                    //自分
                    } .elif idx == 1 bl {
                        _setSegment(plMalletCx, plMalletCy, plMalletAx, plMalletAy, plMalletAx - plMalletCx, plMalletAy - plMalletCy)
                    }

                    circleToCircle(time, cx, cy, unusedV, unusedV, nx, ny, unusedV, unusedV,
                    diskCx, diskCy, diskAx, diskAy, DISK_RAD, 0,
                    sx, sy, ex, ey, MALLET_RAD, 1)

                //壁
                } .elif `between(idx, 2, 7) bl {
                    //手前左
                    @if idx == 2 bl {
                        _setSegment(-SCREEN_WIDTH/2, SLOPE_Y, -GOAL_WIDTH/2, SLOPE_Y, 0, 0)
                    //手前右
                    } .elif idx == 3 bl {
                        _setSegment( SCREEN_WIDTH/2, SLOPE_Y,  GOAL_WIDTH/2, SLOPE_Y, 0, 0)
                    //奥左
                    } .elif idx == 4 bl {
                        _setSegment(-SCREEN_WIDTH/2, SCREEN_HEIGHT - SLOPE_Y, -GOAL_WIDTH/2, SCREEN_HEIGHT - SLOPE_Y, 0, 0)
                    //奥右
                    } .elif idx == 5 bl {
                        _setSegment( SCREEN_WIDTH/2, SCREEN_HEIGHT - SLOPE_Y,  GOAL_WIDTH/2, SCREEN_HEIGHT - SLOPE_Y, 0, 0)
                    //左
                    } .elif idx == 6 bl {
                        _setSegment(-SCREEN_WIDTH/2, 0, -SCREEN_WIDTH/2, SCREEN_HEIGHT, 0, 0)
                    //右
                    } .elif idx == 7 bl {
                        _setSegment( SCREEN_WIDTH/2, 0,  SCREEN_WIDTH/2, SCREEN_HEIGHT, 0, 0)
                    }

                    circleToSegment(time, cx, cy, nx, ny, unusedV, unusedV,
                    diskCx, diskCy, diskAx, diskAy, DISK_RAD,
                    sx, sy, ex, ey, 0)

                //例外
                } .else bl {
                    @break
                }

                @if time >= 0 bl {
                    getReflection(rx, ry, dot,
                    diskVx, diskVy, nx, ny, vx, vy, COLL_FRICTION, COLL_REFLECTION)
                    //反射速度
                    @if dot <= 0 bl {
                        V2_copy(diskVx, rx)
                        //強く当たると音が鳴る
                        @if dot <= -10 bl {
                            //音量は距離で変わる
                            pic = lerp(100, 80, diskAy, 1000)
                            //周波数はランダム
                            cell = rnd(8, 12) * 10
                            @se.play "con2" .opt(pic, cell, 50)
                        }
                    }

                    //反射後の座標
                    __loop 2 $i {
                        v[$i + __id(diskAx)] = v[$i + __id(cx)] + (UNIT_SECOND - time) * v[$i + __id(diskVx)] / UNIT_SECOND + v[$i + __id(nx)] * COLL_CORRECT / UNIT_NORMAL
                    }
                }
            }

            //速度制限
            V2_clampMagByID(diskVx, DISK_SPEED)
            //座標更新
            V2_copy(diskCx, diskAx)
            V2_asAdd(diskAx, diskVx)
            //範囲制限
            V2_clampPointInsideRect2(diskAx,
            -SCREEN_WIDTH / 2 + DISK_RAD,
            0,
            SCREEN_WIDTH / 2 - DISK_RAD,
            SCREEN_HEIGHT)
            //ゴールに入った
            //自分陣地
            @if `diskCy <= DISK_RAD bl {
                result = RESULT_LOST_ROUND
            //相手陣地
            } .elif `diskCy >= SCREEN_HEIGHT - DISK_RAD bl {
                result = RESULT_WON_ROUND
            }

            //----------------------------
            //移動先とピクチャを更新する
            @foreach [ __id(diskCx), __id(enMalletCx), __id(plMalletCx)] .dst(ptr) bl {
                pic = PIC_DISK + idx
                cell = 1 + idx
                //x:-50,50 -> 90,192 y:0,100 -> 58,222
                //エクセルでゴリ押す
                $acc = 10//精度を上げる
                cy = max(1, v[1 + ptr] + 1000)//ゼロ除算を避ける
                sca = muldiv(100 * $acc, 1000, cy)
                sx  = muldiv(100, v[0 + ptr] * 4, cy) + 160
                sy  = lerp(58, 222, sca - 50 * $acc, 50 * $acc)
                sca /= $acc

                @pic.show[pic] {
                    .pos(sx, sy)
                    .scale(sca)
                    .trans(0)
                    .cell(cell)
                    diskPicCmd
                }
            }
        }

        //----------------------------
        //ラウンド終了
        //得点を追加する
        @if result == RESULT_WON_ROUND bl {
            plPoint += 1
        } .elif result == RESULT_LOST_ROUND bl {
            //無敵モード中は相手に得点が入らない
            @if invincibleSwitch.isOff bl {
                enPoint += 1
            }
        }

        //勝敗判定
        @if plPoint >= MAX_POINT bl {
            result = RESULT_WIN
        } .elif enPoint >= MAX_POINT {
            result = RESULT_LOSE
        }

        //ピクチャの移動を待つ
        @wait(0)

        //ディスクを半透明化
        @pic.move[PIC_DISK] {
            .relative
            .pos(0, 0)
            .scale(0)
            .trans(50)
            .rgbs(100, 100, 100, 100)
            .time(0)
        }

        //表示
        __fn _moveResultPicture $str {
            @pic[PIC_TEXT].move {
                .pos(160, 120) .center
                .scale2(200, 30)
                .trans(0)
                .keepRgbs
                .time(0)
            }
            @wait(0)
            @pic[PIC_TEXT].move {
                .pos(160, 120) .center
                .scale2(200, 200)
                .trans(0)
                .keepRgbs
                .time(2)
            }
        }

        //勝利
        @if result == RESULT_WIN bl {
            //勝利数カウント
            airHockeyScore += 1
            checkSetAchievement(achievedAirHocker, airHockeyScore >= ACHIEVE_WIN)

            @scr.flash .once (2) .rgbv(31, 31, 31, 20)
            @pic[PIC_TEXT].strpic __str("\c[2]あなたのかち") textStrPicCmd
            _moveResultPicture()
            @se.play "explo1" .opt(80, 100, 50)
            @se.play "cheers" .opt(100, 100, 50)
            @pic.show[PIC_CHAR] .pos(160, 0) .cell(3) charaPicCmd
            @wait(20)

        //敗北
        } .elif result == RESULT_LOSE bl {
            @scr.flash .once (2) .rgbv(31, 31, 31, 20)
            @pic[PIC_TEXT].strpic __str("\c[10]あなたのまけ") textStrPicCmd
            _moveResultPicture()
            @se.play "explo1" .opt(80, 100, 50)
            @se.play "cheers" .opt(100, 100, 50)

            @loop 5 bl {
                @pic.show[PIC_CHAR] .pos(160, 0) .cell(2) charaPicCmd
                @wait(1)
                @pic.show[PIC_CHAR] .pos(160, 0) .cell(2) .hrev charaPicCmd
                @wait(1)
            }
            @wait(10)

        //ラウンド終了
        } .else bl {
            @pic[PIC_TEXT].strpic __str("\c[2]\", plPoint, "\c[0] - \", enPoint) textStrPicCmd
            _moveResultPicture()
            //勝った
            @if result == RESULT_WON_ROUND bl {
                //一度勝つと行動パターンが変わる
                cpuType = rnd(0, CPU_MAX)

                @se.play "explo1" .opt(80, 100, 50)
                @se.play "Chime2" .opt(100, 100, 50)
                @wait(10)
            //負けた
            } .elif result == RESULT_LOST_ROUND bl {
                @se.play "explo1" .opt(100, 100, 50)
                @wait(10)
            }
        }

        //もういちどやる？
        @if `result == RESULT_WIN || result == RESULT_LOSE bl {
            replayProcess("gameStart", "gameEnd")
        //次のラウンドへ
        } .else bl {
            @label.jump "roundStart"
        }
    }

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