nes, playch10.cpp, vsnes.cpp: Made zapper sensor fuzzier. (#9292)

- Removes the pixel-perfect sensor by averaging pixel brightness over circular area around gun position.
- Treat RGB values equally and don't use lib/util/palette.h's weighted brightness function.
This commit is contained in:
0kmg 2022-02-15 04:33:44 -09:00 committed by GitHub
parent 86b728dea6
commit 17da27f074
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 76 additions and 65 deletions

View File

@ -27705,7 +27705,6 @@ license:CC0
</part>
</software>
<!-- Playable with joypad but zapper does not work -->
<software name="opwolfj" cloneof="opwolf" supported="partial">
<description>Operation Wolf (Jpn)</description>
<year>1989</year>
@ -27727,7 +27726,6 @@ license:CC0
</part>
</software>
<!-- Playable with joypad but zapper does not work -->
<software name="opwolfu" cloneof="opwolf" supported="partial">
<description>Operation Wolf - Take no Prisoners (USA, Rev. 0A)</description>
<year>1989</year>
@ -27748,7 +27746,6 @@ license:CC0
</part>
</software>
<!-- Playable with joypad but zapper does not work -->
<software name="opwolf" supported="partial">
<description>Operation Wolf - Take no Prisoners (Euro)</description>
<year>1992</year>
@ -45595,6 +45592,7 @@ Also notice that VRAM, WRAM & mirror are probably incorrect for some of these se
<feature name="slot" value="discrete_74x377" />
<feature name="pcb" value="COLORDREAMS-74*377" />
<feature name="mirroring" value="vertical" />
<feature name="peripheral" value="zapper" />
<dataarea name="chr" size="32768">
<rom name="chiller (australia) (unl).chr" size="32768" crc="a3dd719f" sha1="3d077a2c78148709c91bb75c099cd3ea04df7f56" offset="00000" status="baddump" />
</dataarea>
@ -48167,6 +48165,7 @@ preliminary proto for the PAL version, still running on NTSC systems) or the gfx
<feature name="vrc2-pin26" value="CHR A12" />
<feature name="vrc2-pin27" value="CHR A15" />
<feature name="vrc2-pin28" value="NC" />
<feature name="peripheral" value="zapper" />
<dataarea name="chr" size="131072">
<rom name="mad city (japan) (beta).chr" size="131072" crc="028dc0ef" sha1="1212b249bfc2c13e76a337d4ceedc5f78d1d5be6" offset="00000" status="baddump" />
</dataarea>
@ -54690,6 +54689,7 @@ preliminary proto for the PAL version, still running on NTSC systems) or the gfx
<feature name="slot" value="uxrom" />
<feature name="pcb" value="NES-UNROM" />
<feature name="mirroring" value="vertical" />
<feature name="peripheral" value="zapper" />
<dataarea name="prg" size="131072">
<rom name="hit marmot (asia) (unl).prg" size="131072" crc="e85b4d3d" sha1="11876755b50d6c48adcbea2b44bd3c77979e5d56" offset="00000" status="baddump" />
</dataarea>
@ -55269,7 +55269,6 @@ preliminary proto for the PAL version, still running on NTSC systems) or the gfx
</part>
</software>
<!-- Operation Wolf currently only works with joypad but not zapper -->
<software name="2uzilght" supported="partial">
<description>2 in 1 Uzi Lightgun</description>
<year>199?</year>
@ -55452,7 +55451,7 @@ preliminary proto for the PAL version, still running on NTSC systems) or the gfx
</part>
</software>
<software name="3suprgun">
<software name="3suprgun" supported="partial">
<description>3 in 1 Supergun (Tw)</description>
<year>1993</year>
<publisher>Micro Genius</publisher>
@ -55460,6 +55459,7 @@ preliminary proto for the PAL version, still running on NTSC systems) or the gfx
<part name="cart" interface="nes_cart">
<feature name="slot" value="cnrom" />
<feature name="pcb" value="NES-CNROM" />
<feature name="peripheral" value="zapper" />
<dataarea name="chr" size="32768">
<rom name="3 in 1 supergun (asia) (unl).chr" size="32768" crc="cc4700b4" sha1="cca724a37896a84c35834f655582e3685c6270ec" offset="00000" status="baddump" />
</dataarea>
@ -70041,6 +70041,7 @@ Also notice that VRAM & WRAM are probably incorrect for some of these sets, at t
<feature name="slot" value="nrom" />
<feature name="pcb" value="NES-NROM-128" />
<feature name="mirroring" value="vertical" />
<feature name="peripheral" value="zapper" />
<dataarea name="prg" size="32768">
<rom name="duck hunt (1987)(fmg)(jp)[p].prg" size="16384" crc="9e44c002" sha1="10075f771add649ddc1df07bf481e30e0c2dd37a" offset="00000" status="baddump" />
<rom size="16384" offset="0x4000" loadflag="reload" />
@ -70407,6 +70408,7 @@ Also notice that VRAM & WRAM are probably incorrect for some of these sets, at t
<feature name="slot" value="nrom" />
<feature name="pcb" value="NES-NROM-128" />
<feature name="mirroring" value="vertical" />
<feature name="peripheral" value="zapper" />
<dataarea name="prg" size="32768">
<rom name="hogan's alley (1987)(fmg)(jp)[p].prg" size="16384" crc="27c51ce8" sha1="13743a35b1617c6a191fc3939e81be0388d36cdb" offset="00000" status="baddump" />
<rom size="16384" offset="0x4000" loadflag="reload" />

