#define _USE_MATH_DEFINES
#define NOMINMAX
#include <windows.h>
#include <gl/GL.h>
#include <gl/GLU.h>
#include <gdiplus.h>
#include <iostream>
#include <fstream>
#include <vector>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <string>
#include <sstream>
#include <iomanip>
#include <algorithm>
#include <unordered_set>
#include <queue>
#include <tuple>
#include <limits>
#include <psapi.h>
#include <tchar.h>
#include <locale>

#pragma comment(lib, "psapi.lib")
#pragma comment(lib, "opengl32.lib")
#pragma comment(lib, "glu32.lib")
#pragma comment(lib, "gdiplus.lib")

using namespace std;

// ===================== 常量定义 =====================
#define SCREEN_W 1280
#define SCREEN_H 720
#define BLOCK_SIZE 30
#define WORLD_D 32
#define PLAYER_HP_MAX 100
#define INVENTORY_SLOT 5
#define BACKPACK_SLOT 10
#define FALL_DAMAGE_HEIGHT 3.0f
#define DRAW_DISTANCE_MIN 20.0f
#define DRAW_DISTANCE_MAX 100.0f
#define DRAW_DISTANCE_STEP 10.0f
#define DRAW_DISTANCE_DEFAULT 50.0f
#define NEAR_PLANE 0.1f    
#define FAR_PLANE 200.0f   
#define CHUNK_SIZE 16
#define TARGET_FPS 60
#define FRAME_TIME (1000 / TARGET_FPS)
#define MAX_MEMORY_MB 500
#define MEMORY_POOL_SIZE (MAX_MEMORY_MB * 1024 * 1024)

// 方块ID定义
#define BLOCK_AIR 1000
#define BLOCK_GRASS 1001
#define BLOCK_DIRT 1002
#define BLOCK_STONE 1003
#define BLOCK_LOG 2101
#define BLOCK_LEAF 2201
#define BLOCK_PLANK 2301
#define BLOCK_WORKBENCH 3001

const float FOV_MIN = 50.0f;
const float FOV_MAX = 140.0f;
const float FOV_STEP = 5.0f;

// ===================== 数据结构 =====================
struct Block {
    int id;
    Block() : id(BLOCK_AIR) {}
    Block(int i) : id(i) {}
    bool isSolid() const { return id != BLOCK_AIR; }
    bool isTransparent() const { return id == BLOCK_LEAF; }
};

// ===================== 全局变量 =====================
int WORLD_W = 1024;
int WORLD_H = 1024;
std::vector<std::vector<std::vector<Block>>> world;

GLuint blockTextures[4000] = { 0 };
int textureCount = 0;

struct Color {
    float r, g, b;
    Color(float r = 0, float g = 0, float b = 0) : r(r / 255.0f), g(g / 255.0f), b(b / 255.0f) {}
};

// 修复:问题5 - 草方块绿色,树叶改为金合欢树叶黄褐色(RGB:191,168,106)
Color blockColorFallback[] = {
    Color(0,0,0),       
    Color(34, 139, 34),  // 草方块:森林绿
    Color(153,102,51),  
    Color(136,136,136),
    Color(153,102,0),   
    Color(191, 168, 106),// 树叶:金合欢树叶色
    Color(204,153,102), 
    Color(153,85,34)
};

std::string blockName[] = {
    "空气", "草方块", "泥土", "石头", "原木", "树叶", "木板", "工作台"
};

// ===================== 3D向量结构 =====================
struct Vector3 {
    float x, y, z;
    Vector3(float _x = 0, float _y = 0, float _z = 0) : x(_x), y(_y), z(_z) {}

    Vector3 operator+(const Vector3& v) const { return Vector3(x + v.x, y + v.y, z + v.z); }
    Vector3 operator-(const Vector3& v) const { return Vector3(x - v.x, y - v.y, z - v.z); }
    Vector3 operator*(float f) const { return Vector3(x * f, y * f, z * f); }
    Vector3 operator/(float f) const { return Vector3(x / f, y / f, z / f); }

    Vector3& operator+=(const Vector3& v) {
        x += v.x;
        y += v.y;
        z += v.z;
        return *this;
    }

    Vector3& operator-=(const Vector3& v) {
        x -= v.x;
        y -= v.y;
        z -= v.z;
        return *this;
    }

    float length() const { return sqrt(x * x + y * y + z * z); }
    Vector3 normalize() const {
        float l = length();
        return l > 0.0001f ? (*this) / l : Vector3(0, 0, 1);
    }
    float dot(const Vector3& v) const { return x * v.x + y * v.y + z * v.z; }
    Vector3 cross(const Vector3& v) const {
        return Vector3(y * v.z - z * v.y, z * v.x - x * v.z, x * v.y - y * v.x);
    }
};

// 物品槽结构
struct Slot {
    int item;
    int count;
    Slot() : item(BLOCK_AIR), count(0) {}
    Slot(int i, int c) : item(i), count(c) {}
};

// 玩家结构
struct Player {
    Vector3 pos;
    Vector3 vel;
    float rotX, rotY;
    int hp;
    Slot inv[INVENTORY_SLOT];
    Slot bag[BACKPACK_SLOT];
    int selectSlot;
    int bagSelectSlot;

    Player() {
        pos = Vector3((float)(int)(WORLD_W / 2.0f), 20.0f, (float)(int)(WORLD_H / 2.0f));
        vel = Vector3(0, 0, 0);
        rotX = rotY = 0;
        hp = PLAYER_HP_MAX;
        selectSlot = 0;
        bagSelectSlot = 0;
        inv[0] = Slot(BLOCK_GRASS, 64);
        inv[1] = Slot(BLOCK_DIRT, 64);
        inv[2] = Slot(BLOCK_STONE, 64);
    }

    Vector3 getForward() const {
        return Vector3(
            sin(rotY) * cos(rotX),
            sin(rotX),
            cos(rotY) * cos(rotX)
        );
    }

    Vector3 getRight() const {
        Vector3 forward = getForward();
        Vector3 up(0, 1, 0);
        return forward.cross(up).normalize();
    }
};

// 区块结构
struct Chunk {
    int x, z;
    std::vector<std::tuple<int, int, int>> blocks;
    bool isGenerated;
    Chunk(int _x, int _z) : x(_x), z(_z), isGenerated(false) {}
    void addBlock(int bx, int by, int bz) { blocks.push_back({ bx, by, bz }); }
};

