mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-16 08:11:35 +02:00
+114
-4
@@ -799,6 +799,7 @@ end
|
||||
function Game:focus(f)
|
||||
Input:reset()
|
||||
TouchControls:reset()
|
||||
self:cancelPointers()
|
||||
end
|
||||
|
||||
function Game:visible(v)
|
||||
@@ -807,12 +808,14 @@ function Game:visible(v)
|
||||
else
|
||||
Input:reset()
|
||||
TouchControls:reset()
|
||||
self:cancelPointers()
|
||||
end
|
||||
end
|
||||
|
||||
function Game:onResume()
|
||||
Input:reset()
|
||||
TouchControls:reset()
|
||||
self:cancelPointers()
|
||||
-- Chip music may survive NX suspend as a duplicate stream; stop it and let
|
||||
-- the active screen re-cue on the next frame (hardware audio check: T19).
|
||||
-- Desktop/mobile window-visible flips must not kill overworld music.
|
||||
@@ -828,6 +831,11 @@ end
|
||||
function Game:recoverInput(event, joystick)
|
||||
Input:reset()
|
||||
TouchControls:reset()
|
||||
-- reset just dropped every source, mod holds included: retire the mods'
|
||||
-- outstanding press tokens so nothing stale can be released later, and
|
||||
-- tell subscribers their live pointers died (#807)
|
||||
if self.mods and self.mods.releaseModInput then self.mods:releaseModInput() end
|
||||
self:cancelPointers()
|
||||
local SwitchDiagnostics = require("src.debug.SwitchDiagnostics")
|
||||
if SwitchDiagnostics.isEnabled() then
|
||||
if joystick then
|
||||
@@ -850,16 +858,118 @@ function Game:joystickremoved(joystick)
|
||||
TouchControls:joystickremoved()
|
||||
end
|
||||
|
||||
function Game:touchpressed(id, x, y)
|
||||
TouchControls:touchpressed(id, x, y)
|
||||
-- Gameplay pointer seam (#807). TouchControls keeps first refusal: a
|
||||
-- pointer that begins on a virtual control belongs to the pad for its
|
||||
-- whole lifecycle and never reaches mods, while one that begins outside
|
||||
-- stays mod-visible even if it later wanders across a control
|
||||
-- (TouchControls only tracks ids it captured at press). Everything a
|
||||
-- subscriber costs -- the per-pointer records in self.modPointers, the
|
||||
-- payload tables -- sits behind wantsHook, so a mod-free boot allocates
|
||||
-- nothing here.
|
||||
|
||||
-- vanilla for input.pointer: nobody consumed the event
|
||||
local function pointerUnclaimed() return false end
|
||||
|
||||
-- coordinates are LOVE window units, the same space render.hud's viewport
|
||||
-- and the touch overlay lay out in
|
||||
function Game:pointerEvent(phase, source, id, x, y, dx, dy, pressure, button)
|
||||
return ModRuntime.call("input.pointer", pointerUnclaimed, self, {
|
||||
phase = phase, source = source, id = id, x = x, y = y,
|
||||
dx = dx or 0, dy = dy or 0, pressure = pressure, button = button,
|
||||
})
|
||||
end
|
||||
|
||||
function Game:touchmoved(id, x, y)
|
||||
function Game:touchpressed(id, x, y, dx, dy, pressure)
|
||||
if TouchControls:touchpressed(id, x, y) then return end
|
||||
if not ModRuntime.wantsHook("input.pointer") then return end
|
||||
-- POKEPORT_TOUCH routes the mouse through here as a stand-in finger
|
||||
-- under the id "mouse" (see main.lua); mods still see its true source
|
||||
local source = id == "mouse" and "mouse" or "touch"
|
||||
self.modPointers = self.modPointers or {}
|
||||
self.modPointers[id] = { source = source, x = x, y = y,
|
||||
pressure = pressure }
|
||||
self:pointerEvent("pressed", source, id, x, y, dx, dy, pressure)
|
||||
end
|
||||
|
||||
function Game:touchmoved(id, x, y, dx, dy, pressure)
|
||||
TouchControls:touchmoved(id, x, y)
|
||||
local p = self.modPointers and self.modPointers[id]
|
||||
if not p then return end
|
||||
-- the POKEPORT_TOUCH mouse path carries no deltas; derive them from the
|
||||
-- pointer's last seen position so drags read the same either way
|
||||
if dx == nil then dx, dy = x - p.x, y - p.y end
|
||||
p.x, p.y = x, y
|
||||
if pressure ~= nil then p.pressure = pressure end
|
||||
if ModRuntime.wantsHook("input.pointer") then
|
||||
self:pointerEvent("moved", p.source, id, x, y, dx, dy, pressure)
|
||||
end
|
||||
end
|
||||
|
||||
function Game:touchreleased(id, x, y)
|
||||
function Game:touchreleased(id, x, y, dx, dy, pressure)
|
||||
TouchControls:touchreleased(id, x, y)
|
||||
local p = self.modPointers and self.modPointers[id]
|
||||
if not p then return end
|
||||
self.modPointers[id] = nil
|
||||
if ModRuntime.wantsHook("input.pointer") then
|
||||
self:pointerEvent("released", p.source, id, x, y, dx, dy, pressure)
|
||||
end
|
||||
end
|
||||
|
||||
-- A real mouse without POKEPORT_TOUCH (#807). Gameplay itself has no
|
||||
-- mouse verbs, so the pointer hook is the only consumer and everything is
|
||||
-- behind the wantsHook gate. A synthesized istouch twin is dropped
|
||||
-- unconditionally: the same contact already arrived through
|
||||
-- Game:touchpressed, and forwarding both would fire a mobile touch twice.
|
||||
function Game:mousepressed(x, y, button, istouch)
|
||||
if istouch then return end
|
||||
if not ModRuntime.wantsHook("input.pointer") then return end
|
||||
self.modPointers = self.modPointers or {}
|
||||
local p = self.modPointers.mouse
|
||||
if p then
|
||||
p.held, p.x, p.y = (p.held or 1) + 1, x, y
|
||||
else
|
||||
self.modPointers.mouse = { source = "mouse", x = x, y = y,
|
||||
held = 1, button = button }
|
||||
end
|
||||
self:pointerEvent("pressed", "mouse", "mouse", x, y, 0, 0, nil, button)
|
||||
end
|
||||
|
||||
-- hover moves are delivered too (button = nil); only pressed pointers are
|
||||
-- tracked, because only they owe a released/cancelled later
|
||||
function Game:mousemoved(x, y, dx, dy, istouch)
|
||||
if istouch then return end
|
||||
local p = self.modPointers and self.modPointers.mouse
|
||||
if p then p.x, p.y = x, y end
|
||||
if not ModRuntime.wantsHook("input.pointer") then return end
|
||||
self:pointerEvent("moved", "mouse", "mouse", x, y, dx, dy, nil, nil)
|
||||
end
|
||||
|
||||
function Game:mousereleased(x, y, button, istouch)
|
||||
if istouch then return end
|
||||
local p = self.modPointers and self.modPointers.mouse
|
||||
if not p then return end
|
||||
p.held = (p.held or 1) - 1
|
||||
if p.held <= 0 then self.modPointers.mouse = nil end
|
||||
if ModRuntime.wantsHook("input.pointer") then
|
||||
self:pointerEvent("released", "mouse", "mouse", x, y, 0, 0, nil, button)
|
||||
end
|
||||
end
|
||||
|
||||
-- Focus/visibility loss and input recovery swallow pointer releases the
|
||||
-- same way they swallow key-ups (the hazard Input:reset exists for):
|
||||
-- every mod-visible pointer gets a "cancelled" instead of leaving
|
||||
-- subscribers waiting on a "released" that can never arrive (#807).
|
||||
-- Cleared even when the subscriber is already gone, so no stale record
|
||||
-- outlives its mod.
|
||||
function Game:cancelPointers()
|
||||
local pointers = self.modPointers
|
||||
if not pointers then return end
|
||||
self.modPointers = nil
|
||||
if not ModRuntime.wantsHook("input.pointer") then return end
|
||||
for id, p in pairs(pointers) do
|
||||
self:pointerEvent("cancelled", p.source, id, p.x, p.y, 0, 0,
|
||||
p.pressure, p.button)
|
||||
end
|
||||
end
|
||||
|
||||
-- Point the loader's mod.save backing at this save's modData so per-mod
|
||||
|
||||
@@ -186,6 +186,21 @@ function Input:overlayReleased(btn)
|
||||
release(self, btn, "touch:" .. btn)
|
||||
end
|
||||
|
||||
-- Programmatic mod input (#807). mod.input taps and holds land here under
|
||||
-- loader-issued "mod:<id>:<n>" source names, riding the same per-source
|
||||
-- bookkeeping as every physical path above, so releasing one can never
|
||||
-- clear a hold a key, stick, hat, the overlay, or another mod still owns.
|
||||
-- A tap is a sourcePress immediately followed by its sourceRelease: the
|
||||
-- queued edge survives into the next step, and the emptied source map
|
||||
-- keeps the hold from being revived (see Input:step's sources == {} rule).
|
||||
function Input:sourcePress(btn, source)
|
||||
press(self, btn, source)
|
||||
end
|
||||
|
||||
function Input:sourceRelease(btn, source)
|
||||
release(self, btn, source)
|
||||
end
|
||||
|
||||
function Input:gamepadpressed(joystick, button)
|
||||
local btn = self.padBindings[button]
|
||||
if btn then
|
||||
|
||||
@@ -423,11 +423,17 @@ local function setDpad(self, touch, dir)
|
||||
if dir then pressBtn(self, dir) end
|
||||
end
|
||||
|
||||
-- Returns true when this touch was captured by a virtual control -- the
|
||||
-- pad's first refusal on the gameplay pointer seam (#807). Capture is
|
||||
-- decided here, at press, and rides self.touches[id] for the touch's
|
||||
-- whole lifecycle; an uncaptured touch is never tracked, so wandering
|
||||
-- across a control later neither presses it nor hides the touch from mods.
|
||||
function TouchControls:touchpressed(id, x, y)
|
||||
-- preview mode is layout-edit only: never press GB buttons
|
||||
if self.preview then return end
|
||||
if not (self.active and self.enabled ~= false and self.img) then return end
|
||||
-- a controller hid the overlay; the first touch only brings it back
|
||||
-- (uncaptured: it began on no control, so mods may still see it)
|
||||
if self.controllerHidden then
|
||||
self.controllerHidden = false
|
||||
return
|
||||
@@ -437,7 +443,7 @@ function TouchControls:touchpressed(id, x, y)
|
||||
if inCircle(L[btn], x, y, SLOP[btn]) then
|
||||
self.touches[id] = { control = btn }
|
||||
pressBtn(self, btn)
|
||||
return
|
||||
return true
|
||||
end
|
||||
end
|
||||
-- square hit zone a bit past the cross art; one owning finger at a time
|
||||
@@ -449,6 +455,7 @@ function TouchControls:touchpressed(id, x, y)
|
||||
local touch = { control = "dpad", dir = nil }
|
||||
self.touches[id] = touch
|
||||
setDpad(self, touch, dpadDir(dz, x, y))
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
Reference in New Issue
Block a user