#include "DxLib.h"
#include <vector>

const int WIDTH = 1200, HEIGHT = 688; // ウィンドウの幅と高さのピクセル数

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
    // ＤＸライブラリを使用する準備
    SetWindowText("JUMP ACTION"); // ウィンドウのタイトル
    SetGraphMode(WIDTH, HEIGHT, 32); // ウィンドウの大きさとカラービット数の指定
    ChangeWindowMode(TRUE); // ウィンドウモードで起動
    if (DxLib_Init() == -1) return -1; // ライブラリ初期化 エラーが起きたら終了
    SetBackgroundColor(0, 0, 0); // 背景色の指定
    SetDrawScreen(DX_SCREEN_BACK); // 描画面を裏画面にする

    int img_bg = LoadGraph("image_Chapter13/ninja_run/bg.png"); // 変数に背景を読み込む
    int img_floor = LoadGraph("image_Chapter13/ninja_run/floor.png"); // 床（ブロック）を読み込む
    int img_ninja[4] = { // 配列に忍者の画像を読み込む
        LoadGraph("image_Chapter13/ninja_run/ninja0.png"),
        LoadGraph("image_Chapter13/ninja_run/ninja1.png"),
        LoadGraph("image_Chapter13/ninja_run/ninja2.png"),
        LoadGraph("image_Chapter13/ninja_run/ninja3.png")
    };

    // 床を管理するための定数、vector(動的配列）
    const int FLOOR_Y = HEIGHT - 88; // 床のy座標（忍者の足元の座標になる）
    const int BLOCK_W = 30; // 1つのブロックの幅
    const int BLOCK_MAX = WIDTH / BLOCK_W + 1; // 床が何個のブロックで構成されるか
    std::vector<int> floor(BLOCK_MAX, 0); // 床の状態 1ならブロックがある、0なら無い（穴）

    enum { TITLE, PLAY, OVER }; // シーンの定数
    int scene = TITLE; // どのシーンかを管理する変数
    int timer = 0; // 経過時間（フレーム数）を数える変数
    int score = 0; // スコアを代入する変数
    int hisco = 10000; // ハイスコアを代入する変数
    int ninja_x = 100; // 忍者のx座標を代入する変数
    int ninja_y = FLOOR_Y; // 忍者のy座標（足元の位置）を代入する変数
    int ninja_vy = 0; // ジャンプ中のy軸方向の速さを代入する変数
    bool ninja_jump = false; // ジャンプ中かを管理するフラグ（ジャンプ中はtrue）
    int dist = 0; // 穴（ブロックの無いマス）を配置するタイミングの計算に使用
    int space = 0; // 穴が何マス続くか

    while (ProcessMessage() == 0) { // メインループ
        ClearDrawScreen(); // 画面をクリアする
        for (int i = 0; i < 30; i++) { // 空のグラデーションを描く
            int col = GetColor(i * 4, 0, 120 - i * 4);
            int y = i * 20;
            DrawBox(0, y, WIDTH, y + 20, col, TRUE);
        }
        DrawGraph(0, HEIGHT - 300, img_bg, TRUE); // 背景（ビル）の表示
        for (int i = 0; i < BLOCK_MAX; i++) { // ブロックを並べて床を描く
            int x = BLOCK_W * i; // ┬ 座標を計算
            int y = HEIGHT - 88; // ┘
            if (floor[i] == 1) DrawGraph(x, y, img_floor, TRUE); // 床のブロック
        }
        timer++; // 経過時間（フレーム数）をカウント

        switch (scene) { // シーンごとに処理を分ける
        case TITLE: // ---------- タイトル画面の処理 ----------
            SetFontSize(80);
            DrawString(420, 200, "NINJA RUN", GetColor(64, 128, 255));
            SetFontSize(40);
            if (timer % 30 < 15) DrawString(400, 540, "PRESS SPACE TO START.", GetColor(0, 0, 0));
            if (CheckHitKey(KEY_INPUT_SPACE)) { // スペースキーでゲーム開始
                floor = std::vector<int>(BLOCK_MAX, 1); // 床を初期状態にする
                scene = PLAY; // ゲームプレイに移行
                timer = 0;
                score = 0;
                ninja_x = 100;
                ninja_y = FLOOR_Y;
                ninja_vy = 0;
                ninja_jump = false;
                dist = 0;
                space = 0;
            }
            break;

        case PLAY: // ---------- ゲームプレイの処理 ----------
            score += 10; // プレイ中、10点ずつ増える
            // 忍者を左右キー、Zキー、Xキーで操作する
            if (CheckHitKey(KEY_INPUT_LEFT) && ninja_x > 50) ninja_x -= 20; // 左へ移動
            if (CheckHitKey(KEY_INPUT_RIGHT) && ninja_x < WIDTH - 100) ninja_x += 20; // 右へ移動
            if (ninja_jump == false) { // ジャンプしていない時
                if (floor[ninja_x / BLOCK_W] == 0) { // 忍者の足元に床がない場合
                    scene = OVER; // ゲームオーバーに移行
                    timer = 0; // 一定時間経過後にタイトルに戻すために、ここで0を代入
                    break;
                }
                if (CheckHitKey(KEY_INPUT_Z)) { // Zキー 小ジャンプ
                    score += 1000;
                    ninja_vy = -60;
                    ninja_jump = true;
                }
                if (CheckHitKey(KEY_INPUT_X)) { // Xキー 大ジャンプ
                    score += 100;
                    ninja_vy = -90;
                    ninja_jump = true;
                }
            }
            if (ninja_jump == true) { // ジャンプ中の処理
                ninja_y += ninja_vy; // ┬ 放物線のような軌跡を描く計算
                ninja_vy += 10;      // ┘
                if (ninja_y >= FLOOR_Y) { // 床に戻ったか？
                    ninja_y = FLOOR_Y;
                    ninja_jump = false; // ジャンプを解除
                }
            }
            DrawGraph(ninja_x - 42, ninja_y - 84, img_ninja[timer % 4], TRUE); // 忍者の表示

            // 床を操作する
            dist += 1; // この変数で穴を配置するタイミングを決める
            if (dist % 60 == 0) space = 4 + GetRand(18); // 60フレームごとに穴が出現
            floor.erase(floor.begin()); // 先頭要素を削除
            if (space > 0) { // spaceは穴が何マス続くかという変数
                floor.push_back(0); // 穴を追加
                space -= 1;
            }
            else {
                floor.push_back(1); // ブロックを追加
            }

            if (timer < 150) { // ゲーム開始後、150フレームの間、操作説明を表示
                SetFontSize(30);
                DrawString(100, 180, "左右キー:左右移動", GetColor(255, 128, 128));
                DrawString(100, 240, "Zキー:小ジャンプ +1000点", GetColor(160, 224, 128));
                DrawString(100, 300, "Xキー:大ジャンプ + 100点", GetColor(128, 192, 255));
            }
            break;

        case OVER: // ---------- ゲームオーバーの処理 ----------
            if (timer < 80) { // 穴に落下する演出
                ninja_y += 2;
                DrawGraph(ninja_x - 42, ninja_y - 84, img_ninja[0], TRUE); // 忍者の表示
            }
            else if (timer < 230) { // GAME OVERの文字を表示
                SetFontSize(60);
                DrawString(460, 200, "GAME OVER", GetColor(255, 0, 0));
            }
            else {
                scene = TITLE; // 一定時間が経過したらタイトルに戻る
            }
            break;
        }

        if (score > hisco) hisco = score; // ハイスコアを更新した時
        SetFontSize(30); // フォントサイズの指定
        DrawFormatString(120, 10, GetColor(128, 255, 128), "SCORE %07d", score); // スコアの表示
        DrawFormatString(880, 10, GetColor(255, 255, 0), "HIGH-SC %07d", hisco); // ハイスコアの表示

        ScreenFlip(); // 裏画面の内容を表画面に反映させる
        WaitTimer(33); // 一定時間待つ
        if (CheckHitKey(KEY_INPUT_ESCAPE) == 1) break; // ESCキーが押されたら終了
    }

    DxLib_End(); // ＤＸライブラリ使用の終了処理
    return 0; // ソフトの終了
}