#pragma once // ----------------------------------------------------------------------------- // Карта тайлов: EMPTY / WALL. Декартов мир, тайл = 1.0 юнита. // Здесь же DDA-рейкаст (Amanatides & Woo) и circle-vs-AABB разрешение коллизий. // Об изометрии этот модуль не знает. // ----------------------------------------------------------------------------- #include #include "engine/math.h" #include "engine/config.h" enum class Tile : uint8_t { EMPTY = 0, WALL = 1, }; struct Tilemap { Tile tiles[MAP_W * MAP_H] = {}; // Тема отделки тайла: какой НАБОР спрайтов пола и стен на нём лежит. // Слой отдельный от tiles намеренно — проходимость и внешний вид это два // разных вопроса, и смешав их в одном значении, мы получили бы отдельный // тип тайла на каждое сочетание. Симуляция этот слой не читает вообще: // рейкаст, коллизии и правила видят только Tile. uint8_t theme[MAP_W * MAP_H] = {}; void Generate(); // единственный захардкоженный уровень static bool InBounds(int x, int y) { return unsigned(x) < unsigned(MAP_W) && unsigned(y) < unsigned(MAP_H); } Tile At(int x, int y) const { return InBounds(x, y) ? tiles[y * MAP_W + x] : Tile::WALL; } void Set(int x, int y, Tile t) { if (InBounds(x, y)) tiles[y * MAP_W + x] = t; } uint8_t ThemeAt(int x, int y) const { return InBounds(x, y) ? theme[y * MAP_W + x] : 0; } void SetTheme(int x, int y, uint8_t t) { if (InBounds(x, y)) theme[y * MAP_W + x] = t; } // Вне карты считаем стеной — так рейкасты и коллизии не убегают наружу. bool IsWall(int x, int y) const { return At(x, y) == Tile::WALL; } bool IsWallAt(Vec2 p) const { return IsWall(int(std::floor(p.x)), int(std::floor(p.y))); } // Amanatides & Woo. true, если отрезок a->b пересекает стену. Без аллокаций. // tHit (если нужен) — параметр входа в стену вдоль отрезка, [0..1]. bool RaycastBlocked(Vec2 a, Vec2 b, float* tHit = nullptr) const; // Выталкивание круга из стен (несколько итераций — из-за углов). void ResolveCircle(Vec2& p, float r) const; // Есть ли стена в круге радиуса r вокруг p (для отбраковки кандидатов). bool CircleHitsWall(Vec2 p, float r) const; };