Загрузка данных


#include <SDL3/SDL.h>
#include <SDL3/SDL_main.h>
#include <cmath>
#include <vector>
#include <algorithm>
#include <cstring>

// Условная компиляция специфичных для веба заголовочных файлов
#ifdef __EMSCRIPTEN__
#include <emscripten.h>
#endif

// Константы графического пайплайна
const int WIDTH = 640;
const int HEIGHT = 600;
const int FPS = 30;
const float FOV = M_PI / 2.5f;
const int CASTED_RAYS = 320;
const int SCALE = WIDTH / CASTED_RAYS;
const float WALL_HEIGHT_SCALE = 10.0f;

Uint32 fireStartTime = 0;
bool gunFiring = false;

// Глобальные указатели контекста подсистемы отрисовки SDL3
SDL_Window* window = nullptr;
SDL_Renderer* renderer = nullptr;
SDL_Texture* screen_texture = nullptr;
Uint32* screen_buffer = nullptr;

Uint32* baseTexture = nullptr;
int baseTexW = 64, baseTexH = 64; // Дефолтные размеры заглушки текстуры

SDL_Texture* gunTex1 = nullptr;
SDL_Texture* gunTex2 = nullptr;

struct Wall {
    float x1, y1, x2, y2;
    Uint32* tex;
    int texW, texH;
    Wall(float _x1, float _y1, float _x2, float _y2,
         Uint32* _tex = nullptr, int _w = 0, int _h = 0)
        : x1(_x1), y1(_y1), x2(_x2), y2(_y2),
          tex(_tex), texW(_w), texH(_h) {}
};

struct Enemy {
    float x, y;
    float speed = 1.0f;
    int hp = 3;
    Uint32* aliveTex; int aW, aH;
    Uint32* deadTex;  int dW, dH;
    Enemy(float _x, float _y,
          Uint32* a, int aw, int ah,
          Uint32* d, int dw, int dh)
        : x(_x), y(_y), aliveTex(a), aW(aw), aH(ah),
          deadTex(d), dW(dw), dH(dh) {}
};

struct Player {
    float x = 150.0f, y = 150.0f;
    float angle = 0.0f;
    float vx = 0.0f, vy = 0.0f;
    const float accel = 0.4f, decel = 0.5f, maxSpeed = 5.0f;
    float rot_speed = 0.12f;
};

std::vector<Wall> staticWalls;
std::vector<Enemy> enemies;

Uint32* fimozTexAlive = nullptr, *fimozTexDead = nullptr;
int fimozW = 64, fimozH = 64, fimozDW = 64, fimozDH = 64;

// Генератор процедурной текстуры (fallback, если файлы не загружены через VFS)
Uint32* createDummyTexture(int w, int h, Uint32 color1, Uint32 color2) {
    Uint32* pixels = new Uint32[w * h];
    for (int y = 0; y < h; y++) {
        for (int x = 0; x < w; x++) {
            pixels[y * w + x] = ((x / 8 + y / 8) % 2 == 0) ? color1 : color2;
        }
    }
    return pixels;
}

// Архитектура уровней (1 - 9)
void loadLevel1() {
    staticWalls.clear(); enemies.clear();
    staticWalls = {
        {100,100,300,100, baseTexture, baseTexW, baseTexH},
        {300,100,300,300, baseTexture, baseTexW, baseTexH},
        {300,300,100,300, baseTexture, baseTexW, baseTexH},
        {100,300,100,100, baseTexture, baseTexW, baseTexH}
    };
    enemies.emplace_back(500,200, fimozTexAlive, fimozW, fimozH, fimozTexDead, fimozDW, fimozDH);
}

void loadLevel2() {
    staticWalls.clear(); enemies.clear();
    staticWalls = {
        {50,50, 600,50, baseTexture, baseTexW, baseTexH},
        {600,50, 600,400, baseTexture, baseTexW, baseTexH},
        {600,400, 50,400, baseTexture, baseTexW, baseTexH},
        {50,400, 50,50, baseTexture, baseTexW, baseTexH}
    };
    enemies.emplace_back(300,200, fimozTexAlive, fimozW, fimozH, fimozTexDead, fimozDW, fimozDH);
}

void loadLevel3() {
    staticWalls.clear(); enemies.clear();
    for (int i = 0; i < 5; ++i)
        staticWalls.emplace_back(100+i*100, 100, 100+i*100, 500, baseTexture, baseTexW, baseTexH);
    enemies.emplace_back(550, 300, fimozTexAlive, fimozW, fimozH, fimozTexDead, fimozDW, fimozDH);
    enemies.emplace_back(400, 150, fimozTexAlive, fimozW, fimozH, fimozTexDead, fimozDW, fimozDH);
}

