diff --git a/BUILD.md b/BUILD.md index d412ce6..929e8ff 100644 --- a/BUILD.md +++ b/BUILD.md @@ -64,7 +64,7 @@ the full ~2.8x speedup; the default uses all cores for no extra gain: rewinds weather + storms via an undo history) wheel over the 2D map: zoom toward cursor (1-8x); drag pans when zoomed SPACE pause while forming / re-evolve once settled (or the on-screen button) - [ / ] drift speed (My/s) -- in Live World: live clock rate (hours/s, hour->month) + [ / ] drift speed (My/s) -- in Live World: live clock rate (hours/s -> up to ~20 years/s) S single tectonic tick (in Live World: step the clock forward, like '.') F fast-forward Phase-1 forming to settled (instant) R reseed planet (restart forming) diff --git a/CLAUDE.md b/CLAUDE.md index c7ca7fa..9b6424f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -61,7 +61,7 @@ scale with dynamic **weather** (clouds, rain, storms, fronts), day/night, and li **ecosystems / civilization** evolving in real time. This is a separate large effort; the fixed-grid Eulerian model + the climate fields are the groundwork for it. - **Clock + day/night groundwork** *(done — see `PlanetLive.cpp`)* — a slow real-time clock - (hours → weeks/months, `liveTime`/`liveRate`, key `W` to enter once settled, `[`/`]` ramp + (hours → up to ~20 years/sec, `liveTime`/`liveRate`, key `W` to enter once settled, `[`/`]` ramp the rate), a moving **day/night terminator** (sun from time-of-day rotation + seasonal declination via `axialTilt`; key `N`), a raylib-free per-cell **insolation field** (`computeInsolation`, the weather hook), a **live seasonal temperature** cycling the @@ -472,7 +472,7 @@ Working and verified (logic tested headless): annual-mean `sTemp` swung toward the existing `summerTemp`/`winterTemp` by the seasonal phase, anti-phased across hemispheres). Viewer: key `W` (settled world) toggles **Live World** — drift freezes and `liveTime` advances at `liveRate` (sim hours/real-second), `[`/`]` ramp it - hour→month; the HUD shows a `Year/Day/HH:MM` calendar (`dayLengthHours`/`yearLengthDays`). + hour→~20 years/sec (`liveRateMax()` = 20 sim-years/s); the HUD shows a `Year/Day/HH:MM` calendar (`dayLengthHours`/`yearLengthDays`). `rebuildLiveOverlay()` builds a per-cell day/night brightness (`illum`, soft terminator, dim night floor) + `shadedColors` (base colour → snow on cold land / sea-ice on cold ocean via `snowTemp`/`seaIceTemp` → day/night dim); both the 3D globe and 2D map draw `displayColors()` @@ -724,7 +724,7 @@ all in 3D + 2D) · `N` day/night terminator (Live World) · `T` tide-coloured co `P` territory / realms view + political borders (Realms tab lists nations) · `X` culture / faiths view + cultural borders (Cultures tab lists peoples, ethos & religion) · `SPACE` or on-screen button pause · -`[`/`]` drift speed (My/sec) — in **Live World** the live-clock rate (hours/sec, hour→month) · +`[`/`]` drift speed (My/sec) — in **Live World** the live-clock rate (hours/sec → up to ~20 years/sec) · `S` single tick (in **Live World** steps the clock forward) · `.`/`,` step the live clock forward/back by one rate-unit (auto-pauses; `,` steps **everything** back incl. weather/storms via an undo history) · @@ -742,7 +742,7 @@ keeps running at a finer timestep (`cflDtMy()*phase3DtScale`) while rivers, lake and fluvial erosion evolve. Live World (`W`, on a settled world): geological drift freezes and a slow real-time clock runs -(`liveTime` in hours, `liveRate` = sim hours/real-second, ramped hour→month with `[`/`]`). A +(`liveTime` in hours, `liveRate` = sim hours/real-second, ramped hour→~20 years/sec with `[`/`]`). A moving day/night terminator (`N`), a live seasonal temperature cycle and a moving snow/sea-ice line animate over whatever colour mode is active; the HUD shows a `Year/Day/HH:MM` calendar. **1–3 moons** orbit (sun-lit phases, orbit rings, solar/lunar eclipses) and, with the distant diff --git a/src/render/Viewer.cpp b/src/render/Viewer.cpp index 2b65c94..6f06bf0 100644 --- a/src/render/Viewer.cpp +++ b/src/render/Viewer.cpp @@ -543,7 +543,7 @@ void Viewer::loadGame(const char* path) { phase3 = (p3 != 0); phase3Prompt = false; phase3PromptAt = phase3 ? elapsedMy : (elapsedMy + planet.cfg.phase3AfterMy); driftRate = dr; - liveWorld = (lw != 0); liveTime = lh; liveRate = std::clamp(lr, 0.25, 720.0); // v8/v13: resume the Live World clock + liveWorld = (lw != 0); liveTime = lh; liveRate = std::clamp(lr, 0.25, liveRateMax()); // v8/v13: resume the Live World clock settleRun = settleNeed; // keep the settled latch consistent dtMy = settled ? planet.cflDtMy() : 0.0; driftAccum = 0.0; formAccum = 0.0; @@ -748,12 +748,19 @@ void Viewer::liveAdvance(double dtClock, double dtWeather) { long year = (long)std::floor(liveTime / yearHours); if (year != lastTerritoryYear) { std::vector beforeNations = (dtWeather > 0.0) ? planet.nationList() : std::vector{}; - if (dtWeather > 0.0) { // only advance wars going forward (not on a step back) - ConflictUpdate wu = planet.stepConflict(year); - for (const WarEvent& e : wu.events) - appendEvent(5, e.severity, liveTime, e.cell, 0, e.title, e.detail); + if (dtWeather > 0.0) { // advancing: simulate each crossed year (not on a step back) + // At high clock rates a frame can span multiple years -- catch up one year at a time so + // wars/territory aren't skipped (bounded: an extreme jump only simulates the most recent). + const long maxCatch = 12; + long firstYear = std::max(lastTerritoryYear + 1, year - maxCatch + 1); + for (long y = firstYear; y <= year; ++y) { + ConflictUpdate wu = planet.stepConflict(y); + for (const WarEvent& e : wu.events) + appendEvent(5, e.severity, liveTime, e.cell, 0, e.title, e.detail); + if (y < year) rebuildTerritory(); // recompute between years so the next year's war sees it + } } - rebuildTerritory(); + rebuildTerritory(); // final recompute (also refreshes a backward year-cross) if (dtWeather > 0.0) detectNationEvents(beforeNations); territoryChanged = true; } diff --git a/src/render/Viewer.hpp b/src/render/Viewer.hpp index 658dded..f5dfe71 100644 --- a/src/render/Viewer.hpp +++ b/src/render/Viewer.hpp @@ -80,7 +80,10 @@ struct Viewer { // temperature cycle and a moving snow line animate. liveRate is sim hours per real second. bool liveWorld = false, dayNightOn = true; double liveTime = 0.0; // hours since the live clock started - double liveRate = 1.0; // sim hours advanced per real second (ramps hour->month) + double liveRate = 1.0; // sim hours advanced per real second (ramps hour -> ~20 years) + // Top clock speed: ~20 sim-years per real second (a century in ~5 s). Kept a code constant like the + // old 720 (1 month/s); the [ / ] ramp and the load-resume clamp both clamp to this. + double liveRateMax() const { return 20.0 * planet.cfg.dayLengthHours * planet.cfg.yearLengthDays; } // Step-back undo history: each forward step snapshots the clock + full weather state so a // backward step restores everything (weather is an integrated path, not analytically reversible). struct WxFrame { double t; WeatherSnapshot w; }; diff --git a/src/render/ViewerInput.cpp b/src/render/ViewerInput.cpp index 8c688ab..88a8634 100644 --- a/src/render/ViewerInput.cpp +++ b/src/render/ViewerInput.cpp @@ -314,7 +314,7 @@ void Viewer::handleInput() { // Speed control: Live World ramps the live clock (sim hours/s, ~hour -> month); // otherwise it sets the drift rate (My simulated per real second). if (IsKeyPressed(KEY_RIGHT_BRACKET)) { - if (liveWorld) liveRate = std::min(liveRate * 1.5, 720.0); + if (liveWorld) liveRate = std::min(liveRate * 1.5, liveRateMax()); else driftRate = std::min(driftRate * 1.5, 80.0); } if (IsKeyPressed(KEY_LEFT_BRACKET)) { diff --git a/src/render/ViewerRender.cpp b/src/render/ViewerRender.cpp index 5e07d64..f8ffd54 100644 --- a/src/render/ViewerRender.cpp +++ b/src/render/ViewerRender.cpp @@ -811,9 +811,10 @@ void Viewer::renderHUD() { int hh = (int)hod, mm = (int)((hod - hh) * 60.0); line(TextFormat("Live World Year %ld Day %ld %02d:%02d%s", year, doy, hh, mm, paused ? " [PAUSED]" : "")); - const double weekH = 7.0 * dayH, monthH = 30.0 * dayH; + const double weekH = 7.0 * dayH, monthH = 30.0 * dayH, yearHH = yrD * dayH; const char* rl; double rv; - if (liveRate >= monthH) { rl = "mo/s"; rv = liveRate / monthH; } + if (liveRate >= yearHH) { rl = "yr/s"; rv = liveRate / yearHH; } + else if (liveRate >= monthH) { rl = "mo/s"; rv = liveRate / monthH; } else if (liveRate >= weekH) { rl = "wk/s"; rv = liveRate / weekH; } else if (liveRate >= dayH) { rl = "d/s"; rv = liveRate / dayH; } else { rl = "h/s"; rv = liveRate; }