// 世界边界检查
#define IN_WORLD_X(x) ((x) >= 0 && (x) < WORLD_W)
#define IN_WORLD_Y(y) ((y) >= 0 && (y) < WORLD_D)
#define IN_WORLD_Z(z) ((z) >= 0 && (z) < WORLD_H)
#define IN_WORLD(x,y,z) (IN_WORLD_X(x) && IN_WORLD_Y(y) && IN_WORLD_Z(z))

// ===================== 全局游戏变量 =====================
Player player;
std::vector<Chunk> chunks;
int g_numChunksX, g_numChunksZ;

bool isMenu = true;
bool isWorldGenMenu = false;
bool isGenerating = false;
bool isDead = false;
bool isBag = false, isCraft = false;
int menuSelect = 0;
int worldGenSelect = 1;
int selectedWorldSize = 1;

LARGE_INTEGER freq, lastTimeCounter;
bool needRedraw = true;
float drawDistance = DRAW_DISTANCE_DEFAULT;
bool isFirstPerson = true;
ULONGLONG fpsLastTime;
int fpsFrames = 0;
int currentFPS = 0;
bool cursorVisible = false;
float fov = 85.0f;
bool showDebug = true;
int hitX, hitY, hitZ;
bool hasHitBlock = false;
LPVOID g_MemoryPool = nullptr;

// 修复:问题4 - 文字渲染计数器,降低渲染频率(每2帧画一次)
int textRenderCounter = 0;

// Win32/OpenGL全局变量
HWND hWnd = NULL;
HDC hDC = NULL;
HGLRC hRC = NULL;
bool running = true;
HFONT hFont = NULL;
ULONG_PTR gdiplusToken = 0;

bool lastRButton = false, lastB = false, lastC = false, lastV = false;
bool lastMinus = false, lastEqual = false, lastLBracket = false, lastRBracket = false;

// ===================== 工具函数 =====================
std::string wstring_to_string(const std::wstring& wstr) {
    if (wstr.empty()) return {};
    int size = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), (int)wstr.size(), NULL, 0, NULL, NULL);
    std::string str(size, 0);
    WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), (int)wstr.size(), &str[0], size, NULL, NULL);
    return str;
}

std::wstring string_to_wstring(const std::string& str) {
    if (str.empty()) return {};
    int size = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), (int)str.size(), NULL, 0);
    std::wstring wstr(size, 0);
    MultiByteToWideChar(CP_UTF8, 0, str.c_str(), (int)str.size(), &wstr[0], size);
    return wstr;
}

float GetCPUUsage() {
    static ULONGLONG lastSysTime = 0, lastProcTime = 0;
    FILETIME ftSysIdle, ftSysKernel, ftSysUser;
    FILETIME ftProcCreation, ftProcExit, ftProcKernel, ftProcUser;

    if (!GetSystemTimes(&ftSysIdle, &ftSysKernel, &ftSysUser) ||
        !GetProcessTimes(GetCurrentProcess(), &ftProcCreation, &ftProcExit, &ftProcKernel, &ftProcUser)) {
        return 0.0f;
    }

    ULONGLONG sysTime = ((((ULONGLONG)ftSysKernel.dwHighDateTime) << 32) | ftSysKernel.dwLowDateTime) +
        ((((ULONGLONG)ftSysUser.dwHighDateTime) << 32) | ftSysUser.dwLowDateTime);
    ULONGLONG procTime = ((((ULONGLONG)ftProcKernel.dwHighDateTime) << 32) | ftProcKernel.dwLowDateTime) +
        ((((ULONGLONG)ftProcUser.dwHighDateTime) << 32) | ftProcUser.dwLowDateTime);

    float cpuUsage = 0.0f;
    if (lastSysTime && lastProcTime) {
        ULONGLONG sysDiff = sysTime - lastSysTime;
        ULONGLONG procDiff = procTime - lastProcTime;
        if (sysDiff > 0) {
            cpuUsage = (float)procDiff / sysDiff * 100.0f;
        }
    }

    lastSysTime = sysTime;
    lastProcTime = procTime;
    return cpuUsage;
}

float GetProcessMemoryUsageMB() {
    PROCESS_MEMORY_COUNTERS pmc;
    if (GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc)))
        return (float)pmc.WorkingSetSize / (1024 * 1024);
    return 0.0f;
}

