mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-12 08:21:02 +02:00
151 lines
5.8 KiB
Lua
151 lines
5.8 KiB
Lua
-- The RING itself, in the running game: does the player actually HEAR the
|
|
-- phone before the hang-up beep?
|
|
--
|
|
-- Phone_StartRinging is `call WaitSFX` and only THEN `ld de, SFX_CALL /
|
|
-- call PlaySFX` (engine/phone/phone.asm:564-567), and RingTwice_StartCall
|
|
-- runs that whole pass twice (:458-469). Both halves matter in this port:
|
|
-- SFX_CALL is $6a (constants/sfx_constants.asm:109), low enough that the
|
|
-- PlaySFX priority gate DROPS it outright while a louder sound is still on
|
|
-- ch5-ch8, so a ring with no wait in front of it can be silent and leave
|
|
-- SFX_HANG_UP as the first phone sound the player ever hears.
|
|
--
|
|
-- POKEPORT_IDENTITY=gold-dev POKEPORT_GAME=gold \
|
|
-- POKEPORT_DRIVER=tests/drivers/gold_phone_ring.lua love .
|
|
-- POKEPORT_SHOT_DIR=/tmp/gold-ring (default)
|
|
--
|
|
-- Listen for two rings about a second apart before the caller-ID page, then
|
|
-- the Click! at the end. The assertions cover the same ground for a run
|
|
-- nobody is listening to: the loud sound is started deliberately first, so a
|
|
-- ring that survives it proves the wait, not luck.
|
|
local U = require("tests.drivers.util")
|
|
|
|
local Phone = require("src.core.gen2.Phone")
|
|
local Sound = require("src.core.Sound")
|
|
|
|
return function(game)
|
|
local out = os.getenv("POKEPORT_SHOT_DIR") or "/tmp/gold-ring"
|
|
local failures = 0
|
|
|
|
local function ok(label, condition, detail)
|
|
if condition then
|
|
print("[ring] ok " .. label)
|
|
else
|
|
failures = failures + 1
|
|
print("[ring] FAIL " .. label .. " " .. tostring(detail))
|
|
end
|
|
end
|
|
|
|
-- Every gated sfx request, in order, with whether it actually started: a
|
|
-- dropped one returns no source (src/core/Sound.lua sfxPriorityGate).
|
|
local order, rings, sounded = {}, 0, 0
|
|
local realPlay = Sound.play
|
|
Sound.play = function(data, name)
|
|
local src = realPlay(data, name)
|
|
order[#order + 1] = tostring(name)
|
|
if tostring(name):find("Call") then
|
|
rings = rings + 1
|
|
if src then sounded = sounded + 1 end
|
|
end
|
|
return src
|
|
end
|
|
|
|
U.wait(45)
|
|
local world = game.world
|
|
assert(world and world.map, "gold world did not boot")
|
|
local save = game.save
|
|
|
|
game.clock = { day = 0, hour = 9, minute = 0 }
|
|
save.phone = nil
|
|
assert(world:setMap("ROUTE_31", 8, 6, "down"), "setMap failed for ROUTE_31")
|
|
U.wait(3)
|
|
-- CheckStandingOnEntrance refuses a call on a door tile, so stand on floor.
|
|
local cx, cy = world.player.cellX, world.player.cellY
|
|
for y = 2, 16 do
|
|
for x = 2, 16 do
|
|
if world.map:cellCollision(x, y) == 0x00 then cx, cy = x, y end
|
|
end
|
|
end
|
|
assert(world:setMap("ROUTE_31", cx, cy, "down"), "no floor cell on ROUTE_31")
|
|
U.wait(3)
|
|
|
|
-- Is there an audio device at all? Without one every source is nil and
|
|
-- "the ring sounded" would fail for a reason that has nothing to do with
|
|
-- the phone, so the control decides whether that half is checked.
|
|
world:playSfxNamed("Sfx_ReadText2")
|
|
local audio = world.lastSfx ~= nil
|
|
print("[ring] audio device: " .. tostring(audio))
|
|
|
|
-- Diagnostic, not an assertion. WaitSFX blocks on the whole sfx channel
|
|
-- set (CheckSFX, home/audio.asm), but the VM's waitsfx hook only polls
|
|
-- World.lastSfx, so a sound started straight through Sound.play -- the
|
|
-- A-press beep src/render/TextBox.lua plays through the Press_AB alias --
|
|
-- is invisible to it while Sound.sfxBusy() can still see it. While these
|
|
-- two disagree, a ring queued inside that sound's window can still be
|
|
-- dropped by the priority gate.
|
|
world.lastSfx = nil -- isolate: the hook must answer about THIS sound alone
|
|
Sound.play(game.data, "Sfx_ReadText2")
|
|
local hook = world.vm and world.vm.waitSfxFn
|
|
print(("[ring] WaitSFX seam: hook says busy=%s, Sound.sfxBusy=%s")
|
|
:format(tostring(hook and not hook()), tostring(Sound.sfxBusy())))
|
|
U.wait(30)
|
|
|
|
Phone.addContact(save, 15) -- Joey, ROUTE_30, reachable from ROUTE_31
|
|
order, rings, sounded = {}, 0, 0
|
|
|
|
-- SFX_READ_TEXT_2 is $08: it outranks SFX_CALL, so this is the sound that
|
|
-- eats an unwaited ring. Started one frame before the call lands, exactly
|
|
-- as the A press that closes a textbox does.
|
|
world:playSfxNamed("Sfx_ReadText2")
|
|
|
|
local calls, shot = 0, false
|
|
for _ = 1, 40 do
|
|
if calls > 0 and not world:busy() then break end
|
|
game.clock.minute = game.clock.minute + 20
|
|
for _ = 1, 20 do
|
|
U.wait(1)
|
|
if world:busy() then break end
|
|
end
|
|
if world:busy() then
|
|
calls = calls + 1
|
|
for _ = 1, 400 do
|
|
if not shot and rings >= 2 then
|
|
-- The caller-ID page, with both rings already behind it. The wait
|
|
-- is for the typewriter: the page is only worth looking at once
|
|
-- the whole RING!…RING! line has printed.
|
|
U.wait(30)
|
|
U.shot(game, out .. "/01-ring.png")
|
|
shot = true
|
|
end
|
|
if not world:busy() then break end
|
|
game.input.pressQueue[#game.input.pressQueue + 1] = "a"
|
|
game.input.state.a = true
|
|
U.wait(2)
|
|
game.input.state.a = false
|
|
U.wait(2)
|
|
end
|
|
end
|
|
end
|
|
|
|
ok("a call rang in the overworld", calls > 0, calls)
|
|
-- RingTwice_StartCall's `call .Ring` plus its fallthrough.
|
|
ok("the phone rang twice", rings == 2, rings)
|
|
if audio then
|
|
ok("and both rings actually sounded past the priority gate",
|
|
sounded == rings, ("%d of %d"):format(sounded, rings))
|
|
end
|
|
|
|
local firstCall, firstHang
|
|
for index, name in ipairs(order) do
|
|
if not firstCall and name:find("Call") then firstCall = index end
|
|
if not firstHang and name:find("Hang") then firstHang = index end
|
|
end
|
|
ok("the ring is the FIRST phone sound, not the hang-up beep",
|
|
firstCall ~= nil and (firstHang == nil or firstCall < firstHang),
|
|
table.concat(order, ","))
|
|
|
|
Sound.play = realPlay
|
|
print(failures == 0 and "PASS gold_phone_ring"
|
|
or ("FAIL gold_phone_ring (%d)"):format(failures))
|
|
love.event.quit(failures == 0 and 0 or 1)
|
|
end
|