Per-category GAME SPEED: overworld/battle/menu + core.logic_speed hook (RFC 0007)

GameSpeed is a single fast-forward multiplier applied uniformly to the
whole logic clock -- overworld walking, menu navigation and battle turns
all scale together. A player who wants 4X battles but 1X overworld (so a
cutscene or NPC dialogue doesn't blur past) has no way to get both.

Splits save.options.speed into speedOverworld/speedBattle/speedMenu, each
cycling independently, with an automatic migration so an existing save's
speed choice carries over. Game.speedCategoryInStack resolves which
category is active by walking the state stack (the same idiom
wideBattleInStack/fillScaleInStack already use), so a menu opened mid-
battle inherits battle speed rather than resetting to whatever "menu"
defaults to. Adds a new core.logic_speed hook so a mod can read or
override the resolved multiplier for the current frame regardless of
which category produced it, sitting after the link-play and run-argument
overrides so neither is a seam a mod can defeat.

RFC 0007 status: Proposed.
This commit is contained in:
david
2026-08-09 20:13:21 -07:00
parent 943ba5dcbf
commit 3c71afb9fa
14 changed files with 602 additions and 34 deletions
+25 -4
View File
@@ -392,14 +392,35 @@ local function buildRows(game)
return true
end },
-- fast-forward the logic clock only; music and sfx keep their tempo
-- (src/core/GameSpeed.lua), so this is safe to leave on
{ id = "speed", label = Strings("GAME SPEED"),
-- (src/core/GameSpeed.lua), so this is safe to leave on. Per-category
-- (RFC 0007): overworld walking, battle turns and menu navigation each
-- cycle their own multiplier -- GameSpeed.CATEGORIES is the single
-- source of truth for which three rows exist.
{ id = "speedOverworld", label = Strings("OVERWORLD SPEED"),
value = function(g)
return GameSpeed.levelLabel(g.save.options.speed)
return GameSpeed.levelLabel(g.save.options.speedOverworld)
end,
step = function(g, dir)
local o = g.save.options
o.speed = GameSpeed.cycle(o.speed, dir)
o.speedOverworld = GameSpeed.cycle(o.speedOverworld, dir)
return true
end },
{ id = "speedBattle", label = Strings("BATTLE SPEED"),
value = function(g)
return GameSpeed.levelLabel(g.save.options.speedBattle)
end,
step = function(g, dir)
local o = g.save.options
o.speedBattle = GameSpeed.cycle(o.speedBattle, dir)
return true
end },
{ id = "speedMenu", label = Strings("MENU SPEED"),
value = function(g)
return GameSpeed.levelLabel(g.save.options.speedMenu)
end,
step = function(g, dir)
local o = g.save.options
o.speedMenu = GameSpeed.cycle(o.speedMenu, dir)
return true
end },
-- the manager's discoverable home (18-mod-manager-ux); inert until