void InitMemoryPool() {
    g_MemoryPool = VirtualAlloc(NULL, MEMORY_POOL_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    if (!g_MemoryPool) { MessageBox(NULL, L"内存池分配失败!", L"错误", MB_ICONERROR); exit(-1); }
}

double noise(int x, int y) {
    int n = x + y * 57; n = (n << 13) ^ n;
    return (1.0 - ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824.0);
}

double interNoise(double x, double y) {
    int fx = (int)floor(x), fy = (int)floor(y);
    double s = noise(fx, fy), t = noise(fx + 1, fy);
    double u = noise(fx, fy + 1), v = noise(fx + 1, fy + 1);
    double dx = x - fx, dy = y - fy;
    double i1 = s + dx * (t - s), i2 = u + dx * (v - u);
    return i1 + dy * (i2 - i1);
}

int getHeight(int x, int z) {
    double h = interNoise(x / 10.0, z / 10.0) * 4 + interNoise(x / 20.0, z / 20.0) * 2 + 10;
    return (int)h;
}

void generateChunk(Chunk& chunk) {
    if (chunk.isGenerated) return;

    int startX = chunk.x * CHUNK_SIZE;
    int startZ = chunk.z * CHUNK_SIZE;
    int endX = std::min(startX + CHUNK_SIZE, WORLD_W);
    int endZ = std::min(startZ + CHUNK_SIZE, WORLD_H);

    for (int x = startX; x < endX; x++) {
        for (int z = startZ; z < endZ; z++) {
            int h = getHeight(x, z);
            for (int y = 0; y < WORLD_D; y++) {
                if (y <= h) {
                    if (y == h) world[x][y][z] = Block(BLOCK_GRASS);
                    else if (y > h - 3) world[x][y][z] = Block(BLOCK_DIRT);
                    else world[x][y][z] = Block(BLOCK_STONE);
                    chunk.addBlock(x, y, z);
                }
                else world[x][y][z] = Block(BLOCK_AIR);
            }

            if (rand() % 100 == 0) {
                int th = h;
                if (th < WORLD_D - 7) {
                    for (int i = 0; i < 5; i++) {
                        world[x][th + i][z] = Block(BLOCK_LOG);
                        chunk.addBlock(x, th + i, z);
                    }
                    for (int dx = -2; dx <= 2; dx++)
                        for (int dz = -2; dz <= 2; dz++)
                            for (int dy = 0; dy < 3; dy++) {
                                int nx = x + dx, ny = th + 5 + dy, nz = z + dz;
                                if (IN_WORLD(nx, ny, nz) && !(abs(dx) == 2 && abs(dz) == 2 && dy == 2)) {
                                    world[nx][ny][nz] = Block(BLOCK_LEAF);
                                    chunk.addBlock(nx, ny, nz);
                                }
                            }
                }
            }
        }
    }
    chunk.isGenerated = true;
}

// ===================== 世界生成 =====================
void initWorld() {
    world.clear();
    world.resize(WORLD_W);
    for (int x = 0; x < WORLD_W; x++) {
        world[x].resize(WORLD_D);
        for (int y = 0; y < WORLD_D; y++) {
            world[x][y].resize(WORLD_H, Block(BLOCK_AIR));
        }
    }

    chunks.clear();
    g_numChunksX = (WORLD_W + CHUNK_SIZE - 1) / CHUNK_SIZE;
    g_numChunksZ = (WORLD_H + CHUNK_SIZE - 1) / CHUNK_SIZE;

    for (int cx = 0; cx < g_numChunksX; cx++)
        for (int cz = 0; cz < g_numChunksZ; cz++)
            chunks.emplace_back(cx, cz);

    int playerChunkX = (int)player.pos.x / CHUNK_SIZE;
    int playerChunkZ = (int)player.pos.z / CHUNK_SIZE;
    int loadRadius = (int)(drawDistance / CHUNK_SIZE) + 1;

    for (int cx = playerChunkX - loadRadius; cx <= playerChunkX + loadRadius; cx++) {
        for (int cz = playerChunkZ - loadRadius; cz <= playerChunkZ + loadRadius; cz++) {
            if (cx >= 0 && cx < g_numChunksX && cz >= 0 && cz < g_numChunksZ) {
                generateChunk(chunks[cx * g_numChunksZ + cz]);
            }
        }
    }
}

// ===================== 射线检测 =====================
// 修复:问题1 - 射线检测floor取整,修复负数坐标穿透问题
bool raycastBlock(Vector3& hitPos, int& hx, int& hy, int& hz) {
    Vector3 start = player.pos + Vector3(0, 1.7f, 0);
    Vector3 dir = player.getForward().normalize();
    float maxDist = drawDistance, step = 0.05f;

    for (float t = 0; t < maxDist; t += step) {
        Vector3 p = start + dir * t;
        int x = (int)floor(p.x), y = (int)floor(p.y), z = (int)floor(p.z);
        if (!IN_WORLD(x, y, z)) return false;
        if (world[x][y][z].isSolid()) {
            hitPos = p; hx = x; hy = y; hz = z;
            return true;
        }
    }
    return false;
}

// ===================== GDI+纹理加载 =====================
GLuint loadTexture(const wchar_t* filename) {
    Gdiplus::Bitmap* bitmap = Gdiplus::Bitmap::FromFile(filename);
    if (!bitmap || bitmap->GetLastStatus() != Gdiplus::Ok) {
        delete bitmap;
        return 0;
    }

    Gdiplus::Rect rect(0, 0, bitmap->GetWidth(), bitmap->GetHeight());
    Gdiplus::BitmapData bitmapData;
    bitmap->LockBits(&rect, Gdiplus::ImageLockModeRead, PixelFormat32bppARGB, &bitmapData);

    GLuint texID;
    glGenTextures(1, &texID);
    glBindTexture(GL_TEXTURE_2D, texID);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

    unsigned char* pixels = (unsigned char*)bitmapData.Scan0;
    int width = bitmap->GetWidth();
    int height = bitmap->GetHeight();
    unsigned char* rgbaPixels = new unsigned char[width * height * 4];
    for (int i = 0; i < width * height; i++) {
        rgbaPixels[i * 4 + 0] = pixels[i * 4 + 2];
        rgbaPixels[i * 4 + 1] = pixels[i * 4 + 1];
        rgbaPixels[i * 4 + 2] = pixels[i * 4 + 0];
        rgbaPixels[i * 4 + 3] = pixels[i * 4 + 3];
    }

    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, rgbaPixels);

    delete[] rgbaPixels;
    bitmap->UnlockBits(&bitmapData);
    delete bitmap;
    return texID;
}

// 初始化方块纹理
void initBlockTextures() {
    blockTextures[BLOCK_GRASS] = loadTexture(L"1001.png");
    blockTextures[BLOCK_DIRT] = loadTexture(L"1002.png");
    blockTextures[BLOCK_STONE] = loadTexture(L"1003.png");
    blockTextures[BLOCK_LOG] = loadTexture(L"2101.png");
    blockTextures[BLOCK_LEAF] = loadTexture(L"2201.png");
    blockTextures[BLOCK_PLANK] = loadTexture(L"2301.png");
    blockTextures[BLOCK_WORKBENCH] = loadTexture(L"3001.png");
}

// ===================== 文字绘制 =====================
// 修复:问题4 - 仅偶数帧渲染文字,降低频率消除闪烁
void drawText(float x, float y, const std::wstring& text, Gdiplus::Color color = Gdiplus::Color(255, 255, 255)) {
    if (textRenderCounter % 2 != 0) return; // 每2帧渲染一次
    if (!hDC || text.empty()) return;

    Gdiplus::Graphics graphics(hDC);
    graphics.SetTextRenderingHint(Gdiplus::TextRenderingHintAntiAliasGridFit);
    graphics.SetSmoothingMode(Gdiplus::SmoothingModeNone);

    Gdiplus::Font font(hDC, hFont);
    Gdiplus::SolidBrush brush(color);
    Gdiplus::PointF pos(x, y);

    graphics.DrawString(text.c_str(), -1, &font, pos, &brush);
}

void drawText(float x, float y, const char* text, Gdiplus::Color color = Gdiplus::Color(255, 255, 255)) {
    drawText(x, y, string_to_wstring(text), color);
}