View File

@ -116,40 +116,42 @@ u8 nes_zapper_device::read_bit34()
int x = m_lightx->read();
int y = m_lighty->read();
// effective range picked up by photodiode, i.e. gun position +- radius
constexpr int xrad = 0;
constexpr int yrad = 0;
// radius of circle picked up by the gun's photodiode
constexpr int radius = 5;
// brightness threshold
constexpr int bright = 0x70;
constexpr int bright = 0xc0;
// # of CRT scanlines that sustain brightness
constexpr int sustain = 20;
constexpr int sustain = 22;
int vpos = m_port->m_screen->vpos();
int hpos = m_port->m_screen->hpos();
// update the screen if necessary
if (!m_port->m_screen->vblank())
if (vpos > y - yrad || (vpos == y - yrad && hpos >= x - xrad))
if (vpos > y - radius || (vpos == y - radius && hpos >= x - radius))
m_port->m_screen->update_now();
// default to no light detected
ret |= 0x08;
int sum = 0;
int scanned = 0;
// check brightness of pixels nearby the gun position
for (int i = x - xrad; i <= x + xrad; i++)
for (int j = y - yrad; j <= y + yrad; j++)
{
rgb_t pix = m_port->m_screen->pixel(i, j);
// only detect light if gun position is near, and behind, where the PPU is drawing on the CRT, from NesDev wiki:
// "Zap Ruder test ROM show that the photodiode stays on for about 26 scanlines with pure white, 24 scanlines with light gray, or 19 lines with dark gray."
if (j <= vpos && j > vpos - sustain && (j != vpos || i <= hpos) && pix.brightness() >= bright)
// sum brightness of pixels nearby the gun position
for (int i = x - radius; i <= x + radius; i++)
for (int j = y - radius; j <= y + radius; j++)
// look at pixels within circular sensor
if ((x - i) * (x - i) + (y - j) * (y - j) <= radius * radius)
{
ret &= ~0x08; // light detected
i = x + xrad;
break;
rgb_t pix = m_port->m_screen->pixel(i, j);
// only detect light if gun position is near, and behind, where the PPU is drawing on the CRT, from NesDev wiki:
// "Zap Ruder test ROM show that the photodiode stays on for about 26 scanlines with pure white, 24 scanlines with light gray, or 19 lines with dark gray."
if (j <= vpos && j > vpos - sustain && (j != vpos || i <= hpos))
sum += pix.r() + pix.g() + pix.b();
scanned++;
}
}
// light not detected if average brightness is below threshold (default bit 3 is 0: light detected)
if (sum < bright * scanned)
ret |= 0x08;
return ret;
}

View File

