CLOSES #1090, CLOSES #1093, CLOSES #1094, CLOSES #1095, CLOSES #1098, CLOSES #1102, CLOSES #1105, CLOSES #1106, CLOSES #1108, CLOSES #1109, CLOSES #1110, CLOSES #1111, CLOSES #1113, CLOSES #1114, CLOSES #1117, CLOSES #1118, CLOSES #1121, CLOSES #1122, CLOSES #1123, CLOSES #1124, CLOSES #1126, CLOSES #1127, CLOSES #1128, CLOSES #1131, CLOSES #1132, CLOSES #1134, CLOSES #1137, CLOSES #1141

This commit is contained in:
bryanthaboi
2026-08-11 21:19:36 -04:00
parent 01aab1d763
commit 0136429d3e
24 changed files with 694 additions and 167 deletions
+13 -2
View File
@@ -203,7 +203,8 @@ function ChipAudio.playMusic(data, header, allowLoops)
cmdCh:push({ cmd = "play", gen = gen, header = header,
allowLoops = allowLoops, audio = slimAudio(data),
channelVolumes = ChipSynth.getChannelVolumes(),
channelPitches = ChipSynth.getChannelPitches() })
channelPitches = ChipSynth.getChannelPitches(),
stereo = ChipSynth.getStereo() })
currentMusic = { source = source, gen = gen, threaded = true,
started = false, finished = false }
-- playback starts in update() once the first buffer arrives (~1 frame)
@@ -214,7 +215,8 @@ local function pushChannelMix()
if workerReady and cmdCh then
cmdCh:push({ cmd = "channelMix",
volumes = ChipSynth.getChannelVolumes(),
pitches = ChipSynth.getChannelPitches() })
pitches = ChipSynth.getChannelPitches(),
stereo = ChipSynth.getStereo() })
end
end
@@ -352,6 +354,15 @@ function ChipAudio.shutdown()
workerReady = false
end
function ChipAudio.setStereo(enabled)
ChipSynth.setStereo(enabled)
pushChannelMix()
end
function ChipAudio.getStereo()
return ChipSynth.getStereo()
end
-- Runtime mix for one hardware channel (1..4). Takes effect on the next
-- synthesized buffer (live music) and on any SFX/cry rendered after the call.
function ChipAudio.setChannelVolume(hw, scale)
+19 -4
View File
@@ -29,6 +29,18 @@ ChipSynth.SAMPLE_RATE = SAMPLE_RATE
ChipSynth.MUSIC_BUFFER_SAMPLES = MUSIC_BUFFER_SAMPLES
ChipSynth.MUSIC_BUFFER_COUNT = MUSIC_BUFFER_COUNT
-- Gen 2 SOUND option (MONO/STEREO): gates Music_StereoPanning's per-song
-- panning byte (audio/engine.asm:1987 wOptions STEREO bit).
local stereoEnabled = false
function ChipSynth.setStereo(enabled)
stereoEnabled = not not enabled
end
function ChipSynth.getStereo()
return stereoEnabled
end
-- Runtime mix per hardware channel (1 pulse, 2 pulse, 3 wave, 4 noise).
-- Volume: 1 = authentic GB, 0 = mute. Pitch: 1 = authentic, 2 = +1 octave,
-- 0.5 = -1 octave. Applied at sample time so a live change reaches the next
@@ -760,11 +772,14 @@ function Channel:nextEventGen2()
-- no-op for the PCM renderer
elseif command == 0xEE then -- unknownmusic0xee
self:word()
elseif command == 0xEF then -- stereo_panning (honor always; options.stereo)
elseif command == 0xEF then
-- audio/engine.asm:1987 Music_StereoPanning: apply only when STEREO is on
local packed = self:byte()
local mask = bit.lshift(1, self.hardware - 1)
local default = bit.bor(bit.lshift(mask, 4), mask)
self.tracks = bit.band(packed, default)
if stereoEnabled then
local mask = bit.lshift(1, self.hardware - 1)
local default = bit.bor(bit.lshift(mask, 4), mask)
self.tracks = bit.band(packed, default)
end
elseif command == 0xF0 then -- sfx_toggle_noise
if self.noiseSampling then
self.noiseSampling = false
+13 -28
View File
@@ -140,7 +140,7 @@ Game2.anchorNewGameClock = anchorNewGameClock
function Game2.new()
local self = setmetatable({
speedOverride = 1,
speedOverride = nil,
capturePath = nil,
world = nil,
status = nil,
@@ -975,27 +975,9 @@ function Game2:load()
local Pipelines = require("src.render.Pipelines")
Pipelines.install(self.data)
Pipelines.applyOptions(self.options)
-- Gold composites the WHOLE-FRAME half of a pipeline (`present`) and not the
-- world half: its overworld draws straight to the window rather than into a
-- canvas the way src/world/OverworldController.lua:4827 hands one to
-- Pipelines.drawWorld, so there is nothing here for drawWorld to replace yet.
-- A restored level for a world-only pipeline is retired rather than left
-- switched on, because on Gold it would render nothing AND hold TILT off
-- (Pipelines.setLevel's tilt exclusion). The stored level in
-- options.pipelines is left untouched, so the mode comes back the day Gold
-- grows a world canvas; Tilt is re-applied from the option the exclusion just
-- cleared.
local retired = false
for _, entry in ipairs(Pipelines.list()) do
if entry.def.drawWorld and not entry.def.present
and Pipelines.level(entry.id) > 0 then
Pipelines.setLevel(entry.id, 0)
retired = true
end
end
if retired then
require("src.render.Tilt").applyOptions(self.options)
end
-- Both halves run on Gold now: `present` folds over the composite in
-- Game2:draw, `drawWorld` owns the world pass in World:drawPipeline. So a
-- restored world level stays switched on, as it does for Gen 1.
-- After the merge, so a font override and a translation mod's catalog
-- (#501) are both in Data before the first screen draws a glyph. Gen 1
@@ -1112,19 +1094,20 @@ function Game2:update(dt)
-- reason and at the same place Gen 1 ticks them (src/core/Game.lua:265):
-- they are presentational, so fast-forward must not speed them up.
require("src.render.Pipelines").update(dt)
if self.phase == "boot" then
FixedStep.maxAccum = 0.25
FixedStep:update(dt)
return
end
if not self.world or not self.world.map then return end
-- GAME SPEED scales the logic clock only, exactly as the Gen 1 path does:
-- audio runs off its own real-time accumulator, so music and sfx keep their
-- tempo at every multiplier. speedOverride is the driver/CLI hook and wins
-- over the saved option.
-- pokegold engine/menus/intro_menu.asm:848 IntroSequence: boot cinema runs on the same clock as the overworld
local speed = math.max(1,
tonumber(self.speedOverride) or tonumber(self.options and self.options.speed)
or 1)
if self.phase == "boot" then
FixedStep.maxAccum = math.max(0.25, speed / 60 + 0.05)
FixedStep:update(dt * speed)
return
end
if not self.world or not self.world.map then return end
FixedStep.maxAccum = math.max(0.25, speed / 60 + 0.05)
FixedStep:update(dt * speed)
end
@@ -1921,6 +1904,8 @@ function Game2:applyOptions()
-- the mod pipeline ladder rides options.pipelines and restores with the rest
-- of the display block, as it does in src/core/Game.lua:1041
require("src.render.Pipelines").applyOptions(options)
-- src/core/Game.lua:1121 mirrors this call for Gen 1
Input:applyBindings(options.bindings)
-- options.touchControls (the launcher editor's per-orientation layouts) and
-- options.haptics, the same two keys Gen 1 hands over here
-- (src/core/Game.lua:1073). One options.lua serves both games, so the pad a
+2
View File
@@ -460,6 +460,8 @@ end
function Music.applyOptions(opts)
Music.setVolumeLevel(opts and opts.musicVol or 7)
Music.setFilterLevel(opts and opts.musicFilter or 0)
-- engine/menus/options_menu.asm SOUND row (wOptions STEREO bit)
require("src.core.ChipAudio").setStereo(opts and opts.sound == "STEREO")
end
local function sourceStopped(src)
+4
View File
@@ -53,6 +53,9 @@ local function handle(cmd)
if cmd.channelPitches ~= nil then
ChipSynth.setChannelPitches(cmd.channelPitches)
end
if cmd.stereo ~= nil then
ChipSynth.setStereo(cmd.stereo)
end
local ok, eng = pcall(ChipSynth.newEngine, data, cmd.header,
{ allowLoops = cmd.allowLoops })
if ok then
@@ -69,6 +72,7 @@ local function handle(cmd)
elseif cmd.cmd == "channelMix" then
if cmd.volumes ~= nil then ChipSynth.setChannelVolumes(cmd.volumes) end
if cmd.pitches ~= nil then ChipSynth.setChannelPitches(cmd.pitches) end
if cmd.stereo ~= nil then ChipSynth.setStereo(cmd.stereo) end
elseif cmd.cmd == "invalidate" then
ChipSynth.invalidateBanks()
elseif cmd.cmd == "quit" then
+2 -2
View File
@@ -96,7 +96,7 @@ function Nests.landmark(data, index)
end
local function landmarkOfMap(data, mapId)
local def = data and data.maps and data.maps[mapId]
local def = data and data.gen2Maps and data.gen2Maps[mapId]
return def and def.landmark
end
@@ -131,7 +131,7 @@ function Nests.find(data, species, region, save)
out[#out + 1] = landmark
end
local enc = data and data.encounters
local enc = data and data.gen2Encounters
for _, key in ipairs({ "grass", "water" }) do
for mapId, entry in pairs((enc and enc[key]) or {}) do
if tableHasSpecies(entry, species) then
+3
View File
@@ -127,6 +127,9 @@ end
Save.filenames = saveNames
local function fs()
-- portable.txt: same standard/portable root as Gen 1 (SaveData.persistenceFs).
local ok, SaveData = pcall(require, "src.core.SaveData")
if ok and SaveData.persistenceFs then return SaveData.persistenceFs() end
return love.filesystem
end