void initFont() {
    hFont = CreateFont(
        24, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE,
        GB2312_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
        CLEARTYPE_QUALITY, DEFAULT_PITCH | FF_DONTCARE,
        _T("微软雅黑")
    );
    if (hFont) {
        SelectObject(hDC, hFont);
    }
}

// 2D/3D切换
void begin2D() {
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluOrtho2D(0, SCREEN_W, 0, SCREEN_H);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glDisable(GL_DEPTH_TEST);
    glDisable(GL_LIGHTING);
    glDisable(GL_TEXTURE_2D);
}

void end2D() {
    glEnable(GL_TEXTURE_2D);
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_LIGHTING);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(fov, (float)SCREEN_W / SCREEN_H, NEAR_PLANE, FAR_PLANE);
    glMatrixMode(GL_MODELVIEW);
}

// ===================== 方块绘制 =====================
void drawBlock(int x, int y, int z, int blockId) {
    if (blockId == BLOCK_AIR) return;

    bool faces[6] = {
        z == 0 || !world[x][y][z - 1].isSolid(),
        z == WORLD_H - 1 || !world[x][y][z + 1].isSolid(),
        x == 0 || !world[x - 1][y][z].isSolid(),
        x == WORLD_W - 1 || !world[x + 1][y][z].isSolid(),
        y == 0 || !world[x][y - 1][z].isSolid(),
        y == WORLD_D - 1 || !world[x][y + 1][z].isSolid()
    };

    // 修复:问题5 - 开启透明混合,修复树叶渲染
    if (blockId == BLOCK_LEAF) {
        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    }

    glEnable(GL_TEXTURE_2D);
    GLuint tex = blockTextures[blockId];
    if (tex != 0) {
        glBindTexture(GL_TEXTURE_2D, tex);
        glColor3f(1,1,1);
    }
    else {
        glDisable(GL_TEXTURE_2D);
        int colorIdx = 0;
        if (blockId == BLOCK_GRASS) colorIdx = 1;
        else if (blockId == BLOCK_DIRT) colorIdx = 2;
        else if (blockId == BLOCK_STONE) colorIdx = 3;
        else if (blockId == BLOCK_LOG) colorIdx = 4;
        else if (blockId == BLOCK_LEAF) colorIdx = 5;
        else if (blockId == BLOCK_PLANK) colorIdx = 6;
        else if (blockId == BLOCK_WORKBENCH) colorIdx = 7;
        glColor3f(blockColorFallback[colorIdx].r, blockColorFallback[colorIdx].g, blockColorFallback[colorIdx].b);
    }

    glPushMatrix();
    glTranslatef((GLfloat)x, (GLfloat)y, (GLfloat)z);

    glBegin(GL_QUADS);
    if (faces[0]) { glNormal3f(0, 0, -1); glTexCoord2f(0, 0); glVertex3f(-0.5, -0.5, -0.5); glTexCoord2f(1, 0); glVertex3f(0.5, -0.5, -0.5); glTexCoord2f(1, 1); glVertex3f(0.5, 0.5, -0.5); glTexCoord2f(0, 1); glVertex3f(-0.5, 0.5, -0.5); }
    if (faces[1]) { glNormal3f(0, 0, 1); glTexCoord2f(0, 0); glVertex3f(-0.5, -0.5, 0.5); glTexCoord2f(1, 0); glVertex3f(0.5, -0.5, 0.5); glTexCoord2f(1, 1); glVertex3f(0.5, 0.5, 0.5); glTexCoord2f(0, 1); glVertex3f(-0.5, 0.5, 0.5); }
    if (faces[2]) { glNormal3f(-1, 0, 0); glTexCoord2f(0, 0); glVertex3f(-0.5, -0.5, -0.5); glTexCoord2f(1, 0); glVertex3f(-0.5, -0.5, 0.5); glTexCoord2f(1, 1); glVertex3f(-0.5, 0.5, 0.5); glTexCoord2f(0, 1); glVertex3f(-0.5, 0.5, -0.5); }
    if (faces[3]) { glNormal3f(1, 0, 0); glTexCoord2f(0, 0); glVertex3f(0.5, -0.5, -0.5); glTexCoord2f(1, 0); glVertex3f(0.5, -0.5, 0.5); glTexCoord2f(1, 1); glVertex3f(0.5, 0.5, 0.5); glTexCoord2f(0, 1); glVertex3f(0.5, 0.5, -0.5); }
    if (faces[4]) { glNormal3f(0, -1, 0); glTexCoord2f(0, 0); glVertex3f(-0.5, -0.5, -0.5); glTexCoord2f(1, 0); glVertex3f(0.5, -0.5, -0.5); glTexCoord2f(1, 1); glVertex3f(0.5, -0.5, 0.5); glTexCoord2f(0, 1); glVertex3f(-0.5, -0.5, 0.5); }
    if (faces[5]) { glNormal3f(0, 1, 0); glTexCoord2f(0, 0); glVertex3f(-0.5, 0.5, -0.5); glTexCoord2f(1, 0); glVertex3f(0.5, 0.5, -0.5); glTexCoord2f(1, 1); glVertex3f(0.5, 0.5, 0.5); glTexCoord2f(0, 1); glVertex3f(-0.5, 0.5, 0.5); }
    glEnd();
    glPopMatrix();

    if (blockId == BLOCK_LEAF) glDisable(GL_BLEND);
    glDisable(GL_TEXTURE_2D);
}

// ===================== UI绘制 =====================
// 修复:问题3 - 血量条居中,位于物品栏正上方,数值居中
void drawHP() {
    begin2D();
    const float barW = 300;
    const float barH = 20;
    // 屏幕下方正中间
    float hpBarX = (SCREEN_W - barW) / 2.0f;
    float hpBarY = SCREEN_H - 100; // 物品栏上方

    // 背景
    glColor3f(0.2f, 0.2f, 0.2f);
    glRectf(hpBarX, hpBarY, hpBarX + barW, hpBarY + barH);
    // 血条
    glColor3f(1, 0, 0);
    glRectf(hpBarX, hpBarY, hpBarX + barW * (float)player.hp / PLAYER_HP_MAX, hpBarY + barH);
    
    // 血量数值居中绘制
    char buf[50];
    sprintf_s(buf, sizeof(buf), "%d/%d", player.hp, PLAYER_HP_MAX);
    glColor3f(1, 1, 1);
    float textX = hpBarX + (barW - 50) / 2.0f;
    float textY = hpBarY + 2;
    drawText(textX, textY, buf);
    end2D();
}

