From 8f80dbab7753ea54ff5013f35ed861dd8fc97969 Mon Sep 17 00:00:00 2001 From: Nolan Nicholson Date: Mon, 9 May 2022 00:00:28 -0700 Subject: [PATCH] CRT lightguns: 50 ms offscreen grace period (#599) When a CRT lightgun starts reporting out-of-screen coordinates, the last good on-screen coordinates are retained and passed through for 50 ms. This improves GunCon 2 performance on NES, and generally makes CRT-based guns a bit more reliable in games with a lot of dark areas. --- input.cpp | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/input.cpp b/input.cpp index ba8feda..6a6d096 100644 --- a/input.cpp +++ b/input.cpp @@ -1586,6 +1586,8 @@ static uint32_t autofire[NUMPLAYERS] = {}; static uint32_t autofirecodes[NUMPLAYERS][BTN_NUM] = {}; static int af_delay[NUMPLAYERS] = {}; +static uint32_t crtgun_timeout[NUMDEV] = {}; + static unsigned char mouse_btn = 0; //emulated mouse static unsigned char mice_btn = 0; static int mouse_req = 0; @@ -4865,7 +4867,7 @@ int input_test(int getchar) } } - if (ev.type == EV_ABS && (input[i].quirk == QUIRK_LIGHTGUN_CRT || input[i].quirk == QUIRK_LIGHTGUN)) + if (ev.type == EV_ABS && input[i].quirk == QUIRK_LIGHTGUN) { menu_lightgun_cb(i, ev.type, ev.code, ev.value); @@ -4881,6 +4883,60 @@ int input_test(int getchar) } } + if (ev.type == EV_ABS && input[i].quirk == QUIRK_LIGHTGUN_CRT) + { + menu_lightgun_cb(i, ev.type, ev.code, ev.value); + + if (ev.code == ABS_X) + { + absinfo.minimum = input[i].guncal[2]; + absinfo.maximum = input[i].guncal[3]; + + // When the gun loses tracking, give it a short grace period + // before passing through the off-screen coordinates. + // The GunCon 1 and 2 both report out-of-screen x values + // more reliably than Y values, so X is used here. + if (ev.value < absinfo.minimum || ev.value > absinfo.maximum) + { + // Grace period of 50 ms. Longer times here make guns a bit + // more reliable on dark screens, but introduce lag to any mechanics + // where you want to shoot offscreen (e.g., to reload.) + if (!crtgun_timeout[i]) crtgun_timeout[i] = GetTimer(50); + } + else + { + crtgun_timeout[i] = 0; + input[i].lastx = ev.value; + } + // For the window between losing the gun signal and the timer + // running out, report the last on-screen coordinate + if (crtgun_timeout[i] && !CheckTimer(crtgun_timeout[i])) + { + ev.value = input[i].lastx; + } + } + else if (ev.code == ABS_Y) + { + absinfo.minimum = input[i].guncal[0]; + absinfo.maximum = input[i].guncal[1]; + + // Handle gun going off-screen + if (crtgun_timeout[i]) + { + // For the window between losing the gun signal and the timer + // running out, report the last on-screen coordinate + if (!CheckTimer(crtgun_timeout[i])) + { + ev.value = input[i].lasty; + } + } + else + { + input[i].lasty = ev.value; + } + } + } + if (ev.type == EV_KEY && user_io_osd_is_visible()) { if (input[i].quirk == QUIRK_WIIMOTE || input[i].quirk == QUIRK_LIGHTGUN_CRT || input[i].quirk == QUIRK_LIGHTGUN)