﻿#include "Stage.h"

#include "DxLib.h"

namespace
{
    // 0: 空白、1: 床、2: ゴール位置の目印
    const int tiles[Stage::mapHeight][Stage::mapWidth] =
    {
        {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,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,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,2,0},
        {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1},
        {0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0},
        {0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0},
        {0,0,0,1,1,1,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,0,0,0,0,0,0,0,0,0,0,0},
        {1,1,1,1,1,1,0,1,1,1,1,0,1,1,1,0,1,1,1,1},
        {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},
    };

    // ピクセル座標をタイル座標に変換する
    int PixelToTile(int pixel)
    {
        // C++の整数除算は負の値を0方向へ丸めるため、自前で下方向へ丸めます。
        if (pixel >= 0)
            return pixel / Stage::tileSize;

        return (pixel - Stage::tileSize + 1) / Stage::tileSize;
    }
}

// ステージの初期化
bool Stage::Initialize()
{
    // マップ上のゴールタイルと同じ位置に、当たり判定用の矩形を置く
    for (int y = 0; y < mapHeight; y++)
    {
        for (int x = 0; x < mapWidth; x++)
        {
            if (tiles[y][x] == TileGoal)
            {
                goal = { x * tileSize, y * tileSize, tileSize, tileSize };
                return true;
            }
        }
    }

    return false;
}

// 指定したタイル座標が床タイルかどうかを返す
bool Stage::IsSolidTile(int tileX, int tileY) const
{
    // マップの左右と上は壁扱いにして、画面外へ進めないようにする
    if (tileX < 0 || tileX >= mapWidth)
        return true;

    if (tileY < 0)
        return true;

    // 下方向は落下できるように、マップ外でも床ではない扱いにする
    if (tileY >= mapHeight)
        return false;

    return tiles[tileY][tileX] == TileSolid;
}

// 指定した矩形が床タイルと重なっているかどうかを返す
bool Stage::IsSolidAtRect(const Rect& rect) const
{
    // 矩形が少しでも重なっているタイル範囲を調べる
    int leftTile = PixelToTile(rect.x);
    int rightTile = PixelToTile(rect.x + rect.width - 1);
    int topTile = PixelToTile(rect.y);
    int bottomTile = PixelToTile(rect.y + rect.height - 1);

    for (int y = topTile; y <= bottomTile; y++)
    {
        for (int x = leftTile; x <= rightTile; x++)
        {
            if (IsSolidTile(x, y))
                return true;
        }
    }

    return false;
}

// ステージの描画
bool Stage::ResolveHorizontal(Rect& rect, int moveX) const
{
    if (moveX == 0) return false;

    rect.x += moveX;
    const int leftTile = PixelToTile(rect.x);
    const int rightTile = PixelToTile(rect.x + rect.width - 1);
    const int topTile = PixelToTile(rect.y);
    const int bottomTile = PixelToTile(rect.y + rect.height - 1);
    bool hit = false;
    int resolvedX = rect.x;

    for (int y = topTile; y <= bottomTile; y++)
    {
        for (int x = leftTile; x <= rightTile; x++)
        {
            if (!IsSolidTile(x, y)) continue;

            const int candidate = (moveX > 0)
                ? x * tileSize - rect.width
                : (x + 1) * tileSize;
            if (!hit ||
                (moveX > 0 && candidate < resolvedX) ||
                (moveX < 0 && candidate > resolvedX))
            {
                resolvedX = candidate;
            }
            hit = true;
        }
    }

    rect.x = resolvedX;
    return hit;
}

bool Stage::ResolveVertical(Rect& rect, int moveY) const
{
    if (moveY == 0) return false;

    rect.y += moveY;
    const int leftTile = PixelToTile(rect.x);
    const int rightTile = PixelToTile(rect.x + rect.width - 1);
    const int topTile = PixelToTile(rect.y);
    const int bottomTile = PixelToTile(rect.y + rect.height - 1);
    bool hit = false;
    int resolvedY = rect.y;

    for (int y = topTile; y <= bottomTile; y++)
    {
        for (int x = leftTile; x <= rightTile; x++)
        {
            if (!IsSolidTile(x, y)) continue;

            const int candidate = (moveY > 0)
                ? y * tileSize - rect.height
                : (y + 1) * tileSize;
            if (!hit ||
                (moveY > 0 && candidate < resolvedY) ||
                (moveY < 0 && candidate > resolvedY))
            {
                resolvedY = candidate;
            }
            hit = true;
        }
    }

    rect.y = resolvedY;
    return hit;
}
void Stage::Draw() const
{
    // マップ全体を走査し、床タイルだけを描画する
    for (int y = 0; y < mapHeight; y++)
    {
        for (int x = 0; x < mapWidth; x++)
        {
            if (tiles[y][x] != TileSolid)
                continue;

            DrawTile(x, y);
        }
    }

    DrawGoal();
}

// ゴールの矩形を取得する
Rect Stage::GetGoal() const
{
    return goal;
}

// 指定したタイル座標のタイルを描画する
void Stage::DrawTile(int tileX, int tileY) const
{
    // タイル座標を画面上のピクセル座標へ変換して描画します。
    DrawBox(tileX * tileSize, tileY * tileSize,
        tileX * tileSize + tileSize, tileY * tileSize + tileSize,
        GetColor(80, 80, 110), TRUE);
}

// ゴールの矩形を描画する
void Stage::DrawGoal() const
{
    DrawBox(goal.x, goal.y, goal.x + goal.width, goal.y + goal.height,
        GetColor(90, 220, 120), TRUE);
}