void drawDebugInfo() {
    begin2D();
    float startX = SCREEN_W - 250;
    float startY = 20;
    float lineHeight = 25;

    glColor4f(0.0f, 0.0f, 0.0f, 0.7f);
    glRectf(startX - 10, startY - 10, SCREEN_W - 10, startY + lineHeight * 5 + 10);

    glColor3f(1, 1, 1);
    char fpsBuf[50]; sprintf_s(fpsBuf, "FPS: %d", currentFPS);
    char posBuf[50]; sprintf_s(posBuf, "坐标: (%.1f, %.1f, %.1f)", player.pos.x, player.pos.y, player.pos.z);
    char cpuBuf[50]; sprintf_s(cpuBuf, "CPU: %.1f%%", GetCPUUsage());
    char drawDistBuf[50]; sprintf_s(drawDistBuf, "能见度: %.0f", drawDistance);
    char fovBuf[50]; sprintf_s(fovBuf, "场视角: %.0f°", fov);

    drawText(startX, startY, fpsBuf);
    drawText(startX, startY + lineHeight * 1, posBuf);
    drawText(startX, startY + lineHeight * 2, cpuBuf);
    drawText(startX, startY + lineHeight * 3, drawDistBuf);
    drawText(startX, startY + lineHeight * 4, fovBuf);
    end2D();
}

// 修复:问题3 - 物品栏屏幕下方正中间绘制
void drawUI() {
    begin2D();
    const float slotSize = 60;
    const float slotGap = 10;
    float totalWidth = INVENTORY_SLOT * slotSize + (INVENTORY_SLOT - 1) * slotGap;
    float startX = (SCREEN_W - totalWidth) / 2.0f;
    float startY = SCREEN_H - 70;

    for (int i = 0; i < INVENTORY_SLOT; i++) {
        float x = startX + i * (slotSize + slotGap);
        float y = startY;
        
        // 插槽背景
        glColor3f(0.2f, 0.2f, 0.2f); 
        glRectf((GLfloat)x, (GLfloat)y, (GLfloat)(x + slotSize), (GLfloat)(y + slotSize));
        
        if (player.inv[i].item != BLOCK_AIR) {
            glEnable(GL_TEXTURE_2D);
            GLuint tex = blockTextures[player.inv[i].item];
            if (tex != 0) {
                glBindTexture(GL_TEXTURE_2D, tex);
                glColor3f(1,1,1);
                glBegin(GL_QUADS);
                glTexCoord2f(0,0); glVertex2f(x+5, y+5);
                glTexCoord2f(1,0); glVertex2f(x+55, y+5);
                glTexCoord2f(1,1); glVertex2f(x+55, y+55);
                glTexCoord2f(0,1); glVertex2f(x+5, y+55);
                glEnd();
                glDisable(GL_TEXTURE_2D);
            }
            else {
                int colorIdx = 0;
                if (player.inv[i].item == BLOCK_GRASS) colorIdx = 1;
                else if (player.inv[i].item == BLOCK_DIRT) colorIdx = 2;
                else if (player.inv[i].item == BLOCK_STONE) colorIdx = 3;
                glColor3f(blockColorFallback[colorIdx].r, blockColorFallback[colorIdx].g, blockColorFallback[colorIdx].b);
                glRectf(x+5, y+5, x+55, y+55);
            }
            char cnt[10]; sprintf_s(cnt, sizeof(cnt), "%d", player.inv[i].count);
            glColor3f(1,1,1); drawText(x+10, y+10, cnt);
        }
        // 选中边框
        if (i == player.selectSlot) { 
            glColor3f(1,1,0); 
            glRectf(x, y, x+slotSize, y+slotSize); 
        }
    }
    end2D();
}

void drawCrosshair() {
    begin2D();
    glColor3f(1, 1, 1);
    glLineWidth(2);
    int cx = SCREEN_W / 2, cy = SCREEN_H / 2, s = 10;
    glBegin(GL_LINES);
    glVertex2i(cx - s, cy); glVertex2i(cx - 3, cy);
    glVertex2i(cx + 3, cy); glVertex2i(cx + s, cy);
    glVertex2i(cx, cy - s); glVertex2i(cx, cy - 3);
    glVertex2i(cx, cy + 3); glVertex2i(cx, cy + s);
    glEnd();
    glLineWidth(1);
    end2D();
}

// 主菜单
void drawMenu() {
    begin2D();
    glColor3f(0.1f, 0.1f, 0.2f);
    glRectf(0, 0, (GLfloat)SCREEN_W, (GLfloat)SCREEN_H);

    glColor3f(1, 1, 1);
    drawText((float)(SCREEN_W / 2 - 180), 50.0f, L"Voxel Horizon 优化3D GL版本 s2603c");

    const wchar_t* options[] = { L"开始游戏", L"读取存档", L"保存游戏", L"退出游戏" };
    int optionCount = sizeof(options) / sizeof(options[0]);
    for (int i = 0; i < optionCount; i++) {
        float y = 200.0f + i * 50.0f;
        if (i == menuSelect) {
            drawText((float)(SCREEN_W / 2 - 40), y, options[i], Gdiplus::Color(255, 255, 0));
        }
        else {
            drawText((float)(SCREEN_W / 2 - 40), y, options[i], Gdiplus::Color(180, 180, 180));
        }
    }

    const wchar_t* helpText[] = {
        L"操作指南:",
        L"W/A/S/D: 移动     空格: 跳跃     鼠标: 视角控制",
        L"鼠标左键: 挖掘方块     鼠标右键: 放置方块     1-5: 切换物品栏",
        L"B: 打开背包     C: 合成系统     V: 切换视角",
        L"-/: 减少能见度     =: 增加能见度     ESC: 返回菜单",
        L"[: 减小场视角     ]: 增加场视角     范围: 50-110°"
    };
    float helpY = 450.0f;
    for (int i = 0; i < sizeof(helpText) / sizeof(helpText[0]); i++) {
        drawText((float)(SCREEN_W / 2 - 200), helpY + i * 25, helpText[i], Gdiplus::Color(150, 150, 150));
    }
    end2D();
}

