Fix bug where restart fast-forwarded the new run by the time spent in a modal dialog
This commit is contained in:
@@ -131,3 +131,26 @@ TEST_CASE("TickDriver::reset clears the accumulator", "[simulation]")
|
|||||||
// Nothing in the accumulator: zero elapsed time should not fire.
|
// Nothing in the accumulator: zero elapsed time should not fire.
|
||||||
REQUIRE(driver.advance(0.0, 1.0) == 0);
|
REQUIRE(driver.advance(0.0, 1.0) == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("TickDriver::reset discards a large pending delta so a restart does not "
|
||||||
|
"fast-forward", "[simulation]")
|
||||||
|
{
|
||||||
|
// Regression guard for the "restart fast-forwards by the time spent in the
|
||||||
|
// Game Over / Win / escape dialog" bug: on restart the frame timer holds the
|
||||||
|
// whole wall-clock duration the modal was open. GameWorldView::resetForNewGame()
|
||||||
|
// now calls TickDriver::reset() to discard that pending delta; without it the
|
||||||
|
// fresh run would burst forward by the dialog duration on its first frame.
|
||||||
|
TickDriver driver;
|
||||||
|
|
||||||
|
// 30 seconds spent in the dialog would otherwise be ~900 ticks at 30 Hz.
|
||||||
|
const double dialogOpenMs = 30000.0;
|
||||||
|
driver.reset();
|
||||||
|
|
||||||
|
// A brand-new run's first normal frame (~16 ms) advances only its own ticks;
|
||||||
|
// the discarded dialog time contributes nothing.
|
||||||
|
REQUIRE(driver.advance(16.0, 1.0) == 0);
|
||||||
|
|
||||||
|
// Sanity: had the dialog delta not been discarded, it would have fired ~900 ticks.
|
||||||
|
TickDriver leaked;
|
||||||
|
REQUIRE(leaked.advance(dialogOpenMs, 1.0) > 800);
|
||||||
|
}
|
||||||
|
|||||||
@@ -227,6 +227,12 @@ void GameWorldView::onFrame()
|
|||||||
{
|
{
|
||||||
m_viewResetPending = false;
|
m_viewResetPending = false;
|
||||||
resetForNewGame();
|
resetForNewGame();
|
||||||
|
// The reset command may have drained after a long-open modal (e.g. the
|
||||||
|
// Game Over dialog), so `elapsed` above still holds the whole time that
|
||||||
|
// dialog was open. Feeding it into the tick driver would fast-forward the
|
||||||
|
// brand-new run by that duration. resetForNewGame() has rebased the time
|
||||||
|
// source; skip this frame's tick advance so the stale delta is discarded.
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notify presentation widgets that queued commands were applied, so a
|
// Notify presentation widgets that queued commands were applied, so a
|
||||||
@@ -2391,6 +2397,12 @@ void GameWorldView::resetForNewGame()
|
|||||||
EventManager::getInstance()->sendEventImmediately(
|
EventManager::getInstance()->sendEventImmediately(
|
||||||
std::make_shared<SelectionChangedEvent>(std::vector<BuildingId>{}));
|
std::make_shared<SelectionChangedEvent>(std::vector<BuildingId>{}));
|
||||||
setGameSpeed(1.0);
|
setGameSpeed(1.0);
|
||||||
|
// Rebase the wall-clock time source so a fresh run starts from a clean time
|
||||||
|
// base. Without this, wall time accumulated while a modal (Game Over, Win, or
|
||||||
|
// the escape menu) was open would be converted into ticks on the new run,
|
||||||
|
// fast-forwarding it by the time the player spent in the dialog.
|
||||||
|
m_frameTimer.restart();
|
||||||
|
m_tickDriver.reset();
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user