载入中...
搜索中...
未找到
HexMapModule.cpp
浏览该文件的文档.
2
3#include "common/Diagnostic.h"
5#include "common/Value.h"
6#include "graphics/Graphics.h"
7#include "graphics/Mesh.h"
11
12#include <simplesquirrel/simplesquirrel.hpp>
13
14#include <functional>
15#include <utility>
16
17namespace eve::hexmap {
18namespace {
19
20[[nodiscard]]
21[[nodiscard]] bool validDirection(std::int32_t direction) noexcept {
22 return direction >= 0 && direction < kHexDirectionCount;
23}
24
31[[nodiscard]] HexCoordinates fromScriptCell(int x, int z) noexcept { return HexCoordinates::fromOffset(x, z); }
32
33[[nodiscard]] HexDirection toDirection(std::int32_t direction) noexcept { return static_cast<HexDirection>(direction); }
34
35} // namespace
36
38
41
42std::int32_t HexMapModule::meshSlot(std::int32_t chunkIndex, HexSurface surface) const noexcept {
43 return chunkIndex * kHexSurfaceCount + static_cast<std::int32_t>(surface);
44}
45
46void HexMapModule::clearMeshSlots() noexcept {
47 const auto slots = static_cast<std::size_t>(map_.chunkCount() * kHexSurfaceCount);
48 meshes_.assign(slots, nullptr);
49 meshPopulated_.assign(slots, 0u);
50}
51
53 scratch_.resize(map_.cellCount());
54}
55
57 if (!gfx) return Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "applyTerrain requires a graphics device", "hexmap"));
59 return Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "applyTerrain requires a planar hex.terrain bake", "hexmap"));
60
61 // `applyHexTerrain` validates through `HexMap::reset` before it mutates, so a
62 // rejected bake leaves the live grid and its meshes untouched.
63 auto applied = applyHexTerrain(map_, bake);
64 if (!applied) return applied;
65 if (!meshes_.empty()) releaseMeshes(gfx);
66 visibility_.reset(map_.cellCount());
67 units_.removeAll(map_, visibility_);
69 clearMeshSlots();
70 return Result<void>::success();
71}
72
74 const std::vector<HexUnitState>& units) {
75 if (!gfx) return Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "adoptGrid requires a graphics device", "hexmap"));
76 if (restored.empty()) return Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "adoptGrid requires a non-empty grid", "hexmap"));
77 if (!meshes_.empty()) releaseMeshes(gfx);
78 map_ = std::move(restored);
79 visibility_.reset(map_.cellCount());
80 units_.removeAll(map_, visibility_);
82 clearMeshSlots();
83 return units_.restore(map_, visibility_, scratch_, units);
84}
85
87 if (!gfx) return;
88 for (graphics::Mesh*& mesh : meshes_) {
89 if (!mesh) continue;
90 (void)gfx->releaseMesh(mesh);
91 mesh = nullptr;
92 }
93 meshPopulated_.assign(meshes_.size(), 0u);
94}
95
96Result<void> HexMapModule::newGrid(graphics::Graphics* gfx, std::int32_t cellCountX, std::int32_t cellCountZ,
97 std::uint32_t seed) {
98 if (!gfx) return Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "newGrid requires a graphics device", "hexmap"));
99 if (cellCountX <= 0 || cellCountZ <= 0) return Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "hex map size must be positive", "hexmap"));
100 // Script-supplied sizes reach a raw cell allocation, so bound them here rather than
101 // let std::bad_alloc escape a native binding. The save format caps the same
102 // quantity at kMaxHexGridDimension.
103 if (cellCountX > kMaxHexGridDimension || cellCountZ > kMaxHexGridDimension)
104 return Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "hex map size exceeds the " + std::to_string(kMaxHexGridDimension) +
105 " cell per side limit", "hexmap"));
106 // Reset before releasing: `HexMap::reset` validates before it mutates, so a
107 // rejected size now leaves the old grid *and* its meshes untouched. Releasing
108 // first detached every GPU mesh while the renderables scripts already hold kept
109 // pointing at them, and the next draw threw on a null mesh.
110 auto reset = map_.reset(cellCountX, cellCountZ, seed);
111 if (!reset) return reset;
112 if (!meshes_.empty()) releaseMeshes(gfx);
113 visibility_.reset(map_.cellCount());
114 units_.removeAll(map_, visibility_);
115 syncScratch();
116 clearMeshSlots();
117 return Result<void>::success();
118}
119
120graphics::Mesh* HexMapModule::chunkMeshAt(std::int32_t chunkIndex, HexSurface surface) const {
121 if (chunkIndex < 0 || chunkIndex >= map_.chunkCount()) return nullptr;
122 const std::int32_t slot = meshSlot(chunkIndex, surface);
123 if (slot < 0 || static_cast<std::size_t>(slot) >= meshes_.size()) return nullptr;
124 // A surface that lost all geometry keeps its last GPU mesh allocated but is
125 // no longer exposed, so a renderable can never be asked to draw stale data.
126 if (meshPopulated_[static_cast<std::size_t>(slot)] == 0u) return nullptr;
127 return meshes_[static_cast<std::size_t>(slot)];
128}
129
130std::int32_t HexMapModule::rebuildChunk(graphics::Graphics* gfx, std::int32_t chunkIndex) {
131 if (!gfx || map_.empty()) return 0;
132 if (chunkIndex < 0 || chunkIndex >= map_.chunkCount()) return 0;
133
134 std::int32_t rebuilt = 0;
135 HexMeshData meshData;
136 for (std::int32_t s = 0; s < kHexSurfaceCount; ++s) {
137 const auto surface = static_cast<HexSurface>(s);
138 const std::int32_t slot = meshSlot(chunkIndex, surface);
139 const auto slotIndex = static_cast<std::size_t>(slot);
140 graphics::Mesh*& mesh = meshes_[slotIndex];
141 std::uint8_t& populated = meshPopulated_[slotIndex];
142 if (surface == HexSurface::Fog) {
143 buildFogMesh(map_, visibility_, chunkIndex, meshData);
144 } else if (surface == HexSurface::Wall) {
145 buildWallMesh(map_, chunkIndex, meshData);
146 } else if (surface == HexSurface::Feature) {
147 buildFeatureMesh(map_, chunkIndex, meshData);
148 } else {
149 buildChunkSurfaceMesh(map_, chunkIndex, surface, meshData);
150 }
151 if (meshData.empty()) {
152 // The surface lost all geometry (for example the last road was removed).
153 // The GPU mesh is retired lazily: it stays allocated until the grid is
154 // replaced, so a renderable that still references it is never dangling.
155 populated = 0u;
156 continue;
157 }
158
159 const int vertexCount = static_cast<int>(meshData.vertexCount());
160 const int indexCount = static_cast<int>(meshData.indices().size());
161 const auto upload = [&]() {
162 return gfx->newMeshFromArrays(meshData.positions().data(), meshData.normals().data(), meshData.uvs().data(),
163 vertexCount, meshData.indices().data(), indexCount);
164 };
165
166 if (mesh &&
167 !gfx->updateMeshVertices(mesh, meshData.positions().data(), meshData.normals().data(),
168 meshData.uvs().data(), vertexCount, meshData.indices().data(), indexCount)) {
169 // The backend rejected the in-place update; recreate rather than keep stale geometry.
170 (void)gfx->releaseMesh(mesh);
171 mesh = nullptr;
172 }
173 if (!mesh) mesh = upload();
174 populated = mesh ? 1u : 0u;
175 if (mesh) ++rebuilt;
176 }
177 return rebuilt;
178}
179
181 if (!gfx || map_.empty()) return 0;
182 std::int32_t rebuilt = 0;
183 for (;;) {
184 const std::int32_t chunkIndex = map_.takeDirtyChunk();
185 if (chunkIndex < 0) break;
186 rebuilt += rebuildChunk(gfx, chunkIndex);
187 }
188 return rebuilt;
189}
190
191// --- spherical map ----------------------------------------------------------
192
194 if (gfx != nullptr) {
195 if (sphereTerrain_ != nullptr) (void)gfx->releaseMesh(sphereTerrain_);
196 if (sphereWater_ != nullptr) (void)gfx->releaseMesh(sphereWater_);
197 }
198 sphereTerrain_ = nullptr;
199 sphereWater_ = nullptr;
200}
201
202Result<void> HexMapModule::newSphere(graphics::Graphics* gfx, std::int32_t subdivision, float radius,
203 std::uint32_t seed) {
204 if (gfx == nullptr) return Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "hex sphere needs a graphics device", "hexmap"));
205 // Same ordering rule as `newGrid`: validate through `reset` first, because
206 // `HexSphereMap::reset` rejects its arguments before touching the map. Releasing
207 // the sphere meshes up front left the planet example's renderables pointing at
208 // GPU meshes that had already been destroyed whenever the arguments were bad.
209 auto reset = sphere_.reset(subdivision, radius, seed);
210 if (!reset) return reset;
212 return Result<void>::success();
213}
214
216 if (gfx == nullptr) return Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "hex sphere needs a graphics device", "hexmap"));
217 if (sphere_.empty()) return Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "hex sphere map has no cells", "hexmap"));
219 return Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "applySphereTerrain requires a hex.sphere bake", "hexmap"));
220
221 auto applied = applyHexSphereTerrain(sphere_, bake);
222 if (!applied.ok()) return Result<void>::failure(applied.status());
223 return rebuildSphere(gfx);
224}
225
227 if (gfx == nullptr) return Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "hex sphere needs a graphics device", "hexmap"));
228 if (sphere_.empty()) return Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "hex sphere map has no cells", "hexmap"));
229
233 buildSphereWaterMesh(sphere_, water);
234
235 bool uploaded = true;
236 const auto upload = [&](HexMeshData& data, graphics::Mesh*& mesh) {
237 if (data.empty()) {
238 // Nothing is flooded any more, for example: retire the mesh rather than draw
239 // stale geometry.
240 if (mesh != nullptr) {
241 (void)gfx->releaseMesh(mesh);
242 mesh = nullptr;
243 }
244 return;
245 }
246 const int vertexCount = static_cast<int>(data.vertexCount());
247 const int indexCount = static_cast<int>(data.indices().size());
248 if (mesh != nullptr &&
249 !gfx->updateMeshVertices(mesh, data.positions().data(), data.normals().data(), data.uvs().data(),
250 vertexCount, data.indices().data(), indexCount)) {
251 // The backend rejected the in-place update; recreate rather than keep stale geometry.
252 (void)gfx->releaseMesh(mesh);
253 mesh = nullptr;
254 }
255 if (mesh == nullptr) {
256 mesh = gfx->newMeshFromArrays(data.positions().data(), data.normals().data(), data.uvs().data(),
257 vertexCount, data.indices().data(), indexCount);
258 }
259 if (mesh == nullptr) uploaded = false;
260 };
261
262 upload(terrain, sphereTerrain_);
263 upload(water, sphereWater_);
264 if (!uploaded) return Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "the graphics backend refused the hex sphere mesh upload", "hexmap"));
265 return Result<void>::success();
266}
267
268// --- script bindings --------------------------------------------------------
269
270void HexMapModule::expose(ssq::Table& table) {
271 // The script class is `eve.HexMap` (the manifest's SCRIPT name and the slot's
272 // class), which deliberately differs from this C++ module's name.
273 auto cls = table.addClass("HexMap", HexMapModule::create, false);
274 expose(cls);
275}
276
277void HexMapModule::expose(ssq::Class& cls) {
278 const auto vm = cls.getHandle();
279
280 cls.addFunc("newGrid", [vm](HexMapModule* self, graphics::Graphics* gfx, int cellCountX, int cellCountZ, int seed) {
281 if (!self) return script::projectResult(vm, Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "hex map module is not available", "hexmap")));
282 if (seed < 0) seed = 0;
283 return script::projectResult(vm, self->newGrid(gfx, cellCountX, cellCountZ, static_cast<std::uint32_t>(seed)));
284 });
285 cls.addFunc("hasGrid", [](HexMapModule* self) { return self != nullptr && self->hasGrid(); });
286 cls.addFunc("cellCountX", [](HexMapModule* self) { return self ? self->map().cellCountX() : 0; });
287 cls.addFunc("cellCountZ", [](HexMapModule* self) { return self ? self->map().cellCountZ() : 0; });
288 cls.addFunc("chunkCountX", [](HexMapModule* self) { return self ? self->map().chunkCountX() : 0; });
289 cls.addFunc("chunkCountZ", [](HexMapModule* self) { return self ? self->map().chunkCountZ() : 0; });
290 cls.addFunc("chunkCount", [](HexMapModule* self) { return self ? self->map().chunkCount() : 0; });
291 cls.addFunc("seed", [](HexMapModule* self) { return self ? static_cast<std::int64_t>(self->map().seed()) : 0; });
292 cls.addFunc("surfaceCount", [](HexMapModule*) { return kHexSurfaceCount; });
293 cls.addFunc("dirtyChunkCount", [](HexMapModule* self) { return self ? self->map().dirtyChunkCount() : 0; });
294 cls.addFunc("markAllChunksDirty", [](HexMapModule* self) {
295 if (self) self->map().markAllChunksDirty();
296 });
297 cls.addFunc("takeDirtyChunk", [](HexMapModule* self) { return self ? self->map().takeDirtyChunk() : -1; });
298 cls.addFunc("rebuildChunk", [](HexMapModule* self, graphics::Graphics* gfx, int chunkIndex) {
299 return self ? self->rebuildChunk(gfx, chunkIndex) : 0;
300 });
301 cls.addFunc("rebuildDirtyChunks",
302 [](HexMapModule* self, graphics::Graphics* gfx) { return self ? self->rebuildDirtyChunks(gfx) : 0; });
303 cls.addFunc("chunkMeshAt", [](HexMapModule* self, int chunkIndex, int surface) -> graphics::Mesh* {
304 if (!self || surface < 0 || surface >= kHexSurfaceCount) return nullptr;
305 return self->chunkMeshAt(chunkIndex, static_cast<HexSurface>(surface));
306 });
307 cls.addFunc("releaseMeshes", [](HexMapModule* self, graphics::Graphics* gfx) {
308 if (self) self->releaseMeshes(gfx);
309 });
310
311 cls.addFunc("chunkCenterX",
312 [](HexMapModule* self, int chunkIndex) { return self ? self->map().chunkCenter(chunkIndex).x : 0.f; });
313 cls.addFunc("chunkCenterY",
314 [](HexMapModule* self, int chunkIndex) { return self ? self->map().chunkCenter(chunkIndex).y : 0.f; });
315 cls.addFunc("chunkCenterZ",
316 [](HexMapModule* self, int chunkIndex) { return self ? self->map().chunkCenter(chunkIndex).z : 0.f; });
317
318 // --- cell queries ---
319 cls.addFunc("elevation", [](HexMapModule* self, int x, int z) {
320 return self ? self->map().elevation(fromScriptCell(x, z)) : 0;
321 });
322 cls.addFunc("waterLevel", [](HexMapModule* self, int x, int z) {
323 return self ? self->map().waterLevel(fromScriptCell(x, z)) : 0;
324 });
325 cls.addFunc("terrainType", [](HexMapModule* self, int x, int z) {
326 return self ? self->map().terrainType(fromScriptCell(x, z)) : 0;
327 });
328 cls.addFunc("urbanLevel", [](HexMapModule* self, int x, int z) {
329 return self ? self->map().urbanLevel(fromScriptCell(x, z)) : 0;
330 });
331 cls.addFunc("farmLevel", [](HexMapModule* self, int x, int z) {
332 return self ? self->map().farmLevel(fromScriptCell(x, z)) : 0;
333 });
334 cls.addFunc("plantLevel", [](HexMapModule* self, int x, int z) {
335 return self ? self->map().plantLevel(fromScriptCell(x, z)) : 0;
336 });
337 cls.addFunc("specialIndex", [](HexMapModule* self, int x, int z) {
338 return self ? self->map().specialIndex(fromScriptCell(x, z)) : 0;
339 });
340 cls.addFunc("isUnderwater", [](HexMapModule* self, int x, int z) {
341 return self != nullptr && self->map().isUnderwater(fromScriptCell(x, z));
342 });
343 cls.addFunc("hasRiver", [](HexMapModule* self, int x, int z) {
344 return self != nullptr && self->map().hasRiver(fromScriptCell(x, z));
345 });
346 cls.addFunc("hasRoad", [](HexMapModule* self, int x, int z) {
347 return self != nullptr && self->map().hasRoad(fromScriptCell(x, z));
348 });
349 cls.addFunc("isWalled", [](HexMapModule* self, int x, int z) {
350 return self != nullptr && self->map().isWalled(fromScriptCell(x, z));
351 });
352 cls.addFunc("cellPositionX", [](HexMapModule* self, int x, int z) {
353 return self ? self->map().cellPosition(fromScriptCell(x, z)).x : 0.f;
354 });
355 cls.addFunc("cellPositionY", [](HexMapModule* self, int x, int z) {
356 return self ? self->map().cellPosition(fromScriptCell(x, z)).y : 0.f;
357 });
358 cls.addFunc("cellPositionZ", [](HexMapModule* self, int x, int z) {
359 return self ? self->map().cellPosition(fromScriptCell(x, z)).z : 0.f;
360 });
361
362 // --- picking ---
363 cls.addFunc("pickCell", [vm](HexMapModule* self, float ox, float oy, float oz, float dx, float dy, float dz) {
364 // Scripts address cells with odd-row **offset** `(column, row)` pairs, which
365 // is what every other binding converts through `fromScriptCell`. The picked
366 // cell has to come back in the same space, or feeding it straight back into
367 // `elevation(x, z)` / `editElevation(x, z, ...)` would address a different
368 // cell (and would report negative columns along the offset row's left edge).
369 const auto project = [](HexCoordinates coordinates) {
370 return Value(Value::Array{Value(static_cast<std::int64_t>(coordinates.offsetX())),
371 Value(static_cast<std::int64_t>(coordinates.offsetZ()))});
372 };
373 if (!self)
375 vm,
377 Diagnostic::error(DiagnosticCode::NotFound, "hex map module is not available", "hexmap.pick")),
378 project);
379 return script::projectResult(vm, self->map().pickCell(HexVec3{ox, oy, oz}, HexVec3{dx, dy, dz}), project);
380 });
381
382 // --- cell authoring ---
383 cls.addFunc("setElevation", [vm](HexMapModule* self, int x, int z, int value) {
384 if (!self) return script::projectResult(vm, Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "hex map module is not available", "hexmap")));
385 return script::projectResult(vm, self->map().setElevation(fromScriptCell(x, z), value));
386 });
387 cls.addFunc("setWaterLevel", [vm](HexMapModule* self, int x, int z, int value) {
388 if (!self) return script::projectResult(vm, Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "hex map module is not available", "hexmap")));
389 return script::projectResult(vm, self->map().setWaterLevel(fromScriptCell(x, z), value));
390 });
391 cls.addFunc("setTerrainType", [vm](HexMapModule* self, int x, int z, int value) {
392 if (!self) return script::projectResult(vm, Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "hex map module is not available", "hexmap")));
393 return script::projectResult(vm, self->map().setTerrainType(fromScriptCell(x, z), value));
394 });
395 cls.addFunc("setUrbanLevel", [vm](HexMapModule* self, int x, int z, int value) {
396 if (!self) return script::projectResult(vm, Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "hex map module is not available", "hexmap")));
397 return script::projectResult(vm, self->map().setUrbanLevel(fromScriptCell(x, z), value));
398 });
399 cls.addFunc("setFarmLevel", [vm](HexMapModule* self, int x, int z, int value) {
400 if (!self) return script::projectResult(vm, Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "hex map module is not available", "hexmap")));
401 return script::projectResult(vm, self->map().setFarmLevel(fromScriptCell(x, z), value));
402 });
403 cls.addFunc("setPlantLevel", [vm](HexMapModule* self, int x, int z, int value) {
404 if (!self) return script::projectResult(vm, Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "hex map module is not available", "hexmap")));
405 return script::projectResult(vm, self->map().setPlantLevel(fromScriptCell(x, z), value));
406 });
407 cls.addFunc("setSpecialIndex", [vm](HexMapModule* self, int x, int z, int value) {
408 if (!self) return script::projectResult(vm, Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "hex map module is not available", "hexmap")));
409 return script::projectResult(vm, self->map().setSpecialIndex(fromScriptCell(x, z), value));
410 });
411 cls.addFunc("setWalled", [vm](HexMapModule* self, int x, int z, bool value) {
412 if (!self) return script::projectResult(vm, Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "hex map module is not available", "hexmap")));
413 return script::projectResult(vm, self->map().setWalled(fromScriptCell(x, z), value));
414 });
415 cls.addFunc("setOutgoingRiver", [vm](HexMapModule* self, int x, int z, int direction) {
416 if (!self) return script::projectResult(vm, Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "hex map module is not available", "hexmap")));
417 if (!validDirection(direction))
418 return script::projectResult(vm, Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "river direction must be in [0, 5]", "hexmap")));
419 return script::projectResult(vm, self->map().setOutgoingRiver(fromScriptCell(x, z), toDirection(direction)));
420 });
421 cls.addFunc("removeRiver", [vm](HexMapModule* self, int x, int z) {
422 if (!self) return script::projectResult(vm, Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "hex map module is not available", "hexmap")));
423 return script::projectResult(vm, self->map().removeRiver(fromScriptCell(x, z)));
424 });
425 cls.addFunc("addRoad", [vm](HexMapModule* self, int x, int z, int direction) {
426 if (!self) return script::projectResult(vm, Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "hex map module is not available", "hexmap")));
427 if (!validDirection(direction))
428 return script::projectResult(vm, Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "road direction must be in [0, 5]", "hexmap")));
429 return script::projectResult(vm, self->map().addRoad(fromScriptCell(x, z), toDirection(direction)));
430 });
431 cls.addFunc("removeRoads", [vm](HexMapModule* self, int x, int z) {
432 if (!self) return script::projectResult(vm, Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "hex map module is not available", "hexmap")));
433 return script::projectResult(vm, self->map().removeRoads(fromScriptCell(x, z)));
434 });
435
436 // --- brush authoring ---
437 cls.addFunc("editElevation", [vm](HexMapModule* self, int x, int z, int radius, int delta) {
438 if (!self) return script::projectResult(vm, Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "hex map module is not available", "hexmap")));
439 return script::projectResult(vm, self->map().editElevation(fromScriptCell(x, z), radius, delta));
440 });
441 cls.addFunc("editWaterLevel", [vm](HexMapModule* self, int x, int z, int radius, int delta) {
442 if (!self) return script::projectResult(vm, Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "hex map module is not available", "hexmap")));
443 return script::projectResult(vm, self->map().editWaterLevel(fromScriptCell(x, z), radius, delta));
444 });
445 cls.addFunc("editTerrainType", [vm](HexMapModule* self, int x, int z, int radius, int terrainType) {
446 if (!self) return script::projectResult(vm, Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "hex map module is not available", "hexmap")));
447 return script::projectResult(vm, self->map().editTerrainType(fromScriptCell(x, z), radius, terrainType));
448 });
449 cls.addFunc("editFeatureLevel", [vm](HexMapModule* self, int x, int z, int radius, int feature, int delta) {
450 if (!self) return script::projectResult(vm, Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "hex map module is not available", "hexmap")));
451 return script::projectResult(vm, self->map().editFeatureLevel(fromScriptCell(x, z), radius, feature, delta));
452 });
453
454 // --- terrain apply (procgen produces the bake) ---
455 cls.addFunc("applyTerrain", [vm](HexMapModule* self, graphics::Graphics* gfx, ssq::Object bake) {
456 if (!self || !gfx) return script::projectResult(vm, Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "hex map module is not available", "hexmap")));
457 auto owned = script::valueFromSquirrel(bake);
458 if (!owned)
459 return script::projectResult(vm, Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "applyTerrain requires a hex.terrain bake object", "hexmap")));
460 auto decoded = hexTerrainBakeFromValue(owned.value());
461 if (!decoded) return script::projectResult(vm, Result<void>::failure(decoded.status()));
462 return script::projectResult(vm, self->applyTerrain(gfx, decoded.value()));
463 });
464
465 // --- fog of war ---
466 cls.addFunc("isExplored", [](HexMapModule* self, int x, int z) {
467 return self != nullptr && self->map().isExplored(fromScriptCell(x, z));
468 });
469 cls.addFunc("isExplorable", [](HexMapModule* self, int x, int z) {
470 return self != nullptr && self->map().isExplorable(fromScriptCell(x, z));
471 });
472 cls.addFunc("setExplored", [vm](HexMapModule* self, int x, int z, bool value) {
473 if (!self) return script::projectResult(vm, Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "hex map module is not available", "hexmap")));
474 return script::projectResult(vm, self->map().setExplored(fromScriptCell(x, z), value));
475 });
476 cls.addFunc("setExplorable", [vm](HexMapModule* self, int x, int z, bool value) {
477 if (!self) return script::projectResult(vm, Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "hex map module is not available", "hexmap")));
478 return script::projectResult(vm, self->map().setExplorable(fromScriptCell(x, z), value));
479 });
480 cls.addFunc("isCellVisible", [](HexMapModule* self, int x, int z) {
481 if (!self) return false;
482 const std::int32_t index = self->map().indexOf(fromScriptCell(x, z));
483 return index >= 0 && self->visibility().isVisible(index);
484 });
485 cls.addFunc("visibleCellCount",
486 [](HexMapModule* self) { return self ? self->visibility().visibleCellCount() : 0; });
487 cls.addFunc("resetVisibility", [](HexMapModule* self) {
488 if (self) self->units().refreshVisibility(self->map(), self->visibility(), self->scratch());
489 });
490
491 // --- units ---
492 cls.addFunc("unitCount", [](HexMapModule* self) { return self ? self->units().unitCount() : 0; });
493 cls.addFunc("addUnit", [vm](HexMapModule* self, int x, int z, float orientation) {
494 if (!self) {
496 vm,
498 Diagnostic::error(DiagnosticCode::InvalidArgument, "hex map module is not available", "hexmap")),
499 [](std::int32_t id) { return Value(static_cast<std::int64_t>(id)); });
500 }
502 vm, self->units().addUnit(self->map(), self->visibility(), self->scratch(), fromScriptCell(x, z), orientation),
503 [](std::int32_t id) { return Value(static_cast<std::int64_t>(id)); });
504 });
505 cls.addFunc("removeUnit", [vm](HexMapModule* self, int unitId) {
506 if (!self) return script::projectResult(vm, Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "hex map module is not available", "hexmap")));
508 vm, self->units().removeUnit(self->map(), self->visibility(), self->scratch(), unitId));
509 });
510 cls.addFunc("removeAllUnits", [](HexMapModule* self) {
511 if (self) self->units().removeAll(self->map(), self->visibility());
512 });
513 cls.addFunc("unitIdAt", [](HexMapModule* self, int x, int z) {
514 return self ? self->units().unitIdAt(fromScriptCell(x, z)) : -1;
515 });
516 // Sample layout: [offsetX, offsetZ, worldX, worldY, worldZ, orientation, traveling].
517 cls.addFunc("unitSample", [vm](HexMapModule* self, int unitId) {
518 const auto project = [](const HexUnitSample& sample) {
519 return Value(Value::Array{
520 Value(static_cast<std::int64_t>(sample.location.offsetX())),
521 Value(static_cast<std::int64_t>(sample.location.offsetZ())),
522 Value(static_cast<double>(sample.position.x)),
523 Value(static_cast<double>(sample.position.y)),
524 Value(static_cast<double>(sample.position.z)),
525 Value(static_cast<double>(sample.orientation)),
526 Value(static_cast<std::int64_t>(sample.traveling ? 1 : 0)),
527 });
528 };
529 if (!self) {
531 vm,
533 Diagnostic::error(DiagnosticCode::NotFound, "hex map module is not available", "hexmap")),
534 project);
535 }
536 return script::projectResult(vm, self->units().advance(self->map(), self->visibility(), self->scratch(), unitId, 0.f),
537 project);
538 });
539 cls.addFunc("travelUnit", [vm](HexMapModule* self, int unitId, ssq::Object path) {
540 if (!self) return script::projectResult(vm, Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "hex map module is not available", "hexmap")));
542 if (!owned) return script::projectResult(vm, Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "unit path must be an array of [x, z] cells", "hexmap")));
543 if (!owned.value().isArray())
544 return script::projectResult(vm, Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "unit path must be an array of [x, z] cells", "hexmap")));
545 std::vector<std::int32_t> cells;
546 const Value& list = owned.value();
547 cells.reserve(list.arraySize());
548 for (std::size_t i = 0; i < list.arraySize(); ++i) {
549 const Value& entry = list.at(i);
550 if (!entry.isArray() || entry.arraySize() < 2)
551 return script::projectResult(vm, Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "unit path entries must be [x, z] pairs", "hexmap")));
552 const Value& ax = entry.at(0);
553 const Value& az = entry.at(1);
554 if (!ax.isNumeric() || !az.isNumeric())
555 return script::projectResult(vm, Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "unit path entries must hold numeric cells", "hexmap")));
556 const std::int32_t index =
557 self->map().indexOf(fromScriptCell(static_cast<int>(ax.asInt()), static_cast<int>(az.asInt())));
558 if (index < 0) return script::projectResult(vm, Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "unit path leaves the hex map", "hexmap")));
559 cells.push_back(index);
560 }
562 self->units().beginTravel(self->map(), self->visibility(), self->scratch(), unitId,
563 cells));
564 });
565 cls.addFunc("advanceUnits", [](HexMapModule* self, float dt) {
566 if (self) self->units().advanceAll(self->map(), self->visibility(), self->scratch(), dt);
567 });
568
569 // --- pathfinding ---
570 // Result layout: an array of [offsetX, offsetZ, turn] triples from origin to goal.
571 cls.addFunc("findPath", [vm](HexMapModule* self, int fromX, int fromZ, int toX, int toZ) {
572 const auto project = [self](const HexPath& path) {
574 entries.reserve(path.size());
575 for (std::size_t i = 0; i < path.cells.size(); ++i) {
576 const HexCoordinates coordinates = self ? self->map().coordinatesAt(path.cells[i]) : HexCoordinates{};
577 const std::int32_t turn = i < path.turns.size() ? path.turns[i] : 0;
578 entries.push_back(Value(Value::Array{Value(static_cast<std::int64_t>(coordinates.offsetX())),
579 Value(static_cast<std::int64_t>(coordinates.offsetZ())),
580 Value(static_cast<std::int64_t>(turn))}));
581 }
582 return Value(std::move(entries));
583 };
584 if (!self) {
586 vm,
588 Diagnostic::error(DiagnosticCode::InvalidArgument, "hex map module is not available", "hexmap")),
589 project);
590 }
592 vm,
593 findPath(self->map(), self->scratch(), fromScriptCell(fromX, fromZ), fromScriptCell(toX, toZ),
594 self->units().tuning().moveRules(), self->units().occupancyQuery()),
595 project);
596 });
597
598 // --- persistence ---
599 cls.addFunc("saveMap", [vm](HexMapModule* self) {
600 if (!self) return script::projectResult(vm, Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "hex map module is not available", "hexmap")));
601 std::vector<std::uint8_t> bytes;
602 auto saved = saveHexMap(self->map(), self->units().snapshot(), bytes);
603 if (!saved) return script::projectResult(vm, std::move(saved));
605 vm, saved.status(), Value::string(std::string(reinterpret_cast<const char*>(bytes.data()), bytes.size())));
606 });
607 cls.addFunc("loadMap", [vm](HexMapModule* self, graphics::Graphics* gfx, std::string blob) {
608 if (!self || !gfx) return script::projectResult(vm, Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "hex map module is not available", "hexmap")));
609 std::vector<std::uint8_t> bytes(blob.begin(), blob.end());
610 HexMap restored;
611 std::vector<HexUnitState> units;
612 auto loaded = loadHexMap(bytes, restored, units);
613 if (!loaded) return script::projectResult(vm, std::move(loaded));
614 return script::projectResult(vm, self->adoptGrid(gfx, std::move(restored), units));
615 });
616
617 // --- spherical map ---
618 cls.addFunc(
619 "newSphere", [vm](HexMapModule* self, graphics::Graphics* gfx, int subdivision, float radius, int seed) {
620 if (!self) return script::projectResult(vm, Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "hex map module is not available", "hexmap")));
621 if (seed < 0) seed = 0;
623 self->newSphere(gfx, subdivision, radius, static_cast<std::uint32_t>(seed)));
624 });
625 cls.addFunc("applySphereTerrain", [vm](HexMapModule* self, graphics::Graphics* gfx, ssq::Object bake) {
626 if (!self) return script::projectResult(vm, Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "hex map module is not available", "hexmap")));
627 auto owned = script::valueFromSquirrel(bake);
628 if (!owned)
629 return script::projectResult(vm, Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "applySphereTerrain requires a hex.sphere bake object", "hexmap")));
630 auto decoded = hexTerrainBakeFromValue(owned.value());
631 if (!decoded) return script::projectResult(vm, Result<void>::failure(decoded.status()));
632 return script::projectResult(vm, self->applySphereTerrain(gfx, decoded.value()));
633 });
634 cls.addFunc("rebuildSphere", [vm](HexMapModule* self, graphics::Graphics* gfx) {
635 if (!self) return script::projectResult(vm, Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "hex map module is not available", "hexmap")));
636 return script::projectResult(vm, self->rebuildSphere(gfx));
637 });
638 cls.addFunc("sphereReady", [](HexMapModule* self) { return self != nullptr && !self->sphere().empty(); });
639 cls.addFunc("sphereCellCount", [](HexMapModule* self) { return self ? self->sphere().cellCount() : 0; });
640 cls.addFunc("spherePentagonCount", [](HexMapModule* self) { return self ? self->sphere().pentagonCount() : 0; });
641 cls.addFunc("sphereSubdivision", [](HexMapModule* self) { return self ? self->sphere().subdivision() : 0; });
642 cls.addFunc("sphereRadius", [](HexMapModule* self) { return self ? self->sphere().sphereRadius() : 0.f; });
643 cls.addFunc("sphereElevationStep", [](HexMapModule* self) { return self ? self->sphere().elevationStep() : 0.f; });
644 cls.addFunc("sphereCellSpacing", [](HexMapModule* self) { return self ? self->sphere().cellSpacing() : 0.f; });
645 cls.addFunc("sphereIsPentagon",
646 [](HexMapModule* self, int cell) { return self != nullptr && self->sphere().isPentagon(cell); });
647 cls.addFunc("sphereNeighborCount",
648 [](HexMapModule* self, int cell) { return self ? self->sphere().neighborCount(cell) : 0; });
649 cls.addFunc("sphereNeighbor", [](HexMapModule* self, int cell, int direction) {
650 return self ? self->sphere().neighbor(cell, direction) : kNoHexSphereCell;
651 });
652 cls.addFunc("sphereDirection", [vm](HexMapModule* self, int cell) {
653 const HexVec3 direction = self ? self->sphere().direction(cell) : HexVec3{};
654 ssq::Array result(vm);
655 result.push(direction.x);
656 result.push(direction.y);
657 result.push(direction.z);
658 return result;
659 });
660 cls.addFunc("sphereCornerDirection", [vm](HexMapModule* self, int cell, int corner) {
661 const HexVec3 direction = self ? self->sphere().cornerDirection(cell, corner) : HexVec3{};
662 ssq::Array result(vm);
663 result.push(direction.x);
664 result.push(direction.y);
665 result.push(direction.z);
666 return result;
667 });
668 cls.addFunc("sphereCornerCount",
669 [](HexMapModule* self, int cell) { return self ? self->sphere().cornerCountOf(cell) : 0; });
670 cls.addFunc("sphereElevation", [](HexMapModule* self, int cell) { return self ? self->sphere().elevation(cell) : 0; });
671 cls.addFunc("sphereWaterLevel",
672 [](HexMapModule* self, int cell) { return self ? self->sphere().waterLevel(cell) : 0; });
673 cls.addFunc("sphereTerrainType",
674 [](HexMapModule* self, int cell) { return self ? self->sphere().terrainType(cell) : 0; });
675 cls.addFunc("sphereIsUnderwater",
676 [](HexMapModule* self, int cell) { return self != nullptr && self->sphere().isUnderwater(cell); });
677 cls.addFunc("sphereCellAt", [](HexMapModule* self, float x, float y, float z) {
678 return self ? self->sphere().topology().cellAt(HexVec3{x, y, z}) : kNoHexSphereCell;
679 });
680 cls.addFunc("sphereDistance", [](HexMapModule* self, int a, int b) {
681 return self ? self->sphere().distance(a, b) : kNoHexSphereCell;
682 });
683 cls.addFunc("spherePickCell",
684 [vm](HexMapModule* self, float ox, float oy, float oz, float dx, float dy, float dz) {
685 const auto project = [](HexSphereCell cell) { return Value(static_cast<std::int64_t>(cell)); };
686 if (!self) {
688 vm,
690 DiagnosticCode::NotFound, "hex map module is not available", "hexmap.sphere.pick")),
691 project);
692 }
694 self->sphere().pickCell(HexVec3{ox, oy, oz}, HexVec3{dx, dy, dz}),
695 project);
696 });
697 cls.addFunc("sphereSetElevation", [vm](HexMapModule* self, int cell, int value) {
698 if (!self) return script::projectResult(vm, Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "hex map module is not available", "hexmap")));
699 return script::projectResult(vm, self->sphere().setElevation(cell, value));
700 });
701 cls.addFunc("sphereSetTerrainType", [vm](HexMapModule* self, int cell, int value) {
702 if (!self) return script::projectResult(vm, Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "hex map module is not available", "hexmap")));
703 return script::projectResult(vm, self->sphere().setTerrainType(cell, value));
704 });
705 cls.addFunc("sphereEditElevation", [vm](HexMapModule* self, int cell, int radius, int delta) {
706 if (!self) return script::projectResult(vm, Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "hex map module is not available", "hexmap")));
707 return script::projectResult(vm, self->sphere().editElevation(cell, radius, delta));
708 });
709 cls.addFunc("sphereEditTerrainType", [vm](HexMapModule* self, int cell, int radius, int terrainType) {
710 if (!self) return script::projectResult(vm, Result<void>::failure(Diagnostic::error(DiagnosticCode::InvalidArgument, "hex map module is not available", "hexmap")));
711 return script::projectResult(vm, self->sphere().editTerrainType(cell, radius, terrainType));
712 });
713 cls.addFunc("sphereTerrainMesh",
714 [](HexMapModule* self) -> graphics::Mesh* { return self ? self->sphereTerrainMesh() : nullptr; });
715 cls.addFunc("sphereWaterMesh",
716 [](HexMapModule* self) -> graphics::Mesh* { return self ? self->sphereWaterMesh() : nullptr; });
717 cls.addFunc("sphereReleaseMeshes", [](HexMapModule* self, graphics::Graphics* gfx) {
718 if (self) self->releaseSphereMeshes(gfx);
719 });
720}
721
722} // namespace eve::hexmap
double value
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
float z
Definition AnimClip.cpp:738
std::string terrain
ActiveSource owned
const std::string & s
std::unordered_map< std::string, QuestRuntime > entries
int ax
Definition CaveMesh.cpp:113
int az
Definition CaveMesh.cpp:113
Stable, structured diagnostics shared by engine modules.
HSQUIRRELVM vm
Definition ECS.cpp:20
HSQOBJECT cls
Definition ECS.cpp:21
std::vector< std::uint8_t > blob
std::uint32_t vertexCount
std::uint32_t indexCount
float u
Definition Grass.cpp:233
Deterministic feature placement for hex decorations and walls.
Script-facing hex map module: one editable map plus its chunk meshes.
Mesh generation for the spherical hex map surface.
Terrain bake hexmap applies; procgen produces the payload.
std::uint64_t bytes
MeleePoint3 b
Definition MeleeHit.cpp:41
MeleePoint3 a
Definition MeleeHit.cpp:40
#define Module_IMPL(ModuleName, newExpr)
Definition Module.h:26
std::array< PixelCell, kPixelChunkSize *kPixelChunkSize > cells
float radius
std::string path
Definition PlayHost.cpp:110
std::uint32_t seed
Definition PointSet.cpp:807
Mesh * mesh
RoadLaneDirection direction
float dz
float dy
float dx
LocalPageCacheEntry slots[ShadowConfig::kLocalSlots]
The single Squirrel projection for common Result, Status and Value.
Cell cell
std::vector< UnitCandidate > units
TacticalUnit::TurnResources turn
TerrainWaterField water
uint32_t index
double oy
double ox
Owning, renderer-independent dynamic values.
static Diagnostic error(DiagnosticCode code, std::string message, std::string path={}, DiagnosticDetails details={}, std::string source={})
Construct an error diagnostic with the standard error severity.
Definition Diagnostic.h:125
Move-only operation result carrying either a value or Status.
Definition Result.h:155
static Result success(T value)
Construct a successful result owning value.
Definition Result.h:164
static Result failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
static Value string(std::string value)
Compatibility factory for a string value.
Definition Value.h:110
std::vector< Value > Array
Definition Value.h:33
virtual bool releaseMesh(Mesh *mesh)
Eagerly releases a mesh created by this Graphics.
Definition Graphics.h:927
virtual bool updateMeshVertices(Mesh *mesh, const float *posXYZ, const float *nrmXYZ, const float *uvST, int vertexCount, const uint32_t *indices, int indexCount)=0
In-place update of a mesh's vertex/index data (CPU -> host-visible VBO). Mirrors bakeMeshMorph: the u...
virtual Mesh * newMeshFromArrays(const float *posXYZ, const float *nrmXYZ, const float *uvST, int vertexCount, const uint32_t *indices, int indexCount)=0
Upload a triangle mesh from packed CPU arrays. Owned by Graphics. posXYZ required (vertexCount*3)....
GPU mesh handle (+ optional CPU morph targets).
Definition Mesh.h:25
Owns the active hex map and the GPU meshes generated from it.
void releaseMeshes(graphics::Graphics *gfx)
Releases every mesh owned by the module.
Result< void > rebuildSphere(graphics::Graphics *gfx)
Rebuilds the whole-sphere terrain and ocean meshes from the current cells.
HexUnitRegistry & units() noexcept
Units of the active map.
~HexMapModule() override
Destructor; GPU meshes must already have been released.
void syncScratch()
Re-sizes the search scratch to the active map. @cost Proportional to the cell count; called by newGri...
void releaseSphereMeshes(graphics::Graphics *gfx) noexcept
Releases every mesh owned by the spherical map.
Result< void > adoptGrid(graphics::Graphics *gfx, HexMap &&restored, const std::vector< HexUnitState > &units)
Replaces the active grid with an already-decoded map.
HexMapModule()
Constructs an empty module with no map and no meshes.
Result< void > applyTerrain(graphics::Graphics *gfx, const HexTerrainBake &bake)
Apply a procgen planar terrain bake and drop every unit.
Result< void > newGrid(graphics::Graphics *gfx, std::int32_t cellCountX, std::int32_t cellCountZ, std::uint32_t seed)
Replaces the active grid and releases the meshes of the previous one.
graphics::Mesh * chunkMeshAt(std::int32_t chunkIndex, HexSurface surface) const
Borrowed GPU mesh of one chunk surface.
Result< void > newSphere(graphics::Graphics *gfx, std::int32_t subdivision, float radius, std::uint32_t seed)
Replaces the spherical map and releases the meshes of the previous one.
std::int32_t rebuildChunk(graphics::Graphics *gfx, std::int32_t chunkIndex)
Rebuilds all surfaces of one chunk.
std::int32_t rebuildDirtyChunks(graphics::Graphics *gfx)
Rebuilds every chunk surface currently marked dirty.
Result< void > applySphereTerrain(graphics::Graphics *gfx, const HexTerrainBake &bake)
Apply a procgen sphere terrain bake and rebuild both meshes.
An editable, chunked, pointy-top hex map.
Definition HexMap.h:72
std::int32_t chunkCount() const noexcept
Total number of chunks.
Definition HexMap.h:102
bool empty() const noexcept
Whether the map holds any cell.
Definition HexMap.h:92
std::int32_t cellCount() const noexcept
Total number of cells.
Definition HexMap.h:104
Result< void > reset(std::int32_t cellCountX, std::int32_t cellCountZ, std::uint32_t seed)
Replaces the grid with a new cellCountX * cellCountZ map.
Definition HexMap.cpp:37
std::int32_t takeDirtyChunk() noexcept
Pops the next dirty chunk.
Definition HexMap.cpp:160
CPU-side triangle soup for one hex chunk surface.
Definition HexMeshData.h:24
bool empty() const noexcept
Whether the mesh has no triangles.
Definition HexMeshData.h:30
const std::vector< float > & uvs() const noexcept
Texture coordinate stream (uv per vertex).
Definition HexMeshData.h:60
std::size_t vertexCount() const noexcept
Number of vertices.
Definition HexMeshData.h:32
const std::vector< float > & positions() const noexcept
Interleaved-free position stream (xyz per vertex).
Definition HexMeshData.h:56
const std::vector< std::uint32_t > & indices() const noexcept
Triangle index stream.
Definition HexMeshData.h:62
const std::vector< float > & normals() const noexcept
Normal stream (xyz per vertex), valid after finalize().
Definition HexMeshData.h:58
void resize(std::int32_t cellCount)
Sizes the scratch for cellCount cells, preserving nothing.
Definition HexSearch.cpp:40
bool empty() const noexcept
Whether the map holds any cell.
Result< void > reset(std::int32_t subdivision, float radius, std::uint32_t seed)
Replaces the map with a new sphere of 10 * 4^subdivision + 2 cells.
void removeAll(HexMap &map, HexVisibility &visibility) noexcept
Removes every unit and clears the visibility counters of the map.
Definition HexUnits.cpp:187
Result< void > restore(HexMap &map, HexVisibility &visibility, HexSearchContext &scratch, const std::vector< HexUnitState > &states)
Replaces every unit with states, granting their vision.
Definition HexUnits.cpp:375
void reset(std::int32_t cellCount)
Sizes the counter array for cellCount cells and zeroes it.
std::variant< std::monostate, std::int64_t, double, std::string, bool > Value
Definition Database.h:26
ssq::Table project(HSQUIRRELVM vm, const eve::Result< void > &result)
Project a completed editing result that has no payload.
void buildFeatureMesh(const HexMap &map, std::int32_t chunkIndex, HexMeshData &out)
Builds the urban, farm, plant and special decorations of one chunk.
constexpr std::int32_t kMaxHexGridDimension
Largest grid edge, in cells, any entry point may request.
Definition HexMap.h:47
void buildSphereWaterMesh(const HexSphereMap &map, HexMeshData &out)
Builds the ocean surface of a spherical hex map.
HexSurface
Surface streams a chunk mesh is generated for.
Definition HexMap.h:19
@ Feature
Urban, farm, plant and special decorations; built by buildFeatureMesh.
@ Wall
City/farm walls, wall towers and bridges; built by buildWallMesh.
@ Fog
Fog-of-war overlay of a chunk.
void buildChunkSurfaceMesh(const HexMap &map, std::int32_t chunkIndex, HexSurface surface, HexMeshData &out)
Builds one chunk surface by dispatching on surface.
Definition HexMapMesh.cpp:5
Result< void > applyHexTerrain(HexMap &map, const HexTerrainBake &bake)
Replace a planar map's cells with a bake.
Result< HexTerrainBake > hexTerrainBakeFromValue(const Value &value)
Parse a script bake payload produced by hexTerrainBakeToValue.
constexpr std::int32_t kHexDirectionCount
Number of hex edges / facing directions.
Definition HexMetrics.h:62
void buildWallMesh(const HexMap &map, std::int32_t chunkIndex, HexMeshData &out)
Builds the wall and bridge geometry of one chunk.
constexpr std::int32_t kHexSurfaceCount
Number of chunk surface streams.
Definition HexMap.h:38
constexpr HexSphereCell kNoHexSphereCell
Returned by a spherical cell query that has no answer.
Result< void > applyHexSphereTerrain(HexSphereMap &map, const HexTerrainBake &bake)
Replace a spherical map's cells with a bake.
std::int32_t HexSphereCell
Dense identifier of one cell of a spherical hex topology.
Result< HexPath > findPath(const HexMap &map, HexSearchContext &scratch, HexCoordinates from, HexCoordinates to, const HexMoveRules &rules, const HexOccupancyQuery &occupied)
Finds the cheapest path between two cells.
void buildSphereTerrainMesh(const HexSphereMap &map, HexMeshData &out)
Builds the ground, terrace and cliff surface of a spherical hex map.
Result< void > loadHexMap(const std::vector< std::uint8_t > &bytes, HexMap &map, std::vector< HexUnitState > &units)
Restores a map and its units from a payload produced by saveHexMap.
void buildFogMesh(const HexMap &map, const HexVisibility &visibility, std::int32_t chunkIndex, HexMeshData &out)
Builds the fog-of-war overlay of one chunk.
Result< void > saveHexMap(const HexMap &map, const std::vector< HexUnitState > &units, std::vector< std::uint8_t > &out)
Serializes a map and its units into a self-describing byte payload.
HexDirection
Hex facing directions, counter-clockwise from north-east.
Definition HexMetrics.h:59
double sample(const Heightmap &map, double u, double v)
Sample.
ssq::Table projectStatusResult(HSQUIRRELVM vm, const Status &status)
Project a checked native status that carries no payload.
ssq::Table projectResult(HSQUIRRELVM vm, Result< void > &&result)
Consume and project a void native Result using the common schema.
Result< Value > valueFromSquirrel(HSQUIRRELVM vm, SQInteger index, const SquirrelValueOptions &options)
Convert one Squirrel value into the canonical owning Value tree.
WidgetDesc list(std::string listId, const std::vector< std::string > &items, const std::function< WidgetDesc(const std::string &, int)> &itemFn)
Expand a string list into a Group of item widgets. itemFn(label, index) builds each row; keys default...
Definition Widget.cpp:715
static constexpr HexCoordinates fromOffset(std::int32_t offsetX, std::int32_t offsetZ) noexcept
Converts an odd-row offset pair into axial coordinates.
Packed terrain produced by a generator and applied onto a hex map.