Add geography atlas reshuffle
This commit is contained in:
parent
842a7b4229
commit
554878ef5e
@ -429,7 +429,7 @@ void Viewer::loadGame(const char* path) {
|
||||
is.read(reinterpret_cast<char*>(&lh), sizeof lh); } // v8: Live World clock
|
||||
if (ver >= 13) is.read(reinterpret_cast<char*>(&lr), sizeof lr); // v13: Live World rate
|
||||
if (!is || std::memcmp(magic, "PLSV", 4) != 0 || ver > SAVE_VERSION) { setStatus("Load failed: bad file"); return; }
|
||||
if (!planet.readState(is, ver >= 4, ver >= 7, ver >= 9, ver >= 10, ver >= 11, ver >= 14, ver >= 15, ver >= 17)) { setStatus("Load failed: corrupt/mismatch"); return; } // v4 biome, v7 biota, v9 moons, v10 weather, v11 storms, v14 old volcanoes, v15 stateful volcanoes, v17 geography
|
||||
if (!planet.readState(is, ver >= 4, ver >= 7, ver >= 9, ver >= 10, ver >= 11, ver >= 14, ver >= 15, ver >= 17, ver >= 18)) { setStatus("Load failed: corrupt/mismatch"); return; } // v4 biome, v7 biota, v9 moons, v10 weather, v11 storms, v14 old volcanoes, v15 stateful volcanoes, v17 geography, v18 geography salt
|
||||
cfg = planet.cfg; // adopt the loaded config
|
||||
elapsedMy = em; settled = (st != 0);
|
||||
planet.drifting = settled; // resume drift boosts iff mid-drift
|
||||
|
||||
@ -15,7 +15,7 @@
|
||||
// ViewerInput.cpp (input/picking/keys) and ViewerRender.cpp (drawing).
|
||||
struct Viewer {
|
||||
// ---- Files / save format ------------------------------------------------
|
||||
static constexpr uint32_t SAVE_VERSION = 17; // v17: +geography/atlas; v16: +event log; v15: stateful volcanoes; v14: old volcanoes; v13: +liveRate; v12: +step-back history; v11: +weather systems; v10: +weather fields; v9: +moons; v8: +Live World clock; v7: +biota; v6: self-describing config; v4: +biome; v3: +phase3
|
||||
static constexpr uint32_t SAVE_VERSION = 18; // v18: geography reshuffle salt; v17: +geography/atlas; v16: +event log; v15: stateful volcanoes; v14: old volcanoes; v13: +liveRate; v12: +step-back history; v11: +weather systems; v10: +weather fields; v9: +moons; v8: +Live World clock; v7: +biota; v6: self-describing config; v4: +biome; v3: +phase3
|
||||
static constexpr int wxSaveMax = 40; // most recent step-back frames persisted in a save
|
||||
static constexpr int EVENT_LOG_MAX = 200;
|
||||
const char* CONFIG_PATH = "planet.cfg";
|
||||
|
||||
@ -224,11 +224,18 @@ void Viewer::handleInput() {
|
||||
if (IsKeyPressed(KEY_O)) showCurrents = !showCurrents; // toggle ocean current arrows
|
||||
if (IsKeyPressed(KEY_K)) showClouds = !showClouds; // toggle weather cloud/rain cover
|
||||
if (IsKeyPressed(KEY_V)) showVolcanoes = !showVolcanoes; // toggle volcano markers (Live World)
|
||||
if (IsKeyPressed(KEY_M) && settled) { // toggle place-name labels (the atlas)
|
||||
if (IsKeyPressed(KEY_M) && settled) { // toggle / reshuffle place-name labels (the atlas)
|
||||
bool reshuffle = IsKeyDown(KEY_LEFT_SHIFT) || IsKeyDown(KEY_RIGHT_SHIFT);
|
||||
if (reshuffle) {
|
||||
planet.reshuffleGeography(); // re-extract current geography + use different names
|
||||
showNames = true;
|
||||
setStatus("Place names reshuffled");
|
||||
} else {
|
||||
if (!planet.geographyBuilt()) planet.generateGeography(); // lazily name the world on first use
|
||||
showNames = !showNames;
|
||||
setStatus(showNames ? "Place names on" : "Place names off");
|
||||
}
|
||||
}
|
||||
if (IsKeyPressed(KEY_C)) { selectedCell = -1; subgrids.clear(); }
|
||||
if (IsKeyPressed(KEY_R)) { cfg.seed = (uint32_t)(GetTime() * 100000) | 1; regen(); }
|
||||
if (IsKeyPressed(KEY_S)) { // one step
|
||||
|
||||
@ -620,8 +620,10 @@ void Viewer::renderHUD() {
|
||||
y += 8;
|
||||
line("hover: cell info | click tile: open detail panel | C close");
|
||||
line("1 elev 2 plates 3 age 4 crust 5 biome 6 temp* 7 precip 8 flora 9 fauna 0 funga (*6 cycles mean/summer/winter/season)");
|
||||
line(TextFormat("B borders [%s] | D vectors [%s] | G grid [%s] | J rivers [%s] | N day/night [%s] | T tides [%s] | O currents [%s] | K clouds [%s] | V volcanoes [%s] | M names [%s]",
|
||||
showBorders ? "on" : "off", showDrift ? "on" : "off", showGrat ? "on" : "off", showRivers ? "on" : "off", dayNightOn ? "on" : "off", showTides ? "on" : "off", showCurrents ? "on" : "off", showClouds ? "on" : "off", showVolcanoes ? "on" : "off", showNames ? "on" : "off"));
|
||||
line(TextFormat("B borders [%s] | D vectors [%s] | G grid [%s] | J rivers [%s] | N day/night [%s] | T tides [%s] | O currents [%s]",
|
||||
showBorders ? "on" : "off", showDrift ? "on" : "off", showGrat ? "on" : "off", showRivers ? "on" : "off", dayNightOn ? "on" : "off", showTides ? "on" : "off", showCurrents ? "on" : "off"));
|
||||
line(TextFormat("K clouds [%s] | V volcanoes [%s] | M names [%s] | Shift+M rename/recheck atlas",
|
||||
showClouds ? "on" : "off", showVolcanoes ? "on" : "off", showNames ? "on" : "off"));
|
||||
line(TextFormat("SPACE pause | [ / ] speed | S step | F fast-fwd | H hydrology [%s] | L biota [%s] | W live [%s] | R reseed | +/-",
|
||||
phase3 ? "on" : "off", planet.biotaPopulated() ? "on" : "off", liveWorld ? "on" : "off"));
|
||||
line("F5 save | F9 load | F12 screenshot | F2 reload planet.cfg");
|
||||
|
||||
@ -54,7 +54,7 @@ void Planet::buildGeometry() {
|
||||
sHasBiota = false;
|
||||
sHumidity.clear(); sCloud.clear(); sRain.clear(); // weather spins up on entering Live World
|
||||
sHasWeather = false; sStorms.clear();
|
||||
geoFeatures.clear(); sGeoRng = cfg.seed ? (cfg.seed ^ 0x6E0C12A7u) : 0x6E0C12A7u;
|
||||
geoFeatures.clear(); sGeoRng = cfg.seed ? (cfg.seed ^ 0x6E0C12A7u) : 0x6E0C12A7u; sGeoSalt = 0;
|
||||
sCellLand.assign(cells.size(), -1); sCellWater.assign(cells.size(), -1);
|
||||
sCellRange.assign(cells.size(), -1); sCellRiver.assign(cells.size(), -1);
|
||||
volcanoes.clear(); sVolRng = cfg.seed ? (cfg.seed ^ 0x70C4F12Au) : 0x70C4F12Au;
|
||||
|
||||
@ -17,7 +17,7 @@ public:
|
||||
std::vector<Plate> plates;
|
||||
std::vector<Moon> moons; // Live World: 1-3 natural satellites (generated + saved)
|
||||
std::vector<Volcano> volcanoes; // Live World: stateful lifecycle volcanoes (saved v15)
|
||||
std::vector<GeoFeature> geoFeatures; // named geographic features / the atlas (saved v17)
|
||||
std::vector<GeoFeature> geoFeatures; // named geographic features / the atlas (saved v17+)
|
||||
|
||||
// Phase flag: false during Phase-1 forming (modest, original tectonics that
|
||||
// settle), true during Phase-2 drift. Gates the increment-4 orogeny boosts
|
||||
@ -146,8 +146,12 @@ public:
|
||||
// Geography stage (the atlas, PlanetGeography.cpp). generateGeography() extracts named
|
||||
// geographic features from the frozen terrain by connectivity (continents/islands, oceans/seas,
|
||||
// lakes, mountain ranges/peaks, rivers) and names them with a separate RNG (tectonic determinism
|
||||
// intact). Saved (v17). The per-cell index arrays give O(1) "which features is this cell in".
|
||||
void generateGeography();
|
||||
// intact). Saved (v17; active reshuffle salt saved v18). The per-cell index arrays give O(1)
|
||||
// "which features is this cell in".
|
||||
void generateGeography(uint32_t nameSalt = 0);
|
||||
// Rebuild the atlas against the current terrain and use a new naming salt, for manually
|
||||
// rechecking/relabeling geography after moving between world-building phases.
|
||||
void reshuffleGeography();
|
||||
// Name a cell that has just become land (e.g. a volcanic island breaching the sea): join an
|
||||
// adjacent existing landmass, else create + name a new Island. Returns the land feature's name
|
||||
// ("" if the atlas isn't built). Appended to geoFeatures (saved). Called by the viewer on a
|
||||
@ -175,11 +179,13 @@ public:
|
||||
// older saves leave weather to spin up on entering Live World.
|
||||
// hasVolcanoes: whether the stream carries a volcano block (save v14+). hasStatefulVolcanoes
|
||||
// means v15+ lifecycle volcanoes; v14's old pure-function block is consumed and discarded.
|
||||
// hasGeography: whether the stream carries the geography/atlas block (save v17+); older saves load
|
||||
// with none (regenerated on demand).
|
||||
// hasGeography: whether the stream carries the geography/atlas block (save v17+); hasGeoSalt:
|
||||
// whether the stream carries the active reshuffle salt (save v18+). Older saves load with none
|
||||
// (regenerated on demand), or with a deterministic first reshuffle if only the salt is absent.
|
||||
bool readState(std::istream& is, bool hasBiome = true, bool hasBiota = true,
|
||||
bool hasMoons = true, bool hasWeather = true, bool hasStorms = true,
|
||||
bool hasVolcanoes = true, bool hasStatefulVolcanoes = true, bool hasGeography = true);
|
||||
bool hasVolcanoes = true, bool hasStatefulVolcanoes = true, bool hasGeography = true,
|
||||
bool hasGeoSalt = true);
|
||||
|
||||
// Helpers for rendering / info.
|
||||
double cellWidthMeters() const; // approx lateral cell spacing
|
||||
@ -265,10 +271,11 @@ private:
|
||||
// Volcanoes (Live World; saved v15). Separate RNG (seeded from cfg.seed in placeVolcanoes)
|
||||
// keeps tectonic determinism intact while lifecycle rolls happen during Live World.
|
||||
uint32_t sVolRng = 1;
|
||||
// Geography (the atlas; saved v17). Per-cell feature index arrays (-1 = none) + a separate RNG
|
||||
// so naming never perturbs the tectonic stream.
|
||||
// Geography (the atlas; saved v17; active reshuffle salt saved v18). Per-cell feature index
|
||||
// arrays (-1 = none) + a separate RNG/salt so naming never perturbs the tectonic stream.
|
||||
std::vector<int> sCellLand, sCellWater, sCellRange, sCellRiver;
|
||||
uint32_t sGeoRng = 1;
|
||||
uint32_t sGeoSalt = 0;
|
||||
|
||||
// Biota: derived density scalars (0..1; recomputed each tick, not saved) and the
|
||||
// on-demand discrete population (saved). sHasBiota latches once generated/loaded.
|
||||
|
||||
@ -26,14 +26,17 @@ const char* featureKindName(FeatureKind k) {
|
||||
return "?";
|
||||
}
|
||||
|
||||
void Planet::generateGeography() {
|
||||
void Planet::generateGeography(uint32_t nameSalt) {
|
||||
const int n = (int)cells.size();
|
||||
const double sea = cfg.seaLevel;
|
||||
sGeoRng = cfg.seed ? (cfg.seed ^ 0x6E0C12A7u) : 0x6E0C12A7u;
|
||||
sGeoSalt = nameSalt;
|
||||
sGeoRng = nameSalt ? nameSalt : (cfg.seed ? (cfg.seed ^ 0x6E0C12A7u) : 0x6E0C12A7u);
|
||||
const uint32_t seedSalt = nameSalt * 0x9E3779B9u;
|
||||
const uint32_t atlasSeed = cfg.seed ^ seedSalt;
|
||||
const uint32_t geoSeed = (cfg.seed ^ 0x6E0C12A7u) ^ seedSalt;
|
||||
|
||||
// Rivers/lakes derive from the hydrology fields -- make sure they exist (routing only, no erosion,
|
||||
// so this changes no elevation and is safe to call on a finished world).
|
||||
if ((int)sDischarge.size() != n || (int)sLakeDepth.size() != n || (int)sFlowTo.size() != n)
|
||||
computeHydrology();
|
||||
|
||||
geoFeatures.clear();
|
||||
@ -56,7 +59,7 @@ void Planet::generateGeography() {
|
||||
};
|
||||
// Per-feature seed: a stable hash of (world seed, kind, anchor cell) -- order-independent.
|
||||
auto featSeed = [&](FeatureKind k, int anchor) -> uint32_t {
|
||||
uint32_t h = (cfg.seed ^ 0x6E0C12A7u) + (uint32_t)((int)k) * 0x85EBCA6Bu;
|
||||
uint32_t h = geoSeed + (uint32_t)((int)k) * 0x85EBCA6Bu;
|
||||
h ^= (uint32_t)(anchor * 2654435761u + 0x165667B1u);
|
||||
h ^= h >> 15; h *= 0x2545F491u; h ^= h >> 13;
|
||||
return h ? h : 1u;
|
||||
@ -69,8 +72,8 @@ void Planet::generateGeography() {
|
||||
return formatName(k, p);
|
||||
};
|
||||
auto bankOf = [&](int regionId, int fallbackAnchor) {
|
||||
return regionId >= 0 ? namegen::bankForRegion(cfg.seed, regionId)
|
||||
: namegen::bankForRegion(cfg.seed, 1000 + fallbackAnchor);
|
||||
return regionId >= 0 ? namegen::bankForRegion(atlasSeed, regionId)
|
||||
: namegen::bankForRegion(atlasSeed, 1000 + fallbackAnchor);
|
||||
};
|
||||
// The label/focus cell: the member nearest the component's (normalized) centroid direction.
|
||||
auto centroidCell = [&](const std::vector<int>& comp) {
|
||||
@ -115,7 +118,7 @@ void Planet::generateGeography() {
|
||||
int anchor = centroidCell(comp);
|
||||
FeatureKind k = (int)comp.size() >= cfg.geoContinentMinCells ? FeatureKind::Continent : FeatureKind::Island;
|
||||
int fi = (int)geoFeatures.size();
|
||||
int bank = namegen::bankForRegion(cfg.seed, fi);
|
||||
int bank = namegen::bankForRegion(atlasSeed, fi);
|
||||
addFeature(k, anchor, fi, (int)comp.size(), bank); // a land feature's regionId is itself
|
||||
for (int i : comp) sCellLand[i] = fi;
|
||||
}
|
||||
@ -246,6 +249,12 @@ void Planet::generateGeography() {
|
||||
}
|
||||
}
|
||||
|
||||
void Planet::reshuffleGeography() {
|
||||
uint32_t x = sGeoSalt ? sGeoSalt : (cfg.seed ? (cfg.seed ^ 0x6E0C12A7u) : 0x6E0C12A7u);
|
||||
x ^= x << 13; x ^= x >> 17; x ^= x << 5;
|
||||
generateGeography(x ? x : 0x6E0C12A7u);
|
||||
}
|
||||
|
||||
// Name a cell that has just become land (a volcanic island breaching the sea). Joins an adjacent
|
||||
// existing landmass if there is one; otherwise mints a new Island feature with a unique name. Returns
|
||||
// the land feature's name. Appended to geoFeatures + sCellLand (saved). Uses the per-cell hash, not a
|
||||
@ -261,8 +270,11 @@ std::string Planet::nameNewLand(int cell) {
|
||||
if (lf >= 0 && lf < (int)geoFeatures.size()) { sCellLand[cell] = lf; return geoFeatures[lf].name; }
|
||||
}
|
||||
// Isolated new island: mint a unique name (dedup against existing feature names).
|
||||
int bank = namegen::bankForRegion(cfg.seed, 1000 + cell);
|
||||
uint32_t seed = (cfg.seed ^ 0x6E0C12A7u) + (uint32_t)(cell * 2654435761u)
|
||||
const uint32_t seedSalt = sGeoSalt * 0x9E3779B9u;
|
||||
const uint32_t atlasSeed = cfg.seed ^ seedSalt;
|
||||
const uint32_t geoSeed = (cfg.seed ^ 0x6E0C12A7u) ^ seedSalt;
|
||||
int bank = namegen::bankForRegion(atlasSeed, 1000 + cell);
|
||||
uint32_t seed = geoSeed + (uint32_t)(cell * 2654435761u)
|
||||
+ (uint32_t)geoFeatures.size() * 40503u;
|
||||
auto inUse = [&](const std::string& nm) {
|
||||
for (const GeoFeature& f : geoFeatures) if (f.name == nm) return true; return false;
|
||||
|
||||
@ -381,6 +381,7 @@ void Planet::writeState(std::ostream& os) const {
|
||||
writePod(os, sVolRng);
|
||||
// v17: named geographic features (the atlas) + per-cell region indices. Feature records carry a
|
||||
// std::string name, so they are written field-by-field (not POD); the index arrays are POD.
|
||||
// v18: active geography reshuffle salt, so repeated renames continue after load.
|
||||
uint64_t nf = geoFeatures.size(); writePod(os, nf);
|
||||
for (const GeoFeature& f : geoFeatures) {
|
||||
writePod(os, f.id);
|
||||
@ -391,16 +392,18 @@ void Planet::writeState(std::ostream& os) const {
|
||||
}
|
||||
writeVec(os, sCellLand); writeVec(os, sCellWater);
|
||||
writeVec(os, sCellRange); writeVec(os, sCellRiver);
|
||||
writePod(os, sGeoSalt);
|
||||
}
|
||||
|
||||
bool Planet::readState(std::istream& is, bool hasBiome, bool hasBiota, bool hasMoons,
|
||||
bool hasWeather, bool hasStorms, bool hasVolcanoes, bool hasStatefulVolcanoes,
|
||||
bool hasGeography) {
|
||||
bool hasGeography, bool hasGeoSalt) {
|
||||
// Save blocks are append-only by version. If a caller asks for an older prefix,
|
||||
// later blocks cannot exist in that stream even if the default arguments say otherwise.
|
||||
if (!hasBiota) { hasMoons = false; hasWeather = false; hasStorms = false; hasVolcanoes = false; }
|
||||
if (!hasWeather) { hasStorms = false; hasVolcanoes = false; } // volcano block follows the weather block
|
||||
if (!hasVolcanoes || !hasStatefulVolcanoes) hasGeography = false; // geography block follows the volcano block
|
||||
if (!hasGeography) hasGeoSalt = false; // salt follows the geography block
|
||||
|
||||
// Read the length-prefixed key=value config block (see writeState). A default
|
||||
// PlanetConfig is parsed over, so fields absent from an older save keep their
|
||||
@ -529,7 +532,7 @@ bool Planet::readState(std::istream& is, bool hasBiome, bool hasBiota, bool hasM
|
||||
|| !std::isfinite(v.ashCarry) || v.phase > 1) return false;
|
||||
}
|
||||
// v17: named geographic features (the atlas). Older saves load with none (regenerate on demand).
|
||||
geoFeatures.clear(); sGeoRng = cfg.seed ? (cfg.seed ^ 0x6E0C12A7u) : 0x6E0C12A7u;
|
||||
geoFeatures.clear(); sGeoRng = cfg.seed ? (cfg.seed ^ 0x6E0C12A7u) : 0x6E0C12A7u; sGeoSalt = 0;
|
||||
sCellLand.assign(cells.size(), -1); sCellWater.assign(cells.size(), -1);
|
||||
sCellRange.assign(cells.size(), -1); sCellRiver.assign(cells.size(), -1);
|
||||
if (hasGeography) {
|
||||
@ -561,6 +564,11 @@ bool Planet::readState(std::istream& is, bool hasBiome, bool hasBiota, bool hasM
|
||||
if (!readVec(is, sCellRange, cells.size())) return false;
|
||||
if (!readVec(is, sCellRiver, cells.size())) return false;
|
||||
if (!okArr(sCellLand) || !okArr(sCellWater) || !okArr(sCellRange) || !okArr(sCellRiver)) return false;
|
||||
if (hasGeoSalt) {
|
||||
readPod(is, sGeoSalt);
|
||||
if (!is) return false;
|
||||
sGeoRng = sGeoSalt ? sGeoSalt : (cfg.seed ? (cfg.seed ^ 0x6E0C12A7u) : 0x6E0C12A7u);
|
||||
}
|
||||
}
|
||||
computeBiotaDensity(); // derived density scalars for the colour views
|
||||
return (bool)is;
|
||||
|
||||
@ -363,7 +363,7 @@ struct PlanetConfig {
|
||||
|
||||
// --- Geography (the atlas) -- see PlanetGeography.cpp ----------------------
|
||||
// Thresholds for extracting + naming geographic features from the frozen terrain. Generated once
|
||||
// on a settled world (key M), saved (v17). Tune to control what counts as a continent vs island,
|
||||
// on a settled world (key M), saved (v17+). Tune to control what counts as a continent vs island,
|
||||
// an ocean vs sea, a named mountain range / major river, and to cap label clutter.
|
||||
int geoContinentMinCells = 40; // land component >= this many cells = Continent (else Island)
|
||||
int geoSeaMaxCells = 60; // ocean/basin <= this many cells = Sea (else Ocean)
|
||||
|
||||
@ -9,7 +9,7 @@
|
||||
// src/sim/PlanetGeography.cpp src/sim/PlanetIO.cpp -o /tmp/tg && /tmp/tg
|
||||
//
|
||||
// Verifies: extraction (continents/oceans/ranges/rivers/lakes), per-cell membership consistency,
|
||||
// names non-empty/unique/deterministic, RNG isolation from tectonics, and a v17 save round-trip.
|
||||
// names non-empty/unique/deterministic, RNG isolation from tectonics, and a v18 save round-trip.
|
||||
|
||||
#include "Planet.hpp"
|
||||
#include "NameGen.hpp"
|
||||
@ -18,6 +18,7 @@
|
||||
#include <algorithm>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
static int failures = 0;
|
||||
static void check(bool cond, const char* what) {
|
||||
@ -68,6 +69,48 @@ int main() {
|
||||
check(continents + islands >= 1, "at least one land mass");
|
||||
check(oceans >= 1, "at least one ocean");
|
||||
|
||||
std::printf("Geography: reshuffle rechecks atlas with alternate names\n");
|
||||
{
|
||||
Planet r; r.generate(cfg); settle(r); drift(r, 40); r.generateGeography();
|
||||
std::vector<FeatureKind> kinds;
|
||||
std::vector<int> anchors;
|
||||
std::vector<std::string> names;
|
||||
for (const GeoFeature& f : r.geography()) {
|
||||
kinds.push_back(f.kind);
|
||||
anchors.push_back(f.anchorCell);
|
||||
names.push_back(f.name);
|
||||
}
|
||||
r.reshuffleGeography();
|
||||
bool sameFeatures = r.geography().size() == names.size();
|
||||
bool renamed = false;
|
||||
if (sameFeatures) {
|
||||
for (size_t i = 0; i < names.size(); ++i) {
|
||||
const GeoFeature& f = r.geography()[i];
|
||||
if (f.kind != kinds[i] || f.anchorCell != anchors[i]) sameFeatures = false;
|
||||
if (f.name != names[i]) renamed = true;
|
||||
}
|
||||
}
|
||||
check(sameFeatures, "reshuffle rebuilds the same current-terrain features");
|
||||
check(renamed, "reshuffle uses alternate place names");
|
||||
check(countKind(r, FeatureKind::Ocean) >= 1, "reshuffle rechecks oceans");
|
||||
|
||||
std::stringstream ss(std::ios::in | std::ios::out | std::ios::binary);
|
||||
r.writeState(ss);
|
||||
Planet loaded;
|
||||
check(loaded.readState(ss), "reshuffled atlas save/load preserves v18 salt");
|
||||
std::vector<std::string> loadedNames;
|
||||
for (const GeoFeature& f : loaded.geography()) loadedNames.push_back(f.name);
|
||||
loaded.reshuffleGeography();
|
||||
bool advancedAfterLoad = loaded.geography().size() == loadedNames.size();
|
||||
if (advancedAfterLoad) {
|
||||
bool anyChanged = false;
|
||||
for (size_t i = 0; i < loadedNames.size(); ++i)
|
||||
if (loaded.geography()[i].name != loadedNames[i]) anyChanged = true;
|
||||
advancedAfterLoad = anyChanged;
|
||||
}
|
||||
check(advancedAfterLoad, "reshuffle continues with new names after load");
|
||||
}
|
||||
|
||||
std::printf("Geography: rebuild clears stale atlas state\n");
|
||||
{
|
||||
Planet r; r.generate(cfg); settle(r); drift(r, 40); r.generateGeography();
|
||||
@ -183,13 +226,13 @@ int main() {
|
||||
for (int i = 0; i < n; ++i) if (std::fabs(x.cells[i].elevation - y.cells[i].elevation) > 1e-9) terrainSame = false;
|
||||
check(terrainSame, "generateGeography never perturbs tectonic evolution");
|
||||
|
||||
std::printf("Geography: save v17 round-trip\n");
|
||||
std::printf("Geography: save v18 round-trip\n");
|
||||
{
|
||||
std::stringstream ss(std::ios::in | std::ios::out | std::ios::binary);
|
||||
p.writeState(ss);
|
||||
Planet r;
|
||||
bool ok = r.readState(ss, true, true, true, true, true, true, true, true);
|
||||
check(ok, "readState accepts a v17 stream");
|
||||
check(ok, "readState accepts a v18 stream");
|
||||
bool match = (r.geography().size() == F.size());
|
||||
if (match)
|
||||
for (size_t i = 0; i < F.size(); ++i)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user