squad-proto/src/world/tilemap.h

43 lines
1.9 KiB
C
Raw Normal View History

Прототип боевого ядра: отряд, хоррор-слой, ближний бой Тактический отряд с автоматическим огнём в изометрии, собственный софтверный растеризатор (raylib только окно, ввод и финальный блит). Ядро (спек, раздел 6): - LaneClear: проверка линии огня по своим, стенам и дальности; - решатель огневых позиций с гистерезисом и коммитом; - резервирование линий огня и расступание из чужих секторов; - пули-снаряды, дружественный урон физически возможен. Хоррор-слой (render-only): свет и туман войны с памятью карты, светящиеся трассеры, кровь, виньетка, зерно, напряжение. Ближний бой: сектор удара, три фазы, связка из трёх ударов, своя дисциплина «не бить сквозь своего». Проверка: squad_proto.exe --accept прогоняет критерии приёмки раздела 11 плюс блок M7 по ближнему бою — все PASS, FRIENDLY_HITS = 0 за 3 минуты боя. Сборка: cmake -S . -B build && cmake --build build --config Release raylib 6.0 подтягивается через FetchContent. Документация — CLAUDE.md как оглавление, содержание в docs/. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 17:00:49 +07:00
#pragma once
// -----------------------------------------------------------------------------
// Карта тайлов: EMPTY / WALL. Декартов мир, тайл = 1.0 юнита.
// Здесь же DDA-рейкаст (Amanatides & Woo) и circle-vs-AABB разрешение коллизий.
// Об изометрии этот модуль не знает.
// -----------------------------------------------------------------------------
#include <cstdint>
#include "core/math.h"
#include "tuning.h"
enum class Tile : uint8_t
{
EMPTY = 0,
WALL = 1,
};
struct Tilemap
{
Tile tiles[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; }
// Вне карты считаем стеной — так рейкасты и коллизии не убегают наружу.
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;
};