void loadLevel4() {
    staticWalls.clear(); enemies.clear();
    for (int i = 0; i < 5; ++i)
        staticWalls.emplace_back(100+i*100, 100, 100+i*100, 500, baseTexture, baseTexW, baseTexH);
    enemies.emplace_back(550, 300, fimozTexAlive, fimozW, fimozH, fimozTexDead, fimozDW, fimozDH);
    enemies.emplace_back(400, 150, fimozTexAlive, fimozW, fimozH, fimozTexDead, fimozDW, fimozDH);
    enemies.emplace_back(300,200, fimozTexAlive, fimozW, fimozH, fimozTexDead, fimozDW, fimozDH);
    enemies.emplace_back(500,200, fimozTexAlive, fimozW, fimozH, fimozTexDead, fimozDW, fimozDH);
}

void loadLevel5() {
    staticWalls.clear(); enemies.clear();
    for (int i = 0; i < 4; ++i) {
        if (i % 2 == 0)
            staticWalls.emplace_back(50, 100 + i * 100, 500, 100 + i * 100, baseTexture, baseTexW, baseTexH);
        else
            staticWalls.emplace_back(150, 100 + i * 100, 600, 100 + i * 100, baseTexture, baseTexW, baseTexH);
    }
    enemies.emplace_back(100, 150, fimozTexAlive, fimozW, fimozH, fimozTexDead, fimozDW, fimozDH);
    enemies.emplace_back(550, 250, fimozTexAlive, fimozW, fimozH, fimozTexDead, fimozDW, fimozDH);
    enemies.emplace_back(100, 350, fimozTexAlive, fimozW, fimozH, fimozTexDead, fimozDW, fimozDH);
}

void loadLevel6() {
    staticWalls.clear(); enemies.clear();
    staticWalls.emplace_back(50, 50, 250, 50, baseTexture, baseTexW, baseTexH);
    staticWalls.emplace_back(250, 50, 250, 250, baseTexture, baseTexW, baseTexH);
    staticWalls.emplace_back(250, 250, 50, 250, baseTexture, baseTexW, baseTexH);
    staticWalls.emplace_back(50, 250, 50, 50, baseTexture, baseTexW, baseTexH);

    staticWalls.emplace_back(350, 300, 550, 300, baseTexture, baseTexW, baseTexH);
    staticWalls.emplace_back(550, 300, 550, 500, baseTexture, baseTexW, baseTexH);
    staticWalls.emplace_back(550, 500, 350, 500, baseTexture, baseTexW, baseTexH);
    staticWalls.emplace_back(350, 500, 350, 300, baseTexture, baseTexW, baseTexH);

    enemies.emplace_back(150, 150, fimozTexAlive, fimozW, fimozH, fimozTexDead, fimozDW, fimozDH);
    enemies.emplace_back(450, 400, fimozTexAlive, fimozW, fimozH, fimozTexDead, fimozDW, fimozDH);
    enemies.emplace_back(300, 100, fimozTexAlive, fimozW, fimozH, fimozTexDead, fimozDW, fimozDH);
}

void loadLevel7() {
    staticWalls.clear(); enemies.clear();
    staticWalls.emplace_back(320, 100, 320, 250, baseTexture, baseTexW, baseTexH);
    staticWalls.emplace_back(320, 350, 320, 500, baseTexture, baseTexW, baseTexH);
    staticWalls.emplace_back(100, 300, 250, 300, baseTexture, baseTexW, baseTexH);
    staticWalls.emplace_back(390, 300, 540, 300, baseTexture, baseTexW, baseTexH);

    enemies.emplace_back(150, 150, fimozTexAlive, fimozW, fimozH, fimozTexDead, fimozDW, fimozDH);
    enemies.emplace_back(480, 150, fimozTexAlive, fimozW, fimozH, fimozTexDead, fimozDW, fimozDH);
    enemies.emplace_back(150, 450, fimozTexAlive, fimozW, fimozH, fimozTexDead, fimozDW, fimozDH);
    enemies.emplace_back(480, 450, fimozTexAlive, fimozW, fimozH, fimozTexDead, fimozDW, fimozDH);
}

void loadLevel8() {
    staticWalls.clear(); enemies.clear();
    for (int i = 0; i < 6; ++i) {
        float x_offset = i * 60;
        staticWalls.emplace_back(100 + x_offset, 100, 100 + x_offset, 200, baseTexture, baseTexW, baseTexH);
        staticWalls.emplace_back(100 + x_offset, 400, 100 + x_offset, 500, baseTexture, baseTexW, baseTexH);
    }
    staticWalls.emplace_back(460, 200, 460, 400, baseTexture, baseTexW, baseTexH);

    enemies.emplace_back(420, 300, fimozTexAlive, fimozW, fimozH, fimozTexDead, fimozDW, fimozDH);
    enemies.emplace_back(420, 250, fimozTexAlive, fimozW, fimozH, fimozTexDead, fimozDW, fimozDH);
    enemies.emplace_back(420, 350, fimozTexAlive, fimozW, fimozH, fimozTexDead, fimozDW, fimozDH);
}

