docs: bring all READMEs current with actual code (post-sweep-10 truth pass)

Every README was read first, checked against the code in its directory, then
rewritten to describe what the code actually does now.

Corrected throughout: map size is config-driven (8192 default) not a fixed
4096; chunks are 24x24x256 not 32x32; road carving is implemented for all four
tiers, not a 'next step'; Data/ and Resources/ are empty, not populated.

Also fixed MATH_MARCHING_CUBES.md, which documented the density sign convention
exactly backwards — it claimed positive was underground, where the code treats
positive as above the surface and counts a corner inside when density < iso.
Getting that backwards inverts every normal, a bug this project has hit before.

The root README now carries accurate run steps: F5 runs the map generator (not
the game), F6 on Scenes/Main.tscn runs the 3D world, and ChunkRadius is a load
radius that should be dropped to 4-8 while iterating.

Docs only. No code changed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Stewart Howe 2026-08-05 05:23:58 -04:00
parent f9ea2da3f4
commit d72ffc0183
13 changed files with 579 additions and 409 deletions

View file

@ -1,19 +1,29 @@
# Client Module - IslaApocalypse # Client Module
**Phase 2 Status:** Active (Mesh Rendering & Vertex Painting Online) The visual layer. Takes finished chunk data and turns it into Godot nodes. It calculates nothing —
no densities, no terrain, no collision.
## Overview
The `Client` directory handles the visual representation of the 3D Voxel Engine. It is strictly responsible for receiving pre-calculated mathematical data from the `Server` and `Core` modules and translating it into Godot-native visual nodes (`MeshInstance3D`). It does not calculate collisions, densities, or terrain generation. ## `ChunkRenderer.cs`
## Key Architecture & Components A `MeshInstance3D` created per chunk by the server, which then calls `RenderChunk(data)`.
### 1. The Visualizer (`ChunkRenderer.cs`) - **Mesh generation.** Calls `MarchingCubes.GenerateMesh()`, passing the density field, the block
A script attached to a `MeshInstance3D` node that physically draws a single 32x32 chunk of the world. IDs, and the per-column data the mesher needs to colour correctly: the true (unrounded) surface
* **Mesh Generation:** Calls `MarchingCubes.GenerateMesh()` from the `Core` module, passing in the `Densities` and `BlockIDs` scalar fields. heights, the biome per column, and the road surface material per column.
* **Vertex Painting:** Dynamically creates a `StandardMaterial3D` and enables `VertexColorUseAsAlbedo = true`. This tells Godot to ignore standard texture maps and instead color the mesh using the exact RGB values we assigned to the vertices based on their `BlockID` (e.g., Green for Grass, Brown for Wasteland). - **Vertex colouring.** Creates a `StandardMaterial3D` with `VertexColorUseAsAlbedo = true`, so Godot
* **World Positioning:** Multiplies the `ChunkPosition` (e.g., [1, 0]) by `Constants.CHUNK_SIZE` and `Constants.VOXEL_SCALE` (1.0f) to perfectly align the chunk in the global 3D space. renders the per-vertex colours the mesher assigned rather than a texture. Colours fade between
* **Backface Rendering:** `CullMode` is explicitly disabled to ensure players don't see through the bottom of the map if they clip inside a mountain. materials across a tunable band instead of switching at whole-metre steps.
- **World positioning.** `ChunkPosition × CHUNK_SIZE × VOXEL_SCALE` on X and Z, `y = 0` — chunks are
## Next Immediate Steps (Phase 2, Step 4) placed by their footprint and carry their full height internally.
* Currently, the renderer draws the exact topography. Once the `Server` implements **Road Carving**, the `Client` will automatically inherit those flattened vertices and paint them with the `ASPHALT` vertex color without needing any code changes here. - **Backface rendering.** `CullMode` is disabled, so the world is still visible from underneath or
* Future implementations will include reading `BlockIDs` to paint different UV maps/textures instead of just raw RGB vertex colors. from inside terrain.
## Notes
- **One material instance per chunk.** Each renderer builds its own `StandardMaterial3D`. Since they
are all identical, a single shared material would do — an easy win whenever performance work starts.
- Chunk positioning is relative to this node's parent, so **moving the `World` node in the scene
moves the entire rendered world** with it.
## Not here yet
No textures or UV mapping (raw vertex colour only), no LOD, no player, no UI, no input handling.

View file

@ -1,27 +1,57 @@
# Core Module - IslaApocalypse # Core Module
**Phase 2 Status:** Active (Voxel Math & Parsing Online) Shared, stateless logic used by both the server and client paths: the `.dat` parser, the mesher, the
voxel data containers, and the material lookups. **No Godot nodes, no scene state.** These scripts
## Overview define what things are and how to calculate them; they never remember what is currently happening.
The `Core` directory contains the mathematical foundation, data structures, and parser required to bridge the 2D procedural generation with the 3D Voxel Engine. It is strictly logic-driven and contains no Godot Nodes or visual elements.
## Components
## Key Architecture & Components
### `MapDataParser.cs` — the data bridge
### 1. The Data Bridge (`MapDataParser.cs`) Deserializes the binary `.dat` blueprint written by `/Tools` into a `WorldBlueprint` held in RAM:
Responsible for deserializing the highly compressed binary `.dat` file generated by the `/Tools` pipeline. map size, the float heightmap, the biome map, town locations, and **all four tiers** of A\* road
* **Outputs:** A `WorldBlueprint` object held in RAM. vectors (Highways, Branch Roads, Rugged Roads, Trails).
* **Capabilities:** Reads the 2D heightmap, biome map, and town locations. Crucially, it parses all four tiers of A* road vectors (Highways, Branch Roads, Rugged Roads, Trails) for the Server to use in terrain flattening.
Read order is fixed and must match the writer exactly: magic string `"ISLA_V1"` → map size →
### 2. Voxel Data Containers (`ChunkData.cs` & `Constants.cs`) per-pixel `{height float, biome int}` in x-major order → towns → the four road tiers in order.
* **Chunk Dimensions:** Defined in `Constants.cs`. Currently optimized to `32x32` horizontal with a massive `256` vertical height limit to allow for true AAA-scale mountain ranges without heightmap compression.
* **Seamless Borders:** `ChunkData.cs` expands its scalar field arrays (`Densities` and `BlockIDs`) by `+1` on all axes. This allows the Marching Cubes algorithm to sample the neighboring chunk's data, ensuring meshes connect perfectly without gaps. **Wire-format hazard.** Biome and town-tier enums are serialized as their **ordinal values**, with
no version gate and no range validation on read. **Never reorder or insert members** in `Enums.cs`
### 3. The Math Engine (`MarchingCubes.cs`) append only, at the end. Reordering silently reinterprets every pixel of every existing `.dat`.
Transforms the raw `ChunkData` scalar fields into physical Godot `ArrayMesh` geometry. (`RoadTier` is exempt: road tiers are stored in separate file sections, so that enum never hits disk.)
* **Analytical Normals:** Instead of flat shading, the algorithm calculates exact 3D slope gradients at the corners of each voxel. This produces smooth, realistic lighting across curved terrain.
* **Vertex Painting:** Reads the `BlockID` assigned to each voxel corner and applies physical RGB colors directly to the mesh vertices before committing the geometry. ### `ChunkData.cs` + `Constants.cs` — voxel containers and tuning
- **Chunk dimensions:** `24 × 24` horizontal, `256` vertical (`Constants.cs`).
### 4. Biome & Material Mapping (`BiomePalette.cs`, `BlockRegistry.cs`) - **The `+1` padding is structural.** `Densities` and `BlockIDs` are one cell larger on every axis —
Translates the 2D world into physical 3D materials. `[25, 257, 25]` — so the mesher can evaluate the boundary cells shared with the neighbouring chunk
* **BlockRegistry:** A byte-based lookup table defining physical materials (`SAND`, `STONE`, `WASTELAND_DIRT`, `ASPHALT`). and the meshes meet without gaps.
* **BiomePalette Logic:** Uses the 2D `Biome` pixel combined with the 3D `currentY` depth to determine the block type. For example, `Biome.Wasteland` combined with a depth of 0 yields `WASTELAND_DIRT`, while a depth > 4 yields `STONE`. - **Per-column data for rendering:** `SurfaceHeights` (the true, unrounded surface), `ColumnBiomes`,
and `ColumnRoadMaterial` (0 = not a road). These let the mesher colour by real height rather than a
rounded integer.
- **Tunables in `Constants.cs`:** `BLEND_BAND_METERS` (how far one surface material fades into the
next) and the per-tier road block — width, shoulder, grade-smoothing and surface material for each
of the four road tiers, plus the derived cull padding.
### `MarchingCubes.cs` — the mesher
Turns a chunk's density field into a Godot `ArrayMesh`.
- **Analytical normals:** exact density gradients at each cell corner, interpolated along the edge by
the same fraction used for the vertex position — smooth lighting without Godot's normal pass.
- **Deterministic vertex welding:** shared vertices are keyed by an integer `EdgeKey` ordered
lowest-to-highest, so neighbouring chunks compute identical keys and no float drift creeps in.
- **Vertex colouring:** each vertex is coloured from its true float depth below the surface, fading
between the two materials either side of a boundary rather than switching at an integer depth.
**Density sign convention (load-bearing):** density is **positive above** the surface and **negative
below** it. A corner counts as inside the terrain when `density < ISO_LEVEL`.
### `BiomePalette.cs` + `BlockRegistry.cs` + `BlockData.cs` — materials
- **`BlockRegistry`:** byte-ID lookup for every block (`AIR`, `BEDROCK`, `STONE`, `DIRT`, `SAND`, the
three grasses, `SNOW`, `WASTELAND_DIRT`, `ASPHALT`).
- **`BiomePalette`** answers "what material is here?" twice, deliberately:
- `GetVoxelID(...)` — the authoritative **integer** classification by whole-block depth. Decides
what is actually stored in each voxel; used for everything non-visual.
- `GetBlendedVoxelIDs(...)` — the **rendering** version. Same question by *float* depth, returning
the two materials either side of the nearest boundary plus how far between them the point is, so
the renderer fades instead of snapping.
Both take a road-surface byte, so a trail is surfaced in dirt where a highway is surfaced in
asphalt.