// 存档系统
void saveGame() {
    std::ofstream fout("save.dat", std::ios::binary);
    if (!fout) return;
    fout.write((char*)&WORLD_W, sizeof(WORLD_W));
    fout.write((char*)&WORLD_H, sizeof(WORLD_H));
    fout.write((char*)&player, sizeof(player));
    for (auto& wx : world) for (auto& wy : wx) for (auto& b : wy) fout.write((char*)&b, sizeof(Block));
    fout.close();
}

void loadGame() {
    std::ifstream fin("save.dat", std::ios::binary);
    if (!fin) { initWorld(); return; }
    fin.read((char*)&WORLD_W, sizeof(WORLD_W));
    fin.read((char*)&WORLD_H, sizeof(WORLD_H));

    world.clear();
    world.resize(WORLD_W);
    for (int x = 0; x < WORLD_W; x++) {
        world[x].resize(WORLD_D);
        for (int y = 0; y < WORLD_D; y++) {
            world[x][y].resize(WORLD_H);
        }
    }

    fin.read((char*)&player, sizeof(player));
    for (auto& wx : world) for (auto& wy : wx) for (auto& b : wy) fin.read((char*)&b, sizeof(Block));

    chunks.clear();
    g_numChunksX = (WORLD_W + CHUNK_SIZE - 1) / CHUNK_SIZE;
    g_numChunksZ = (WORLD_H + CHUNK_SIZE - 1) / CHUNK_SIZE;
    for (int cx = 0; cx < g_numChunksX; cx++) for (int cz = 0; cz < g_numChunksZ; cz++) chunks.emplace_back(cx, cz);
    for (int x = 0; x < WORLD_W; x++) for (int y = 0; y < WORLD_D; y++) for (int z = 0; z < WORLD_H; z++)
        if (world[x][y][z].isSolid()) {
            int cx = x / CHUNK_SIZE, cz = z / CHUNK_SIZE;
            if (cx >= 0 && cx < g_numChunksX && cz >= 0 && cz < g_numChunksZ) {
                chunks[cx * g_numChunksZ + cz].addBlock(x, y, z);
                chunks[cx * g_numChunksZ + cz].isGenerated = true;
            }
        }
    fin.close();
}

// 3D世界渲染
void draw3DWorld() {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity();

    Vector3 cam = player.pos + Vector3(0, 1.7f, 0);
    Vector3 target = cam + player.getForward();

    if (!isFirstPerson) {
        cam = player.pos + player.getForward() * -8.0f + Vector3(0, 4, 0);
        target = player.pos;
    }

    gluLookAt(cam.x, cam.y, cam.z, target.x, target.y, target.z, 0, 1, 0);

    int playerChunkX = (int)player.pos.x / CHUNK_SIZE;
    int playerChunkZ = (int)player.pos.z / CHUNK_SIZE;
    int loadRadius = (int)(drawDistance / CHUNK_SIZE) + 1;

    for (int cx = playerChunkX - loadRadius; cx <= playerChunkX + loadRadius; cx++) {
        for (int cz = playerChunkZ - loadRadius; cz <= playerChunkZ + loadRadius; cz++) {
            if (cx >= 0 && cx < g_numChunksX && cz >= 0 && cz < g_numChunksZ) {
                Chunk& chunk = chunks[cx * g_numChunksZ + cz];
                if (!chunk.isGenerated) {
                    generateChunk(chunk);
                }

                for (auto& pos : chunk.blocks) {
                    int x = std::get<0>(pos), y = std::get<1>(pos), z = std::get<2>(pos);
                    Vector3 center((float)x, (float)y, (float)z);
                    if ((center - cam).length() > drawDistance) continue;
                    drawBlock(x, y, z, world[x][y][z].id);
                }
            }
        }
    }
}

// 修复:问题1 - 重构碰撞检测,标准0.8宽×1.8高碰撞箱
bool checkCollision(Vector3 p, float w = 0.8f, float h = 1.8f) {
    int mx = (int)floor(p.x - w / 2.0f);
    int Mx = (int)ceil(p.x + w / 2.0f);
    int my = (int)floor(p.y);
    int My = (int)ceil(p.y + h);
    int mz = (int)floor(p.z - w / 2.0f);
    int Mz = (int)ceil(p.z + w / 2.0f);

    for (int x = mx; x <= Mx; x++) {
        for (int y = my; y <= My; y++) {
            for (int z = mz; z <= Mz; z++) {
                if (IN_WORLD(x, y, z) && world[x][y][z].isSolid()) {
                    return true;
                }
            }
        }
    }
    return false;
}

// 修复:问题1/2 - 物理逻辑重构,防止穿透,跳跃高度1.275格
void playerPhysics() {
    if (isDead) return;
    LARGE_INTEGER now; QueryPerformanceCounter(&now);
    float dt = (now.QuadPart - lastTimeCounter.QuadPart) / (float)freq.QuadPart;
    lastTimeCounter = now;
    dt = std::min(dt, 0.1f);

    const float colliderW = 0.8f;
    const float colliderH = 1.8f;
    player.vel.y -= 9.8f * dt; // 标准重力
    Vector3 np = player.pos;

    // X轴移动(分步处理,防止穿透)
    np.x += player.vel.x * dt;
    if (checkCollision(np, colliderW, colliderH)) {
        np.x = player.pos.x;
        player.vel.x = 0;
    }
    // Z轴移动
    np.z += player.vel.z * dt;
    if (checkCollision(np, colliderW, colliderH)) {
        np.z = player.pos.z;
        player.vel.z = 0;
    }
    // Y轴移动
    bool ground = false;
    np.y += player.vel.y * dt;
    if (checkCollision(np, colliderW, colliderH)) {
        if (player.vel.y < 0) { // 落地
            ground = true;
            np.y = (float)floor(np.y) + colliderH;
        } else { // 顶头
            np.y = player.pos.y;
        }
        player.vel.y = 0;
    }

    // 边界限制
    np.x = clamp(np.x, colliderW/2, WORLD_W - colliderW/2);
    np.z = clamp(np.z, colliderW/2, WORLD_H - colliderW/2);
    np.y = clamp(np.y, 0.0f, WORLD_D - colliderH);

    // 坠落伤害
    static float fallY = player.pos.y;
    if (ground) {
        float d = fallY - np.y;
        if (d > FALL_DAMAGE_HEIGHT) player.hp -= max(0, (int)((d - FALL_DAMAGE_HEIGHT) * 10));
        fallY = np.y;
    } else if (player.vel.y < 0) fallY = max(fallY, np.y);

    player.pos = np;
    if (player.hp <= 0) { isDead = true; cursorVisible = true; }
}