@ -224,40 +224,42 @@ uint8_t playch10_state::pc10_in1_r()
int x = ioport("GUNX")->read();
int y = ioport("GUNY")->read();
// effective range picked up by photodiode, i.e. gun position +- radius
constexpr int xrad = 0;
constexpr int yrad = 0;
// radius of circle picked up by gun's photodiode
constexpr int radius = 5;
// brightness threshold
constexpr int bright = 0x70;
constexpr int bright = 0xc0;
// # of CRT scanlines that sustain brightness
constexpr int sustain = 20;
constexpr int sustain = 22;
int vpos = m_ppu->screen().vpos();
int hpos = m_ppu->screen().hpos();
// update the screen if necessary
if (!m_ppu->screen().vblank())
if (vpos > y - yrad || (vpos == y - yrad && hpos >= x - xrad))
if (vpos > y - radius || (vpos == y - radius && hpos >= x - radius))
m_ppu->screen().update_now();
// default to no light detected
ret |= 0x08;
int sum = 0;
int scanned = 0;
// check brightness of pixels nearby the gun position
for (int i = x - xrad; i <= x + xrad; i++)
for (int j = y - yrad; j <= y + yrad; j++)
{
rgb_t pix = m_ppu->screen().pixel(i, j);
// only detect light if gun position is near, and behind, where the PPU is drawing on the CRT, from NesDev wiki:
// "Zap Ruder test ROM show that the photodiode stays on for about 26 scanlines with pure white, 24 scanlines with light gray, or 19 lines with dark gray."
if (j <= vpos && j > vpos - sustain && (j != vpos || i <= hpos) && pix.brightness() >= bright)
// sum brightness of pixels nearby the gun position
for (int i = x - radius; i <= x + radius; i++)
for (int j = y - radius; j <= y + radius; j++)
// look at pixels within circular sensor
if ((x - i) * (x - i) + (y - j) * (y - j) <= radius * radius)
{
ret &= ~0x08; // light detected
i = x + xrad;
break;
rgb_t pix = m_ppu->screen().pixel(i, j);
// only detect light if gun position is near, and behind, where the PPU is drawing on the CRT, from NesDev wiki:
// "Zap Ruder test ROM show that the photodiode stays on for about 26 scanlines with pure white, 24 scanlines with light gray, or 19 lines with dark gray."
if (j <= vpos && j > vpos - sustain && (j != vpos || i <= hpos))
sum += pix.r() + pix.g() + pix.b();
scanned++;
}
}
// light not detected if average brightness is below threshold (default bit 3 is 0: light detected)
if (sum < bright * scanned)
ret |= 0x08;
// now, add the trigger if not masked
if (!m_cntrl_mask)

View File

@ -280,37 +280,42 @@ void vsnes_state::gun_in0_w(uint8_t data)
int x = ioport("GUNX")->read();
int y = ioport("GUNY")->read();
// effective range picked up by photodiode, i.e. gun position +- radius
constexpr int xrad = 0;
constexpr int yrad = 0;
// radius of circle picked up by the gun's photodiode
constexpr int radius = 5;
// brightness threshold
constexpr int bright = 0x70;
constexpr int bright = 0xc0;
// # of CRT scanlines that sustain brightness
constexpr int sustain = 20;
constexpr int sustain = 22;
int vpos = m_ppu1->screen().vpos();
int hpos = m_ppu1->screen().hpos();
// update the screen if necessary
if (!m_ppu1->screen().vblank())
if (vpos > y - yrad || (vpos == y - yrad && hpos >= x - xrad))
if (vpos > y - radius || (vpos == y - radius && hpos >= x - radius))
m_ppu1->screen().update_now();
// check brightness of pixels nearby the gun position
for (int i = x - xrad; i <= x + xrad; i++)
for (int j = y - yrad; j <= y + yrad; j++)
{
rgb_t pix = m_ppu1->screen().pixel(i, j);
int sum = 0;
int scanned = 0;
// only detect light if gun position is near, and behind, where the PPU is drawing on the CRT, from NesDev wiki:
// "Zap Ruder test ROM show that the photodiode stays on for about 26 scanlines with pure white, 24 scanlines with light gray, or 19 lines with dark gray."
if (j <= vpos && j > vpos - sustain && (j != vpos || i <= hpos) && pix.brightness() >= bright)
// sum brightness of pixels nearby the gun position
for (int i = x - radius; i <= x + radius; i++)
for (int j = y - radius; j <= y + radius; j++)
// look at pixels within circular sensor
if ((x - i) * (x - i) + (y - j) * (y - j) <= radius * radius)
{
m_input_latch[0] |= 0x40;
i = x + xrad;
break;
rgb_t pix = m_ppu1->screen().pixel(i, j);
// only detect light if gun position is near, and behind, where the PPU is drawing on the CRT, from NesDev wiki:
// "Zap Ruder test ROM show that the photodiode stays on for about 26 scanlines with pure white, 24 scanlines with light gray, or 19 lines with dark gray."
if (j <= vpos && j > vpos - sustain && (j != vpos || i <= hpos))
sum += pix.r() + pix.g() + pix.b();
scanned++;
}
}
// light detected if average brightness is above threshold
if (sum >= bright * scanned)
m_input_latch[0] |= 0x40;
}
m_input_strobe[0] = data;