Live stepper robustness: manual steps always snapshot; clearer no-history status
Moved the step-back snapshot out of liveAdvance: a manual forward step now ALWAYS records the pre-step state (rate-independent, so changing the rate or mixing play+step no longer makes the back-step jump), while a continuous run records a throttled snapshot (~1/sec). Step-back with no recorded past (e.g. immediately after a load) now shows a clear status instead of silently freezing the storms. Forward-then-back reverses weather + storms as before. Note: a load restores the current storms (v11) but not their past trajectory, so stepping back BEFORE the loaded moment can only rewind the sky -- reversing loaded storms needs the rewind history, which isn't persisted (see follow-up). Build clean; logic/weather suites pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
b19d2a1703
commit
7631eb4964
@ -305,7 +305,9 @@ void Viewer::stepSim() {
|
|||||||
if (liveWorld) {
|
if (liveWorld) {
|
||||||
// --- Live World: advance the slow clock; geology is frozen --------
|
// --- Live World: advance the slow clock; geology is frozen --------
|
||||||
double dtH = (!paused) ? liveRate * GetFrameTime() : 0.0; // simulated hours this frame
|
double dtH = (!paused) ? liveRate * GetFrameTime() : 0.0; // simulated hours this frame
|
||||||
liveAdvance(dtH, dtH); // liveAdvance records step-back snapshots itself
|
if (dtH > 0.0 && (wxUndo.empty() || liveTime - wxUndo.back().t >= liveRate - 1e-9))
|
||||||
|
wxPushSnapshot(); // throttled history during a continuous run (~1 snapshot/sec)
|
||||||
|
liveAdvance(dtH, dtH);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!paused && !settled) {
|
if (!paused && !settled) {
|
||||||
@ -344,17 +346,6 @@ void Viewer::stepSim() {
|
|||||||
// integrated, non-reversible path, so it advances by dtWeather (0 = hold, used for a backward
|
// integrated, non-reversible path, so it advances by dtWeather (0 = hold, used for a backward
|
||||||
// step which still rewinds the deterministic sky: day/night, tides, seasons, moon phases).
|
// step which still rewinds the deterministic sky: day/night, tides, seasons, moon phases).
|
||||||
void Viewer::liveAdvance(double dtClock, double dtWeather) {
|
void Viewer::liveAdvance(double dtClock, double dtWeather) {
|
||||||
// Record a step-back snapshot of the PRE-advance state at ~one-step cadence, on ANY forward
|
|
||||||
// advance (continuous run or manual step) -- so stepping back reverses weather + storms whether
|
|
||||||
// they were born while stepping or during a continuous run. interval ~ liveRate means roughly
|
|
||||||
// one snapshot per real second regardless of the clock rate (bounded ring, drops oldest).
|
|
||||||
if (dtClock > 0.0) {
|
|
||||||
double interval = std::max(1e-6, liveRate);
|
|
||||||
if (wxUndo.empty() || liveTime - wxUndo.back().t >= interval - 1e-9) {
|
|
||||||
if ((int)wxUndo.size() >= wxUndoMax) wxUndo.erase(wxUndo.begin());
|
|
||||||
wxUndo.push_back(WxFrame{ liveTime, planet.captureWeather() });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
liveTime = std::max(0.0, liveTime + dtClock);
|
liveTime = std::max(0.0, liveTime + dtClock);
|
||||||
double days = liveTime / planet.cfg.dayLengthHours;
|
double days = liveTime / planet.cfg.dayLengthHours;
|
||||||
double dayOfYear01 = days / planet.cfg.yearLengthDays; dayOfYear01 -= std::floor(dayOfYear01);
|
double dayOfYear01 = days / planet.cfg.yearLengthDays; dayOfYear01 -= std::floor(dayOfYear01);
|
||||||
@ -375,10 +366,17 @@ void Viewer::liveAdvance(double dtClock, double dtWeather) {
|
|||||||
rebuildLiveOverlay();
|
rebuildLiveOverlay();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step the live clock forward one rate-unit. Auto-pauses (like a video frame-step); liveAdvance
|
// Push the current (pre-advance) weather state onto the bounded step-back ring.
|
||||||
// records the pre-step snapshot so the backward step can restore weather + storms exactly.
|
void Viewer::wxPushSnapshot() {
|
||||||
|
if ((int)wxUndo.size() >= wxUndoMax) wxUndo.erase(wxUndo.begin());
|
||||||
|
wxUndo.push_back(WxFrame{ liveTime, planet.captureWeather() });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step the live clock forward one rate-unit. Auto-pauses (like a video frame-step); always records
|
||||||
|
// the pre-step snapshot first (rate-independent) so the backward step restores weather + storms.
|
||||||
void Viewer::liveStepForward() {
|
void Viewer::liveStepForward() {
|
||||||
paused = true;
|
paused = true;
|
||||||
|
wxPushSnapshot();
|
||||||
liveAdvance(liveRate, liveRate);
|
liveAdvance(liveRate, liveRate);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -393,8 +391,10 @@ void Viewer::liveStepBack() {
|
|||||||
liveTime = f.t;
|
liveTime = f.t;
|
||||||
planet.restoreWeather(f.w);
|
planet.restoreWeather(f.w);
|
||||||
liveAdvance(0.0, 0.0); // recompute the sky/overlay at the restored time (weather held)
|
liveAdvance(0.0, 0.0); // recompute the sky/overlay at the restored time (weather held)
|
||||||
|
setStatus("Step back");
|
||||||
} else {
|
} else {
|
||||||
liveAdvance(-liveRate, 0.0); // no history: deterministic sky rewinds, weather holds
|
liveAdvance(-liveRate, 0.0); // no recorded past (e.g. right after a load): sky rewinds, weather holds
|
||||||
|
setStatus("Step back (sky only - no earlier weather; play/step forward first)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -145,6 +145,7 @@ struct Viewer {
|
|||||||
void liveAdvance(double dtClock, double dtWeather); // advance the Live World clock + fields
|
void liveAdvance(double dtClock, double dtWeather); // advance the Live World clock + fields
|
||||||
void liveStepForward(); // step the clock forward one rate-unit (snapshots for undo)
|
void liveStepForward(); // step the clock forward one rate-unit (snapshots for undo)
|
||||||
void liveStepBack(); // step everything back one frame (restores weather/storms)
|
void liveStepBack(); // step everything back one frame (restores weather/storms)
|
||||||
|
void wxPushSnapshot(); // push the current weather state onto the step-back ring
|
||||||
Rectangle mapViewRect() const; // 2D map projection rect after zoom/pan (scissor stays mapRect)
|
Rectangle mapViewRect() const; // 2D map projection rect after zoom/pan (scissor stays mapRect)
|
||||||
|
|
||||||
// ---- Input (ViewerInput.cpp) --------------------------------------------
|
// ---- Input (ViewerInput.cpp) --------------------------------------------
|
||||||
|
|||||||
@ -211,7 +211,7 @@ void Viewer::handleInput() {
|
|||||||
// backward restores the snapshot from the last forward step -> everything (incl. weather +
|
// backward restores the snapshot from the last forward step -> everything (incl. weather +
|
||||||
// storms) steps back, within the current paused stepping session.
|
// storms) steps back, within the current paused stepping session.
|
||||||
if (IsKeyPressed(KEY_PERIOD) && liveWorld) { liveStepForward(); setStatus("Step forward"); }
|
if (IsKeyPressed(KEY_PERIOD) && liveWorld) { liveStepForward(); setStatus("Step forward"); }
|
||||||
if (IsKeyPressed(KEY_COMMA) && liveWorld) { liveStepBack(); setStatus(wxUndo.empty() ? "Step back (sky only)" : "Step back"); }
|
if (IsKeyPressed(KEY_COMMA) && liveWorld) { liveStepBack(); } // liveStepBack sets its own status
|
||||||
if (IsKeyPressed(KEY_F)) { // fast-forward to settled
|
if (IsKeyPressed(KEY_F)) { // fast-forward to settled
|
||||||
if (!settled) {
|
if (!settled) {
|
||||||
while (!settled) stepOnce();
|
while (!settled) stepOnce();
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user