// 修复:问题2 - 跳跃逻辑修复,仅地面可跳,高度1.275格
void inputControl() {
    Vector3 hp; hasHitBlock = raycastBlock(hp, hitX, hitY, hitZ);

    static bool lastUp = false, lastDown = false, lastEnter = false;
    bool up = GetAsyncKeyState(VK_UP) & 0x8000;
    bool down = GetAsyncKeyState(VK_DOWN) & 0x8000;
    bool enter = GetAsyncKeyState(VK_RETURN) & 0x8000;

    if (isMenu) {
        int optionCount = 4;
        if (up && !lastUp) menuSelect = (menuSelect - 1 + optionCount) % optionCount;
        if (down && !lastDown) menuSelect = (menuSelect + 1) % optionCount;
        if (enter && !lastEnter) {
            if (menuSelect == 0) { isMenu = false; cursorVisible = false; ShowCursor(FALSE); }
            else if (menuSelect == 1) { loadGame(); MessageBox(NULL, L"存档已读取!", L"提示", MB_OK); }
            else if (menuSelect == 2) { saveGame(); MessageBox(NULL, L"游戏已保存!", L"提示", MB_OK); }
            else if (menuSelect == 3) running = false;
        }
        lastUp = up; lastDown = down; lastEnter = enter;
        return;
    }

    if (isGenerating || isDead) return;

    if (!isMenu && !isBag && !cursorVisible) {
        RECT r; GetWindowRect(hWnd, &r);
        int cx = r.left + SCREEN_W / 2, cy = r.top + SCREEN_H / 2;
        POINT m; GetCursorPos(&m);
        float dx = (float)(m.x - cx) * 0.002f, dy = (float)(m.y - cy) * 0.002f;
        player.rotY -= dx; player.rotX -= dy;
        player.rotX = clamp(player.rotX, -(float)M_PI/2+0.01f, (float)M_PI/2-0.01f);
        SetCursorPos(cx, cy);
    }

    // 移动逻辑
    float moveSpeed = 8.0f;
    Vector3 fwd = player.getForward();
    Vector3 rgt = player.getRight();
    Vector3 moveDir(0,0,0);

    if (GetAsyncKeyState('W') & 0x8000) moveDir += Vector3(fwd.x, 0, fwd.z);
    if (GetAsyncKeyState('S') & 0x8000) moveDir -= Vector3(fwd.x, 0, fwd.z);
    if (GetAsyncKeyState('A') & 0x8000) moveDir -= Vector3(rgt.x, 0, rgt.z);
    if (GetAsyncKeyState('D') & 0x8000) moveDir += Vector3(rgt.x, 0, rgt.z);

    if (moveDir.length() > 0.001f) moveDir = moveDir.normalize();
    player.vel.x = moveDir.x * moveSpeed;
    player.vel.z = moveDir.z * moveSpeed;

    // 修复:问题2 - 跳跃(1.275格高度,v=√(2gh)=5.0f),仅地面可跳
    static bool lastSpace = false;
    bool space = GetAsyncKeyState(VK_SPACE) & 0x8000;
    bool isOnGround = (player.vel.y == 0);
    if (space && !lastSpace && isOnGround) {
        player.vel.y = 5.0f; // 精准对应1.275格跳跃高度
    }
    lastSpace = space;

    // 挖掘/放置
    if (GetAsyncKeyState(VK_LBUTTON) & 0x8000 && hasHitBlock) {
        int t = world[hitX][hitY][hitZ].id;
        world[hitX][hitY][hitZ] = Block(BLOCK_AIR);
        for (int i = 0; i < INVENTORY_SLOT; i++) {
            if (player.inv[i].item == t || player.inv[i].item == BLOCK_AIR) {
                player.inv[i].item = t; player.inv[i].count++; break;
            }
        }
    }

    bool rButton = GetAsyncKeyState(VK_RBUTTON) & 0x8000;
    if (rButton && !lastRButton && hasHitBlock && player.inv[player.selectSlot].count > 0) {
        Vector3 d = player.getForward().normalize();
        int px = hitX + (d.x > 0.1f ? 1 : (d.x < -0.1f ? -1 : 0));
        int py = hitY + (d.y > 0.1f ? 1 : (d.y < -0.1f ? -1 : 0));
        int pz = hitZ + (d.z > 0.1f ? 1 : (d.z < -0.1f ? -1 : 0));
        if (IN_WORLD(px, py, pz) && world[px][py][pz].id == BLOCK_AIR) {
            world[px][py][pz] = Block(player.inv[player.selectSlot].item);
            player.inv[player.selectSlot].count--;
            if (player.inv[player.selectSlot].count == 0) player.inv[player.selectSlot].item = BLOCK_AIR;
            int cx = px / CHUNK_SIZE, cz = pz / CHUNK_SIZE;
            if (cx >=0 && cx < g_numChunksX && cz >=0 && cz < g_numChunksZ) {
                chunks[cx * g_numChunksZ + cz].addBlock(px, py, pz);
            }
        }
    }
    lastRButton = rButton;

    // 快捷栏
    if (GetAsyncKeyState('1') & 0x8000) player.selectSlot = 0;
    if (GetAsyncKeyState('2') & 0x8000) player.selectSlot = 1;
    if (GetAsyncKeyState('3') & 0x8000) player.selectSlot = 2;
    if (GetAsyncKeyState('4') & 0x8000) player.selectSlot = 3;
    if (GetAsyncKeyState('5') & 0x8000) player.selectSlot = 4;

    // 功能键
    bool bKey = GetAsyncKeyState('B') & 0x8000;
    if (bKey && !lastB) { isBag = !isBag; cursorVisible = isBag; ShowCursor(cursorVisible); }
    lastB = bKey;

    bool cKey = GetAsyncKeyState('C') & 0x8000;
    if (cKey && !lastC) { isCraft = !isCraft; cursorVisible = isCraft; ShowCursor(cursorVisible); }
    lastC = cKey;

    bool vKey = GetAsyncKeyState('V') & 0x8000;
    if (vKey && !lastV) { isFirstPerson = !isFirstPerson; }
    lastV = vKey;

    bool minusKey = GetAsyncKeyState(VK_OEM_MINUS) & 0x8000;
    if (minusKey && !lastMinus) { drawDistance = max(DRAW_DISTANCE_MIN, drawDistance - DRAW_DISTANCE_STEP); }
    lastMinus = minusKey;

    bool equalKey = GetAsyncKeyState(VK_OEM_PLUS) & 0x8000;
    if (equalKey && !lastEqual) { drawDistance = min(DRAW_DISTANCE_MAX, drawDistance + DRAW_DISTANCE_STEP); }
    lastEqual = equalKey;

    bool lbracketKey = GetAsyncKeyState(VK_OEM_4) & 0x8000;
    if (lbracketKey && !lastLBracket) {
        fov = max(FOV_MIN, fov - FOV_STEP);
        glMatrixMode(GL_PROJECTION); glLoadIdentity();
        gluPerspective(fov, (float)SCREEN_W/SCREEN_H, NEAR_PLANE, FAR_PLANE);
        glMatrixMode(GL_MODELVIEW);
    }
    lastLBracket = lbracketKey;

    bool rbracketKey = GetAsyncKeyState(VK_OEM_6) & 0x8000;
    if (rbracketKey && !lastRBracket) {
        fov = min(FOV_MAX, fov + FOV_STEP);
        glMatrixMode(GL_PROJECTION); glLoadIdentity();
        gluPerspective(fov, (float)SCREEN_W/SCREEN_H, NEAR_PLANE, FAR_PLANE);
        glMatrixMode(GL_MODELVIEW);
    }
    lastRBracket = rbracketKey;

    static bool lastEsc = false;
    bool esc = GetAsyncKeyState(VK_ESCAPE) & 0x8000;
    if (esc && !lastEsc) { isMenu = true; cursorVisible = true; ShowCursor(TRUE); }
    lastEsc = esc;
}