View file

@ -13,16 +13,26 @@ Based on the answer, it draws a specific set of triangles *through* the invisibl
### 1. Density (The "Float" Value) ### 1. Density (The "Float" Value)
In smooth voxels, a point in space isn't just "Solid" (1) or "Empty" (0). It has a continuous **Density** value. In smooth voxels, a point in space isn't just "Solid" (1) or "Empty" (0). It has a continuous **Density** value.
* ` 1.0` = Deep underground (Solid Rock).
* ` 0.5` = Slightly underground (Dirt). **Mind the sign — this project's convention is the opposite of many tutorials.** Density here is
essentially *"how far above the surface am I?"*, computed as `(y surfaceY)` normalised by the local
slope. So it counts **upward**, not downward:
* ` 1.0` = High in the sky (Empty Space).
* ` 0.5` = Slightly above ground (Air).
* ` 0.0` = The exact surface of the ground. * ` 0.0` = The exact surface of the ground.
* `-0.5` = Slightly above ground (Air). * `-0.5` = Slightly underground (Dirt).
* `-1.0` = High in the sky (Empty Space). * `-1.0` = Deep underground (Solid Rock).
### 2. The Iso-Level (The Threshold) ### 2. The Iso-Level (The Threshold)
The Iso-Level is the exact density value where the "skin" of the world is drawn. Usually, this is `0.0`. The Iso-Level is the exact density value where the "skin" of the world is drawn. Here it is `0.0`
* If a corner's density is **greater** than `0.0`, it is "Inside" the terrain. (`Constants.ISO_LEVEL`).
* If a corner's density is **less** than `0.0`, it is "Outside" in the air. * If a corner's density is **less** than `0.0`, it is "Inside" the terrain.
* If a corner's density is **greater** than `0.0`, it is "Outside" in the air.
This matches the code: `MarchingCubes.GenerateMesh` sets a corner's bit when
`cornerDensities[i] < isolevel`. Getting this backwards inverts every surface normal and makes the
world render inside-out — which is a real bug this project has hit before.
### 3. The Triangulation Table (The Magic Array) ### 3. The Triangulation Table (The Magic Array)
A cube has 8 corners. Each corner can either be Inside or Outside. A cube has 8 corners. Each corner can either be Inside or Outside.

View file

@ -1,46 +1,50 @@
# 📜 Core Scripts Directory - Isla Apocalypse # /Core/Scripts
## Overview Pure C# data structures, shared enums, and static maths. Compiled into the shared assembly used by
The `/Core/Scripts` directory is the dedicated code repository for all pure C# data structures, universal enumerations, and static mathematical utilities. Everything in this folder is compiled into the shared assembly used by both the authoritative `/Server` and the rendering `/Client`. both `/Server` and `/Client`.
**Core Philosophy: "Pure Data, Zero State."** **"Pure data, zero state."** These scripts define *what* things are and *how* to calculate them. They
Scripts in this folder define *what* things are and *how* to calculate them, but they never remember *current* game events. They do not track who is online, what chunks are loaded, or what time of day it is. They are stateless, universal blueprints. never track live game events — who is online, which chunks are loaded, what time it is.
--- ## What's actually here
## 🧩 Current Systems (Phase 2) ### Voxel materials
- **`BlockData.cs`** — struct describing one block type (ID, name, IsSolid, BaseColor).
### 1. The Voxel Palette System - **`BlockRegistry.cs`** — the byte-ID master list (`AIR` 0, `BEDROCK`, `STONE`, `DIRT`, `SAND`, the
* **`BlockData.cs`**: The fundamental `struct` defining the properties of a single voxel (ID, Name, IsSolid, BaseColor). three grasses, `SNOW`, `WASTELAND_DIRT`, `ASPHALT`) with a safe `GetBlock` lookup.
* **`BlockRegistry.cs`**: The static master dictionary. Contains the hardcoded IDs for every block in the game (Air, Bedrock, Dirt, Asphalt) and provides a safe lookup method (`GetBlock`) for both the mesher and the physics engine. - **`BiomePalette.cs`** — decides which material sits where, given biome, depth, and whether the
* **`BiomePalette.cs`**: The translation layer between 2D and 3D. Contains the logic that dictates the vertical stacking of blocks based on biome and depth (e.g., ensuring mountains have stone beneath snow, and roads are topped with asphalt). column is roadbed. Two paths on purpose: an **integer** classification that decides what is stored
in each voxel, and a **float-depth** version used for rendering that returns the two materials
### 2. Universal Identifiers either side of a boundary so colours fade rather than snap.
* **`Enums.cs`**: The master repository for global enums (`Biome`, `TownTier`, `MapHalf`). By keeping these here, the offline Map Generator, the Server, and the Client are guaranteed to use the exact same integer values for biomes and POIs.
⚠ Note that `BlockRegistry` and the mesher's colour table hold **different RGB values** for some
--- blocks. The mesher's table is what you actually see; `BlockData.BaseColor` is currently unused by
rendering.
## 🚀 Future Roadmap & Planned Additions (Phase 2 & 3)
### Shared identifiers
As we build the 3D Chunk Manager and multiplayer networking, this folder will expand to include: - **`Enums.cs`** — `Biome`, `TownTier`, `MapHalf`, `RoadTier`.
### 3. The Blueprint Parser **`Biome` and `TownTier` are the `.dat` wire format.** They are declared without explicit values,
* **`MapDataParser.cs`** *(Next Step)*: A static utility to open and decode the `MapData_Seed_[X].dat` binary file into usable C# arrays for the Server to ingest. so each member's ordinal *is* the number written to disk, with no version gate and no validation on
read. **Append only, at the end — never reorder or insert.** Doing so silently reinterprets every
### 4. Chunk Data Structures pixel and every settlement in every existing blueprint.
* **`ChunkData.cs`**: The raw 3D array (`byte[,,]`) that holds the voxel IDs for a specific 16x16x64 area. `RoadTier` is exempt: road tiers live in separate sections of the file, so it is never serialized.
* *Note:* This structure will include the `IsPlayerProtected` boolean flag to support the "Land Claim" delta-backup system, preventing player bases from being wiped out by chunk corruption.
### World data
### 5. Math & Coordinate Utilities - **`MapDataParser.cs`** — decodes the `.dat` blueprint into a `WorldBlueprint` (heightmap, biome map,
* **`VoxelMath.cs`**: Static helper functions for translating massive 3D World Coordinates into local Chunk Coordinates (e.g., finding out which specific chunk file to load when a player walks to X: 5000, Z: -200). towns, four road tiers).
- **`ChunkData.cs`** — one chunk's density and block-ID fields, `+1` padded on every axis so the
### 6. Network Packet Definitions mesher can reach into the neighbouring chunk, plus the per-column data the renderer needs.
* Structs that define the exact byte layout of multiplayer messages (e.g., `PlayerDigPacket`, `ChatPacket`) to ensure precise Server/Client synchronization. - **`Constants.cs`** — chunk dimensions, `ISO_LEVEL`, `VOXEL_SCALE`, and the visual/road tunables
(material blend band; per-tier road width, shoulder, grade smoothing and surface).
---
### Maths
## ⚠️ Directory Rules & Best Practices - **`MarchingCubes.cs`** — density field → `ArrayMesh`, with analytical normals and deterministic
integer-keyed vertex welding.
1. **No Godot Node Inheritance:** Scripts here should rarely, if ever, inherit from `Node` or `Node3D`. They are pure C# classes, structs, or static utilities. - **`MATH_MARCHING_CUBES.md`** — an explainer of the algorithm.
2. **No `_Process` or `_Ready`:** Because these are not attached to active objects in the game world, they do not use Godot's frame-by-frame loop functions.
3. **Strictly Independent:** A script in `/Core/Scripts` cannot reference anything in `/Client` or `/Server`. The dependency flows one way: the Client and Server look *in* to the Core; the Core never looks *out*. ## Rules
1. **No Godot node inheritance.** Pure classes, structs and statics.
2. **No `_Process` / `_Ready`.** Nothing here is attached to a live scene object.
3. **Dependencies flow inward only.** `/Server` and `/Client` reference `/Core`; `/Core` never
references them.