void loadLevel9() {
    staticWalls.clear(); enemies.clear();
    for (int x = 150; x <= 500; x += 150) {
        for (int y = 150; y <= 450; y += 150) {
            staticWalls.emplace_back(x - 10, y, x + 10, y, baseTexture, baseTexW, baseTexH);
            staticWalls.emplace_back(x, y - 10, x, y + 10, baseTexture, baseTexW, baseTexH);
        }
    }
    enemies.emplace_back(300, 300, fimozTexAlive, fimozW, fimozH, fimozTexDead, fimozDW, fimozDH);
}

bool is_running = true;

// Главный итератор рендеринга (Single-Frame Callback)
void main_loop_tick() {
    SDL_Event event;
    while (SDL_PollEvent(&event)) {
        if (event.type == SDL_EVENT_QUIT) {
            is_running = false;
#ifdef __EMSCRIPTEN__
            emscripten_cancel_main_loop();
#endif
        }
    }

    // РЕНДЕРИНГ ЗАДНЕГО ПЛАНА (Небо / Пол)
    std::fill_n(screen_buffer, WIDTH * (HEIGHT / 2), 0xFF3B3B3B); // Серый потолок
    std::fill_n(screen_buffer + WIDTH * (HEIGHT / 2), WIDTH * (HEIGHT / 2), 0xFF141414); // Темный пол

    // СИМУЛЯЦИЯ РЕЙКАСТИНГА (Рендер заглушки стен для демонстрации)
    for (int x = 0; x < WIDTH; x++) {
        // Простая синусоида для визуальной симуляции 3D-пространства, если геометрия пуста
        int wall_h = static_cast<int>((HEIGHT / 2) + std::sin(x * 0.05f) * 50);
        int start_y = std::max(0, (HEIGHT - wall_h) / 2);
        int end_y = std::min(HEIGHT, (HEIGHT + wall_h) / 2);
        
        for (int y = start_y; y < end_y; y++) {
            screen_buffer[y * WIDTH + x] = 0xFFBC1111; // Красные стены
        }
    }

    // Инъекция фреймбуфера в WebGL/SDL3 контекст текстуры
    SDL_UpdateTexture(screen_texture, nullptr, screen_buffer, WIDTH * sizeof(Uint32));
    SDL_RenderTexture(renderer, screen_texture, nullptr, nullptr);
    SDL_RenderPresent(renderer);
}

int main(int argc, char* argv[]) {
    // Подготовка и аллокация процедурных текстур
    baseTexture = createDummyTexture(baseTexW, baseTexH, 0xFF00FF00, 0xFF000000);
    fimozTexAlive = createDummyTexture(fimozW, fimozH, 0xFFFF0000, 0xFFFFFFFF);
    fimozTexDead = createDummyTexture(fimozDW, fimozDH, 0xFF0000FF, 0xFF000000);

    // Инициализация графической подсистемы SDL3
    if (!SDL_Init(SDL_INIT_VIDEO)) {
        return -1;
    }

    window = SDL_CreateWindow("Raycaster Engine Wasm", WIDTH, HEIGHT, 0);
    if (!window) return -1;

    renderer = SDL_CreateRenderer(window, nullptr);
    if (!renderer) return -1;

    // Выделение памяти под пиксельный буфер (ARGB8888)
    screen_buffer = new Uint32[WIDTH * HEIGHT];
screen_texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, WIDTH, HEIGHT);// Загрузка дефолтной картыloadLevel5();// Разделение логики выполнения: WebEnvironment против Native OS#ifdef EMSCRIPTEN// 0 указывает на автоматическую синхронизацию с VSync (RequestAnimationFrame)// 1 активирует бесконечную симуляцию циклаemscripten_set_main_loop(main_loop_tick, 0, 1);#elsewhile (is_running) {main_loop_tick();SDL_Delay(1000 / FPS);}#endif// Корректная деаллокация ресурсов в порядке, обратном инициализацииdelete[] screen_buffer;delete[] baseTexture;delete[] fimozTexAlive;delete[] fimozTexDead;SDL_DestroyTexture(screen_texture);SDL_DestroyRenderer(renderer);SDL_DestroyWindow(window);SDL_Quit();return 0;}