# Генерация рейда — `$.raid`

Порт из audm-neko (`world_plan.gd`, `village_plan.gd`, `world_stream.gd`,
`raid_weather.gd`). Мир рейда — **одна большая связная карта**: районы слева
направо (деревня → ПГТ → город → промзона → пойма), рельеф шумом, постройки по
районам, точки интереса, выходы, а содержимое появляется **стримингом** по
чанкам вокруг игрока.

```js
$.ready(() => {
    const raid = $.raid.start({ seed: 1234, width: 2400, chunk: 32, radius: 2 });
    raid.districts();     // [{ id, title, from, to, kind, loot… }]
    raid.heightAt(500);   // рельеф (высота поверхности)
    raid.buildings();     // дома с координатами и размерами
    raid.exits();         // куда эвакуироваться
    $.raid.weather();     // погода этого рейда

    $.update(() => {
        $.raid.stream($('#hero').pos().x);   // что загрузить и выгрузить
        $.raid.fill({                        // наполнить мир
            onBuilding: (b) => spawnHouse(b),
            onLoot: (l) => placeCache(l),
            onNpc: (n) => placeEnemy(n),
        });
    });
});
```

---

## 1. Создание и план

| Вызов | Что делает |
|---|---|
| `$.raid.start(spec?)` | создать план, сгенерировать и сделать текущим |
| `$.raid.create(spec)` | создать план, не делая текущим |
| `$.raid.current()` / `end()` | текущий рейд / закончить |
| `$.raid.heightAt(x)` / `stream(x)` | рельеф и стриминг текущего рейда |

`spec`: `seed`, `width` (по умолчанию 2400 тайлов), `chunk` (32), `radius`
(окно стриминга), `groundY`, `street`, `relief`, `lootCap`, `npcCap`,
`districts` (своя раскладка районов).

## 2. Что отдаёт план

| Метод | Возвращает |
|---|---|
| `districts()` | районы слева направо: `{ id, title, kind, from, to, forest, loot, relief }` |
| `districtAt(x)` | район на координате |
| `heightAt(x)` | высота поверхности (число) |
| `buildings()` | `{ x, y, w, h, kind, district, rooms }` |
| `exits()` | `{ x, key, title }` — по одному на район плюс «дальний» |
| `points()` | точки интереса (`fuel`, `school`, `hospital`, `club`…) |
| `spawns()` | план `{ loot, npcs }` по всему миру |
| `weather()` | погода плана |

План — только **числа**: он дёшев, его можно сгенерировать целиком и держать.
Объекты мира создаёт игра по чанкам.

## 3. Стриминг

```js
const { load, unload } = $.raid.stream(heroX);   // номера чанков
$.raid.fill({ onChunk, onBuilding, onLoot, onNpc });
```

`stream(x)` держит окно `radius` чанков вокруг позиции: новые попадают в
`load`, ушедшие — в `unload`. Чанки, ожидающие наполнения, игра забирает через
`takePending()` (или через `fill()`), причём **один раз**: повторный вызов
ничего не вернёт, поэтому лут и NPC не двоятся.

`chunkContent(index)` отдаёт содержимое чанка для тех, кто наполняет мир сам:
`{ from, to, heights, buildings, loot, npcs, points, exits }`.

## 4. Детерминированность

Мир полностью определяется сидом: `create({ seed: 777 })` даёт те же районы,
дома и выходы при каждом запуске. Реализация — xorshift32 с **размешиванием
сида** (финализатор MurmurHash3): без него соседние сиды давали почти
одинаковые первые числа, и вся первая генерация (границы районов, первый дом,
погода) повторялась. Смена сида — `reseed(value)`.

## 5. Погода

```js
$.raid.weather();        // погода текущего рейда (одна на рейд)
$.raid.weather(seed);    // по сиду
$.raid.weathers();       // список видов
```

Виды: `clear`, `overcast`, `rain`, `storm`, `fog`. У каждого — влияние на игру:
`fog` (плотность), `rain`, `wind`, `loud` (насколько слышно шаги: в грозу тише),
`light`. Ясная погода выпадает чаще, гроза — реже.

## 6. Сейв

```js
const data = $.raid.current().save();   // { seed, width, chunk, radius, loaded }
$.raid.start(data);                     // продолжить тот же рейд
```

План не сохраняется целиком: он воспроизводится из сида, поэтому в сейве
достаточно сида, параметров и списка загруженных чанков.

## 7. Ограничения

* **мир плоский по вертикали**: рельеф — одна высота на координату X, подземного
  слоя и пещер нет (в оригинале он был зарезервирован);
* **нет дорог и рек**: в оригинале были асфальт, тропы и вода — здесь только
  районы и застройка;
* **комнаты не планируются**: `rooms` — оценка числа комнат, сами стены режет
  игра (или тайлмап);
* **модульные постройки не портированы**: `village_build.gd` собирал дома из
  частей — здесь постройка описана прямоугольником;
* **наполнение мира за игрой**: генератор не создаёт ни спрайтов, ни тел, ни
  лута — он отдаёт числа, а `fill()` превращает их в объекты;
* **нет бюджета спавна по радиусу**: капы (`lootCap`/`npcCap`) глобальные, а
  «сколько держать в памяти» решает окно стриминга.

## 8. Проверка

```bash
# детерминированность, раскладка районов, дома, выходы, капы, стриминг, погода
build/_deps/quickjs-build/qjs tests/js/raid_test.mjs
```