View file

@ -1,49 +1,41 @@
# 🗄️ Data Directory - Isla Apocalypse # /Data
## Overview Intended handoff point for static world data written by `/Tools` and read by `/Server`.
The `/Data` directory is the static repository for all generated world files, visual previews, and configuration structures. It acts as the handoff point between the offline `/Tools` (which write the data) and the live `/Server` (which reads the data).
## ⚠️ Currently empty
**Core Philosophy: "The Immutable Source of Truth."**
Files stored in this primary directory are considered *Base Blueprints*. They are the starting point of the world before any player interacts with it. The Game Client should almost never read from this folder directly; it receives this data via the Server. **This directory holds nothing but this README.** The generator does **not** write here.
--- Generated world data goes to Godot's `user://` path instead — outside the project entirely. On Linux
that is:
## 📦 Current Contents (Phase 1)
```
### 1. Binary Blueprints (`.dat`) ~/.local/share/godot/app_userdata/islaApocolypse/
* **Format:** Custom serialized binary (`MapData_Seed_[X].dat`). MapData_Seed_<seed>.dat the blueprint (~512 MB at 8K)
* **Purpose:** Contains the highly compressed, raw mathematical output of the 2D Generator (Map Size, Height floats, Biome IDs, POI coordinates, Highway vectors). Map_Seed_<seed>.png visual snapshot of the map
* **Usage:** Read by the Server's Chunk Manager at startup to construct the untouched 3D voxel world. ```
### 2. Map Previews (`.png`) That is a good arrangement and worth keeping: it means a 512 MB blueprint can never be committed by
* **Format:** 1024x1024 RGBA Images (`Map_Seed_[X]_[Time].png`). accident, and regenerating a world never dirties the repo.
* **Purpose:** High-resolution visual snapshots of the generated seeds.
* **Usage:** Used by the developer/server admin to visually review and select the best island layout before spinning up the 3D server. ## The blueprint format, for reference
--- `MapData_Seed_<seed>.dat` is custom binary, written by `MapGenerator.ExportMapData` and read by
`Core/Scripts/MapDataParser.cs`. Order is fixed and the two sides must match exactly:
## 🚀 Future Roadmap & Planned Additions
1. `"ISLA_V1"` — length-prefixed magic string
As the game expands, this folder will be subdivided to organize different types of static game data: 2. map size (int32)
3. for every pixel, x-major: height (float32) + biome (int32)
### 3. /Prefabs (Planned - Phase 2/3) 4. town count, then per town: x, y (float32), tier (int32)
* **Format:** `.json` or custom `.prefab` binaries. 5. the four road tiers in order — Highway, Branch, Rugged, Trail — each as a path count, then per
* **Purpose:** 3D building templates (e.g., `GasStation_Tier3.json`) created by the Prefab Editor. The Server uses these files to spawn structures at the coordinates dictated by the master `.dat` blueprint. path a point count and its x/y float pairs
### 4. /Configs (Planned - Phase 3) **Enum ordinals are the serialized values** and there is no version gate beyond the magic string and
* **Format:** `.json` or `.cfg`. no range validation on read. See the warning in `Core/Scripts/README.md` before touching `Enums.cs`.
* **Purpose:** Human-readable configuration files for game balance.
* `LootTables.json`: Defines what items spawn in which containers based on POI Tier. ## If this folder ever does hold data
* `VoxelPalette.json`: Maps the 2D Biome IDs to 3D Voxel types.
* `SpawnRates.json`: Defines zombie/animal spawn limits and Blood Moon horde scaling. - **Data files only** — no scripts, no logic.
- **Keep large generated `.dat`/`.png` files out of git.** Commit only a deliberately chosen release
--- seed, if any.
- Live player saves and terrain edits belong in `user://`, never in the project folder.
## ⚠️ Directory Rules & Best Practices
1. **NO Scripts or Code:** This directory must strictly contain data files (`.dat`, `.png`, `.json`, `.csv`). It contains zero executable logic.
2. **Live Saves vs. Base Data:** * This folder holds the **Base Game Data**.
* *Live Player Saves* and *Terrain Deformation Diff Files* should generally be saved to Godot's safe `user://` path on the host machine, NOT in this project folder. This prevents live server saves from accidentally being bundled into the game's base code.
3. **Version Control (Git) Warnings:**
* `.dat` and `.png` files can become very large.
* **Rule:** Only commit the "Official Release" seed files to your repository. Use a `.gitignore` file to ignore random test generations so you don't bloat your project repository with gigabytes of discarded test maps.

124
README.md
View file

