Persist step-back history in saves (v12): rewind storms past a loaded moment
Weather is integrated and path-dependent, so reversing it can only restore recorded snapshots -- and a save held only the current moment, so after a load there was no past to step back to and loaded storms froze on backward step. Save bumped to v12: it now appends the most recent wxSaveMax(40) step-back frames (each = humidity/cloud/rain + storms + RNG) after the planet state. loadGame restores them (and still drops any stale pre-load history), so after a load you can step backward ~40 steps and the storms reverse along their saved track. Pre-v12 saves load with no step-back history (you can still step forward then back). Adds ~10 MB to a save (the user chose the short window). All five suites pass; GUI build clean. Docs updated. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
7631eb4964
commit
1358e81426
7
BUILD.md
7
BUILD.md
@ -76,9 +76,10 @@ CLI flags (applied before the first load/generate):
|
||||
planet.cfg human-editable key=value config of every PlanetConfig parameter;
|
||||
auto-created on first run, reload live with F2. Range-checked on
|
||||
load; an invalid file reverts to safe defaults (not overwritten).
|
||||
planet.save binary snapshot (versioned, currently v11: +weather systems/storms; v10
|
||||
+weather fields; v9 +moons; v8 +Live World clock; v7 +biota): seed + config +
|
||||
full planet state; F5 writes it, F9 reloads and resumes deterministically. As of v6
|
||||
planet.save binary snapshot (versioned, currently v12: +step-back history (~40 frames, so a
|
||||
load can rewind storms); v11 +weather systems/storms; v10 +weather fields; v9
|
||||
+moons; v8 +Live World clock; v7 +biota): seed + config + full planet state; F5
|
||||
writes it, F9 reloads and resumes deterministically. As of v6
|
||||
the config is stored as a self-describing key=value block (like
|
||||
planet.cfg), so adding/removing config fields no longer breaks saves
|
||||
(unknown keys ignored, missing keys default). v6 cannot load pre-v6
|
||||
|
||||
18
CLAUDE.md
18
CLAUDE.md
@ -427,10 +427,12 @@ Working and verified (logic tested headless):
|
||||
snapshots the full state — `Planet::captureWeather()`/`restoreWeather()` (humidity/cloud/rain/
|
||||
storms/RNG) into a bounded `wxUndo` ring — and **`,` restores the previous snapshot**, so the
|
||||
step really reverses *everything* (clouds, rain, **moving storms**) plus the deterministic sky.
|
||||
`liveAdvance` records a snapshot at ~one-step cadence on **any** forward advance (continuous run or
|
||||
manual step), so storms born during a continuous run also rewind (an earlier version cleared the
|
||||
history on run, which left run-born storms frozen on step-back); `,` searches the ring by time, the
|
||||
ring drops oldest past `wxUndoMax`. `S` in Live World aliases the forward step.
|
||||
A manual step **always** records (rate-independent, via `wxPushSnapshot`); a continuous run records
|
||||
a throttled snapshot (~1/sec) so a run is rewindable too. `,` searches the ring by time, the ring
|
||||
drops oldest past `wxUndoMax`. The most recent `wxSaveMax`(40) frames are **persisted in the save
|
||||
(v12)** so a load can rewind storms past the saved moment; a load also drops any stale pre-load
|
||||
history. With no recorded past (e.g. immediately after a pre-v12 load) `,` rewinds the sky only and
|
||||
says so. `S` in Live World aliases the forward step.
|
||||
- Mouse hover (in either view) shows per-cell info. Clicking a tile opens a
|
||||
right-side detail panel: tile info header + the tile's subgrid drawn as a
|
||||
flat hoverable grid of subtiles (neighbor-owned subtiles dimmed). A high-res
|
||||
@ -610,11 +612,13 @@ key=value text block** instead of a raw POD dump, v7 appends the **biota populat
|
||||
block — three Organism lists per cell, gated by a flag byte, v8 appends the **Live World**
|
||||
clock — a flag byte + `liveTime`, v9 appends the **moons** block, v10 appends the **weather** block —
|
||||
humidity/cloud/rain, flag-gated, v11 also persists the **weather systems** + RNG so a load resumes
|
||||
active storms); newer-than-supported is
|
||||
active storms, v12 appends the most recent **step-back frames** — `wxSaveMax`(40) weather snapshots
|
||||
— so a load can rewind storms past the saved moment); newer-than-supported is
|
||||
rejected. Older saves (no biota block) load fine with an empty population (press `L`);
|
||||
pre-v8 saves load with Live World off; pre-v9 saves synthesize moons from the seed; pre-v10
|
||||
saves spin weather up live; pre-v11 saves load with no active storms (they respawn). `loadGame`
|
||||
clears the step-back `wxUndo` history so a load can't restore stale pre-load weather.
|
||||
saves spin weather up live; pre-v11 saves load with no active storms (they respawn); pre-v12 saves
|
||||
load with no step-back history (you can still step forward then back). A load drops any **stale**
|
||||
pre-load `wxUndo` history and reloads the saved one.
|
||||
**As of v6, adding/removing PlanetConfig fields no longer breaks saves** — the saved
|
||||
config is parsed like `planet.cfg` (unknown keys ignored, missing keys keep defaults),
|
||||
written at `precision(17)` so doubles round-trip exactly. (v6 cannot load pre-v6 saves —
|
||||
|
||||
@ -202,8 +202,10 @@ recurve (`weatherSystemSpeed`), **intensify** over warm sea / **decay+cull** ove
|
||||
behind them. Tropical systems past `weatherHurricaneStr` are hurricanes/typhoons; rendered as
|
||||
animated cyclonic spiral markers (eye for cyclones) spinning by hemisphere, in 3D + 2D, under `K`.
|
||||
The systems (+ their RNG/next-id) are **saved (v11)** alongside the humidity/cloud/rain fields, so a
|
||||
load resumes active storms; `loadGame` clears the step-back `wxUndo` ring so a load can't restore a
|
||||
stale pre-load weather state.
|
||||
load resumes active storms; a load also drops any stale pre-load `wxUndo` step-back ring and (v12)
|
||||
restores the most recent `wxSaveMax`(40) step-back frames from the file, so stepping back after a load
|
||||
can rewind storms past the saved moment. Weather is integrated/path-dependent, so reversing it past
|
||||
a save is only possible via this stored history — it can't be re-derived from the loaded moment.
|
||||
|
||||
## Live World viewer controls (follow-cam, 2D zoom, clock stepper)
|
||||
|
||||
|
||||
@ -262,6 +262,18 @@ void Viewer::saveGame(const char* path) {
|
||||
os.write(reinterpret_cast<const char*>(&lw), sizeof lw); // v8: Live World flag
|
||||
os.write(reinterpret_cast<const char*>(&liveTime), sizeof liveTime); // v8: live clock (hours)
|
||||
planet.writeState(os);
|
||||
// v12: persist the most recent step-back frames so a load can rewind storms past the moment.
|
||||
auto wD = [&](const std::vector<double>& v){ uint64_t m = v.size(); os.write((char*)&m, 8); if (m) os.write((const char*)v.data(), (std::streamsize)(m * sizeof(double))); };
|
||||
uint32_t hn = (uint32_t)std::min<size_t>(wxUndo.size(), (size_t)wxSaveMax);
|
||||
os.write((char*)&hn, 4);
|
||||
for (size_t i = wxUndo.size() - hn; i < wxUndo.size(); ++i) {
|
||||
const WxFrame& f = wxUndo[i];
|
||||
os.write((char*)&f.t, 8);
|
||||
wD(f.w.humidity); wD(f.w.cloud); wD(f.w.rain);
|
||||
uint64_t sc = f.w.storms.size(); os.write((char*)&sc, 8);
|
||||
if (sc) os.write((const char*)f.w.storms.data(), (std::streamsize)(sc * sizeof(WeatherSystem)));
|
||||
os.write((char*)&f.w.rng, 4); os.write((char*)&f.w.nextId, 4);
|
||||
}
|
||||
setStatus(os ? std::string("Saved ") + path : "Save failed");
|
||||
}
|
||||
|
||||
@ -291,6 +303,21 @@ void Viewer::loadGame(const char* path) {
|
||||
dtMy = settled ? planet.cflDtMy() : 0.0;
|
||||
driftAccum = 0.0; formAccum = 0.0;
|
||||
wxUndo.clear(); followId = 0; // drop stale step-back history / follow target
|
||||
if (ver >= 12) { // v12: restore the saved step-back frames (rewind past load)
|
||||
auto rD = [&](std::vector<double>& v){ uint64_t m = 0; is.read((char*)&m, 8);
|
||||
if (!is || m > 4000000ull) { v.clear(); return; } v.resize((size_t)m);
|
||||
if (m) is.read((char*)v.data(), (std::streamsize)(m * sizeof(double))); };
|
||||
uint32_t hn = 0; is.read((char*)&hn, 4);
|
||||
for (uint32_t k = 0; k < hn && is; ++k) {
|
||||
WxFrame f; is.read((char*)&f.t, 8);
|
||||
rD(f.w.humidity); rD(f.w.cloud); rD(f.w.rain);
|
||||
uint64_t sc = 0; is.read((char*)&sc, 8); if (sc > 1000000ull) sc = 0;
|
||||
f.w.storms.resize((size_t)sc);
|
||||
if (sc) is.read((char*)f.w.storms.data(), (std::streamsize)(sc * sizeof(WeatherSystem)));
|
||||
is.read((char*)&f.w.rng, 4); is.read((char*)&f.w.nextId, 4);
|
||||
if (is) wxUndo.push_back(std::move(f));
|
||||
}
|
||||
}
|
||||
paused = true; selectedCell = -1; subgrids.clear();
|
||||
buildBorders(planet, borderR, borders, ridgeBorders);
|
||||
buildDriftArrows(planet, driftR, driftArrows, plateLabels);
|
||||
|
||||
@ -15,7 +15,8 @@
|
||||
// ViewerInput.cpp (input/picking/keys) and ViewerRender.cpp (drawing).
|
||||
struct Viewer {
|
||||
// ---- Files / save format ------------------------------------------------
|
||||
static constexpr uint32_t SAVE_VERSION = 11; // 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 = 12; // 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
|
||||
const char* CONFIG_PATH = "planet.cfg";
|
||||
const char* SAVE_PATH = "planet.save";
|
||||
std::string configPath = "planet.cfg"; // initial config (--config overrides)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user