From 3f88d1c49aaf50886f80e58c07bfcfe081f0c370 Mon Sep 17 00:00:00 2001 From: Shane McGovern Date: Sun, 2 Aug 2026 23:10:06 +0100 Subject: [PATCH] Add 3X game speed and R2/L2 shoulder button speed hotkeys Add 3X as a speed option between 2X and 4X in GameSpeed.LEVELS (#677). Add controller hotkeys: rightshoulder (R2) cycles speed up through the level list, leftshoulder (L2) cycles speed down. Keyboard equivalent is hotkey 1 (cycles up). All hotkeys are gated during transitions, scripted cutscenes, and link play (same guards as the color hotkey at key 2). The _cycleSpeed helper wraps the save-options update with the same busy/overworld guard used by the existing color-cycle hotkey. Fixes #677 --- src/core/Game.lua | 32 ++++++++++++++++++++++++++++++++ src/core/GameSpeed.lua | 2 +- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/src/core/Game.lua b/src/core/Game.lua index dcc87fba..d89e5dd4 100644 --- a/src/core/Game.lua +++ b/src/core/Game.lua @@ -509,6 +509,24 @@ function Game:wheelmoved(_, dy) end end +function Game:_cycleSpeed(dir) + if not (self.save and self.save.options) then return end + local busy + local ow = self.overworld + if ow then + local top = self.stack:top() + busy = ow.transitioning + or (top == ow and ( + (ow.runner and ow.runner.isRunning and ow.runner:isRunning()) + or (ow.scriptMoves and #ow.scriptMoves > 0) + or ow.engaging or ow.emote)) + end + if busy then return end + local GameSpeed = require("src.core.GameSpeed") + self.save.options.speed = GameSpeed.cycle(self.save.options.speed, dir) + self:writeOptions() +end + function Game:keypressed(key) if self.stack and self.stack:top() and self.stack:top().onKeyPressed then self.stack:top():onKeyPressed(key) @@ -546,6 +564,11 @@ function Game:keypressed(key) elseif key == "=" then self:zoomStep(1) return + elseif key == "1" then + -- cycle GAME SPEED (0.25X → 200X, logic only; audio unaffected); + -- R2/L2 on gamepad do the same (see gamepadpressed) + self:_cycleSpeed(1) + return elseif key == "2" then -- cycle COLORS (GBC / OG / OG INV / GBC INV / CLASSIC); the pack change -- forces Game.overworld:reloadMap, which rebuilds the live NPC array, so @@ -626,6 +649,15 @@ function Game:gamepadpressed(joystick, button) -- a controller is being used: the touch overlay steps aside until the -- next screen touch (mobile only; a no-op elsewhere) TouchControls:noteGamepad() + -- shoulder buttons cycle GAME SPEED (R2/rightshoulder = faster, + -- L2/leftshoulder = slower; same as keyboard hotkey 1) + if button == "rightshoulder" then + self:_cycleSpeed(1) + return + elseif button == "leftshoulder" then + self:_cycleSpeed(-1) + return + end -- BindingsMenu's pad capture rides the same top-state routing as keys local top = self.stack and self.stack:top() if top and top.onGamepadPressed then diff --git a/src/core/GameSpeed.lua b/src/core/GameSpeed.lua index 85b81b3c..0a5ddde3 100644 --- a/src/core/GameSpeed.lua +++ b/src/core/GameSpeed.lua @@ -18,7 +18,7 @@ local GameSpeed = {} -- attempt is long enough that the iteration loop, not the engine, is the -- bottleneck. Vsync caps how much a real frame can do, so past 10X the -- multiplier is increasingly a ceiling rather than a rate. -GameSpeed.LEVELS = { 1, 2, 4, 10, 20, 30, 50, 75, 100,200 } +GameSpeed.LEVELS = { 1, 2, 3, 4, 10, 20, 30, 50, 75, 100, 200 } GameSpeed.DEFAULT = 1 function GameSpeed.levelLabel(v)