@ -1,28 +1,108 @@
# IslaApocalypse - Voxel Engine Architecture # IslaApocalypse
**Current Version:** v0.0.1 (Phase 2 - Voxel Engine Online, v0.0.x Prototyping / Tech Demo) **Version:** v0.0.1 — prototyping / salvage. Not a playable build.
**Engine:** Godot 4 (C#) **Engine:** Godot **4.7.1** · C# / .NET 8 (`Godot.NET.Sdk 4.7.1`)
## Project Overview A procedurally generated, multiplayer-ready voxel survival game on a post-nuclear Caribbean island.
IslaApocalypse is a procedurally generated, multiplayer-ready survival/driving game. The engine uses a two-phase generation system: first building a massive 4096x4096 2D topographical blueprint, and then dynamically translating that data into a 3D Marching Cubes voxel world at runtime. Terrain is smooth (Marching Cubes), not blocky.
## Development State > **Design intent, decisions and rationale live in the design vault, not here.** This README and the
* **Phase 1: 2D Blueprint Engine** -> **[COMPLETE]** > per-directory ones describe **what the code does**. For *why*, see the vault repo
* **Phase 2: 3D Voxel Engine** -> **[IN PROGRESS - Basic Rendering Online]** > (`islaApocalypse-vault-v0.1`).
### Current Capabilities (What works right now): ---
1. **The 2D Blueprint (`MapGenerator.cs`):** Generates a 4096x4096 map using `FastNoiseLite`. It features dynamic sea levels, a massive impact crater, tiered city placements (Capitol, Hubs, Villages), and a heavily optimized A* pathfinding network (Highways, Branch Roads, Trails) that utilizes Chaikin's smoothing algorithm.
2. **Binary Serialization:** Exports the entire 16-million pixel array and path vectors into a lightweight, byte-aligned `.dat` file.
3. **The Data Bridge (`MapDataParser.cs`):** Seamlessly deserializes the `.dat` file back into RAM as a `WorldBlueprint` for the server to read.
4. **Authoritative Chunk Management (`ServerChunkManager.cs`):** Dynamically locates the Capitol City, converts pixel coordinates to 3D chunk coordinates, and generates a grid of `ChunkData` objects (32x32x256 meters) around the player.
5. **Marching Cubes Rendering (`MarchingCubes.cs`):** Translates 2D heightmaps into 3D geometry. Features analytical normals for smooth lighting and dynamically paints Vertex Colors based on the 2D `BiomeMap` (Wasteland, Sand, Grass, Stone).
## Directory Architecture ## How the world gets built
* `/Tools/`: Contains the v1.0 2D Map Generator and preview scenes. Run `MapCaptureTool.tscn` to generate a new world seed.
* `/Core/`: The math and data foundation. Contains the Binary Parser, Marching Cubes algorithm, Biome Palettes, and `ChunkData` structs.
* `/Server/`: The Authoritative Server logic. Manages chunk loading, calculates voxel densities, and handles chunk culling.
* `/Client/`: The visual layer. Takes raw `ChunkData` and translates it into Godot `MeshInstance3D` objects with vertex-colored materials.
## Next Immediate Steps (Phase 2, Step 4) Two phases, with a file in between.
* **Road Carving:** Inject A* road vector data into the `ServerChunkManager` to override the natural heightmap, physically flattening the 3D terrain and painting Asphalt blocks to create drivable highways through the mountains.
* **Chunk Seams:** Implement normal-blending across chunk borders to remove grid-line lighting artifacts. 1. **2D blueprint** (`Tools/Scripts/MapGenerator.cs`) — FastNoiseLite topography, dynamic sea level,
an impact crater, biome zoning, tiered town placement, and an A\* road network in four tiers
(Highway, Branch, Rugged, Trail). Written to a binary `.dat`.
2. **3D voxel world** (`Server/` + `Core/`) — the `.dat` is parsed into RAM, and chunks are built
around the Capitol: a density field per chunk, meshed with Marching Cubes, vertex-coloured by
biome and depth, with roads carved into the terrain.
**Map size is config-driven**, not fixed. The shipped `ServerConfig.json` selects the `8K` profile →
**8192 × 8192**, where 1 pixel = 1 metre = 1 voxel footprint.
---
## ⚠️ How to actually run it
**Pressing Play (F5) does NOT run the game.** The project's main scene is the 2D map generator, so F5
regenerates the entire world (minutes — and it currently stalls in the A\* road pass) and overwrites
your `.dat`.
**To view the 3D world:**
1. Open `Scenes/Main.tscn`.
2. Press **F6***Run Current Scene*.
It reads `ServerConfig.json`, loads the existing `.dat`, and builds the chunk grid.
**To generate a new world** (only when you actually want a new map): press **F5**, or open
`Tools/Scenes/MapPreview.tscn` and press F6.
### Before you run it — set `ChunkRadius`
`ServerConfig.json`, at the project root:
```json
{ "WorldSeed": 1063685222, "MapProfile": "8K", "TownDensity": "Normal", "ChunkRadius": 32 }
```
`ChunkRadius` is a **load radius in chunks**, not a chunk size. The grid built at boot is
`(2 × radius)²` chunks, all synchronously:
| ChunkRadius | Chunks built | Cost |
|---|---|---|
| 4 | 64 | loads in seconds |
| 8 | 256 | still quick |
| 32 (shipped) | 4,096 | ~3.6 GB, minutes |
**Use 48 while iterating.** 32 is for looking at a lot of world at once.
`MapProfile` accepts `4K` / `6K` / `8K` / `10K` → 4096 / 6144 / 8192 / 10240.
### Where the generated world lives
Outputs go to Godot's `user://`, **not** into this repo — on Linux,
`~/.local/share/godot/app_userdata/islaApocolypse/`:
- `MapData_Seed_<seed>.dat` — the blueprint (~512 MB at 8K)
- `Map_Seed_<seed>.png` — a visual snapshot of the map, for reviewing and picking seeds
**`WorldSeed` must match an existing `.dat`**, or the load fails with
`CRITICAL ERROR: Map file not found` and you get an empty scene.
---
## Directory layout
| Path | What's in it |
|---|---|
| `Core/` | Shared, stateless logic: the `.dat` parser, Marching Cubes, chunk data, block registry, biome palette, constants. Used by both the server and client paths. |
| `Server/` | Authoritative world building: loads the blueprint, builds chunks around the Capitol, computes densities, carves roads. |
| `Client/` | Rendering: turns finished chunk data into `MeshInstance3D` geometry with a vertex-coloured material. |
| `Tools/` | Offline developer tooling — the 2D map generator and its scenes. Never shipped. |
| `Scenes/` | Runtime scenes. `Main.tscn` is the 3D world. |
| `Data/` | Handoff folder for static world data. Currently documentation only. |
| `Resources/` | Intended home for Godot `.tres` asset definitions. Currently documentation only. |
---
## Current state
**Working:** 2D generation end to end at 8K · `.dat` write and read · chunk building · Marching Cubes
meshing with analytical normals and seam-free integer vertex welding · road carving for **all four
tiers** with per-tier width and grade · biome/depth vertex colouring with soft material fades.
**Known issues:**
- **A\* road generation is slow enough to stall a full regeneration** — a 67-million-node grid at
1 px granularity, with a same-sized setup loop. The dominant open problem.
- **Faint seam lines along chunk borders** — a normals/shading difference between independently
meshed chunks, not a gap in the geometry.
- **No player, no collision, no water, and no camera controller** — there is no input handling in the
codebase at all. Inspect the world with the editor camera.
- The world is built once at boot: **no chunk streaming or unloading** yet.
- Two road issues remain: roadbed elevation can jump where two separate stretches of road pass close
together, and road *materials* are only a coarse asphalt/dirt split.

View file

@ -1,49 +1,24 @@
# 📦 Resources Directory - Isla Apocalypse # /Resources
## Overview Intended home for Godot `.tres` asset definitions and static assets — block properties, item data,
The `/Resources` directory acts as the game's central library for all static assets and Godot-specific data containers (primarily `.tres` files, 3D models, textures, and audio). materials, models, audio, UI themes.
**Core Philosophy: "The Reusable Building Blocks."** ## ⚠️ Currently empty
If the Server is the brain and the Client is the eyes, Resources are the memories. They define the *properties* of things in the world. If there are 50 zombies on screen, you don't want 50 copies of their texture and max health data in your RAM. Instead, all 50 zombies reference a single `ZombieBase.tres` resource.
**This directory holds nothing but this README.** No resources exist yet.
---
Block properties currently live **in code**, not in resources: `Core/Scripts/BlockRegistry.cs` builds
## 🧩 Core Systems (Phase 2 & 3) its dictionary of `BlockData` (ID, name, IsSolid, colour) in a static constructor. Terrain rendering
uses a `StandardMaterial3D` created at runtime per chunk in `ChunkRenderer.cs`, not a saved material.
### 1. Voxel & Block Definitions
* **Format:** Custom `.tres` files based on a `BlockData` C# script (located in `/Core`). Moving that data into `.tres` files is a reasonable future step — it would make block definitions
* **Function:** Defines the properties of every voxel in the game. editable without recompiling and diffable in git — but nothing depends on it today.
* **Examples:** `Dirt.tres` (Texture: brown_dirt.png, Durability: 100, StepSound: dirt_crunch.ogg), `Asphalt.tres` (Texture: road.png, Durability: 500, StepSound: hard_step.ogg). Both the Client and Server read these to know how a block looks and behaves.
## Conventions, if and when it fills up
### 2. Item & Inventory Data
* **Format:** Custom `.tres` files for `ItemData`. 1. **`.tres`, not `.res`.** Text resources are readable in a diff; binary ones are not.
* **Function:** Defines every item a player can hold, craft, or drop. 2. **Stateless only.** A resource holds a weapon's *max* ammo, never its *current* ammo. Live state
* **Examples:** `StoneAxe.tres` (Icon: axe.png, Damage: 15, BlockDamage: 40, MaxStack: 1). The Server uses this to calculate damage; the Client uses this to draw the icon in your hotbar. belongs to the server.
3. **Subdivide early**`Blocks/`, `Items/`, `Models/`, `Audio/`, `UI/` — before it becomes a junk
### 3. Audio & Visual Assets drawer.
* **Materials & Shaders:** The `.material` and `.gdshader` files used by the Client's chunk mesher (e.g., `TerrainMaterial.tres`). 4. Resources are plain data, so both `/Server` and `/Client` can safely reference them.
* **3D Models:** The `.glb` or `.gltf` files for weapons, dropped items, and entities.
* **Audio Streams:** The `.wav` or `.ogg` files for ambient wind, UI clicks, and zombie attacks.
### 4. UI Themes & Fonts
* Godot `Theme` resources that dictate the colors, fonts, and border styles of the entire user interface, ensuring the inventory menus match the main menu.
---
## 🚀 Directory Organization Strategy
To prevent this folder from turning into a massive junk drawer, it must be strictly subdivided:
* `/Resources/Blocks/`
* `/Resources/Items/`
* `/Resources/Models/`
* `/Resources/Audio/`
* `/Resources/UI/`
---
## ⚠️ Directory Rules & Best Practices
1. **Text Over Binary:** Always save custom Godot resources as `.tres` (Text Resource) rather than `.res` (Binary Resource). Text resources can be read by version control (Git), allowing you to see exactly when someone changed the damage of a Stone Axe from 15 to 20.
2. **Stateless Data Only:** A Resource should never track live game state. For example, `Pistol.tres` holds the *Max Ammo* capacity (15), but it should NEVER hold the *Current Ammo* (7). Current ammo is a live state tracked by the Server.
3. **Shared Access:** Because Resources are just data, they are completely safe to be referenced by both the `/Server` and the `/Client`.

View file

@ -1,37 +1,46 @@
# 🌍 /Scenes - The Playable Game # /Scenes — the runtime scenes
## Overview Unlike `/Tools`, these are what actually runs the game. They **do not generate the world** — they
This directory contains the core runtime scenes for *Isla Apocalypse*. Unlike the `/Tools` directory, the scenes here are exactly what the end-user (or the Dedicated Server) will run. load and interpret an existing `.dat` blueprint.
**Core Philosophy:** These scenes do **not** generate the world from scratch. They strictly load and interpret the immutable `MapData.dat` blueprint provided by the developers. This ensures lightning-fast server boot times and complete parity between the server and the clients. ## `Main.tscn` — the 3D world
--- | Node | What it is |
|---|---|
## 🏗️ Core Scenes | `World` (`Node3D`) | Root. Carries `ServerChunkManager.cs` — this is what builds the world. |
| `Camera3D` | Child of `World`. The server teleports it above the Capitol at startup. |
### `Main.tscn` (The Entry Point) | `DirectionalLight3D` | Sunlight. |
**Status:** In Transition (Prepping for 1:1 Scale Ingestion) | `WorldEnvironment` | Ambient light. |
This is the master root scene. Currently, it acts as the Dedicated Server environment, responsible for reading the 2D blueprint and translating it into a physical 3D voxel world using the `ServerChunkManager`. ### Running it
#### ⚔️ The Great 4096 Struggle (The 3D Ramifications) **Open this scene and press F6** (*Run Current Scene*).
Our decision to upgrade the master blueprint from 1024x1024 to a massive 4096x4096 (1:1 scale) fundamentally changed how `Main.tscn` must operate.
**Do not press F5.** The project's main scene is the 2D map generator, not this — F5 regenerates
While the 2D generator struggled with math and UI layouts, the 3D server must now deal with **Memory Management**. the whole world and overwrites your `.dat`.
* **The Scale Shift:** In the old 1024 version, 1 data pixel equaled 4 meters of 3D space. Now, 1 pixel = 1 meter. This perfectly accommodates human-scale base building (like *7 Days to Die*), but it means the total data footprint of the world is 16 times larger.
* **The Chunking Mandate:** `Main.tscn` can no longer afford to load the entire map into RAM at startup. The `ServerChunkManager` must operate strictly on a localized, dynamic grid—only loading the 1-meter chunks immediately surrounding active players. At boot the manager reads `ServerConfig.json`, loads `MapData_Seed_<WorldSeed>.dat` from `user://`,
finds the Capitol, and builds a `(2 × ChunkRadius)²` grid of chunks around it. **Set `ChunkRadius` to
#### 🛡️ The Arsenal is Ready 48 first** unless you specifically want the full 4,096-chunk, ~3.6 GB boot.
Despite the massive scale increase, `Main.tscn` is structurally prepared for the 4096 blueprint thanks to heavy engine optimizations built during Phase 1:
1. **The EdgeKey Welder:** Completely eliminates floating-point drift and mesh seams across the new high-density chunk borders. If the seed in `ServerConfig.json` doesn't match an existing `.dat`, the load fails and you get an
2. **Analytical Normals:** Bypasses Godot's slow native normal calculation, using mathematical gradients to light the massive 1:1 voxel world instantly. empty scene.
3. **The +1 Border Fix:** Chunk arrays are padded by exactly 1 unit to flawlessly sample neighbor data without requiring expensive cross-chunk blurring.
### Two things that will confuse you
---
- **The camera cannot be moved at runtime.** There is no camera controller and no input handling
## 🚀 The Immediate Roadmap anywhere in the codebase. The server overrides whatever transform you save here, placing the camera
When Phase 2 resumes, `Main.tscn` will undergo the following upgrades to ingest the new 4096 map: above the Capitol looking straight down — which is also a degenerate `LookAt`, so its roll is
arbitrary and Godot logs a warning. To view from elsewhere you have to edit the teleport code.
1. **Re-syncing the Parser:** Uncommenting the Logistical Network logic in `MapDataParser.cs` so the server can read the newly fixed roads and towns. - **Moving the `World` node moves the entire world.** Chunks are added as its children, so a stray
2. **Dynamic Chunk Loading:** Scrapping the hardcoded `10x10` test grid in favor of a proximity-based chunk loader. drag in the editor offsets all rendered terrain. If everything looks displaced, check `World`'s
3. **Terrain Flattening:** Implementing logic for the voxel engine to read the road and town data and automatically flatten the 3D terrain beneath them. transform is zeroed.
## `MapCaptureTool.tscn`
A `SubViewportContainer` wrapping `Tools/Scenes/MapPreview.tscn` at 4096×4096. It exists so the map
can be rendered offscreen at full resolution regardless of monitor size. Only needed for the PNG
snapshot path — `MapPreview.tscn` generates the `.dat` on its own.
## Not here yet
No player scene, no UI, no menu. `Main.tscn` is currently a viewer, not a game.

View file

@ -1,22 +1,52 @@
# Server Module - IslaApocalypse # Server Module
**Phase 2 Status:** Active (Authoritative Chunk Management Online) Authoritative world building. Turns the static `WorldBlueprint` in RAM into physical 3D chunks. In a
future multiplayer setup this is the side that dictates terrain and ships chunk data to clients.
## Overview
The `Server` directory contains the authoritative logic for the 3D Voxel Engine. It acts as the bridge between the static 2D `WorldBlueprint` residing in RAM and the physical 3D environment generated around the player. This architecture ensures that in a future multiplayer environment, the server dictates the terrain and sends chunk data to the clients. ## `ServerChunkManager.cs`
## Key Architecture & Components Attached to the `World` root node of `Scenes/Main.tscn`. Runs the whole 3D world at boot.
### 1. The Authoritative Node (`ServerChunkManager.cs`) ### Startup
Attached to the root of the `Main.tscn` world scene, this script manages the lifecycle of the entire 3D environment. 1. Loads `ServerConfig.json` and the seed's `.dat` blueprint.
* **Initialization:** Upon boot, it reads the `WorldBlueprint` and dynamically locates the exact X/Y pixel coordinates of the `Capitol` city to use as the initial world spawn point. 2. **Finds the Capitol** in the parsed town list and uses it as the world origin point.
* **Coordinate Translation:** Converts 2D pixel coordinates into 3D chunk grid coordinates (e.g., Map Pixel [2048, 2048] becomes Chunk [64, 64]). 3. Converts its pixel position to chunk coordinates (`pixel / CHUNK_SIZE`).
* **Memory Management:** Maintains an `_activeChunks` dictionary to track which chunks are currently loaded in RAM, preventing duplicate generation and allowing for future chunk culling/unloading when the player moves away. 4. Builds a `(2 × ChunkRadius)²` grid of chunks around it — **synchronously, all at boot**.
5. Teleports the `Camera3D` to 120 m above the Capitol, looking down.
### 2. Terrain Math & Generation
* **True Vertical Scaling:** Height calculation directly multiplies the raw `FastNoiseLite` float (0.0 to 1.0) against `Constants.CHUNK_HEIGHT` (256 meters). This avoids heightmap compression and allows for towering, AAA-scale mountain cliffs. **Two things to know about startup.** The chunk grid is built in one blocking pass with no
* **Density Calculation:** Uses exact surface interpolation to calculate the signed 3D distance of every voxel relative to the surface. This prevents terrain collapsing on horizontal edges and creates mathematically perfect slopes for the Marching Cubes algorithm. streaming or unloading, so `ChunkRadius` directly controls boot cost — 32 means 4,096 chunks and
* **Biome Injection:** Samples the `BiomeMap` at the exact X/Z world coordinate to assign a specific `BlockID` (via `BiomePalette`) to every voxel in the chunk. roughly 3.6 GB. And the camera `LookAt` points straight down, which is a degenerate case: the up
vector ends up parallel to the view direction, so camera roll is undefined and Godot logs a warning.
## Next Immediate Steps (Phase 2, Step 4)
* **A* Road Carving:** The Server Chunk Manager needs to be updated to cross-reference the `Highways` and `BranchRoads` vectors from the blueprint. If a chunk contains a road, the server must mathematically flatten the `exactSurfaceY` and override the `BlockID` to `ASPHALT`. ### Per-chunk generation
- **Road culling first.** Only the road segments whose bounding box reaches this chunk are kept,
each tagged with its `RoadTier`. The padding is derived from the widest shoulder any tier has plus
a margin, so widening a road cannot silently truncate it at chunk edges.
- **Surface height per column** (`GetExactSurface`) — the blueprint height scaled into the chunk's
usable vertical band, then modified by any road carving.
- **Density per voxel**`(y surfaceY)` normalised by the local slope, giving a signed distance to
the surface. Positive above, negative below.
- **Block IDs per voxel** via `BiomePalette`, plus the per-column data the renderer needs.
- Hands the finished chunk to a `ChunkRenderer`.
### Road carving
All four tiers carve, each with its own character (widest and smoothest for highways, narrow and
terrain-hugging for trails — values in `Constants.cs`).
For each column, the carve finds **the nearest road whose shoulder actually reaches it** — not simply
the nearest road, since tiers have different reach and a nearby footpath must not shadow a highway
still covering the column. It then reads the roadbed height at the closest point *along* that
segment, blending between a straight ramp between the segment's endpoints ("holds a grade") and the
terrain directly beneath ("hugs the land") according to the tier.
Inside the road radius the column is flattened to that height and flagged with the tier's surface
material; out to the shoulder radius it eases back to natural ground with a smoothstep.
`HeightAtPixel` samples the heightmap **bilinearly** — road path points are fractional, and nearest-cell
sampling produced a metre-scale staircase along the roadbed.
## Not here yet
No chunk streaming or unloading, no collision, no networking, no player. The "server" is currently a
node in the same scene as the renderer — the server/client split is structural, not a process
boundary.

View file

@ -1,52 +1,52 @@
# 🧰 Tools Directory - Isla Apocalypse # /Tools
## Overview Offline developer utilities. **Nothing here ships.** These generate the source data that `/Server`
The `/Tools` directory houses all offline developer utilities, administrative scenes, and generation scripts. and `/Client` later consume, which keeps world-generation logic out of the shipped client.
**Core Philosophy:** Nothing in this folder is shipped to the end-user. These tools are used exclusively by the developers or server admins to generate the immutable "Source Data" (blueprints, prefabs, configs) that the `/Server` and `/Client` will later ingest. By keeping this logic completely isolated, we ensure the game client remains lightweight and secure from reverse-engineered world-generation exploits. ## The 2D world generator
--- **Script:** `Tools/Scripts/MapGenerator.cs` · **Scenes:** `Tools/Scenes/MapPreview.tscn` and
`Scenes/MapCaptureTool.tscn`
## 🛠️ Current Tools
Builds the topographical and logistical foundation of the island before any 3D exists.
### 1. The 2D World Generator (`MapCaptureTool.tscn` & `MapPreview.tscn`)
**Status:** Active / Upgraded to 1:1 Scale Architecture - **Resolution is config-driven**, from `ServerConfig.json`'s `MapProfile`: `4K`/`6K`/`8K`/`10K` →
4096 / 6144 / 8192 / 10240. **Shipped default is 8K.** 1 pixel = 1 in-game metre = 1 voxel
This tool handles the heavy mathematical lifting of procedural generation. It creates the topographical and logistical foundation of the island before any 3D rendering occurs. footprint. (The `MapSize` field initialiser in the script says 4096, but it is overwritten from
config before use.)
* **Primary Script:** `MapGenerator.cs` - **Topography:** FastNoiseLite plus distance-falloff maths for a guaranteed mainland island, a
* **Resolution:** 4096 x 4096 pixels (1:1 Scale. 1 Pixel = 1 In-Game Meter / 1 Voxel footprint). mountain spine, dynamic temperature-driven sea level, and an impact crater pushed into the northern
* **Capture Architecture:** Uses a dynamic, C#-driven offscreen `SubViewport` and a custom `MapDrawProxy`. This bypasses Godot's UI layout engine, allowing the generation of massive, high-resolution maps regardless of the developer's physical monitor size. coast (carved at 80 % of its radius so a landbridge always survives).
* **Key Sub-Systems:** - **Logistics:** `AStarGrid2D` pathfinding producing a looping continental highway, a branch to the
* *Topography:* Uses `FastNoiseLite` and distance-falloff math to guarantee a mainland island shape with cold/mountainous northern regions. mountain hub, and county roads daisy-chained outward with Prim's algorithm. Paths are decimated
* *Logistics:* A* Pathfinding that generates looping coastal highways, connecting branch roads, and rugged mountain trails. Pathing is dynamically scaled to the map resolution. (RamerDouglasPeucker) then smoothed (4 Chaikin passes).
* *Zoning:* Biome mapping and hierarchical Town/POI placement based on slope, temperature, and proximity. - **Zoning:** biome mapping and tiered town/POI placement by slope, temperature and proximity.
**Outputs (Saved to `user://`):** ### ⚠️ A\* is the known bottleneck
1. **The Blueprint (`MapData_Seed_[X].dat`):** A heavily compressed binary file containing the map size, heightmap floats, biome enums, road vectors, and POI coordinates. This is the master file read by the Dedicated Server.
2. **The Snapshot (`Map_Seed_[X].png`):** A high-resolution 1:1 visual render of the map layout (including all drawn logistical routes) for admin review and seed selection. Timestamps have been removed to prevent directory bloating. The grid is one node per pixel — **67 million nodes at 8K** — and the weight-setup pass walks every
one of them. A full generation can stall long enough that runs get abandoned. `await` yields are
--- sprinkled through the road stage, but they cannot interrupt a single engine-side `GetPointPath` call,
so they only help between paths. Expect a regeneration to take minutes, or to need abandoning.
## 🚀 Future Roadmap & Planned Additions
### Outputs — written to `user://`, not into the project
As we move into Phase 2 (3D Voxels) and Phase 3 (Gameplay), this directory will expand to include the following standalone utilities:
| File | What it is |
### 2. The Prefab Editor (Planned - Phase 3) |---|---|
A standalone 3D sandbox scene used to build custom Points of Interest (POIs) block-by-block (e.g., houses, bunkers, gas stations). | `MapData_Seed_<seed>.dat` | The binary blueprint the server reads (~512 MB at 8K) |
* **Function:** Allows the developer to build structures visually and define "Loot Container" or "Zombie Spawn" block locations. | `Map_Seed_<seed>.png` | Visual snapshot of the map, for reviewing and picking seeds |
* **Output:** Generates lightweight `.prefab` or `.json` files that the Server's Chunk Manager reads to spawn buildings at the coordinates dictated by the `MapData.dat` file.
On Linux: `~/.local/share/godot/app_userdata/islaApocolypse/`.
### 3. Voxel Palette Manager (Planned - Phase 2)
A data-entry tool or inspector script to map 2D Biome Enums to 3D Voxel IDs. ### Running it
* **Function:** Defines that `Biome.Jungle` at `Height 0.5` should spawn "Voxel ID 12 (Jungle Grass)", while `Height 0.2` should spawn "Voxel ID 4 (Dirt)".
Open `Tools/Scenes/MapPreview.tscn` and press **F6**. It is also the project's main scene, so
### 4. Loot & Balance Tweaker (Planned - Phase 3) **pressing F5 anywhere runs this** — which is why F5 regenerates your world instead of starting the
A simple UI tool to adjust spawn weights and loot tables without having to dig through massive JSON files manually. game. Use `Scenes/MapCaptureTool.tscn` when you specifically want the full-resolution PNG.
* **Output:** Bakes final configuration files for the Server to use when rolling loot table RNG.
## Rules
--- 1. **No client dependencies.** Nothing here may reference UI, player controllers or shaders in
`/Client`.
## ⚠️ Directory Rules 2. **Data out, nothing in.** Tools emit `.dat`/`.png`/`.json`; they never write to a live server save.
1. **No Client Dependencies:** Scripts in this folder must *never* reference UI, Player Controllers, or Shaders located in the `/Client` folder. 3. **No magic numbers.** Distances and radii scale off `MapSize` or the derived `scaleFactor`, so the
2. **Data Agnostic:** Tools here should output raw data (`.dat`, `.json`, `.png`). They should not directly modify the live server's save state to avoid corruption. generator behaves the same at every profile.

View file

@ -1,47 +1,37 @@
# 🎬 /Tools/Scenes - Developer Environments # /Tools/Scenes — developer environments
## Overview Standalone scenes used to run the offline generation scripts. **Never add these to a game export.**
This directory contains the standalone Godot scenes (`.tscn` files) used to execute our offline generation scripts. These scenes act as isolated "sandboxes" for the developers to build the world data.
## `MapPreview.tscn`
**CRITICAL RULE:** These scenes must **never** be added to the main game's build export or scene tree. They are run strictly inside the Godot Editor by pressing **F6 (Play Current Scene)**.
The 2D map generator itself.
--- - **Root:** `TextureRect` · **Script:** `MapGenerator.cs`
- Runs the noise maths, builds the heightmap and biome map, plots the A\* road network, writes the
## 🏗️ Core Scenes `.dat`, and draws the map via `_Draw()`.
### 1. `MapPreview.tscn` **This is the project's main scene** (`project.godot` → `run/main_scene`). That means **pressing
The original 2D map generation scene. F5 anywhere in the project runs this**, regenerating the world and overwriting your `.dat` — it does
* **Root Node:** `TextureRect` not start the game. To view the 3D world, open `Scenes/Main.tscn` and press **F6** instead.
* **Attached Script:** `MapGenerator.cs`
* **Function:** Runs the FastNoiseLite math, builds the `_heightMap`, and executes the `_Draw()` commands to visually plot the A* road networks and town circles. ## `MapCaptureTool.tscn`
### 2. `MapCaptureTool.tscn` (The Wrapper) *(Lives in `/Scenes`, wraps this directory's scene.)* A `SubViewportContainer``SubViewport`
The master scene used to actually execute and capture the map generation. (4096×4096) → `MapPreview`. It exists purely to render the map offscreen at full resolution: Godot's
* **Root Node:** `SubViewportContainer` UI layout engine would otherwise scale a 4096-pixel render down to the editor window, producing a
* **Child Nodes:** `SubViewport` -> `MapPreview` tiny or black PNG.
* **Function:** Acts as a massive, invisible "virtual monitor" to capture the high-resolution PNG of the map without the Godot UI layout engine interfering.
Note the generator writes the `.dat` on its own — the capture wrapper only matters for the
--- full-resolution image.
## ⚔️ The Great 4096 Struggle (Beating the UI Engine) ## Generating a new world
When we upgraded *Isla Apocalypse* from a 1024 to a 4096 (1:1 scale) map, we encountered a massive rendering roadblock. Godot's UI Layout Engine is designed to make things fit on a player's physical monitor. 1. Open `MapPreview.tscn` (or `Scenes/MapCaptureTool.tscn` for the full-resolution PNG).
2. Press **F6***Play Current Scene*.
When we asked `MapPreview` to render a 4096x4096 texture, Godot panicked. It saw that the developer's monitor was only 1080p, so it violently crushed the map down to fit the screen, resulting in exported PNGs that were either 1KB postage stamps or completely black. 3. **Expect minutes, not seconds**, and expect the editor to be unresponsive. The A\* road stage is
the slow part and can stall long enough to be worth abandoning; watch the console for
#### The "Virtual Monitor" Solution `[A*] 1/4 … 4/4` progress. See `/Tools/Scripts/README.md`.
To bypass the UI engine, we had to build `MapCaptureTool.tscn`. 4. On success the console prints the export path. Files land in `user://`
1. **The Container Trap:** We initially used a `SubViewportContainer` with "Stretch" enabled. This was a trap. It locked the resolution to the editor window. We had to forcefully disable `Stretch` in the C# code. `~/.local/share/godot/app_userdata/islaApocolypse/`.
2. **The `SubViewport`:** We placed `MapPreview` inside a `SubViewport` set explicitly to 4096x4096. This acts as an offscreen virtual monitor that doesn't care about the physical screen size.
3. **The `MapDrawProxy`:** We realized that `Texture.GetImage()` only grabs the base pixels, completely ignoring the colored roads and towns drawn by `_Draw()`. We had to dynamically instantiate a `Control` proxy node to stamp the roads onto the virtual monitor *before* taking the picture. **Then update `ServerConfig.json`'s `WorldSeed`** to match the new `.dat`, or the 3D scene will fail
4. **The Flash Timing:** Generating a 4096 map takes time. We had to implement `await ToSignal(GetTree(), "process_frame");` to let Godot unlock the scene tree, and `await ToSignal(RenderingServer... FramePostDraw)` to force the camera to wait until the GPU physically finished painting the roads before snapping the photo. to load it.
---
## 🚀 How to Generate a New World
1. Open `MapCaptureTool.tscn` in the Godot Editor.
2. Ensure you do **not** click the main Play button.
3. Press **F6** (Play Current Scene).
4. The editor will freeze for 10-30 seconds while the `ExportMapData` function writes the binary `.dat` file.
5. Once the console prints the success message, check your `user://` folder for the massive, full-resolution `.png` and the `.dat` blueprint.

View file

@ -1,7 +1,17 @@
# MapGenerator.cs - Architecture & Historical Decisions # MapGenerator.cs - Architecture & Historical Decisions
**Date:** March 2026 **Date:** March 2026
**Purpose:** Generates a 4096x4096 2D topographical blueprint (Topography, Biomes, Cities, and Road Networks) to be parsed and translated into a 3D Voxel World. **Purpose:** Generates the 2D topographical blueprint (Topography, Biomes, Cities, and Road Networks)
to be parsed and translated into a 3D Voxel World.
> ⚠ **Historical document — read it as a record of decisions, not as current spec.** It was written
> when the map was 4096², and references to that size throughout should be read as "the map size of
> the day". **Map size is now config-driven** (`MapProfile` in `ServerConfig.json`; 8192 by default),
> and the reasoning it describes — scaling everything off `MapSize` rather than hardcoding — is
> exactly what makes that work.
>
> For the current behaviour see `Tools/Scripts/README.md`. For design rationale and the decisions
> behind the project, see the design vault.
--- ---

View file

@ -1,31 +1,61 @@
# 📜 /Tools/Scripts - Generation Logic # /Tools/Scripts — generation logic
## Overview C# backend for the offline developer tools. Decoupled from the live server and client.
This directory contains the raw C# backend logic for all of our offline developer tools. These scripts are strictly for generating source data (Maps, Prefabs, Balance Configs) and are completely decoupled from the live game server and client.
## `MapGenerator.cs`
---
Generates the entire 2D blueprint. Roughly in order:
## 🏗️ Core Scripts
1. **Config** — reads `ServerConfig.json`, which sets `MapSize` (8192 by default) and the crater
### `MapGenerator.cs` radius. The `MapSize = 4096` field initialiser is a fallback that is immediately overwritten.
**The Master World Builder.** This script is responsible for generating the 2D topographical and logistical blueprint of *Isla Apocalypse*. It runs FastNoiseLite math, calculates distance falloffs, plots A* road networks, and zones the biomes. 2. **Topography** — FastNoiseLite base height plus a mountain spine, minus a squircle distance
falloff, giving a guaranteed island. Noise frequency is divided by `scaleFactor`
#### ⚔️ The Great 4096 Struggle (The 1:1 Scale Transition) (`MapSize / 1024f`) so terrain features stay the same real-world size at any map profile.
Originally, this script generated a 1024x1024 map where 1 pixel equaled 1 chunk (4 meters). To accommodate industry-standard human-scale base building, we transitioned the engine to a 1:1 scale (1 pixel = 1 meter), effectively quadrupling the map size to 4096x4096. 3. **Sea level and water** — temperature-driven sea level; flood fill separates true ocean from
inland lakes; a mainland fill guarantees one contiguous landmass.
This transition broke almost everything, requiring a massive architectural overhaul: 4. **The crater** — placed along the northern coast and carved to below sea level, but only out to
**80 % of its radius**, which guarantees a landbridge rather than severing the island.
1. **The "TV Static" Math Fix:** Multiplying the map size by 4 caused the noise generator to pack 4 kilometers of terrain into 1 kilometer of space, creating visual static. We introduced a dynamic `scaleFactor` (`MapSize / 1024f`) to stretch the noise frequency back out, preserving the massive, sweeping "chonky" biomes while maintaining the 1:1 scale. 5. **Biomes and towns** — biome zoning by height and temperature; tiered town placement (Capitol,
2. **Eradicating Magic Numbers:** Hardcoded pixel checks (e.g., "check 10 pixels for water") were completely broken by the upscaling. We refactored the logic to use dynamic map percentages for slope checking, water detection, and A* highway repulsion. Hubs, Villages, Outposts, POIs) filtered by slope, water proximity and spacing.
3. **The 1KB Postage Stamp (Defeating the UI Engine):** Trying to capture a 4096 image inside a standard Godot UI layout node resulted in the engine crushing the map into a tiny 1KB square. To fix this, `SaveMapSnapshot()` was rewritten to build an invisible, offscreen `SubViewport` entirely in C#. 6. **Roads** — see below.
4. **The Draw Proxy:** Because raw `Texture.GetImage()` does not capture Godot's `_Draw()` functions, we created `MapDrawProxy`. This stamps the colored roads and towns onto the offscreen monitor. We then implemented asynchronous thread-locking (`await ToSignal(RenderingServer...)`) to force the code to wait for the GPU to physically paint the roads before snapping the PNG. 7. **Export** — writes the `.dat` blueprint, then renders the PNG snapshot.
**Outputs:** ### The road network
* `MapData_Seed_[X].dat` - The binary map data for the Server.
* `Map_Seed_[X].png` - The 4096 high-resolution visual render. `AStarGrid2D` over the whole map at one node per pixel. Mountains are made expensive rather than
impassable (`1 + elevation³ × 400`), water and the crater are marked solid.
---
- **Continental loop** — highway nodes sorted by radial angle and connected in a ring.
## ⚠️ Scripting Rules - **Mountain branch** — a spur from the loop to the snow hub.
1. **No Magic Numbers:** Any distance, radius, or length must be calculated as a percentage of `MapSize` or use the `scaleFactor`. - **County roads** — Prim's algorithm daisy-chains remaining towns onto the network. Villages become
2. **GPU Respect:** Any script exporting visual data must use `CallDeferred` or `await` frames to ensure the Godot rendering engine has finished processing. Rugged roads, everything else Trails.
- **Abandon protocol** — a town further than 8 % of the map from the network is skipped rather than
pathed to, so one unreachable outpost cannot hang generation.
- **Smoothing** — every path is decimated with RamerDouglasPeucker (tolerance 4.0) then smoothed
with 4 Chaikin passes.
**This is the project's dominant performance problem.** At 8K the grid is 67 million nodes, and the
weight-setup pass touches every one before the first path is requested. The `await` yields between
stages cannot interrupt a single engine-side `GetPointPath` call, so a long path still blocks. Full
regenerations frequently get abandoned.
### The PNG snapshot
`SaveMapSnapshot()` builds an offscreen `SubViewport` in code rather than relying on the scene's
layout, because Godot's UI layout engine will otherwise crush a 4096+ render down to the editor
window size. A `MapDrawProxy` control re-issues the `_Draw()` calls into that viewport — `GetImage()`
captures only the base texture and would otherwise miss the roads and towns entirely — and the code
awaits two `RenderingServer.FramePostDraw` signals so the GPU has actually painted before the image
is read back.
Road colours on the snapshot, useful for identifying a road: **red** = Highway, **black** = Branch,
**dark brown** = Rugged, **light brown** = Trail.
### Outputs
`MapData_Seed_<seed>.dat` and `Map_Seed_<seed>.png`, both to `user://`.
## Rules
1. **No magic numbers.** Distances, radii and thresholds derive from `MapSize` or `scaleFactor`, so
the generator behaves identically at 4K and 10K.
2. **Respect the GPU.** Anything exporting visual data must `await` the appropriate frame signals
before reading pixels back.