mirror of
https://github.com/libretro/libretro-cap32.git
synced 2026-11-07 11:06:04 -10:00
[GUN]: added two players support, partially based on LilaQ PR, but more optimized.
Co-authored-by: LilaQ [email protected] Resolves #174, #175 and fixes https://github.com/libretro/libretro-cap32/issues/142
This commit is contained in:
1 parent
c23855092f
commit
b8b22931aa
10 files changed
+158
-107
No files matched your search
+1
-1
@@ -28,4 +28,4 @@ external/cmocka/cmocka-lib
|
||||
tests/
|
||||
tests-db/
|
||||
unit-tests/test-db
|
||||
unit-tests/autorun
|
||||
unit-tests/test-dsk
|
||||
+3
-3
@@ -375,7 +375,7 @@ t_disk_format disk_format[MAX_DISK_FORMAT] = {
|
||||
{ "169K Vendor Format", 40, 1, 9, 2, 0x52, 0xe5, {{ 0x41, 0x46, 0x42, 0x47, 0x43, 0x48, 0x44, 0x49, 0x45 }} }
|
||||
};
|
||||
|
||||
t_lightgun gun = { 0, 0, GUN_SLEEP, 0 };
|
||||
t_lightgun gun[2] = {{ 0, 0, GUN_SLEEP, 0 }, { 0, 0, GUN_SLEEP, 0 }};
|
||||
|
||||
#define psg_write \
|
||||
{ \
|
||||
@@ -516,8 +516,7 @@ uint8_t z80_IN_handler (reg_pair port)
|
||||
if (PSG.reg_select == 14) { // PSG port A?
|
||||
if (!(PSG.RegisterAY.Index[7] & 0x40)) { // port A in input mode?
|
||||
ret_val = keyboard_matrix[CPC.keyboard_line & 0x0f]; // read keyboard matrix node status
|
||||
|
||||
if ((CPC.keyboard_line & 0x0f) == 9) { //read line 9 GunStick & state != sleep
|
||||
if ((1 << (CPC.keyboard_line & 0x0f)) & 0x0240) { // joystick gunstick two inputs using bit lines (9 => 0x200 / 6 => 0x40)
|
||||
//checking and return gun value
|
||||
ret_val &= CPC.gun_IN
|
||||
? CPC.gun_IN()
|
||||
@@ -1564,6 +1563,7 @@ int video_init (void)
|
||||
|
||||
CPC.scr_bpp = retro_getGfxBpp();
|
||||
CPC.scr_density = 2 >> (CPC.scr_bpp >> 4);
|
||||
printf("\n>>>>>>> b:%u d:%u\n", CPC.scr_bpp, CPC.scr_density);
|
||||
CPC.scr_style = retro_getStyle();
|
||||
CPC.scr_bps = retro_getGfxBps();
|
||||
CPC.scr_pos = CPC.scr_base = retro_getScreenPtr();
|
||||
|
||||
+89
-48
@@ -53,16 +53,13 @@
|
||||
#include "retro_gun.h"
|
||||
|
||||
extern uint32_t * video_buffer;
|
||||
extern uint8_t keyboard_matrix[16];
|
||||
extern t_CPC CPC;
|
||||
|
||||
typedef struct{
|
||||
int x, y;
|
||||
unsigned int timer;
|
||||
} t_light;
|
||||
t_light light = {0,0,0};
|
||||
|
||||
void ev_lightgun();
|
||||
static t_light light[2];
|
||||
|
||||
#define GUNSTICK_NONE 0xff
|
||||
#define GUNSTICK_HIT 0xfd
|
||||
@@ -71,42 +68,81 @@ void ev_lightgun();
|
||||
#define GUNSTICK_FIRE_MASK 0x10
|
||||
#define GUNSTICK_TIMER 4
|
||||
|
||||
/**
|
||||
* TODO:
|
||||
* I'm using the same code I used on Wii (Wiituka) &
|
||||
* It could be unified with the pointer detection in PHASER code that Colin has done.
|
||||
**/
|
||||
|
||||
static unsigned gunstick_luminance(uint32_t pixel)
|
||||
{
|
||||
unsigned r, g, b;
|
||||
|
||||
switch (retro_video.depth) {
|
||||
case DEPTH_16BPP:
|
||||
r = (pixel >> 11) & 0x1F; r = (r << 3) | (r >> 2);
|
||||
g = (pixel >> 5) & 0x3F; g = (g << 2) | (g >> 4);
|
||||
b = pixel & 0x1F; b = (b << 3) | (b >> 2);
|
||||
break;
|
||||
|
||||
case DEPTH_8BPP:
|
||||
r = ((pixel >> 5) & 0x07) * 36;
|
||||
g = ((pixel >> 2) & 0x07) * 36;
|
||||
b = (pixel & 0x03) * 85;
|
||||
break;
|
||||
|
||||
default:
|
||||
r = (pixel >> 16) & 0xFF;
|
||||
g = (pixel >> 8) & 0xFF;
|
||||
b = pixel & 0xFF;
|
||||
break;
|
||||
}
|
||||
|
||||
return (299 * r) + (587 * g) + (114 * b);
|
||||
}
|
||||
|
||||
void gunstick_reset(void)
|
||||
{
|
||||
memset(light, 0, sizeof(light));
|
||||
|
||||
/* Guillermo Tell uses a light-yellow flash. Keep grey targets (Solo)
|
||||
* and allow a black sample followed by a flash (Mike Gunner). */
|
||||
lightgun_cfg.threshold_color = gunstick_luminance(lightgun_cfg.luminance_color);
|
||||
}
|
||||
|
||||
uint32_t _gunstick_get_screen(int x, int y)
|
||||
{
|
||||
return *(
|
||||
(video_buffer + (x >> retro_video.raw_density_byte)) +
|
||||
(y * retro_video.bps)
|
||||
);
|
||||
if ((unsigned)x >= (unsigned)EMULATION_SCREEN_WIDTH ||
|
||||
(unsigned)y >= (unsigned)EMULATION_SCREEN_HEIGHT)
|
||||
return 0;
|
||||
|
||||
int index = y * EMULATION_SCREEN_WIDTH + x - 1;
|
||||
|
||||
switch (retro_video.depth) {
|
||||
case DEPTH_8BPP:
|
||||
return ((uint8_t *)video_buffer)[index];
|
||||
case DEPTH_16BPP:
|
||||
return ((uint16_t *)video_buffer)[index];
|
||||
default:
|
||||
return video_buffer[index];
|
||||
}
|
||||
}
|
||||
|
||||
bool _gunstick_check(void)
|
||||
bool _gunstick_check(unsigned port)
|
||||
{
|
||||
uint32_t gcolor;
|
||||
uint32_t gcolor, luminance;
|
||||
|
||||
if (gun.state == GUN_XYGET)
|
||||
if (gun[port].state == GUN_XYGET)
|
||||
{
|
||||
light.x = gun.x;
|
||||
light.y = gun.y;
|
||||
gun.state = GUN_SSEND;
|
||||
light[port].x = gun[port].x;
|
||||
light[port].y = gun[port].y;
|
||||
gun[port].state = GUN_SSEND;
|
||||
}
|
||||
|
||||
gcolor = _gunstick_get_screen(light.x, light.y);
|
||||
gcolor = _gunstick_get_screen(light[port].x, light[port].y);
|
||||
luminance = gunstick_luminance(gcolor);
|
||||
#ifdef DEBUG_GUNSTICK
|
||||
printf("gunstick: 0x%X (%u,%u) [0x%X]\n", gcolor, light.x, light.y, lightgun_cfg.whitecolor);
|
||||
printf("gunstick: 0x%X[0x%X] (%u,%u) [0x%X]\n", gcolor, luminance, light[port].x, light[port].y, lightgun_cfg.threshold_color);
|
||||
#endif
|
||||
|
||||
if (gcolor == GUNSTICK_PREPARE_COLOR)
|
||||
gun.state = GUN_PREPARE;
|
||||
|
||||
if ((gun.state != GUN_PREPARE) && (gcolor == lightgun_cfg.whitecolor))
|
||||
if ( luminance > lightgun_cfg.threshold_color)
|
||||
{
|
||||
gun.state = GUN_SLEEP;
|
||||
gun[port].state = GUN_SLEEP;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -115,43 +151,48 @@ bool _gunstick_check(void)
|
||||
|
||||
void gunstick_emulator_update(void)
|
||||
{
|
||||
if (light.timer)
|
||||
light.timer --;
|
||||
|
||||
ev_lightgun();
|
||||
|
||||
// send joy fire on gunstick pressed and update state
|
||||
if (gun.pressed)
|
||||
{
|
||||
keyboard_matrix[GUNSTICK_FIRE_KEYCODE >> 4] &= ~GUNSTICK_FIRE_MASK;
|
||||
gun.state = GUN_SHOOT;
|
||||
} else {
|
||||
keyboard_matrix[GUNSTICK_FIRE_KEYCODE >> 4] |= GUNSTICK_FIRE_MASK;
|
||||
for (unsigned port = 0; port < 2; port++) {
|
||||
if (light[port].timer)
|
||||
light[port].timer--;
|
||||
|
||||
ev_lightgun(port);
|
||||
|
||||
if (gun[port].pressed && gun[port].x >= 0)
|
||||
gun[port].state = GUN_SHOOT;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned char gunstick_emulator_IN()
|
||||
{
|
||||
if (gun.state == GUN_SLEEP)
|
||||
return GUNSTICK_NONE;
|
||||
unsigned line = CPC.keyboard_line & 0x0f;
|
||||
unsigned port;
|
||||
|
||||
if (line == 9) port = 0;
|
||||
else if (line == 6) port = 1;
|
||||
else return GUNSTICK_NONE;
|
||||
|
||||
unsigned char result = gun[port].pressed ? (GUNSTICK_NONE & ~GUNSTICK_FIRE_MASK) : GUNSTICK_NONE;
|
||||
|
||||
if (gun[port].x < 0 || gun[port].state == GUN_SLEEP)
|
||||
return result;
|
||||
|
||||
// on shoot update current light X/Y
|
||||
// some games prove that the light is not always on target.
|
||||
if (gun.state == GUN_SHOOT)
|
||||
if (gun[port].state == GUN_SHOOT)
|
||||
{
|
||||
light.timer = GUNSTICK_TIMER;
|
||||
gun.state = GUN_XYGET;
|
||||
light[port].timer = GUNSTICK_TIMER;
|
||||
gun[port].state = GUN_XYGET;
|
||||
}
|
||||
|
||||
// wait timer finished
|
||||
// TODO: after shoot maybe we need another timer (better dettection if user press long)
|
||||
if (!light.timer)
|
||||
gun.state = GUN_SLEEP;
|
||||
else if (_gunstick_check())
|
||||
return GUNSTICK_HIT;
|
||||
if (!light[port].timer)
|
||||
gun[port].state = GUN_SLEEP;
|
||||
else if (_gunstick_check(port))
|
||||
return result & GUNSTICK_HIT;
|
||||
|
||||
// need a fail answer when you have missed or in any other case
|
||||
return GUNSTICK_NONE;
|
||||
return result;
|
||||
}
|
||||
|
||||
void gunstick_emulator_OUT(){}
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
#ifndef LIGHTGUN_GUNSTICK_H__
|
||||
#define LIGHTGUN_GUNSTICK_H__
|
||||
|
||||
void gunstick_reset(void);
|
||||
void gunstick_emulator_update(void);
|
||||
unsigned char gunstick_emulator_IN();
|
||||
void gunstick_emulator_OUT();
|
||||
|
||||
@@ -51,6 +51,7 @@ typedef struct{
|
||||
gun_state state;
|
||||
int pressed;
|
||||
} t_lightgun;
|
||||
extern t_lightgun gun;
|
||||
extern t_lightgun gun[2];
|
||||
void ev_lightgun(unsigned port);
|
||||
|
||||
#endif
|
||||
+11
-14
@@ -17,7 +17,7 @@
|
||||
*/
|
||||
|
||||
|
||||
/* PHASER Logic
|
||||
/* Magnum Phaser Logic
|
||||
David Skywalker - libretro port Phaser code
|
||||
based on the new cpc caprice core by Colin Pitrat - https://github.com/ColinPitrat/caprice32
|
||||
*/
|
||||
@@ -36,29 +36,26 @@ extern t_CPC CPC;
|
||||
extern t_CRTC CRTC;
|
||||
extern t_VDU VDU;
|
||||
|
||||
// libretro update lightgun position
|
||||
void ev_lightgun();
|
||||
|
||||
#define PHASER_SCREEN_SHIFT 4
|
||||
|
||||
void phaser_emulator_update(void)
|
||||
{
|
||||
ev_lightgun();
|
||||
ev_lightgun(0);
|
||||
|
||||
if(gun.pressed)
|
||||
if(gun[0].pressed)
|
||||
{
|
||||
gun.state = GUN_SHOOT;
|
||||
gun[0].state = GUN_SHOOT;
|
||||
} else {
|
||||
gun.state = GUN_PREPARE;
|
||||
gun[0].state = GUN_PREPARE;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// When the phazer is not pressed, the CRTC is constantly refreshing R16 & R17:
|
||||
// https://www.cpcwiki.eu/index.php/Amstrad_Magnum_Phaser
|
||||
void phaser_emulator_OUT()
|
||||
{
|
||||
if (gun.state != GUN_PREPARE)
|
||||
if (gun[0].state != GUN_PREPARE)
|
||||
return;
|
||||
|
||||
CRTC.registers[17] += 1;
|
||||
@@ -67,15 +64,15 @@ void phaser_emulator_OUT()
|
||||
void phaser_emulator_CRTC()
|
||||
{
|
||||
// If the trigger is pressed, it only updates it when the phazer receives light from the screen.
|
||||
if (gun.state != GUN_SHOOT)
|
||||
if (gun[0].state != GUN_SHOOT)
|
||||
return;
|
||||
|
||||
unsigned int x = CPC.scr_pos - CPC.scr_base;
|
||||
unsigned int x = (CPC.scr_pos - CPC.scr_base) << CPC.scr_density;
|
||||
unsigned int y = VDU.scrln;
|
||||
|
||||
|
||||
unsigned int address = CRTC.addr + CRTC.char_count + PHASER_SCREEN_SHIFT;
|
||||
|
||||
if (gun.x >= x && gun.x < x + 16 && gun.y >= y && gun.y < y + 2)
|
||||
|
||||
if (gun[0].x >= x && gun[0].x < x + 16 && gun[0].y >= y && gun[0].y < y + 2)
|
||||
{
|
||||
CRTC.registers[16] = address >> 8;
|
||||
CRTC.registers[17] = address & 0xff;
|
||||
|
||||
@@ -75,6 +75,7 @@ extern dc_storage* dc;
|
||||
|
||||
// LIGHTGUN
|
||||
#include "retro_gun.h"
|
||||
#include "lightgun/lightgun.h"
|
||||
extern t_lightgun_cfg lightgun_cfg;
|
||||
|
||||
// LOG
|
||||
@@ -1238,6 +1239,8 @@ static void update_variables(void)
|
||||
retro_ui_update_text();
|
||||
computer_reset();
|
||||
}
|
||||
|
||||
lightgun_prepare(lightgun_cfg.guntype);
|
||||
}
|
||||
|
||||
void Emu_init()
|
||||
@@ -1742,22 +1745,10 @@ void retro_set_controller_port_device( unsigned port, unsigned device )
|
||||
if ( port > 1 )
|
||||
return;
|
||||
|
||||
switch (device)
|
||||
{
|
||||
case RETRO_DEVICE_AMSTRAD_LIGHTGUN:
|
||||
lightgun_prepare(lightgun_cfg.guntype);
|
||||
amstrad_devices[port] = RETRO_DEVICE_AMSTRAD_LIGHTGUN;
|
||||
break;
|
||||
|
||||
default:
|
||||
// please do not deinit the lightgun config
|
||||
if (lightgun_cfg.gunconfigured == LIGHTGUN_TYPE_UNCONFIGURED)
|
||||
{
|
||||
lightgun_prepare(LIGHTGUN_TYPE_NONE);
|
||||
}
|
||||
amstrad_devices[port] = device;
|
||||
break;
|
||||
}
|
||||
amstrad_devices[port] = device;
|
||||
gun[port].pressed = 0;
|
||||
gun[port].state = GUN_SLEEP;
|
||||
lightgun_prepare(lightgun_cfg.guntype);
|
||||
|
||||
LOGI("retro_set_controller_port_device: (%d)=%d\n", port, device);
|
||||
}
|
||||
|
||||
+18
-18
@@ -969,24 +969,24 @@ void ev_process_cursor()
|
||||
);
|
||||
}
|
||||
|
||||
void ev_lightgun()
|
||||
void ev_lightgun(unsigned port)
|
||||
{
|
||||
if(input_state_cb(0, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_IS_OFFSCREEN))
|
||||
{
|
||||
gun.state = GUN_PREPARE;
|
||||
gun.x = 0xfff;
|
||||
gun.y = 0xfff;
|
||||
t_lightgun *g = &gun[port];
|
||||
int x, y;
|
||||
|
||||
g->pressed = input_state_cb(port, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_TRIGGER) != 0;
|
||||
x = input_state_cb(port, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_SCREEN_X);
|
||||
y = input_state_cb(port, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_SCREEN_Y);
|
||||
|
||||
if (x == -0x8000 || y == -0x8000 ||
|
||||
input_state_cb(port, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_IS_OFFSCREEN)) {
|
||||
g->state = GUN_PREPARE;
|
||||
g->x = g->y = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
gun.x = ((input_state_cb(0, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_SCREEN_X) + 0x7fff) * EMULATION_SCREEN_WIDTH) / 0xfffe;
|
||||
gun.y = ((input_state_cb(0, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_SCREEN_Y) + 0x7fff) * EMULATION_SCREEN_HEIGHT) / 0xfffe;
|
||||
|
||||
if(input_state_cb(0, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_TRIGGER)
|
||||
|| (input_state_cb(0, RETRO_DEVICE_MOUSE, 0, RETRO_DEVICE_ID_MOUSE_LEFT)))
|
||||
{
|
||||
gun.pressed = true;
|
||||
} else {
|
||||
gun.pressed = false;
|
||||
}
|
||||
}
|
||||
|
||||
g->x = ((x + 0x7fff) * (retro_video.screen_render_width - 1)) / 0xfffe;
|
||||
g->y = ((y + 0x7fff) * (retro_video.screen_render_height - 1)) / 0xfffe;
|
||||
if (retro_video.screen_crop)
|
||||
g->x += EMULATION_CROP;
|
||||
}
|
||||
+24
-5
@@ -65,6 +65,12 @@ static uint32_t cursor_color = 0;
|
||||
|
||||
void lightgun_prepare(lightgun_type guntype)
|
||||
{
|
||||
if (lightgun_cfg.gunconfigured != guntype) {
|
||||
memset(gun, 0, sizeof(gun));
|
||||
gunstick_reset();
|
||||
}
|
||||
|
||||
lightgun_cfg.gun_draw = NULL;
|
||||
cursor_color = retro_video.cursor_color;
|
||||
lightgun_cfg.gunconfigured = guntype;
|
||||
|
||||
@@ -102,10 +108,17 @@ void lightgun_prepare(lightgun_type guntype)
|
||||
{
|
||||
case DEPTH_24BPP:
|
||||
lightgun_cfg.whitecolor = colours[11];
|
||||
lightgun_cfg.luminance_color = colours[0]; // grey color
|
||||
break;
|
||||
|
||||
case DEPTH_16BPP:
|
||||
lightgun_cfg.whitecolor = colours[11] + (colours[11] << 16);
|
||||
lightgun_cfg.whitecolor = colours[11] & 0xffff;
|
||||
lightgun_cfg.luminance_color = colours[0] & 0xffff;
|
||||
break;
|
||||
|
||||
case DEPTH_8BPP:
|
||||
lightgun_cfg.whitecolor = colours[11] & 0xff;
|
||||
lightgun_cfg.luminance_color = colours[0] & 0xff;
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -118,9 +131,15 @@ void lightgun_prepare(lightgun_type guntype)
|
||||
|
||||
void lightgun_draw(void)
|
||||
{
|
||||
// check margins
|
||||
if (gun.x < 16 || gun.y < 16)
|
||||
return;
|
||||
for (unsigned port = 0; port < 2; port++) {
|
||||
if (
|
||||
gun[port].x < 16 ||
|
||||
gun[port].y < 16 ||
|
||||
gun[port].x >= EMULATION_SCREEN_WIDTH - 16 ||
|
||||
gun[port].y >= EMULATION_SCREEN_HEIGHT - 16
|
||||
)
|
||||
continue;
|
||||
|
||||
draw_char(video_buffer, gun.x - (4 * EMULATION_SCALE), gun.y - 4, FNT_CROSS, cursor_color);
|
||||
draw_char(video_buffer, gun[port].x - (4 * EMULATION_SCALE), gun[port].y - 4, FNT_CROSS, cursor_color);
|
||||
}
|
||||
}
|
||||
@@ -51,7 +51,8 @@ typedef struct
|
||||
lightgun_type gunconfigured;
|
||||
bool show;
|
||||
unsigned int whitecolor;
|
||||
// TODO: prepare color (gunstick)
|
||||
unsigned int luminance_color;
|
||||
unsigned int threshold_color;
|
||||
|
||||
void (*gun_update)(void);
|
||||
void (*gun_draw)(void);
|
||||
|
||||
Reference in new issue
Block a user