// OpenGL初始化
void initGL() {
    glEnable(GL_DEPTH_TEST);
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glEnable(GL_COLOR_MATERIAL);
    glEnable(GL_NORMALIZE);
    glShadeModel(GL_SMOOTH);

    float lightPos[] = { 0.5f,1.0f,0.5f,0 };
    float lightAmb[] = { 0.3f,0.3f,0.3f,1 };
    float lightDif[] = { 0.7f,0.7f,0.7f,1 };
    glLightfv(GL_LIGHT0, GL_POSITION, lightPos);
    glLightfv(GL_LIGHT0, GL_AMBIENT, lightAmb);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, lightDif);

    glClearColor(0.53f, 0.81f, 0.92f, 1.0f);
    glMatrixMode(GL_PROJECTION);
    gluPerspective(fov, (float)SCREEN_W / SCREEN_H, NEAR_PLANE, FAR_PLANE);
    glMatrixMode(GL_MODELVIEW);

    initFont();
    initBlockTextures();
}

// 渲染循环
void render() {
    // 修复:问题4 - 文字渲染计数器自增
    textRenderCounter++;

    if (isMenu) {
        drawMenu();
    }
    else {
        draw3DWorld();
        drawHP();
        drawUI();
        drawCrosshair();
        drawDebugInfo();
    }
    SwapBuffers(hDC);
}

// 窗口过程
LRESULT CALLBACK WndProc(HWND h, UINT msg, WPARAM w, LPARAM l) {
    switch (msg) {
    case WM_DESTROY:
        running = false;
        PostQuitMessage(0);
        break;
    case WM_SIZE:
        if (hRC) {
            int width = LOWORD(l);
            int height = HIWORD(l);
            glViewport(0, 0, width, height);
            glMatrixMode(GL_PROJECTION);
            glLoadIdentity();
            gluPerspective(fov, (float)width / height, NEAR_PLANE, FAR_PLANE);
            glMatrixMode(GL_MODELVIEW);
        }
        break;
    default:
        return DefWindowProc(h, msg, w, l);
    }
    return 0;
}

// 主函数
int main() {
    Gdiplus::GdiplusStartupInput gdiplusStartupInput;
    Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

    setlocale(LC_ALL, "zh_CN.UTF-8");
    QueryPerformanceFrequency(&freq);
    QueryPerformanceCounter(&lastTimeCounter);
    srand((unsigned int)time(NULL));
    InitMemoryPool();
    loadGame();

    WNDCLASSEX wc = { 0 };
    wc.cbSize = sizeof(WNDCLASSEX);
    wc.lpfnWndProc = WndProc;
    wc.hInstance = GetModuleHandle(NULL);
    wc.lpszClassName = L"OpenGLGame";
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    RegisterClassEx(&wc);

    hWnd = CreateWindowEx(0, L"OpenGLGame", L"Voxel Horizon Dev",
        WS_OVERLAPPEDWINDOW, 100, 100, SCREEN_W, SCREEN_H, NULL, NULL, wc.hInstance, NULL);

    hDC = GetDC(hWnd);
    PIXELFORMATDESCRIPTOR pfd = { 0 };
    pfd.nSize = sizeof(pfd); pfd.nVersion = 1;
    pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
    pfd.iPixelType = PFD_TYPE_RGBA;
    pfd.cColorBits = 32; pfd.cDepthBits = 24;
    int pf = ChoosePixelFormat(hDC, &pfd);
    SetPixelFormat(hDC, pf, &pfd);
    hRC = wglCreateContext(hDC);
    wglMakeCurrent(hDC, hRC);

    initGL();
    ShowWindow(hWnd, SW_SHOW);
    ShowCursor(TRUE);
    fpsLastTime = GetTickCount64();

    MSG msg;
    while (running) {
        if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
            continue;
        }

        ULONGLONG start = GetTickCount64();
        inputControl();
        if (!isMenu) playerPhysics();
        render();

        ULONGLONG frame = GetTickCount64() - start;
        if (frame < FRAME_TIME) Sleep((DWORD)(FRAME_TIME - frame));

        fpsFrames++;
        ULONGLONG currentTime = GetTickCount64();
        if (currentTime - fpsLastTime >= 1000) {
            currentFPS = fpsFrames;
            fpsFrames = 0;
            fpsLastTime = currentTime;
        }
    }

    if (hFont) DeleteObject(hFont);
    wglMakeCurrent(NULL, NULL);
    wglDeleteContext(hRC);
    ReleaseDC(hWnd, hDC);
    VirtualFree(g_MemoryPool, 0, MEM_RELEASE);
    Gdiplus::GdiplusShutdown(gdiplusToken);
    return 0;
}