Merge branch 'master' of https://git.do.srb2.org/KartKrew/Kart into conditions-cascading

# Conflicts:
#	src/k_battle.c
#	src/k_hud_track.cpp
#	src/k_kart.c
#	src/p_inter.c
This commit is contained in:
toaster 2023-03-14 21:59:19 +00:00
commit 2e1efaff0c
50 changed files with 1243 additions and 1263 deletions

View file

@ -449,7 +449,7 @@ boolean AM_Responder(event_t *ev)
{
//faB: prevent alt-tab in win32 version to activate automap just before
// minimizing the app; doesn't do any harm to the DOS version
if (!gamekeydown[0][KEY_LALT] && !gamekeydown[0][KEY_RALT])
if (!G_GetDeviceGameKeyDownArray(0)[KEY_LALT] && !G_GetDeviceGameKeyDownArray(0)[KEY_RALT])
{
bigstate = 0; //added : 24-01-98 : toggle off large view
AM_Start();
@ -1069,7 +1069,7 @@ static void AM_drawWalls(UINT8 pass)
else if (backc1 != frontc1 || backc2 != frontc2)
{
if (!(pass & PASS_INTANGIBLE))
;
;
else if (abs(backc1 - frontc1) < maxstep
|| abs(backc2 - frontc2) < maxstep)
{
@ -1115,7 +1115,7 @@ static void AM_drawWalls(UINT8 pass)
else
{
ffloor_t *rover = NULL;
if (lines[i].frontsector->ffloors || lines[i].backsector->ffloors)
{
if (lines[i].backsector->ffloors == NULL)

View file

@ -36,7 +36,7 @@ size_t Gain<C>::filter(tcb::span<Sample<C>> input_buffer, tcb::span<Sample<C>> b
template <size_t C>
void Gain<C>::gain(float new_gain)
{
new_gain_ = std::clamp(new_gain, 0.0f, 1.0f);
new_gain_ = std::max(new_gain, 0.f);
}
template <size_t C>

View file

@ -1485,7 +1485,7 @@ void CL_UpdateServerList (void)
static void M_ConfirmConnect(void)
{
if (G_PlayerInputDown(0, gc_a, 1) || gamekeydown[0][KEY_ENTER])
if (G_PlayerInputDown(0, gc_a, 1) || G_GetDeviceGameKeyDownArray(0)[KEY_ENTER])
{
if (totalfilesrequestednum > 0)
{
@ -1512,7 +1512,7 @@ static void M_ConfirmConnect(void)
M_StopMessage(0);
}
else if (G_PlayerInputDown(0, gc_b, 1) || G_PlayerInputDown(0, gc_x, 1) || gamekeydown[0][KEY_ESCAPE])
else if (G_PlayerInputDown(0, gc_b, 1) || G_PlayerInputDown(0, gc_x, 1) || G_GetDeviceGameKeyDownArray(0)[KEY_ESCAPE])
{
cl_mode = CL_ABORTED;
M_StopMessage(0);
@ -1962,7 +1962,7 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic
renderdeltatics = FRACUNIT;
rendertimefrac = FRACUNIT;
memset(deviceResponding, false, sizeof (deviceResponding));
G_ResetAllDeviceResponding();
if (netgame)
{
@ -1979,7 +1979,7 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic
{
if (G_PlayerInputDown(0, gc_b, 1)
|| G_PlayerInputDown(0, gc_x, 1)
|| gamekeydown[0][KEY_ESCAPE])
|| G_GetDeviceGameKeyDownArray(0)[KEY_ESCAPE])
cl_mode = CL_ABORTED;
}
}

View file

@ -28,7 +28,9 @@ typedef enum
ev_keyup,
ev_console,
ev_mouse,
ev_joystick,
ev_gamepad_axis,
ev_gamepad_device_added,
ev_gamepad_device_removed,
} evtype_t;
// Event structure.
@ -38,7 +40,7 @@ struct event_t
INT32 data1; // keys / mouse/joystick buttons
INT32 data2; // mouse/joystick x move
INT32 data3; // mouse/joystick y move
INT32 device; // which player's device it belongs to
INT32 device; // which device ID it belongs to (controller ID)
};
//

View file

@ -172,6 +172,54 @@ UINT8 ctrldown = 0; // 0x1 left, 0x2 right
UINT8 altdown = 0; // 0x1 left, 0x2 right
boolean capslock = 0; // gee i wonder what this does.
static void HandleGamepadDeviceAdded(event_t *ev)
{
I_Assert(ev != NULL);
I_Assert(ev->type == ev_gamepad_device_added);
G_RegisterAvailableGamepad(ev->device);
CONS_Debug(DBG_GAMELOGIC, "Registered available gamepad device %d\n", ev->device);
}
static void HandleGamepadDeviceRemoved(event_t *ev)
{
int i = 0;
I_Assert(ev != NULL);
I_Assert(ev->type == ev_gamepad_device_removed);
G_UnregisterAvailableGamepad(ev->device);
CONS_Debug(DBG_GAMELOGIC, "Unregistered available gamepad device %d\n", ev->device);
// Downstream responders need to update player gamepad assignments, pause, etc
for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
{
INT32 device = G_GetDeviceForPlayer(i);
if (device == ev->device)
{
G_SetDeviceForPlayer(i, -1);
}
}
}
/// Respond to added/removed device events, for bookkeeping available gamepads.
static void HandleGamepadDeviceEvents(event_t *ev)
{
I_Assert(ev != NULL);
switch (ev->type)
{
case ev_gamepad_device_added:
HandleGamepadDeviceAdded(ev);
break;
case ev_gamepad_device_removed:
HandleGamepadDeviceRemoved(ev);
break;
default:
break;
}
}
//
// D_ProcessEvents
// Send all the events of the given timestamp down the responder chain
@ -184,11 +232,13 @@ void D_ProcessEvents(void)
boolean eaten;
boolean menuresponse = false;
memset(deviceResponding, false, sizeof (deviceResponding));
G_ResetAllDeviceResponding();
for (; eventtail != eventhead; eventtail = (eventtail+1) & (MAXEVENTS-1))
{
ev = &events[eventtail];
HandleGamepadDeviceEvents(ev);
// Screenshots over everything so that they can be taken anywhere.
if (M_ScreenshotResponder(ev))
continue; // ate the event
@ -989,7 +1039,7 @@ void D_ClearState(void)
// clear cmd building stuff
memset(gamekeydown, 0, sizeof (gamekeydown));
memset(deviceResponding, false, sizeof (deviceResponding));
G_ResetAllDeviceResponding();
// Reset the palette
if (rendermode != render_none)
@ -1524,6 +1574,8 @@ void D_SRB2Main(void)
CONS_Printf("I_StartupGraphics()...\n");
I_StartupGraphics();
I_StartupInput();
if (rendermode != render_none)
{
I_NewTwodeeFrame();

View file

@ -237,9 +237,6 @@ static CV_PossibleValue_t usemouse_cons_t[] = {{0, "Off"}, {1, "On"}, {2, "Force
#ifdef LJOYSTICK
static CV_PossibleValue_t joyport_cons_t[] = {{1, "/dev/js0"}, {2, "/dev/js1"}, {3, "/dev/js2"},
{4, "/dev/js3"}, {0, NULL}};
#else
// accept whatever value - it is in fact the joystick device number
static CV_PossibleValue_t usejoystick_cons_t[] = {{-1, "MIN"}, {MAXGAMEPADS, "MAX"}, {0, NULL}};
#endif
static CV_PossibleValue_t teamscramble_cons_t[] = {{0, "Off"}, {1, "Random"}, {2, "Points"}, {0, NULL}};
@ -332,13 +329,6 @@ consvar_t cv_skipmapcheck = CVAR_INIT ("skipmapcheck", "Off", CV_SAVE, CV_OnOff,
consvar_t cv_usemouse = CVAR_INIT ("use_mouse", "Off", CV_SAVE|CV_CALL,usemouse_cons_t, I_StartupMouse);
consvar_t cv_usejoystick[MAXSPLITSCREENPLAYERS] = {
CVAR_INIT ("use_device", "1", CV_SAVE|CV_CALL, usejoystick_cons_t, I_InitJoystick1),
CVAR_INIT ("use_device2", "2", CV_SAVE|CV_CALL, usejoystick_cons_t, I_InitJoystick2),
CVAR_INIT ("use_device3", "3", CV_SAVE|CV_CALL, usejoystick_cons_t, I_InitJoystick3),
CVAR_INIT ("use_device4", "4", CV_SAVE|CV_CALL, usejoystick_cons_t, I_InitJoystick4)
};
#if (defined (LJOYSTICK) || defined (HAVE_SDL))
consvar_t cv_joyscale[MAXSPLITSCREENPLAYERS] = {
CVAR_INIT ("padscale", "1", CV_SAVE|CV_CALL, NULL, I_JoyScale),
@ -1040,7 +1030,6 @@ void D_RegisterClientCommands(void)
for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
{
CV_RegisterVar(&cv_usejoystick[i]);
CV_RegisterVar(&cv_joyscale[i]);
#ifdef LJOYSTICK
CV_RegisterVar(&cv_joyport[i]);
@ -3374,7 +3363,7 @@ static void Got_Respawn(UINT8 **cp, INT32 playernum)
if (!P_IsObjectOnGround(players[respawnplayer].mo))
return;
K_DoIngameRespawn(&players[respawnplayer]);
P_DamageMobj(players[respawnplayer].mo, NULL, NULL, 1, DMG_DEATHPIT);
demo_extradata[playernum] |= DXD_RESPAWN;
}
}
@ -6231,6 +6220,7 @@ static void FollowerAny_OnChange(UINT8 pnum)
return; // don't send anything there.
SendNameAndColor(pnum);
G_SetPlayerGamepadIndicatorToPlayerColor(pnum);
}
// sends the follower change for players
@ -6382,6 +6372,8 @@ static void Color_OnChange(void)
}
}
lastgoodcolor[0] = cv_playercolor[0].value;
G_SetPlayerGamepadIndicatorToPlayerColor(0);
}
/** Sends a color change for the secondary splitscreen player, unless that
@ -6410,6 +6402,8 @@ static void Color2_OnChange(void)
}
}
lastgoodcolor[1] = cv_playercolor[1].value;
G_SetPlayerGamepadIndicatorToPlayerColor(1);
}
static void Color3_OnChange(void)
@ -6433,6 +6427,8 @@ static void Color3_OnChange(void)
}
}
lastgoodcolor[2] = cv_playercolor[2].value;
G_SetPlayerGamepadIndicatorToPlayerColor(2);
}
static void Color4_OnChange(void)
@ -6456,6 +6452,8 @@ static void Color4_OnChange(void)
}
}
lastgoodcolor[3] = cv_playercolor[3].value;
G_SetPlayerGamepadIndicatorToPlayerColor(3);
}
/** Displays the result of the chat being muted or unmuted.

View file

@ -47,7 +47,6 @@ extern consvar_t cv_splitplayers;
extern consvar_t cv_seenames;
extern consvar_t cv_usemouse;
extern consvar_t cv_usejoystick[MAXSPLITSCREENPLAYERS];
extern consvar_t cv_joyscale[MAXSPLITSCREENPLAYERS];
#ifdef LJOYSTICK
extern consvar_t cv_joyport[MAXSPLITSCREENPLAYERS];

View file

@ -513,7 +513,7 @@ struct player_t
UINT16 spinouttimer; // Spin-out from a banana peel or oil slick (was "pw_bananacam")
UINT8 spinouttype; // Determines the mode of spinout/wipeout, see kartspinoutflags_t
UINT8 instashield; // Instashield no-damage animation timer
INT32 invulnhitlag; // Numbers of tics of hitlag added this tic for "potential" damage -- not real damage
INT32 nullHitlag; // Numbers of tics of hitlag that will ultimately be ignored by subtracting from hitlag
UINT8 wipeoutslow; // Timer before you slowdown when getting wiped out
UINT8 justbumped; // Prevent players from endlessly bumping into each other
UINT8 tumbleBounces;
@ -625,9 +625,7 @@ struct player_t
UINT32 roundscore; // battle score this round
UINT8 emeralds;
UINT8 bumpers;
INT16 karmadelay;
tic_t overtimekarma; // time to live in overtime comeback
INT16 spheres;
tic_t spheredigestion;
@ -666,8 +664,10 @@ struct player_t
INT16 lastsidehit, lastlinehit;
// These track how many things tried to damage you, not
// whether you actually took damage.
// TimesHit tracks how many times something tried to
// damage you or how many times you tried to damage
// something else. It does not track whether damage was
// actually dealt.
UINT8 timeshit; // times hit this tic
UINT8 timeshitprev; // times hit before
// That's TIMES HIT, not TIME SHIT, you doofus! -- in memoriam

View file

@ -5730,7 +5730,7 @@ const char *const MOBJFLAG2_LIST[] = {
"JUSTATTACKED", // can be pushed by other moving mobjs
"FIRING", // turret fire
"SUPERFIRE", // Firing something with Super Sonic-stopping properties. Or, if mobj has MF_MISSILE, this is the actual fire from it.
"\x01", // free: 1<<20 (name un-matchable)
"ALREADYHIT", // This object was already damaged THIS tic, resets even during hitlag
"STRONGBOX", // Flag used for "strong" random monitors.
"OBJECTFLIP", // Flag for objects that always have flipped gravity.
"SKULLFLY", // Special handling: skull in flight.

View file

@ -748,10 +748,10 @@ extern int
/// Render flats on walls
#define WALLFLATS
/// Divide volume of music and sounds by this much (loudest sounds on earth)
#define VOLUME_DIVIDER 4
#define USER_VOLUME_SCALE 2
#define MAX_VOLUME ( 100 * VOLUME_DIVIDER / USER_VOLUME_SCALE )
// Volume scale is 0-100 in new mixer. 100 is treated as -0dB or 100% gain. No more weirdness to work around SDL_mixer
// problems
#define MAX_VOLUME 100
#ifdef HAVE_CURL
#define MASTERSERVER

View file

@ -96,7 +96,8 @@ static struct {
// EZT_ITEMDATA
SINT8 itemtype;
UINT8 itemamount, bumpers;
UINT8 itemamount;
INT32 health;
// EZT_STATDATA
UINT8 skinid, kartspeed, kartweight;
@ -356,7 +357,7 @@ void G_ReadDemoExtraData(void)
if (players[p].mo)
{
// Is this how this should work..?
K_DoIngameRespawn(&players[p]);
P_DamageMobj(players[p].mo, NULL, NULL, 1, DMG_DEATHPIT);
}
}
if (extradata & DXD_WEAPONPREF)
@ -814,13 +815,13 @@ void G_WriteGhostTic(mobj_t *ghost, INT32 playernum)
if (ghost->player && (
ghostext[playernum].itemtype != ghost->player->itemtype ||
ghostext[playernum].itemamount != ghost->player->itemamount ||
ghostext[playernum].bumpers != ghost->player->bumpers
ghostext[playernum].health < ghost->health
))
{
ghostext[playernum].flags |= EZT_ITEMDATA;
ghostext[playernum].itemtype = ghost->player->itemtype;
ghostext[playernum].itemamount = ghost->player->itemamount;
ghostext[playernum].bumpers = ghost->player->bumpers;
ghostext[playernum].health = ghost->health;
}
if (ghost->player && (
@ -882,7 +883,7 @@ void G_WriteGhostTic(mobj_t *ghost, INT32 playernum)
{
WRITESINT8(demobuf.p, ghostext[playernum].itemtype);
WRITEUINT8(demobuf.p, ghostext[playernum].itemamount);
WRITEUINT8(demobuf.p, ghostext[playernum].bumpers);
WRITEINT32(demobuf.p, ghostext[playernum].health);
}
if (ghostext[playernum].flags & EZT_STATDATA)
{
@ -1065,7 +1066,7 @@ void G_ConsGhostTic(INT32 playernum)
{
ghostext[playernum].itemtype = READSINT8(demobuf.p);
ghostext[playernum].itemamount = READUINT8(demobuf.p);
ghostext[playernum].bumpers = READUINT8(demobuf.p);
ghostext[playernum].health = READINT32(demobuf.p);
}
if (xziptic & EZT_STATDATA)
{
@ -1135,7 +1136,7 @@ void G_ConsGhostTic(INT32 playernum)
if (players[playernum].itemtype != ghostext[playernum].itemtype
|| players[playernum].itemamount != ghostext[playernum].itemamount
|| players[playernum].bumpers != ghostext[playernum].bumpers)
|| testmo->health < ghostext[playernum].health)
{
if (demosynced)
CONS_Alert(CONS_WARNING, M_GetText("Demo playback has desynced (item/bumpers)!\n"));
@ -1143,7 +1144,7 @@ void G_ConsGhostTic(INT32 playernum)
players[playernum].itemtype = ghostext[playernum].itemtype;
players[playernum].itemamount = ghostext[playernum].itemamount;
players[playernum].bumpers = ghostext[playernum].bumpers;
testmo->health = ghostext[playernum].health;
}
if (players[playernum].kartspeed != ghostext[playernum].kartspeed

View file

@ -523,7 +523,7 @@ void G_UpdateTimeStickerMedals(UINT16 map, boolean showownrecord)
break;
}
case ET_MAP:
{
{
if (emblem->flags & ME_SPBATTACK && cv_dummyspbattack.value)
break;
goto bademblem;
@ -873,32 +873,49 @@ INT16 G_SoftwareClipAimingPitch(INT32 *aiming)
return (INT16)((*aiming)>>16);
}
// Default controls for keyboard. These are hardcoded and cannot be changed.
static INT32 keyboardMenuDefaults[][2] = {
{gc_a, KEY_ENTER},
{gc_c, KEY_BACKSPACE},
{gc_x, KEY_ESCAPE},
{gc_left, KEY_LEFTARROW},
{gc_right, KEY_RIGHTARROW},
{gc_up, KEY_UPARROW},
{gc_down, KEY_DOWNARROW},
static INT32 G_GetValueFromControlTable(INT32 deviceID, INT32 deadzone, INT32 *controltable)
{
INT32 i;
// special control
{gc_start, KEY_ESCAPE},
// 8 total controls*
};
if (deviceID <= UNASSIGNED_DEVICE)
{
// An invalid device can't have any binds!
return 0;
}
#define KEYBOARDDEFAULTSSPLIT 7
for (i = 0; i < MAXINPUTMAPPING; i++)
{
INT32 key = controltable[i];
INT32 value = 0;
// Invalid key number.
if (G_KeyIsAvailable(key, deviceID) == false)
{
continue;
}
value = G_GetDeviceGameKeyDownArray(deviceID)[key];
if (value >= deadzone)
{
return value;
}
}
// Not pressed.
return 0;
}
INT32 G_PlayerInputAnalog(UINT8 p, INT32 gc, UINT8 menuPlayers)
{
INT32 deviceID;
INT32 i, j;
INT32 deadzone = 0;
boolean trydefaults = true;
boolean tryingotherID = false;
INT32 *controltable = &(gamecontrol[p][gc][0]);
const INT32 deadzone = (JOYAXISRANGE * cv_deadzone[p].value) / FRACUNIT;
const INT32 keyboard_player = G_GetPlayerForDevice(KEYBOARD_MOUSE_DEVICE);
const boolean in_menu = (menuPlayers > 0);
const boolean main_player = (p == 0);
INT32 deviceID = UNASSIGNED_DEVICE;
INT32 value = -1;
INT32 avail_gamepad_id = 0;
INT32 i;
if (p >= MAXSPLITSCREENPLAYERS)
{
@ -908,118 +925,96 @@ INT32 G_PlayerInputAnalog(UINT8 p, INT32 gc, UINT8 menuPlayers)
return 0;
}
deadzone = (JOYAXISRANGE * cv_deadzone[p].value) / FRACUNIT;
deviceID = G_GetDeviceForPlayer(p);
deviceID = cv_usejoystick[p].value;
retrygetcontrol:
for (i = 0; i < MAXINPUTMAPPING; i++)
if ((in_menu == true && G_KeyBindIsNecessary(gc) == true) // In menu: check for all unoverrideable menu default controls.
|| (in_menu == false && gc == gc_start)) // In gameplay: check for the unoverrideable start button to be able to bring up the menu.
{
INT32 key = controltable[i];
INT32 menukey = KEY_NULL;
INT32 value = 0;
boolean processinput = true;
// for menus, keyboards have defaults!
if (deviceID == 0)
value = G_GetValueFromControlTable(KEYBOARD_MOUSE_DEVICE, JOYAXISRANGE/4, &(menucontrolreserved[gc][0]));
if (value > 0) // Check for press instead of bound.
{
// In menus, check indexes 0 through 5 (everything besides gc_start)
// Outside of menus, only consider the hardcoded input for gc_start at index 6
INT32 maxj = menuactive ? KEYBOARDDEFAULTSSPLIT : KEYBOARDDEFAULTSSPLIT+1;
j = (!menuactive) ? KEYBOARDDEFAULTSSPLIT : 0;
for (; j < maxj; j++) // check keyboardMenuDefaults
{
// the gc we're looking for
if (gc == keyboardMenuDefaults[j][0])
{
menukey = keyboardMenuDefaults[j][1];
break;
}
// The key is mapped to *something else*...?
// Then don't process that as it would conflict with our hardcoded inputs.
else if (key == keyboardMenuDefaults[j][1])
{
processinput = false;
break;
}
}
}
// Invalid key number.
if (!G_KeyIsAvailable(key, deviceID) && !G_KeyIsAvailable(menukey, deviceID))
{
continue;
}
if (processinput)
{
// It's possible to access this control right now, so let's disable the default control backup for later.
trydefaults = false;
value = gamekeydown[deviceID][key];
if (menukey && gamekeydown[deviceID][menukey])
value = gamekeydown[deviceID][menukey];
if (value >= deadzone)
// This is only intended for P1.
if (main_player == true)
{
return value;
}
else
{
return 0;
}
}
}
// If you're on controller, try your keyboard-based binds as an immediate backup.
// Do not do this if there are more than 1 local player.
if (p == 0 && deviceID > 0 && !tryingotherID && menuPlayers < 2 && !splitscreen)
// Player 1 is always allowed to use the keyboard in 1P, even if they got disconnected.
if (main_player == true && keyboard_player == -1 && deviceID == UNASSIGNED_DEVICE)
{
deviceID = 0;
goto retrygetcontrol;
deviceID = KEYBOARD_MOUSE_DEVICE;
}
if (menuPlayers == 0)
// First, try our actual binds.
value = G_GetValueFromControlTable(deviceID, deadzone, &(gamecontrol[p][gc][0]));
if (value > 0)
{
return 0;
return value;
}
// We don't want menus to become unnavigable if people unbind
// all of their controls, so we do several things in this scenario.
// First: try other controllers.
if (!tryingotherID)
// If you're on gamepad in 1P, and you didn't have a gamepad bind for this, then try your keyboard binds.
if (main_player == true && keyboard_player == -1 && deviceID > KEYBOARD_MOUSE_DEVICE)
{
deviceID = MAXDEVICES;
tryingotherID = true;
}
loweringid:
deviceID--;
if (deviceID > 0)
{
for (i = 0; i < menuPlayers; i++)
value = G_GetValueFromControlTable(KEYBOARD_MOUSE_DEVICE, deadzone, &(gamecontrol[p][gc][0]));
if (value > 0)
{
if (deviceID != cv_usejoystick[i].value)
continue;
// Controller taken? Try again...
goto loweringid;
return value;
}
goto retrygetcontrol;
}
if (trydefaults && G_KeyBindIsNecessary(gc))
if (in_menu == true)
{
// If we still haven't found anything and the keybind is necessary,
// try it all again but with default binds.
trydefaults = false;
controltable = &(gamecontroldefault[gc][0]);
tryingotherID = false;
deviceID = cv_usejoystick[p].value;
goto retrygetcontrol;
if (main_player == true)
{
// We are P1 controlling menus. We should be able to
// control the menu with any unused gamepads, so
// that gamepads are able to navigate to the player
// setup menu in the first place.
for (avail_gamepad_id = 0; avail_gamepad_id < G_GetNumAvailableGamepads(); avail_gamepad_id++)
{
INT32 tryDevice = G_GetAvailableGamepadDevice(avail_gamepad_id);
if (tryDevice <= KEYBOARD_MOUSE_DEVICE)
{
continue;
}
for (i = 0; i < menuPlayers; i++)
{
if (tryDevice == G_GetDeviceForPlayer(i))
{
// Don't do this for already taken devices.
break;
}
}
if (i == menuPlayers)
{
// This gamepad isn't being used, so we can
// use it for P1 menu navigation.
value = G_GetValueFromControlTable(tryDevice, deadzone, &(gamecontrol[p][gc][0]));
if (value > 0)
{
return value;
}
}
}
}
// Still nothing bound after everything. Try default gamepad controls.
value = G_GetValueFromControlTable(deviceID, deadzone, &(gamecontroldefault[gc][0]));
if (value > 0)
{
return value;
}
}
// Literally not bound at all, so it can't be pressed at all.
return 0;
}
@ -1542,7 +1537,7 @@ void G_DoLoadLevelEx(boolean resetplayer, gamestate_t newstate)
// clear cmd building stuff
memset(gamekeydown, 0, sizeof (gamekeydown));
memset(deviceResponding, false, sizeof (deviceResponding));
G_ResetAllDeviceResponding();
// clear hud messages remains (usually from game startup)
CON_ClearHUD();
@ -1862,7 +1857,7 @@ boolean G_Responder(event_t *ev)
case ev_mouse:
return true; // eat events
case ev_joystick:
case ev_gamepad_axis:
return true; // eat events
default:
@ -1897,13 +1892,6 @@ boolean G_CouldView(INT32 playernum)
if (( player->pflags & PF_NOCONTEST ))
return false;
// I don't know if we want this actually, but I'll humor the suggestion anyway
if ((gametyperules & GTR_BUMPERS) && !demo.playback)
{
if (player->bumpers <= 0)
return false;
}
// SRB2Kart: we have no team-based modes, YET...
if (G_GametypeHasTeams())
{
@ -2145,7 +2133,19 @@ void G_Ticker(boolean run)
{
if (playeringame[i])
{
K_PlayerLoseLife(&players[i]);
if (players[i].bot == true && grandprixinfo.gp == true && grandprixinfo.masterbots == false)
{
players[i].botvars.difficulty--;
if (players[i].botvars.difficulty < 1)
{
players[i].botvars.difficulty = 1;
}
}
else
{
K_PlayerLoseLife(&players[i]);
}
}
}
@ -2421,7 +2421,6 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
INT32 itemtype;
INT32 itemamount;
INT32 growshrinktimer;
INT32 bumper;
boolean songcredit = false;
UINT16 nocontrol;
INT32 khudfault;
@ -2496,7 +2495,6 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
itemtype = 0;
itemamount = 0;
growshrinktimer = 0;
bumper = ((gametyperules & GTR_BUMPERS) ? K_StartingBumperCount() : 0);
if (gametyperules & GTR_SPHERES)
{
rings = 0;
@ -2543,7 +2541,6 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
else
growshrinktimer = 0;
bumper = players[player].bumpers;
rings = players[player].rings;
spheres = players[player].spheres;
kickstartaccel = players[player].kickstartaccel;
@ -2644,9 +2641,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
p->itemtype = itemtype;
p->itemamount = itemamount;
p->growshrinktimer = growshrinktimer;
p->bumpers = bumper;
p->karmadelay = comebacktime;
p->overtimekarma = 0;
p->karmadelay = 0;
p->eggmanblame = -1;
p->lastdraft = -1;
p->karthud[khud_fault] = khudfault;

View file

@ -19,6 +19,9 @@
#include "d_net.h"
#include "console.h"
#include "i_joy.h" // JOYAXISRANGE
#include "r_draw.h" // GTC_ macros for assigning gamepad indicator colors
#include "v_video.h" // V_GetColor for assigning gamepad indictaor colors
#include "z_zone.h"
#define MAXMOUSESENSITIVITY 100 // sensitivity steps
@ -35,11 +38,11 @@ consvar_t cv_controlperkey = CVAR_INIT ("controlperkey", "One", CV_SAVE, onecont
// current state of the keys
// FRACUNIT for fully pressed, 0 for not pressed
INT32 gamekeydown[MAXDEVICES][NUMINPUTS];
boolean deviceResponding[MAXDEVICES];
// two key codes (or virtual key) per game control
INT32 gamecontrol[MAXSPLITSCREENPLAYERS][num_gamecontrols][MAXINPUTMAPPING];
INT32 gamecontroldefault[num_gamecontrols][MAXINPUTMAPPING]; // default control storage
INT32 menucontrolreserved[num_gamecontrols][MAXINPUTMAPPING];
// lists of GC codes for selective operation
/*
@ -68,13 +71,82 @@ const INT32 gcl_full[num_gcl_full] = {
};
*/
INT32 G_GetDevicePlayer(INT32 deviceID)
static INT32 g_gamekeydown_device0[NUMINPUTS];
static INT32 g_available_gamepad_devices;
static INT32 g_gamepad_device_ids[MAXGAMEPADS];
static INT32* g_gamepad_gamekeydown[MAXGAMEPADS];
static boolean g_device0_responding;
static boolean g_gamepad_responding[MAXGAMEPADS];
static INT32 g_player_devices[MAXSPLITSCREENPLAYERS] = {-1, -1, -1, -1};
void G_RegisterAvailableGamepad(INT32 device_id)
{
I_Assert(device_id >= 1);
if (g_available_gamepad_devices == MAXGAMEPADS)
{
// too many!
return;
}
g_gamepad_device_ids[g_available_gamepad_devices] = device_id;
g_gamepad_gamekeydown[g_available_gamepad_devices] = Z_CallocAlign(NUMINPUTS * sizeof(INT32), PU_STATIC, NULL, 4);
g_gamepad_responding[g_available_gamepad_devices] = false;
g_available_gamepad_devices += 1;
}
void G_UnregisterAvailableGamepad(INT32 device_id)
{
int i = 0;
I_Assert(device_id >= 1);
if (g_available_gamepad_devices <= 0)
{
return;
}
for (i = 0; i < g_available_gamepad_devices; i++)
{
if (g_gamepad_device_ids[i] == device_id)
{
int32_t *old_gamekeydown = g_gamepad_gamekeydown[i];
g_gamepad_device_ids[i] = g_gamepad_device_ids[g_available_gamepad_devices - 1];
g_gamepad_gamekeydown[i] = g_gamepad_gamekeydown[g_available_gamepad_devices - 1];
g_gamepad_responding[i] = g_gamepad_responding[g_available_gamepad_devices - 1];
Z_Free(old_gamekeydown);
g_available_gamepad_devices -= 1;
return;
}
}
}
INT32 G_GetNumAvailableGamepads(void)
{
return g_available_gamepad_devices;
}
INT32 G_GetAvailableGamepadDevice(INT32 available_index)
{
if (available_index < 0 || available_index >= G_GetNumAvailableGamepads())
{
return -1;
}
return g_gamepad_device_ids[available_index];
}
INT32 G_GetPlayerForDevice(INT32 device_id)
{
INT32 i;
for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
{
if (deviceID == cv_usejoystick[i].value)
if (device_id == g_player_devices[i])
{
return i;
}
@ -83,6 +155,203 @@ INT32 G_GetDevicePlayer(INT32 deviceID)
return -1;
}
INT32 G_GetDeviceForPlayer(INT32 player)
{
int i;
if (G_GetPlayerForDevice(KEYBOARD_MOUSE_DEVICE) == player)
{
return KEYBOARD_MOUSE_DEVICE;
}
for (i = 0; i < G_GetNumAvailableGamepads() + 1; i++)
{
INT32 device = G_GetAvailableGamepadDevice(i);
if (G_GetPlayerForDevice(device) == player)
{
return device;
}
}
return -1;
}
void G_SetDeviceForPlayer(INT32 player, INT32 device)
{
int i;
I_Assert(player >= 0 && player < MAXSPLITSCREENPLAYERS);
I_Assert(device >= -1);
g_player_devices[player] = device;
if (device == -1)
{
return;
}
if (device != KEYBOARD_MOUSE_DEVICE)
{
I_SetGamepadPlayerIndex(device, player);
}
for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
{
if (i == player)
{
continue;
}
if (g_player_devices[i] == device)
{
g_player_devices[i] = -1;
if (device > 0)
{
I_SetGamepadPlayerIndex(device, -1);
}
}
}
}
void G_SetPlayerGamepadIndicatorToPlayerColor(INT32 player)
{
INT32 device;
INT32 skin;
UINT16 skincolor;
UINT8 *colormap;
byteColor_t byte_color;
I_Assert(player >= 0 && player < MAXSPLITSCREENPLAYERS);
device = G_GetDeviceForPlayer(player);
if (device <= 0)
{
return;
}
skin = cv_skin[player].value;
skincolor = cv_playercolor[player].value;
colormap = R_GetTranslationColormap(skin, skincolor, GTC_MENUCACHE);
if (colormap == NULL)
{
return;
}
byte_color = V_GetColor(colormap[104]).s;
I_SetGamepadIndicatorColor(device, byte_color.red, byte_color.green, byte_color.blue);
}
INT32* G_GetDeviceGameKeyDownArray(INT32 device)
{
int i;
I_Assert(device >= 0);
if (device == KEYBOARD_MOUSE_DEVICE)
{
return g_gamekeydown_device0;
}
for (i = 0; i < g_available_gamepad_devices; i++)
{
if (g_gamepad_device_ids[i] == device)
{
return g_gamepad_gamekeydown[i];
}
}
return NULL;
}
boolean G_IsDeviceResponding(INT32 device)
{
int i;
I_Assert(device >= 0);
if (device == KEYBOARD_MOUSE_DEVICE)
{
return g_device0_responding;
}
for (i = 0; i < g_available_gamepad_devices; i++)
{
INT32 device_id = G_GetAvailableGamepadDevice(i);
if (device_id == device)
{
return g_gamepad_responding[i];
}
}
return false;
}
void G_SetDeviceResponding(INT32 device, boolean responding)
{
int i;
I_Assert(device >= 0);
if (device == KEYBOARD_MOUSE_DEVICE)
{
g_device0_responding = responding;
return;
}
for (i = 0; i < g_available_gamepad_devices; i++)
{
INT32 device_id = G_GetAvailableGamepadDevice(i);
if (device_id == device)
{
g_gamepad_responding[i] = responding;
return;
}
}
}
void G_ResetAllDeviceResponding(void)
{
int i;
int num_gamepads;
g_device0_responding = false;
num_gamepads = G_GetNumAvailableGamepads();
for (i = 0; i < num_gamepads; i++)
{
g_gamepad_responding[i] = false;
}
}
static boolean AutomaticControllerReassignmentIsAllowed(INT32 device)
{
boolean device_is_gamepad = device > 0;
boolean device_is_unassigned = G_GetPlayerForDevice(device) == -1;
boolean gamestate_is_in_level = gamestate == GS_LEVEL;
return device_is_gamepad && device_is_unassigned && gamestate_is_in_level;
}
static INT32 AssignDeviceToFirstUnassignedPlayer(INT32 device)
{
int i;
for (i = 0; i < splitscreen + 1; i++)
{
if (G_GetDeviceForPlayer(i) == -1)
{
G_SetDeviceForPlayer(i, device);
return i;
}
}
return -1;
}
//
// Remaps the inputs to game controls.
//
@ -94,15 +363,15 @@ void G_MapEventsToControls(event_t *ev)
{
INT32 i;
if (ev->device >= 0 && ev->device < MAXDEVICES)
if (ev->device >= 0)
{
switch (ev->type)
{
case ev_keydown:
//case ev_keyup:
//case ev_mouse:
//case ev_joystick:
deviceResponding[ev->device] = true;
//case ev_gamepad_axis:
G_SetDeviceResponding(ev->device, true);
break;
default:
@ -119,7 +388,16 @@ void G_MapEventsToControls(event_t *ev)
case ev_keydown:
if (ev->data1 < NUMINPUTS)
{
gamekeydown[ev->device][ev->data1] = JOYAXISRANGE;
G_GetDeviceGameKeyDownArray(ev->device)[ev->data1] = JOYAXISRANGE;
if (AutomaticControllerReassignmentIsAllowed(ev->device))
{
INT32 assigned = AssignDeviceToFirstUnassignedPlayer(ev->device);
if (assigned >= 0)
{
CONS_Alert(CONS_NOTICE, "Player %d device was reassigned\n", assigned + 1);
}
}
}
#ifdef PARANOIA
else
@ -132,7 +410,7 @@ void G_MapEventsToControls(event_t *ev)
case ev_keyup:
if (ev->data1 < NUMINPUTS)
{
gamekeydown[ev->device][ev->data1] = 0;
G_GetDeviceGameKeyDownArray(ev->device)[ev->data1] = 0;
}
#ifdef PARANOIA
else
@ -147,32 +425,32 @@ void G_MapEventsToControls(event_t *ev)
if (ev->data2 < 0)
{
// Left
gamekeydown[ev->device][KEY_MOUSEMOVE + 2] = abs(ev->data2);
gamekeydown[ev->device][KEY_MOUSEMOVE + 3] = 0;
G_GetDeviceGameKeyDownArray(ev->device)[KEY_MOUSEMOVE + 2] = abs(ev->data2);
G_GetDeviceGameKeyDownArray(ev->device)[KEY_MOUSEMOVE + 3] = 0;
}
else
{
// Right
gamekeydown[ev->device][KEY_MOUSEMOVE + 2] = 0;
gamekeydown[ev->device][KEY_MOUSEMOVE + 3] = abs(ev->data2);
G_GetDeviceGameKeyDownArray(ev->device)[KEY_MOUSEMOVE + 2] = 0;
G_GetDeviceGameKeyDownArray(ev->device)[KEY_MOUSEMOVE + 3] = abs(ev->data2);
}
// Y axis
if (ev->data3 < 0)
{
// Up
gamekeydown[ev->device][KEY_MOUSEMOVE] = abs(ev->data3);
gamekeydown[ev->device][KEY_MOUSEMOVE + 1] = 0;
G_GetDeviceGameKeyDownArray(ev->device)[KEY_MOUSEMOVE] = abs(ev->data3);
G_GetDeviceGameKeyDownArray(ev->device)[KEY_MOUSEMOVE + 1] = 0;
}
else
{
// Down
gamekeydown[ev->device][KEY_MOUSEMOVE] = 0;
gamekeydown[ev->device][KEY_MOUSEMOVE + 1] = abs(ev->data3);
G_GetDeviceGameKeyDownArray(ev->device)[KEY_MOUSEMOVE] = 0;
G_GetDeviceGameKeyDownArray(ev->device)[KEY_MOUSEMOVE + 1] = abs(ev->data3);
}
break;
case ev_joystick: // buttons are virtual keys
case ev_gamepad_axis: // buttons are virtual keys
if (ev->data1 >= JOYAXISSETS)
{
#ifdef PARANOIA
@ -190,12 +468,12 @@ void G_MapEventsToControls(event_t *ev)
if (ev->data2 != INT32_MAX)
{
gamekeydown[ev->device][KEY_AXIS1 + (JOYANALOGS * 4) + (i * 2)] = max(0, ev->data2);
G_GetDeviceGameKeyDownArray(ev->device)[KEY_AXIS1 + (JOYANALOGS * 4) + (i * 2)] = max(0, ev->data2);
}
if (ev->data3 != INT32_MAX)
{
gamekeydown[ev->device][KEY_AXIS1 + (JOYANALOGS * 4) + (i * 2) + 1] = max(0, ev->data3);
G_GetDeviceGameKeyDownArray(ev->device)[KEY_AXIS1 + (JOYANALOGS * 4) + (i * 2) + 1] = max(0, ev->data3);
}
}
else
@ -206,14 +484,14 @@ void G_MapEventsToControls(event_t *ev)
if (ev->data2 < 0)
{
// Left
gamekeydown[ev->device][KEY_AXIS1 + (i * 4)] = abs(ev->data2);
gamekeydown[ev->device][KEY_AXIS1 + (i * 4) + 1] = 0;
G_GetDeviceGameKeyDownArray(ev->device)[KEY_AXIS1 + (i * 4)] = abs(ev->data2);
G_GetDeviceGameKeyDownArray(ev->device)[KEY_AXIS1 + (i * 4) + 1] = 0;
}
else
{
// Right
gamekeydown[ev->device][KEY_AXIS1 + (i * 4)] = 0;
gamekeydown[ev->device][KEY_AXIS1 + (i * 4) + 1] = abs(ev->data2);
G_GetDeviceGameKeyDownArray(ev->device)[KEY_AXIS1 + (i * 4)] = 0;
G_GetDeviceGameKeyDownArray(ev->device)[KEY_AXIS1 + (i * 4) + 1] = abs(ev->data2);
}
}
@ -222,14 +500,14 @@ void G_MapEventsToControls(event_t *ev)
if (ev->data3 < 0)
{
// Up
gamekeydown[ev->device][KEY_AXIS1 + (i * 4) + 2] = abs(ev->data3);
gamekeydown[ev->device][KEY_AXIS1 + (i * 4) + 3] = 0;
G_GetDeviceGameKeyDownArray(ev->device)[KEY_AXIS1 + (i * 4) + 2] = abs(ev->data3);
G_GetDeviceGameKeyDownArray(ev->device)[KEY_AXIS1 + (i * 4) + 3] = 0;
}
else
{
// Down
gamekeydown[ev->device][KEY_AXIS1 + (i * 4) + 2] = 0;
gamekeydown[ev->device][KEY_AXIS1 + (i * 4) + 3] = abs(ev->data3);
G_GetDeviceGameKeyDownArray(ev->device)[KEY_AXIS1 + (i * 4) + 2] = 0;
G_GetDeviceGameKeyDownArray(ev->device)[KEY_AXIS1 + (i * 4) + 3] = abs(ev->data3);
}
}
}
@ -405,12 +683,13 @@ boolean G_KeyBindIsNecessary(INT32 gc)
switch (gc)
{
case gc_a:
case gc_b:
case gc_c:
case gc_x:
case gc_up:
case gc_down:
case gc_left:
case gc_right:
case gc_start:
//case gc_start: // Is necessary, but handled special.
return true;
default:
return false;
@ -421,25 +700,31 @@ boolean G_KeyBindIsNecessary(INT32 gc)
// Returns false if a key is deemed unreachable for this device.
boolean G_KeyIsAvailable(INT32 key, INT32 deviceID)
{
boolean gamepad_key = false;
// Invalid key number.
if (key <= 0 || key >= NUMINPUTS)
{
return false;
}
// Valid controller-specific virtual key, but no controller attached for player.
if (key >= KEY_JOY1 && key < JOYINPUTEND && deviceID <= 0)
// Only allow gamepad keys for gamepad devices,
// and vice versa.
gamepad_key = (key >= KEY_JOY1 && key < JOYINPUTEND);
if (deviceID == KEYBOARD_MOUSE_DEVICE)
{
return false;
if (gamepad_key == true)
{
return false;
}
}
// Valid mouse-specific virtual key, but no mouse attached for player. TODO HOW TO DETECT ACTIVE MOUSE CONNECTION
/*
if (key >= KEY_MOUSE1 && key < MOUSEINPUTEND && ????????)
else
{
return false;
if (gamepad_key == false)
{
return false;
}
}
*/
return true;
}
@ -538,7 +823,7 @@ void G_DefineDefaultControls(void)
gamecontroldefault[gc_z ][0] = 'd';
gamecontroldefault[gc_l ][0] = 'q';
gamecontroldefault[gc_r ][0] = 'e';
gamecontroldefault[gc_start ][0] = KEY_ESCAPE; // *
gamecontroldefault[gc_start ][0] = KEY_ESCAPE;
gamecontroldefault[gc_rankings][0] = KEY_TAB;
// Gamepad controls
@ -560,6 +845,16 @@ void G_DefineDefaultControls(void)
gamecontroldefault[gc_down ][2] = KEY_AXIS1+3; // Axis Y+
gamecontroldefault[gc_left ][2] = KEY_AXIS1+0; // Axis X-
gamecontroldefault[gc_right][2] = KEY_AXIS1+1; // Axis X+
// Menu reserved controls
menucontrolreserved[gc_up ][0] = KEY_UPARROW;
menucontrolreserved[gc_down ][0] = KEY_DOWNARROW;
menucontrolreserved[gc_left ][0] = KEY_LEFTARROW;
menucontrolreserved[gc_right][0] = KEY_RIGHTARROW;
menucontrolreserved[gc_a ][0] = KEY_ENTER;
menucontrolreserved[gc_c ][0] = KEY_BACKSPACE;
menucontrolreserved[gc_x ][0] = KEY_ESCAPE;
menucontrolreserved[gc_start][0] = KEY_ESCAPE; // Handled special
}
void G_CopyControls(INT32 (*setupcontrols)[MAXINPUTMAPPING], INT32 (*fromcontrols)[MAXINPUTMAPPING], const INT32 *gclist, INT32 gclen)

View file

@ -107,12 +107,14 @@ extern consvar_t cv_controlperkey;
// current state of the keys: JOYAXISRANGE or 0 when boolean.
// Or anything inbetween for analog values
#define MAXDEVICES (MAXGAMEPADS + 1) // Gamepads + keyboard & mouse
#define KEYBOARD_MOUSE_DEVICE (0)
#define UNASSIGNED_DEVICE (-1)
extern INT32 gamekeydown[MAXDEVICES][NUMINPUTS];
extern boolean deviceResponding[MAXDEVICES];
// several key codes (or virtual key) per game control
extern INT32 gamecontrol[MAXSPLITSCREENPLAYERS][num_gamecontrols][MAXINPUTMAPPING];
extern INT32 gamecontroldefault[num_gamecontrols][MAXINPUTMAPPING]; // default control storage
extern INT32 menucontrolreserved[num_gamecontrols][MAXINPUTMAPPING];
/*
#define num_gcl_accelerate 1
@ -135,7 +137,33 @@ extern const INT32 gcl_full[num_gcl_full];
// peace to my little coder fingers!
// check a gamecontrol being active or not
INT32 G_GetDevicePlayer(INT32 deviceID);
/*
*/
/// Register a device index (from ev_gamepad_device_added) as an Available Gamepad
void G_RegisterAvailableGamepad(INT32 device_id);
/// Unregister a device index (from ev_gamepad_device_removed) as an Available Gamepad
void G_UnregisterAvailableGamepad(INT32 device_id);
/// Get the number of Available Gamepads registered.
INT32 G_GetNumAvailableGamepads(void);
/// Get the device ID for a given Available Gamepad Index, or -1. 0 <= available_index < G_GetNumAvailableGamepads()
INT32 G_GetAvailableGamepadDevice(INT32 available_index);
INT32 G_GetPlayerForDevice(INT32 deviceID);
/// Get gamepad device for given player, or -1.
INT32 G_GetDeviceForPlayer(INT32 player);
/// Set the given player index's assigned device. If the device is in use by another player, that player is unassigned.
void G_SetDeviceForPlayer(INT32 player, INT32 device);
void G_SetPlayerGamepadIndicatorToPlayerColor(INT32 player);
/// Get the gamekeydown array (NUMINPUTS values) for the given device, or NULL if the device id is invalid.
INT32* G_GetDeviceGameKeyDownArray(INT32 device);
boolean G_IsDeviceResponding(INT32 device);
void G_SetDeviceResponding(INT32 device, boolean responding);
void G_ResetAllDeviceResponding(void);
// remaps the input event to a game control.
void G_MapEventsToControls(event_t *ev);

View file

@ -58,6 +58,9 @@ struct JoyType_t
extern JoyType_t Joystick[MAXSPLITSCREENPLAYERS];
void I_SetGamepadPlayerIndex(INT32 device_id, INT32 index);
void I_SetGamepadIndicatorColor(INT32 device_id, UINT8 red, UINT8 green, UINT8 blue);
#ifdef __cplusplus
} // extern "C"
#endif

View file

@ -214,7 +214,8 @@ void I_PauseSong(void);
*/
void I_ResumeSong(void);
/** \brief The I_SetMusicVolume function
/** \brief Sets the volume of the Music mixing channel. Distinguished from the song's individual volume. The scale of
the volume is determined by the interface implementation.
\param volume volume to set at
@ -222,6 +223,13 @@ void I_ResumeSong(void);
*/
void I_SetMusicVolume(int volume);
/** \brief Sets the current song's volume, independent of the overall music channel volume. The volume scale is 0-100,
* as a linear gain multiplier. This is distinguished from SetMusicVolume which may or may not be linear.
*/
void I_SetCurrentSongVolume(int volume);
// TODO refactor fades to control Song Volume exclusively in tandem with RR musicdef volume multiplier.
boolean I_SetSongTrack(INT32 track);
/// ------------------------

View file

@ -205,9 +205,8 @@ void I_JoyScale4(void);
// Called by D_SRB2Main.
/** \brief to startup a joystick
*/
void I_InitJoystick(UINT8 index);
/// Startup input subsystems.
void I_StartupInput(void);
/** \brief to startup the first joystick
*/

View file

@ -23907,7 +23907,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
16, // mass
0, // damage
sfx_None, // activesound
MF_SOLID|MF_NOCLIP|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags
MF_SOLID|MF_NOCLIP|MF_NOCLIPTHING|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags
S_NULL // raisestate
},

View file

@ -39,7 +39,7 @@ UINT8 numtargets = 0; // Capsules busted
INT32 K_StartingBumperCount(void)
{
if (battleprisons)
return 1; // always 1 hit in Break the Capsules
return 0; // always 1 hit in Prison Break
return cv_kartbumpers.value;
}
@ -95,8 +95,6 @@ void K_CheckBumpers(void)
UINT8 nobumpers = 0;
UINT8 eliminated = 0;
const boolean singleplayer = (battleprisons || bossinfo.valid);
if (!(gametyperules & GTR_BUMPERS))
return;
@ -113,7 +111,7 @@ void K_CheckBumpers(void)
numingame++;
if (players[i].bumpers <= 0) // if you don't have any bumpers, you're probably not a winner
if (!P_MobjWasRemoved(players[i].mo) && players[i].mo->health <= 0) // if you don't have any bumpers, you're probably not a winner
{
nobumpers++;
}
@ -124,7 +122,7 @@ void K_CheckBumpers(void)
}
}
if (singleplayer
if (K_Cooperative()
? nobumpers > 0 && nobumpers >= numingame
: eliminated >= numingame - 1)
{
@ -135,7 +133,7 @@ void K_CheckBumpers(void)
if (players[i].spectator)
continue;
if (singleplayer)
if (K_Cooperative())
players[i].pflags |= PF_NOCONTEST;
P_DoPlayerExit(&players[i]);
@ -170,7 +168,7 @@ void K_CheckEmeralds(player_t *player)
return;
}
player->roundscore++; // lol
player->roundscore = 100; // lmao
for (i = 0; i < MAXPLAYERS; i++)
{
@ -179,11 +177,6 @@ void K_CheckEmeralds(player_t *player)
continue;
}
if (&players[i] == player)
{
continue;
}
P_DoPlayerExit(&players[i]);
}
}
@ -319,6 +312,21 @@ static inline boolean IsOnInterval(tic_t interval)
return ((leveltime - starttime) % interval) == 0;
}
static UINT32 CountEmeraldsSpawned(const mobj_t *mo)
{
switch (mo->type)
{
case MT_EMERALD:
return mo->extravalue1;
case MT_MONITOR:
return Obj_MonitorGetEmerald(mo);
default:
return 0U;
}
}
void K_RunPaperItemSpawners(void)
{
const boolean overtime = (battleovertime.enabled >= 10*TICRATE);
@ -362,7 +370,7 @@ void K_RunPaperItemSpawners(void)
emeraldsSpawned |= players[i].emeralds;
if ((players[i].exiting > 0 || (players[i].pflags & PF_ELIMINATED))
|| ((gametyperules & GTR_BUMPERS) && players[i].bumpers <= 0))
|| ((gametyperules & GTR_BUMPERS) && !P_MobjWasRemoved(players[i].mo) && players[i].mo->health <= 0))
{
continue;
}
@ -382,10 +390,7 @@ void K_RunPaperItemSpawners(void)
mo = (mobj_t *)th;
if (mo->type == MT_EMERALD)
{
emeraldsSpawned |= mo->extravalue1;
}
emeraldsSpawned |= CountEmeraldsSpawned(mo);
}
if (canmakeemeralds)
@ -444,15 +449,7 @@ void K_RunPaperItemSpawners(void)
mo = (mobj_t *)th;
if (mo->type == MT_EMERALD)
{
emeraldsSpawned |= mo->extravalue1;
}
if (mo->type == MT_MONITOR)
{
emeraldsSpawned |= Obj_MonitorGetEmerald(mo);
}
emeraldsSpawned |= CountEmeraldsSpawned(mo);
if (mo->type != MT_PAPERITEMSPOT)
continue;
@ -738,16 +735,20 @@ void K_SetupMovingCapsule(mapthing_t *mt, mobj_t *mobj)
void K_SpawnPlayerBattleBumpers(player_t *p)
{
if (!p->mo || p->bumpers <= 0)
const UINT8 bumpers = K_Bumpers(p);
if (bumpers <= 0)
{
return;
}
{
INT32 i;
angle_t diff = FixedAngle(360*FRACUNIT/p->bumpers);
angle_t diff = FixedAngle(360*FRACUNIT / bumpers);
angle_t newangle = p->mo->angle;
mobj_t *bump;
for (i = 0; i < p->bumpers; i++)
for (i = 0; i < bumpers; i++)
{
bump = P_SpawnMobjFromMobj(p->mo,
P_ReturnThrustX(p->mo, newangle + ANGLE_180, 64*FRACUNIT),
@ -784,21 +785,49 @@ void K_BattleInit(boolean singleplayercontext)
if (gametyperules & GTR_BUMPERS)
{
INT32 maxbumpers = K_StartingBumperCount();
const INT32 startingHealth = K_BumpersToHealth(K_StartingBumperCount());
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i] || players[i].spectator)
continue;
players[i].bumpers = maxbumpers;
if (players[i].mo)
{
players[i].mo->health = maxbumpers;
players[i].mo->health = startingHealth;
}
K_SpawnPlayerBattleBumpers(players+i);
}
}
}
UINT8 K_Bumpers(player_t *player)
{
if ((gametyperules & GTR_BUMPERS) == 0)
{
return 0;
}
if (P_MobjWasRemoved(player->mo))
{
return 0;
}
if (player->mo->health < 1)
{
return 0;
}
if (player->mo->health > UINT8_MAX)
{
return UINT8_MAX;
}
return (player->mo->health - 1);
}
INT32 K_BumpersToHealth(UINT8 bumpers)
{
return (bumpers + 1);
}

View file

@ -38,6 +38,8 @@ void K_RunBattleOvertime(void);
void K_SetupMovingCapsule(mapthing_t *mt, mobj_t *mobj);
void K_SpawnPlayerBattleBumpers(player_t *p);
void K_BattleInit(boolean singleplayercontext);
UINT8 K_Bumpers(player_t *player);
INT32 K_BumpersToHealth(UINT8 bumpers);
#ifdef __cplusplus
} // extern "C"

View file

@ -43,9 +43,6 @@ boolean K_BananaBallhogCollide(mobj_t *t1, mobj_t *t2)
{
boolean damageitem = false;
if ((t1->threshold > 0 && t2->hitlag > 0) || (t2->threshold > 0 && t1->hitlag > 0))
return true;
if (((t1->target == t2) || (!(t2->flags & (MF_ENEMY|MF_BOSS)) && (t1->target == t2->target))) && (t1->threshold > 0 || (t2->type != MT_PLAYER && t2->threshold > 0)))
return true;
@ -133,9 +130,6 @@ boolean K_BananaBallhogCollide(mobj_t *t1, mobj_t *t2)
boolean K_EggItemCollide(mobj_t *t1, mobj_t *t2)
{
if ((t1->threshold > 0 && t2->hitlag > 0) || (t2->threshold > 0 && t1->hitlag > 0))
return true;
// Push fakes out of other item boxes
if (t2->type == MT_RANDOMITEM || t2->type == MT_EGGMANITEM)
{
@ -154,14 +148,7 @@ boolean K_EggItemCollide(mobj_t *t1, mobj_t *t2)
if (!P_CanPickupItem(t2->player, 2))
return true;
if ((gametyperules & GTR_BUMPERS) && t2->player->bumpers <= 0)
{
return true;
}
else
{
K_StartEggmanRoulette(t2->player);
}
K_StartEggmanRoulette(t2->player);
if (t2->player->flamedash && t2->player->itemtype == KITEM_FLAMESHIELD)
{
@ -182,10 +169,7 @@ boolean K_EggItemCollide(mobj_t *t1, mobj_t *t2)
if (t1->target && t1->target->player)
{
if ((gametyperules & GTR_CIRCUIT) || t1->target->player->bumpers > 0)
t2->player->eggmanblame = t1->target->player-players;
else
t2->player->eggmanblame = t2->player-players;
t2->player->eggmanblame = t1->target->player - players;
if (t1->target->hnext == t1)
{
@ -323,6 +307,10 @@ tic_t K_MineExplodeAttack(mobj_t *actor, fixed_t size, boolean spin)
// Set this flag to ensure that the inital action won't be triggered twice.
actor->flags2 |= MF2_DEBRIS;
// Set this flag to ensure the hitbox timer doesn't get extended with every player hit
actor->flags |= MF_NOHITLAGFORME;
actor->hitlag = 0; // same deal
if (!spin)
{
if (minehitlag == 0)
@ -340,9 +328,6 @@ tic_t K_MineExplodeAttack(mobj_t *actor, fixed_t size, boolean spin)
boolean K_MineCollide(mobj_t *t1, mobj_t *t2)
{
if ((t1->threshold > 0 && t2->hitlag > 0) || (t2->threshold > 0 && t1->hitlag > 0))
return true;
if (((t1->target == t2) || (!(t2->flags & (MF_ENEMY|MF_BOSS)) && (t1->target == t2->target))) && (t1->threshold > 0 || (t2->type != MT_PLAYER && t2->threshold > 0)))
return true;
@ -394,9 +379,6 @@ boolean K_MineCollide(mobj_t *t1, mobj_t *t2)
boolean K_LandMineCollide(mobj_t *t1, mobj_t *t2)
{
if ((t1->threshold > 0 && t2->hitlag > 0) || (t2->threshold > 0 && t1->hitlag > 0))
return true;
if (((t1->target == t2) || (!(t2->flags & (MF_ENEMY|MF_BOSS)) && (t1->target == t2->target))) && (t1->threshold > 0 || (t2->type != MT_PLAYER && t2->threshold > 0)))
return true;
@ -483,9 +465,6 @@ boolean K_DropTargetCollide(mobj_t *t1, mobj_t *t2)
{
mobj_t *draggeddroptarget = (t1->type == MT_DROPTARGET_SHIELD) ? t1->target : NULL;
if ((t1->threshold > 0 && t2->hitlag > 0) || (t2->threshold > 0 && t1->hitlag > 0))
return true;
if (((t1->target == t2) || (t1->target == t2->target)) && ((t1->threshold > 0 && t2->type == MT_PLAYER) || (t2->type != MT_PLAYER && t2->threshold > 0)))
return true;
@ -696,6 +675,17 @@ void K_LightningShieldAttack(mobj_t *actor, fixed_t size)
boolean K_BubbleShieldCollide(mobj_t *t1, mobj_t *t2)
{
if (t1->type == MT_PLAYER)
{
// Bubble Shield already has a hitbox, and it gets
// teleported every tic so the Bubble itself will
// always make contact with other objects.
//
// Therefore, we don't need a second, smaller hitbox
// on the player. It'll just cause unwanted hitlag.
return true;
}
if (t2->type == MT_PLAYER)
{
// Counter desyncs
@ -715,8 +705,13 @@ boolean K_BubbleShieldCollide(mobj_t *t1, mobj_t *t2)
}
// Player Damage
P_DamageMobj(t2, ((t1->type == MT_BUBBLESHIELD) ? t1->target : t1), t1, 1, DMG_NORMAL|DMG_WOMBO);
S_StartSound(t1, sfx_s3k44);
P_DamageMobj(t2, t1->target, t1, 1, DMG_NORMAL|DMG_WOMBO);
if (t2->player->timeshit > t2->player->timeshitprev)
{
// Don't play from t1 else it gets cut out... for some reason.
S_StartSound(t2, sfx_s3k44);
}
}
else
{
@ -746,9 +741,6 @@ boolean K_BubbleShieldCollide(mobj_t *t1, mobj_t *t2)
boolean K_KitchenSinkCollide(mobj_t *t1, mobj_t *t2)
{
if ((t1->threshold > 0 && t2->hitlag > 0) || (t2->threshold > 0 && t1->hitlag > 0))
return true;
if (((t1->target == t2) || (!(t2->flags & (MF_ENEMY|MF_BOSS)) && (t1->target == t2->target))) && (t1->threshold > 0 || (t2->type != MT_PLAYER && t2->threshold > 0)))
return true;

View file

@ -1995,10 +1995,12 @@ static boolean K_drawKartPositionFaces(void)
if (LUA_HudEnabled(hud_battlebumpers))
{
if ((gametyperules & GTR_BUMPERS) && players[rankplayer[i]].bumpers > 0)
const UINT8 bumpers = K_Bumpers(&players[rankplayer[i]]);
if (bumpers > 0)
{
V_DrawMappedPatch(bumperx-2, Y, V_HUDTRANS|V_SLIDEIN|V_SNAPTOLEFT, kp_tinybumper[0], colormap);
for (j = 1; j < players[rankplayer[i]].bumpers; j++)
for (j = 1; j < bumpers; j++)
{
bumperx += 5;
V_DrawMappedPatch(bumperx, Y, V_HUDTRANS|V_SLIDEIN|V_SNAPTOLEFT, kp_tinybumper[1], colormap);
@ -2023,7 +2025,7 @@ static boolean K_drawKartPositionFaces(void)
if (i == strank)
V_DrawScaledPatch(FACE_X, Y, V_HUDTRANS|V_SLIDEIN|V_SNAPTOLEFT, kp_facehighlight[(leveltime / 4) % 8]);
if ((gametyperules & GTR_BUMPERS) && players[rankplayer[i]].bumpers <= 0)
if ((gametyperules & GTR_BUMPERS) && (players[rankplayer[i]].pflags & PF_ELIMINATED))
V_DrawScaledPatch(FACE_X-4, Y-3, V_HUDTRANS|V_SLIDEIN|V_SNAPTOLEFT, kp_ranknobumpers);
else
{
@ -2356,7 +2358,7 @@ void K_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, IN
if (tab[i].num == whiteplayer)
V_DrawScaledPatch(x, y-4, 0, kp_facehighlight[(leveltime / 4) % 8]);
if ((gametyperules & GTR_BUMPERS) && players[tab[i].num].bumpers <= 0)
if ((gametyperules & GTR_BUMPERS) && (players[tab[i].num].pflags & PF_ELIMINATED))
V_DrawScaledPatch(x-4, y-7, 0, kp_ranknobumpers);
else
{
@ -2866,14 +2868,16 @@ static void K_drawKartBumpersOrKarma(void)
}
else
{
INT32 maxbumper = K_StartingBumperCount();
const INT32 maxbumper = K_StartingBumperCount();
const UINT8 bumpers = K_Bumpers(stplyr);
V_DrawMappedPatch(fx+1, fy-2, V_HUDTRANS|V_SLIDEIN|splitflags, kp_rankbumper, colormap);
if (stplyr->bumpers > 9 || maxbumper > 9)
if (bumpers > 9 || maxbumper > 9)
{
UINT8 ln[2];
ln[0] = (stplyr->bumpers / 10 % 10);
ln[1] = (stplyr->bumpers % 10);
ln[0] = (bumpers / 10 % 10);
ln[1] = (bumpers % 10);
V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|V_SLIDEIN|splitflags, fontv[PINGNUM_FONT].font[ln[0]]);
V_DrawScaledPatch(fx+17, fy, V_HUDTRANS|V_SLIDEIN|splitflags, fontv[PINGNUM_FONT].font[ln[1]]);
@ -2886,7 +2890,7 @@ static void K_drawKartBumpersOrKarma(void)
}
else
{
V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|V_SLIDEIN|splitflags, kp_facenum[(stplyr->bumpers) % 10]);
V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|V_SLIDEIN|splitflags, kp_facenum[(bumpers) % 10]);
V_DrawScaledPatch(fx+27, fy, V_HUDTRANS|V_SLIDEIN|splitflags, kp_facenum[(maxbumper) % 10]);
}
}
@ -2903,17 +2907,15 @@ static void K_drawKartBumpersOrKarma(void)
}
else
{
INT32 maxbumper = K_StartingBumperCount();
const INT32 maxbumper = K_StartingBumperCount();
const UINT8 bumpers = K_Bumpers(stplyr);
if (stplyr->bumpers > 9 && maxbumper > 9)
if (bumpers > 9 && maxbumper > 9)
V_DrawMappedPatch(LAPS_X, LAPS_Y, V_HUDTRANS|V_SLIDEIN|splitflags, kp_bumperstickerwide, colormap);
else
V_DrawMappedPatch(LAPS_X, LAPS_Y, V_HUDTRANS|V_SLIDEIN|splitflags, kp_bumpersticker, colormap);
if (gametyperules & GTR_KARMA) // TODO BETTER HUD
V_DrawKartString(LAPS_X+47, LAPS_Y+3, V_HUDTRANS|V_SLIDEIN|splitflags, va("%d/%d %d", stplyr->bumpers, maxbumper, stplyr->overtimekarma / TICRATE));
else
V_DrawKartString(LAPS_X+47, LAPS_Y+3, V_HUDTRANS|V_SLIDEIN|splitflags, va("%d/%d", stplyr->bumpers, maxbumper));
V_DrawKartString(LAPS_X+47, LAPS_Y+3, V_HUDTRANS|V_SLIDEIN|splitflags, va("%d/%d", bumpers, maxbumper));
}
}
}
@ -3366,12 +3368,6 @@ static void K_drawKartNameTags(void)
continue;
}
if ((gametyperules & GTR_BUMPERS) && (ntplayer->bumpers <= 0))
{
// Dead in Battle
continue;
}
if (!P_CheckSight(stplyr->mo, ntplayer->mo))
{
// Can't see
@ -3644,6 +3640,10 @@ static void K_drawKartMinimap(void)
if (!players[i].mo || players[i].spectator || !players[i].mo->skin || players[i].exiting)
continue;
// This player is out of the game!
if ((gametyperules & GTR_BUMPERS) && (players[i].pflags & PF_ELIMINATED))
continue;
if (i == displayplayers[0] || i == displayplayers[1] || i == displayplayers[2] || i == displayplayers[3])
{
// Draw display players on top of everything else
@ -3651,10 +3651,6 @@ static void K_drawKartMinimap(void)
continue;
}
// Now we know it's not a display player, handle non-local player exceptions.
if ((gametyperules & GTR_BUMPERS) && players[i].bumpers <= 0)
continue;
if (players[i].hyudorotimer > 0)
{
if (!((players[i].hyudorotimer < TICRATE/2
@ -3866,19 +3862,22 @@ static void K_drawKartFinish(boolean finish)
//else -- 1/2p, scrolling FINISH
{
INT32 x, xval, ox, interpx;
INT32 x, xval, ox, interpx, pwidth;
x = ((vid.width<<FRACBITS)/vid.dupx);
xval = (SHORT(kptodraw[pnum]->width)<<FRACBITS);
x = ((TICRATE - timer)*(xval > x ? xval : x))/TICRATE;
ox = ((TICRATE - (timer - 1))*(xval > x ? xval : x))/TICRATE;
pwidth = max(xval, x);
x = ((TICRATE - timer) * pwidth) / TICRATE;
ox = ((TICRATE - (timer - 1)) * pwidth) / TICRATE;
interpx = R_InterpolateFixed(ox, x);
if (r_splitscreen && stplyr == &players[displayplayers[1]])
interpx = -interpx;
V_DrawFixedPatch(interpx + (STCD_X<<FRACBITS) - (xval>>1),
V_DrawFixedPatch(interpx + (STCD_X<<FRACBITS) - (pwidth / 2),
(STCD_Y<<FRACBITS) - (SHORT(kptodraw[pnum]->height)<<(FRACBITS-1)),
FRACUNIT,
splitflags, kptodraw[pnum], NULL);
@ -4160,7 +4159,7 @@ static void K_drawBattleFullscreen(void)
K_drawKartFinish(true);
}
else if (stplyr->bumpers <= 0 && stplyr->karmadelay && !stplyr->spectator && drawcomebacktimer)
else if (stplyr->karmadelay && !stplyr->spectator && drawcomebacktimer)
{
UINT16 t = stplyr->karmadelay/(10*TICRATE);
INT32 txoff, adjust = (r_splitscreen > 1) ? 4 : 6; // normal string is 8, kart string is 12, half of that for ease
@ -4894,8 +4893,7 @@ void K_drawKartHUD(void)
battlefullscreen = (!(gametyperules & GTR_CIRCUIT)
&& (stplyr->exiting
|| ((gametyperules & GTR_BUMPERS) && (stplyr->bumpers <= 0)
&& ((gametyperules & GTR_KARMA) && (stplyr->karmadelay > 0))
|| (((gametyperules & GTR_KARMA) && (stplyr->karmadelay > 0))
&& !(stplyr->pflags & PF_ELIMINATED)
&& stplyr->playerstate == PST_LIVE)));

View file

@ -5,8 +5,8 @@
#include "core/static_vec.hpp"
#include "k_battle.h"
#include "k_boss.h"
#include "k_hud.h"
#include "k_kart.h"
#include "k_objects.h"
#include "m_fixed.h"
#include "p_local.h"
@ -314,7 +314,7 @@ bool is_player_tracking_target(player_t *player = stplyr)
return false;
}
if (battleprisons || bossinfo.valid)
if (K_Cooperative())
{
return false;
}
@ -338,6 +338,23 @@ bool is_player_tracking_target(player_t *player = stplyr)
return false;
}
if (player->emeralds != 0 && K_IsPlayerWanted(stplyr))
{
// The player who is about to win because of emeralds
// gets a TARGET on them
if (K_NumEmeralds(player) == 6) // 6 out of 7
{
return true;
}
// WANTED player sees TARGETs on players holding
// emeralds
if (K_IsPlayerWanted(stplyr))
{
return true;
}
}
return K_IsPlayerWanted(player);
}

View file

@ -374,9 +374,6 @@ boolean K_IsPlayerLosing(player_t *player)
if (battleprisons && numtargets == 0)
return true; // Didn't even TRY?
if (battleprisons || (gametyperules & GTR_BOSS))
return (player->bumpers <= 0); // anything short of DNF is COOL
if (player->position == 1)
return false;
@ -3269,11 +3266,6 @@ fixed_t K_GetKartSpeed(player_t *player, boolean doboostpower, boolean dorubberb
{
finalspeed = K_GetKartSpeedFromStat(player->kartspeed);
if (gametyperules & GTR_BUMPERS && player->bumpers <= 0)
{
finalspeed = 3 * finalspeed / 2;
}
if (player->spheres > 0)
{
fixed_t sphereAdd = (FRACUNIT/40); // 100% at max
@ -3333,12 +3325,6 @@ fixed_t K_GetKartAccel(player_t *player)
return FixedMul(k_accel, FRACUNIT / 4);
}
// karma bomb gets 2x acceleration
if ((gametyperules & GTR_BUMPERS) && player->bumpers <= 0)
{
k_accel *= 2;
}
// Marble Garden Top gets 1200% accel
if (player->curshield == KSHIELD_TOP)
{
@ -3654,7 +3640,7 @@ void K_DoPowerClash(player_t *t1, player_t *t2) {
P_SetScale(clash, 3*clash->destscale/2);
}
void K_BattleAwardHit(player_t *player, player_t *victim, mobj_t *inflictor, UINT8 bumpersRemoved)
void K_BattleAwardHit(player_t *player, player_t *victim, mobj_t *inflictor, UINT8 damage)
{
UINT8 points = 1;
boolean trapItem = false;
@ -3692,7 +3678,7 @@ void K_BattleAwardHit(player_t *player, player_t *victim, mobj_t *inflictor, UIN
}
else if (gametyperules & GTR_BUMPERS)
{
if ((victim->bumpers > 0) && (victim->bumpers <= bumpersRemoved))
if ((victim->mo->health > 0) && (victim->mo->health <= damage))
{
// +2 points for finishing off a player
points = 2;
@ -4071,10 +4057,14 @@ void K_UpdateStumbleIndicator(player_t *player)
}
}
#define MIN_WAVEDASH_CHARGE (5*TICRATE/8)
static boolean K_IsLosingSliptideZip(player_t *player)
{
if (player->mo == NULL || P_MobjWasRemoved(player->mo) == true)
return true;
if (!K_Sliptiding(player) && player->sliptideZip < MIN_WAVEDASH_CHARGE)
return true;
if (!K_Sliptiding(player) && player->drift == 0
&& P_IsObjectOnGround(player->mo) && player->sneakertimer == 0
&& player->driftboost == 0)
@ -4341,81 +4331,22 @@ void K_DebtStingPlayer(player_t *player, mobj_t *source)
P_SetPlayerMobjState(player->mo, S_KART_SPINOUT);
}
void K_HandleBumperChanges(player_t *player, UINT8 prevBumpers)
void K_TakeBumpersFromPlayer(player_t *player, player_t *victim, UINT8 amount)
{
if (!(gametyperules & GTR_BUMPERS))
{
// Bumpers aren't being used
return;
}
// TODO: replace all console text print-outs with a real visual
if (player->bumpers > 0 && prevBumpers == 0)
{
K_DoInvincibility(player, 8 * TICRATE);
if (netgame)
{
CONS_Printf(M_GetText("%s is back in the game!\n"), player_names[player-players]);
}
}
else if (player->bumpers == 0 && prevBumpers > 0)
{
if (battleprisons || bossinfo.valid)
{
player->pflags |= (PF_NOCONTEST|PF_ELIMINATED);
}
}
K_CalculateBattleWanted();
K_CheckBumpers();
}
UINT8 K_DestroyBumpers(player_t *player, UINT8 amount)
{
UINT8 oldBumpers = player->bumpers;
if (!(gametyperules & GTR_BUMPERS))
{
return 0;
}
amount = min(amount, player->bumpers);
if (amount == 0)
{
return 0;
}
player->bumpers -= amount;
K_HandleBumperChanges(player, oldBumpers);
return amount;
}
UINT8 K_TakeBumpersFromPlayer(player_t *player, player_t *victim, UINT8 amount)
{
UINT8 oldPlayerBumpers = player->bumpers;
UINT8 oldVictimBumpers = victim->bumpers;
const UINT8 oldPlayerBumpers = K_Bumpers(player);
UINT8 tookBumpers = 0;
if (!(gametyperules & GTR_BUMPERS))
{
return 0;
}
amount = min(amount, victim->bumpers);
amount = min(amount, K_Bumpers(victim));
if (amount == 0)
{
return 0;
return;
}
while ((tookBumpers < amount) && (victim->bumpers > 0))
while (tookBumpers < amount)
{
UINT8 newbumper = player->bumpers;
const UINT8 newbumper = (oldPlayerBumpers + tookBumpers);
angle_t newangle, diff;
fixed_t newx, newy;
@ -4457,24 +4388,14 @@ UINT8 K_TakeBumpersFromPlayer(player_t *player, player_t *victim, UINT8 amount)
P_SetMobjState(newmo, S_BATTLEBUMPER1);
}
player->bumpers++;
victim->bumpers--;
tookBumpers++;
}
if (tookBumpers == 0)
{
// No change occured.
return 0;
}
// :jartcookiedance:
player->mo->health += tookBumpers;
// Play steal sound
S_StartSound(player->mo, sfx_3db06);
K_HandleBumperChanges(player, oldPlayerBumpers);
K_HandleBumperChanges(victim, oldVictimBumpers);
return tookBumpers;
}
#define MINEQUAKEDIST 4096
@ -5138,8 +5059,7 @@ void K_SpawnBoostTrail(player_t *player)
I_Assert(!P_MobjWasRemoved(player->mo));
if (!P_IsObjectOnGround(player->mo)
|| player->hyudorotimer != 0
|| ((gametyperules & GTR_BUMPERS) && player->bumpers <= 0 && player->karmadelay))
|| player->hyudorotimer != 0)
return;
if (player->mo->eflags & MFE_VERTICALFLIP)
@ -7098,12 +7018,6 @@ mobj_t *K_FindJawzTarget(mobj_t *actor, player_t *source, angle_t range)
}
else
{
if (player->bumpers <= 0)
{
// Don't pay attention to dead players
continue;
}
// Z pos too high/low
if (abs(player->mo->z - (actor->z + actor->momz)) > FixedMul(RING_DIST/8, mapobjectscale))
{
@ -7809,13 +7723,6 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
player->spheres = 40;
// where's the < 0 check? see below the following block!
if ((gametyperules & GTR_BUMPERS) && (player->bumpers <= 0))
{
// Deplete 1 every tic when removed from the game.
player->spheres--;
player->spheredigestion = 0;
}
else
{
tic_t spheredigestion = TICRATE; // Base rate of 1 every second when playing.
tic_t digestionpower = ((10 - player->kartspeed) + (10 - player->kartweight))-1; // 1 to 17
@ -7860,12 +7767,12 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
if (!(gametyperules & GTR_KARMA) || (player->pflags & PF_ELIMINATED))
{
player->karmadelay = comebacktime;
player->karmadelay = 0;
}
else if (player->karmadelay > 0 && !P_PlayerInPain(player))
{
player->karmadelay--;
if (P_IsDisplayPlayer(player) && player->bumpers <= 0 && player->karmadelay <= 0)
if (P_IsDisplayPlayer(player) && player->karmadelay <= 0)
comebackshowninfo = true; // client has already seen the message
}
@ -8029,14 +7936,6 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
K_UpdateTripwire(player);
if (battleovertime.enabled && !(player->pflags & PF_ELIMINATED) && player->bumpers <= 0 && player->karmadelay <= 0)
{
if (player->overtimekarma)
player->overtimekarma--;
else
P_DamageMobj(player->mo, NULL, NULL, 1, DMG_TIMEOVER);
}
if ((battleovertime.enabled >= 10*TICRATE) && !(player->pflags & PF_ELIMINATED) && !player->exiting)
{
fixed_t distanceToBarrier = 0;
@ -8066,7 +7965,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
if (player->eggmanexplode)
{
if (player->spectator || ((gametyperules & GTR_BUMPERS) && player->bumpers <= 0))
if (player->spectator)
player->eggmanexplode = 0;
else
{
@ -8407,30 +8306,14 @@ void K_KartPlayerAfterThink(player_t *player)
K_LookForRings(player->mo);
}
if (player->invulnhitlag > 0)
if (player->nullHitlag > 0)
{
// Hitlag from what would normally be damage but the
// player was invulnerable.
//
// If we're constantly getting hit the same number of
// times, we're probably standing on a damage floor.
//
// Checking if we're hit more than before ensures
// that:
//
// 1) repeating damage doesn't count
// 2) new damage sources still count
if (player->timeshit <= player->timeshitprev)
if (!P_MobjWasRemoved(player->mo))
{
if (!P_MobjWasRemoved(player->mo))
{
player->mo->hitlag -= player->invulnhitlag;
player->mo->eflags &= ~(MFE_DAMAGEHITLAG);
}
player->mo->hitlag -= player->nullHitlag;
}
player->invulnhitlag = 0;
player->nullHitlag = 0;
}
}
@ -9405,6 +9288,8 @@ static void K_KartDrift(player_t *player, boolean onground)
K_SpawnAIZDust(player);
player->sliptideZip++;
if (player->sliptideZip == MIN_WAVEDASH_CHARGE)
S_StartSound(player->mo, sfx_waved5);
if (abs(player->aizdrifttilt) < ANGLE_22h)
{
@ -9432,35 +9317,38 @@ static void K_KartDrift(player_t *player, boolean onground)
player->sliptideZipDelay++;
if (player->sliptideZipDelay > TICRATE/2)
{
fixed_t maxZipPower = 2*FRACUNIT;
fixed_t minZipPower = 1*FRACUNIT;
fixed_t powerSpread = maxZipPower - minZipPower;
if (player->sliptideZip >= MIN_WAVEDASH_CHARGE)
{
fixed_t maxZipPower = 2*FRACUNIT;
fixed_t minZipPower = 1*FRACUNIT;
fixed_t powerSpread = maxZipPower - minZipPower;
int minPenalty = 2*1 + (9-9); // Kinda doing a similar thing to driftspark stage timers here.
int maxPenalty = 2*9 + (9-1); // 1/9 gets max, 9/1 gets min, everyone else gets something in between.
int penaltySpread = maxPenalty - minPenalty;
int yourPenalty = 2*player->kartspeed + (9 - player->kartweight); // And like driftsparks, speed hurts double.
int minPenalty = 2*1 + (9-9); // Kinda doing a similar thing to driftspark stage timers here.
int maxPenalty = 2*9 + (9-1); // 1/9 gets max, 9/1 gets min, everyone else gets something in between.
int penaltySpread = maxPenalty - minPenalty;
int yourPenalty = 2*player->kartspeed + (9 - player->kartweight); // And like driftsparks, speed hurts double.
yourPenalty -= minPenalty; // Normalize; minimum penalty should take away 0 power.
yourPenalty -= minPenalty; // Normalize; minimum penalty should take away 0 power.
fixed_t yourPowerReduction = FixedDiv(yourPenalty * FRACUNIT, penaltySpread * FRACUNIT);
fixed_t yourPower = maxZipPower - FixedMul(yourPowerReduction, powerSpread);
int yourBoost = FixedInt(FixedMul(yourPower, player->sliptideZip * FRACUNIT));
fixed_t yourPowerReduction = FixedDiv(yourPenalty * FRACUNIT, penaltySpread * FRACUNIT);
fixed_t yourPower = maxZipPower - FixedMul(yourPowerReduction, powerSpread);
int yourBoost = FixedInt(FixedMul(yourPower, player->sliptideZip * FRACUNIT));
/*
CONS_Printf("SZ %d MZ %d mZ %d pwS %d mP %d MP %d peS %d yPe %d yPR %d yPw %d yB %d\n",
player->sliptideZip, maxZipPower, minZipPower, powerSpread, minPenalty, maxPenalty, penaltySpread, yourPenalty, yourPowerReduction, yourPower, yourBoost);
*/
/*
CONS_Printf("SZ %d MZ %d mZ %d pwS %d mP %d MP %d peS %d yPe %d yPR %d yPw %d yB %d\n",
player->sliptideZip, maxZipPower, minZipPower, powerSpread, minPenalty, maxPenalty, penaltySpread, yourPenalty, yourPowerReduction, yourPower, yourBoost);
*/
player->sliptideZipBoost += yourBoost;
player->sliptideZipBoost += yourBoost;
K_SpawnDriftBoostExplosion(player, 0);
player->sliptideZip = 0;
player->sliptideZipDelay = 0;
K_SpawnDriftBoostExplosion(player, 0);
S_StartSoundAtVolume(player->mo, sfx_waved3, 2*255/3); // Boost
}
S_StopSoundByID(player->mo, sfx_waved1);
S_StopSoundByID(player->mo, sfx_waved2);
S_StopSoundByID(player->mo, sfx_waved4);
S_StartSoundAtVolume(player->mo, sfx_waved3, 2*255/3); // Boost
player->sliptideZip = 0;
player->sliptideZipDelay = 0;
}
}
else
@ -9586,7 +9474,7 @@ void K_KartUpdatePosition(player_t *player)
else if (yourEmeralds == myEmeralds)
{
// Bumpers are the second tier tie breaker
if (players[i].bumpers > player->bumpers)
if (K_Bumpers(&players[i]) > K_Bumpers(player))
{
position++;
}
@ -11210,18 +11098,6 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
player->mo->renderflags &= ~RF_DONTDRAW;
}
if (!(gametyperules & GTR_BUMPERS) || player->bumpers > 0)
{
player->mo->renderflags &= ~(RF_TRANSMASK|RF_BRIGHTMASK);
}
else // dead in match? you da bomb
{
K_DropItems(player); //K_StripItems(player);
K_StripOther(player);
player->mo->renderflags |= RF_GHOSTLY;
player->flashing = player->karmadelay;
}
if (player->trickpanel == 1)
{
const angle_t lr = ANGLE_45;
@ -11469,8 +11345,9 @@ void K_CheckSpectateStatus(void)
P_SpectatorJoinGame(&players[respawnlist[i]]);
}
// Reset the match if you're in an empty server
if (!mapreset && gamestate == GS_LEVEL && leveltime >= starttime && (numingame < 2 && numingame+i >= 2)) // use previous i value
// Reset the match when 2P joins 1P, DUEL mode
// Reset the match when 3P joins 1P and 2P, DUEL mode must be disabled
if (!mapreset && gamestate == GS_LEVEL && (numingame < 3 && numingame+i >= 2)) // use previous i value
{
S_ChangeMusicInternal("chalng", false); // COME ON
mapreset = 3*TICRATE; // Even though only the server uses this for game logic, set for everyone for HUD
@ -11720,7 +11597,7 @@ UINT32 K_PointLimitForGametype(void)
return cv_pointlimit.value;
}
if (battleprisons || bossinfo.valid)
if (K_Cooperative())
{
return 0;
}
@ -11744,4 +11621,19 @@ UINT32 K_PointLimitForGametype(void)
return ptsCap;
}
boolean K_Cooperative(void)
{
if (battleprisons)
{
return true;
}
if (bossinfo.valid)
{
return true;
}
return false;
}
//}

View file

@ -104,9 +104,7 @@ void K_UpdateStumbleIndicator(player_t *player);
void K_UpdateSliptideZipIndicator(player_t *player);
INT32 K_ExplodePlayer(player_t *player, mobj_t *inflictor, mobj_t *source);
void K_DebtStingPlayer(player_t *player, mobj_t *source);
void K_HandleBumperChanges(player_t *player, UINT8 prevBumpers);
UINT8 K_DestroyBumpers(player_t *player, UINT8 amount);
UINT8 K_TakeBumpersFromPlayer(player_t *player, player_t *victim, UINT8 amount);
void K_TakeBumpersFromPlayer(player_t *player, player_t *victim, UINT8 amount);
void K_MineFlashScreen(mobj_t *source);
void K_SpawnMineExplosion(mobj_t *source, UINT8 color, tic_t delay);
void K_RunFinishLineBeam(void);
@ -210,6 +208,8 @@ void K_EggmanTransfer(player_t *source, player_t *victim);
tic_t K_TimeLimitForGametype(void);
UINT32 K_PointLimitForGametype(void);
boolean K_Cooperative(void);
#ifdef __cplusplus
} // extern "C"
#endif

View file

@ -60,13 +60,6 @@
// And just some randomness for the exits.
#include "m_random.h"
#if defined(HAVE_SDL)
#include "SDL.h"
#if SDL_VERSION_ATLEAST(2,0,0)
#include "sdl/sdlmain.h" // JOYSTICK_HOTPLUG
#endif
#endif
#ifdef PC_DOS
#include <stdio.h> // for snprintf
int snprintf(char *str, size_t n, const char *fmt, ...);
@ -3500,11 +3493,18 @@ void M_DrawProfileControls(void)
// Get userbound controls...
for (k = 0; k < MAXINPUTMAPPING; k++)
{
int device;
keys[k] = optionsmenu.tempcontrols[gc][k];
if (keys[k] == KEY_NULL)
continue;
set++;
if (!G_KeyIsAvailable(keys[k], cv_usejoystick[0].value))
device = G_GetDeviceForPlayer(0);
if (device == -1)
{
device = 0;
}
if (!G_KeyIsAvailable(keys[k], device))
continue;
available++;
};

View file

@ -240,6 +240,22 @@ boolean M_Responder(event_t *ev)
return false;
}
if (gamestate == GS_MENU && ev->type == ev_gamepad_device_removed && G_GetPlayerForDevice(ev->device) != -1)
{
int i;
INT32 player = G_GetPlayerForDevice(ev->device);
// Unassign all controllers
for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
{
G_SetDeviceForPlayer(i, -1);
}
// Return to the title because a controller was removed at the menu.
CONS_Alert(CONS_NOTICE, "Player %d's assigned gamepad was removed. Returning to the title screen.", player);
D_StartTitle();
}
if (ev->type == ev_keydown && ev->data1 < NUMKEYS)
{
// Record keyboard presses

View file

@ -232,8 +232,8 @@ static int player_get(lua_State *L)
lua_pushinteger(L, plr->spinouttimer);
else if (fastcmp(field,"instashield"))
lua_pushinteger(L, plr->instashield);
else if (fastcmp(field,"invulnhitlag"))
lua_pushinteger(L, plr->invulnhitlag);
else if (fastcmp(field,"nullHitlag"))
lua_pushinteger(L, plr->nullHitlag);
else if (fastcmp(field,"wipeoutslow"))
lua_pushinteger(L, plr->wipeoutslow);
else if (fastcmp(field,"justbumped"))
@ -398,8 +398,6 @@ static int player_get(lua_State *L)
plr->roundscore = luaL_checkinteger(L, 3);
else if (fastcmp(field,"emeralds"))
lua_pushinteger(L, plr->emeralds);
else if (fastcmp(field,"bumpers"))
lua_pushinteger(L, plr->bumpers);
else if (fastcmp(field,"karmadelay"))
lua_pushinteger(L, plr->karmadelay);
else if (fastcmp(field,"spheres"))
@ -616,8 +614,8 @@ static int player_set(lua_State *L)
plr->spinouttimer = luaL_checkinteger(L, 3);
else if (fastcmp(field,"instashield"))
plr->instashield = luaL_checkinteger(L, 3);
else if (fastcmp(field,"invulnhitlag"))
plr->invulnhitlag = luaL_checkinteger(L, 3);
else if (fastcmp(field,"nullHitlag"))
plr->nullHitlag = luaL_checkinteger(L, 3);
else if (fastcmp(field,"wipeoutslow"))
plr->wipeoutslow = luaL_checkinteger(L, 3);
else if (fastcmp(field,"justbumped"))
@ -782,8 +780,6 @@ static int player_set(lua_State *L)
lua_pushinteger(L, plr->roundscore);
else if (fastcmp(field,"emeralds"))
plr->emeralds = luaL_checkinteger(L, 3);
else if (fastcmp(field,"bumpers"))
plr->bumpers = luaL_checkinteger(L, 3);
else if (fastcmp(field,"karmadelay"))
plr->karmadelay = luaL_checkinteger(L, 3);
else if (fastcmp(field,"spheres"))

View file

@ -123,7 +123,7 @@ static void SetDeviceOnPress(void)
{
if (deviceResponding[i])
{
CV_SetValue(&cv_usejoystick[0], i); // Force-set this joystick as the current joystick we're using for P1 (which is the only one controlling menus)
G_SetDeviceForPlayer(0, i); // Force-set this joystick as the current joystick we're using for P1 (which is the only one controlling menus)
CONS_Printf("SetDeviceOnPress: Device for %d set to %d\n", 0, i);
return;
}
@ -307,7 +307,7 @@ void M_MapProfileControl(event_t *ev)
UINT8 where = n; // By default, we'll save the bind where we're supposed to map.
INT32 i;
//SetDeviceOnPress(); // Update cv_usejoystick
//SetDeviceOnPress(); // Update player gamepad assignments
// Only consider keydown and joystick events to make sure we ignore ev_mouse and other events
// See also G_MapEventsToControls
@ -325,11 +325,11 @@ void M_MapProfileControl(event_t *ev)
}
#endif
break;
case ev_joystick:
case ev_gamepad_axis:
if (ev->data1 >= JOYAXES)
{
#ifdef PARANOIA
CONS_Debug(DBG_GAMELOGIC, "Bad joystick axis event %d\n", ev->data1);
CONS_Debug(DBG_GAMELOGIC, "Bad gamepad axis event %d\n", ev->data1);
#endif
return;
}

View file

@ -381,10 +381,10 @@ void M_CharacterSelectInit(void)
{
for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
{
// Un-set devices for other players.
if (i != 0 || optionsmenu.profile)
// Un-set devices for all players if not editing profile
if (!optionsmenu.profile)
{
CV_SetValue(&cv_usejoystick[i], -1);
G_SetDeviceForPlayer(i, -1);
CONS_Printf("M_CharacterSelectInit: Device for %d set to %d\n", i, -1);
}
}
@ -527,7 +527,12 @@ static boolean M_DeviceAvailable(INT32 deviceID, UINT8 numPlayers)
for (i = 0; i < numPlayers; i++)
{
if (cv_usejoystick[i].value == deviceID)
int player_device = G_GetDeviceForPlayer(i);
if (player_device == -1)
{
continue;
}
if (player_device == deviceID)
{
// This one's already being used.
return false;
@ -541,6 +546,7 @@ static boolean M_DeviceAvailable(INT32 deviceID, UINT8 numPlayers)
static boolean M_HandlePressStart(setup_player_t *p, UINT8 num)
{
INT32 i, j;
INT32 num_gamepads_available;
if (optionsmenu.profile)
return false; // Don't allow for the possibility of SOMEHOW another player joining in.
@ -569,24 +575,38 @@ static boolean M_HandlePressStart(setup_player_t *p, UINT8 num)
}
// Now detect new devices trying to join.
for (i = 0; i < MAXDEVICES; i++)
num_gamepads_available = G_GetNumAvailableGamepads();
for (i = 0; i < num_gamepads_available + 1; i++)
{
if (deviceResponding[i] != true)
INT32 device = 0;
if (i > 0)
{
device = G_GetAvailableGamepadDevice(i - 1);
}
if (device == KEYBOARD_MOUSE_DEVICE && num != 0)
{
// Only player 1 can be assigned to the KBM device.
continue;
}
if (G_IsDeviceResponding(device) != true)
{
// No buttons are being pushed.
continue;
}
if (M_DeviceAvailable(i, setup_numplayers) == true)
if (M_DeviceAvailable(device, setup_numplayers) == true)
{
// Available!! Let's use this one!!
// if P1 is setting up using keyboard (device 0), save their last used device.
// this is to allow them to retain controller usage when they play alone.
// Because let's face it, when you test mods, you're often lazy to grab your controller for menuing :)
if (!i && !num)
if (i == 0 && num == 0)
{
setup_player[num].ponedevice = cv_usejoystick[num].value;
setup_player[num].ponedevice = G_GetDeviceForPlayer(num);
}
else if (num)
{
@ -594,14 +614,13 @@ static boolean M_HandlePressStart(setup_player_t *p, UINT8 num)
memcpy(&gamecontrol[num], gamecontroldefault, sizeof(gamecontroldefault));
}
G_SetDeviceForPlayer(num, device);
CONS_Printf("M_HandlePressStart: Device for %d set to %d\n", num, device);
CV_SetValue(&cv_usejoystick[num], i);
CONS_Printf("M_HandlePressStart: Device for %d set to %d\n", num, i);
for (j = num+1; j < MAXSPLITSCREENPLAYERS; j++)
for (j = num + 1; j < MAXSPLITSCREENPLAYERS; j++)
{
// Un-set devices for other players.
CV_SetValue(&cv_usejoystick[j], -1);
G_SetDeviceForPlayer(j, -1);
CONS_Printf("M_HandlePressStart: Device for %d set to %d\n", j, -1);
}
@ -617,7 +636,7 @@ static boolean M_HandlePressStart(setup_player_t *p, UINT8 num)
menucmd[j].buttonsHeld |= MBT_X;
}
memset(deviceResponding, false, sizeof(deviceResponding));
G_ResetAllDeviceResponding();
return true;
}
}
@ -669,11 +688,8 @@ static boolean M_HandleCSelectProfile(setup_player_t *p, UINT8 num)
menucmd[i].buttonsHeld |= MBT_X;
}
if (num > 0)
{
CV_StealthSetValue(&cv_usejoystick[num], -1);
CONS_Printf("M_HandleCSelectProfile: Device for %d set to %d\n", num, -1);
}
G_SetDeviceForPlayer(num, -1);
CONS_Printf("M_HandleCSelectProfile: Device for %d set to %d\n", num, -1);
return true;
}
@ -1479,22 +1495,18 @@ void M_CharacterSelectTick(void)
else
CV_StealthSet(&cv_follower[i], followers[setup_player[i].followern].name);
CV_StealthSetValue(&cv_followercolor[i], setup_player[i].followercolor);
G_SetPlayerGamepadIndicatorToPlayerColor(i);
}
CV_StealthSetValue(&cv_splitplayers, setup_numplayers);
// P1 is alone, set their old device just in case.
if (setup_numplayers < 2 && setup_player[0].ponedevice)
{
CV_StealthSetValue(&cv_usejoystick[0], setup_player[0].ponedevice);
}
#if defined (TESTERS)
M_MPOptSelectInit(0);
#else
M_SetupPlayMenu(0);
#endif
}
}
else // In a game

View file

@ -167,12 +167,6 @@ boolean Obj_OrbinautJawzCollide(mobj_t *t1, mobj_t *t2)
boolean tumbleitem = false;
boolean sprung = false;
if ((orbinaut_selfdelay(t1) > 0 && t2->hitlag > 0)
|| (orbinaut_selfdelay(t2) > 0 && t1->hitlag > 0))
{
return true;
}
if (t1->health <= 0 || t2->health <= 0)
{
return true;

View file

@ -13595,7 +13595,7 @@ void A_ReaperThinker(mobj_t *actor)
continue;
player = &players[i];
if (player && player->mo && player->bumpers && player->score >= maxscore)
if (player && player->mo && K_Bumpers(player) && player->score >= maxscore)
{
targetplayermo = player->mo;
maxscore = player->score;

View file

@ -39,6 +39,7 @@
#include "p_spec.h"
#include "k_objects.h"
#include "k_roulette.h"
#include "k_boss.h"
// CTF player names
#define CTFTEAMCODE(pl) pl->ctfteam ? (pl->ctfteam == 1 ? "\x85" : "\x84") : ""
@ -114,13 +115,6 @@ boolean P_CanPickupItem(player_t *player, UINT8 weapon)
if (player->exiting || mapreset || (player->pflags & PF_ELIMINATED))
return false;
if ((gametyperules & GTR_BUMPERS) // No bumpers in Match
#ifndef OTHERKARMAMODES
&& !weapon
#endif
&& player->bumpers <= 0)
return false;
if (weapon)
{
// Item slot already taken up
@ -285,9 +279,6 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
if (!P_CanPickupItem(player, 3) || (player->itemamount && player->itemtype != special->threshold))
return;
if ((gametyperules & GTR_BUMPERS) && player->bumpers <= 0)
return;
player->itemtype = special->threshold;
if ((UINT16)(player->itemamount) + special->movecount > 255)
player->itemamount = 255;
@ -320,9 +311,6 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
P_KillMobj(special, toucher, toucher, DMG_NORMAL);
return;
case MT_ITEMCAPSULE:
if ((gametyperules & GTR_BUMPERS) && player->bumpers <= 0)
return;
if (special->scale < special->extravalue1) // don't break it while it's respawning
return;
@ -350,8 +338,6 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
return;
if (player == special->target->player)
return;
if (player->bumpers <= 0)
return;
if (special->target->player->exiting || player->exiting)
return;
@ -452,7 +438,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
S_StartSound(special, sfx_s1a2);
return;
case MT_CDUFO: // SRB2kart
if (special->fuse || !P_CanPickupItem(player, 1) || ((gametyperules & GTR_BUMPERS) && player->bumpers <= 0))
if (special->fuse || !P_CanPickupItem(player, 1))
return;
K_StartItemRoulette(player);
@ -1395,6 +1381,11 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
P_PlayDeathSound(target);
}
if (K_Cooperative())
{
target->player->pflags |= (PF_NOCONTEST|PF_ELIMINATED);
}
break;
case MT_METALSONIC_RACE:
@ -1937,18 +1928,30 @@ static boolean P_KillPlayer(player_t *player, mobj_t *inflictor, mobj_t *source,
switch (type)
{
case DMG_DEATHPIT:
// Respawn kill types
// Fell off the stage
if (player->roundconditions.fell_off == true)
{
player->roundconditions.fell_off = true;
player->roundconditions.checkthisframe = true;
}
K_DoIngameRespawn(player);
player->mo->health -= K_DestroyBumpers(player, 1);
return false;
if (gametyperules & GTR_BUMPERS)
{
player->mo->health--;
}
if (player->mo->health <= 0)
{
return true;
}
// Quick respawn; does not kill
return K_DoIngameRespawn(player), false;
case DMG_SPECTATOR:
// disappearifies, but still gotta put items back in play
break;
default:
// Everything else REALLY kills
if (leveltime < starttime)
@ -2004,11 +2007,46 @@ static boolean P_KillPlayer(player_t *player, mobj_t *inflictor, mobj_t *source,
player->pflags |= PF_ELIMINATED;
}
K_DestroyBumpers(player, player->bumpers);
return true;
}
static void AddTimesHit(player_t *player)
{
const INT32 oldtimeshit = player->timeshit;
player->timeshit++;
// overflow prevention
if (player->timeshit < oldtimeshit)
{
player->timeshit = oldtimeshit;
}
}
static void AddNullHitlag(player_t *player, tic_t oldHitlag)
{
if (player == NULL)
{
return;
}
// Hitlag from what would normally be damage but the
// player was invulnerable.
//
// If we're constantly getting hit the same number of
// times, we're probably standing on a damage floor.
//
// Checking if we're hit more than before ensures that:
//
// 1) repeating damage doesn't count
// 2) new damage sources still count
if (player->timeshit <= player->timeshitprev)
{
player->nullHitlag += (player->mo->hitlag - oldHitlag);
}
}
/** Damages an object, which may or may not be a player.
* For melee attacks, source and inflictor are the same.
*
@ -2029,6 +2067,7 @@ static boolean P_KillPlayer(player_t *player, mobj_t *inflictor, mobj_t *source,
boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype)
{
player_t *player;
player_t *playerInflictor;
boolean force = false;
boolean spbpop = false;
@ -2097,9 +2136,6 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
if (!(target->flags & MF_SHOOTABLE))
return false; // shouldn't happen...
}
if (!(damagetype & DMG_DEATHMASK) && (target->eflags & MFE_PAUSED))
return false;
}
if (target->flags2 & MF2_SKULLFLY)
@ -2118,20 +2154,16 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
}
player = target->player;
playerInflictor = inflictor ? inflictor->player : NULL;
if (playerInflictor)
{
AddTimesHit(playerInflictor);
}
if (player) // Player is the target
{
{
const INT32 oldtimeshit = player->timeshit;
player->timeshit++;
// overflow prevention
if (player->timeshit < oldtimeshit)
{
player->timeshit = oldtimeshit;
}
}
AddTimesHit(player);
if (player->pflags & PF_GODMODE)
return false;
@ -2174,9 +2206,6 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
const boolean hardhit = (type == DMG_EXPLODE || type == DMG_KARMA || type == DMG_TUMBLE); // This damage type can do evil stuff like ALWAYS combo
INT16 ringburst = 5;
// Do not die from damage outside of bumpers health system
damage = 0;
// Check if the player is allowed to be damaged!
// If not, then spawn the instashield effect instead.
if (!force)
@ -2184,16 +2213,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
boolean invincible = true;
sfxenum_t sfx = sfx_None;
if (gametyperules & GTR_BUMPERS)
{
if (player->bumpers <= 0 && player->karmadelay)
{
// No bumpers & in WAIT, can't be hurt
K_DoInstashield(player);
return false;
}
}
else
if (!(gametyperules & GTR_BUMPERS))
{
if (damagetype & DMG_STEAL)
{
@ -2220,12 +2240,32 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
if (invincible && type != DMG_STUMBLE)
{
const INT32 oldhitlag = target->hitlag;
const INT32 oldHitlag = target->hitlag;
const INT32 oldHitlagInflictor = inflictor ? inflictor->hitlag : 0;
// Damage during hitlag should be a no-op
// for invincibility states because there
// are no flashing tics. If the damage is
// from a constant source, a deadlock
// would occur.
if (target->eflags & MFE_PAUSED)
{
player->timeshit--; // doesn't count
if (playerInflictor)
{
playerInflictor->timeshit--;
}
return false;
}
laglength = max(laglength / 2, 1);
K_SetHitLagForObjects(target, inflictor, laglength, false);
player->invulnhitlag += (target->hitlag - oldhitlag);
AddNullHitlag(player, oldHitlag);
AddNullHitlag(playerInflictor, oldHitlagInflictor);
if (player->timeshit > player->timeshitprev)
{
@ -2255,6 +2295,11 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
allowcombo = false;
}
if (allowcombo == false && (target->eflags & MFE_PAUSED))
{
return false;
}
// DMG_EXPLODE excluded from flashtic checks to prevent dodging eggbox/SPB with weak spinout
if ((target->hitlag == 0 || allowcombo == false) && player->flashing > 0 && type != DMG_EXPLODE && type != DMG_STUMBLE)
{
@ -2262,31 +2307,35 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
K_DoInstashield(player);
return false;
}
else if (target->flags2 & MF2_ALREADYHIT) // do not deal extra damage in the same tic
{
K_SetHitLagForObjects(target, inflictor, laglength, true);
return false;
}
}
}
// We successfully damaged them! Give 'em some bumpers!
if (type != DMG_STING && type != DMG_STUMBLE)
if (gametyperules & GTR_BUMPERS)
{
UINT8 takeBumpers = 1;
if (damagetype & DMG_STEAL)
{
takeBumpers = 2;
// Steals 2 bumpers
damage = 2;
}
}
else
{
// Do not die from damage outside of bumpers health system
damage = 0;
}
if (type == DMG_KARMA)
{
takeBumpers = player->bumpers;
}
}
else
{
if (type == DMG_KARMA)
{
// Take half of their bumpers for karma comeback damage
takeBumpers = max(1, player->bumpers / 2);
}
}
if (type == DMG_STING || type == DMG_STUMBLE)
{
damage = 0;
}
else
{
// We successfully damaged them! Give 'em some bumpers!
if (source && source != player->mo && source->player)
{
@ -2305,18 +2354,12 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
K_TryHurtSoundExchange(target, source);
K_BattleAwardHit(source->player, player, inflictor, takeBumpers);
damage = K_TakeBumpersFromPlayer(source->player, player, takeBumpers);
if (K_Cooperative() == false)
{
K_BattleAwardHit(source->player, player, inflictor, damage);
}
if (type == DMG_KARMA)
{
// Destroy any remainder bumpers from the player for karma comeback damage
damage = K_DestroyBumpers(player, player->bumpers);
}
else
{
source->player->overtimekarma += 5*TICRATE;
}
K_TakeBumpersFromPlayer(source->player, player, damage);
if (damagetype & DMG_STEAL)
{
@ -2332,10 +2375,6 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
Obj_GardenTopDestroy(source->player);
}
}
else
{
damage = K_DestroyBumpers(player, takeBumpers);
}
if (!(damagetype & DMG_STEAL))
{
@ -2453,6 +2492,8 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
K_SetHitLagForObjects(target, inflictor, laglength, true);
target->flags2 |= MF2_ALREADYHIT;
if (target->health <= 0)
{
P_KillMobj(target, inflictor, source, damagetype);

View file

@ -956,6 +956,8 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing)
|| tm.thing->type == MT_BANANA || tm.thing->type == MT_EGGMANITEM || tm.thing->type == MT_BALLHOG
|| tm.thing->type == MT_SSMINE || tm.thing->type == MT_LANDMINE || tm.thing->type == MT_SINK
|| tm.thing->type == MT_GARDENTOP
|| tm.thing->type == MT_MONITOR
|| tm.thing->type == MT_BATTLECAPSULE
|| (tm.thing->type == MT_PLAYER)))
{
// see if it went over / under
@ -971,6 +973,8 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing)
|| thing->type == MT_BANANA || thing->type == MT_EGGMANITEM || thing->type == MT_BALLHOG
|| thing->type == MT_SSMINE || thing->type == MT_LANDMINE || thing->type == MT_SINK
|| thing->type == MT_GARDENTOP
|| thing->type == MT_MONITOR
|| thing->type == MT_BATTLECAPSULE
|| (thing->type == MT_PLAYER)))
{
// see if it went over / under
@ -1390,13 +1394,6 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing)
return BMIT_CONTINUE;
}
if ((gametyperules & GTR_BUMPERS)
&& ((thing->player->bumpers && !tm.thing->player->bumpers)
|| (tm.thing->player->bumpers && !thing->player->bumpers)))
{
return BMIT_CONTINUE;
}
if (!P_MobjWasRemoved(thing) && !P_MobjWasRemoved(tm.thing))
{
if (thing->player->eggmanexplode)

View file

@ -4602,9 +4602,6 @@ boolean P_SupermanLook4Players(mobj_t *actor)
if (players[c].mo->health <= 0)
continue; // dead
if ((gametyperules & GTR_BUMPERS) && players[c].bumpers <= 0)
continue; // other dead
playersinthegame[stop] = &players[c];
stop++;
}
@ -6143,6 +6140,8 @@ static void P_MobjSceneryThink(mobj_t *mobj)
if (mobj->target && !P_MobjWasRemoved(mobj->target) && mobj->target->player
&& mobj->target->health > 0 && !mobj->target->player->spectator)
{
const UINT8 bumpers = K_Bumpers(mobj->target->player);
fixed_t rad = 32*mobj->target->scale;
fixed_t offz;
angle_t ang, diff;
@ -6152,10 +6151,10 @@ static void P_MobjSceneryThink(mobj_t *mobj)
else
ang = FixedAngle(mobj->info->speed);
if (mobj->target->player->bumpers <= 1)
if (bumpers <= 1)
diff = 0;
else
diff = FixedAngle(360*FRACUNIT/mobj->target->player->bumpers);
diff = FixedAngle(360*FRACUNIT / bumpers);
ang = (ang*leveltime) + (diff * (mobj->threshold-1));
@ -6192,9 +6191,9 @@ static void P_MobjSceneryThink(mobj_t *mobj)
mobj->color = mobj->target->color;
}
if (mobj->target->player->bumpers < 2)
if (bumpers < 2)
P_SetMobjState(mobj, S_BATTLEBUMPER3);
else if (mobj->target->player->bumpers < 3)
else if (bumpers < 3)
P_SetMobjState(mobj, S_BATTLEBUMPER2);
else
P_SetMobjState(mobj, S_BATTLEBUMPER1);
@ -6211,7 +6210,7 @@ static void P_MobjSceneryThink(mobj_t *mobj)
P_SetThingPosition(mobj);
}
if (mobj->target->player->bumpers <= mobj->threshold)
if (bumpers <= mobj->threshold)
{
// Do bumper destruction
P_KillMobj(mobj, NULL, NULL, DMG_NORMAL);
@ -6245,7 +6244,7 @@ static void P_MobjSceneryThink(mobj_t *mobj)
mobj->color = mobj->target->color;
K_MatchGenericExtraFlags(mobj, mobj->target);
if ((!(gametyperules & GTR_BUMPERS) || mobj->target->player->bumpers <= 0)
if (!(gametyperules & GTR_BUMPERS)
#if 1 // Set to 0 to test without needing to host
|| (P_IsDisplayPlayer(mobj->target->player))
#endif
@ -8261,7 +8260,9 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
desty = mobj->target->y;
}
mobj->flags &= ~(MF_NOCLIPTHING);
P_MoveOrigin(mobj, destx, desty, mobj->target->z);
mobj->flags |= MF_NOCLIPTHING;
break;
}
case MT_FLAMESHIELD:
@ -8442,7 +8443,7 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
statenum_t state = (mobj->state-states);
if (!mobj->target || !mobj->target->health || !mobj->target->player || mobj->target->player->spectator
|| (!(gametyperules & GTR_BUMPERS) || mobj->target->player->bumpers))
|| !(gametyperules & GTR_BUMPERS))
{
P_RemoveMobj(mobj);
return false;
@ -9857,8 +9858,10 @@ void P_MobjThinker(mobj_t *mobj)
if ((mobj->flags & MF_BOSS) && mobj->spawnpoint && (bossdisabled & (1<<mobj->spawnpoint->args[0])))
return;
mobj->flags2 &= ~(MF2_ALREADYHIT);
// Don't run any thinker code while in hitlag
if (mobj->hitlag > 0)
if ((mobj->player ? mobj->hitlag - mobj->player->nullHitlag : mobj->hitlag) > 0)
{
mobj->eflags |= MFE_PAUSED;
mobj->hitlag--;
@ -11890,38 +11893,15 @@ void P_SpawnPlayer(INT32 playernum)
P_SetScale(overheadarrow, mobj->destscale);
}
if (gametyperules & GTR_BUMPERS)
if ((gametyperules & GTR_BUMPERS) && !p->spectator)
{
if (p->spectator)
// At leveltime == 2, K_TimerInit will get called and reset
// the bumpers to the initial value for the level.
if (leveltime > 2) // Reset those bumpers!
{
// HEY! No being cheap...
p->bumpers = 0;
}
else if ((p->bumpers > 0) || (leveltime < starttime) || (pcount <= 1))
{
if ((leveltime < starttime) || (pcount <= 1)) // Start of the map?
{
if (leveltime > 2) // Reset those bumpers!
{
p->bumpers = K_StartingBumperCount();
K_SpawnPlayerBattleBumpers(p);
}
else // temp, will get overwritten in K_BattleInit
{
p->bumpers = 1;
}
}
}
else if (p->bumpers <= 0)
{
p->bumpers = K_StartingBumperCount();
mobj->health = K_BumpersToHealth(K_StartingBumperCount());
K_SpawnPlayerBattleBumpers(p);
}
if (p->bumpers > 0)
{
mobj->health = p->bumpers;
}
}
// I'm not refactoring the loop at the top of this file.
@ -11936,9 +11916,13 @@ void P_SpawnPlayer(INT32 playernum)
}
// Spectating when there is literally any other player in
// the level enables director cam.
// the level enables director cam. Or if the first player
// enters the game, spectate them.
// TODO: how do we support splitscreen?
K_ToggleDirector(players[consoleplayer].spectator && pcount > 0);
if (playernum == consoleplayer || pcount == 1)
{
K_ToggleDirector(players[consoleplayer].spectator && pcount > 0);
}
}
void P_AfterPlayerSpawn(INT32 playernum)

View file

@ -191,7 +191,7 @@ typedef enum
MF2_JUSTATTACKED = 1<<16, // can be pushed by other moving mobjs
MF2_FIRING = 1<<17, // turret fire
MF2_SUPERFIRE = 1<<18, // Firing something with Super Sonic-stopping properties. Or, if mobj has MF_MISSILE, this is the actual fire from it.
// free: 1<<19
MF2_ALREADYHIT = 1<<19, // This object was already damaged THIS tic, resets even during hitlag
MF2_STRONGBOX = 1<<20, // Flag used for "strong" random monitors.
MF2_OBJECTFLIP = 1<<21, // Flag for objects that always have flipped gravity.
MF2_SKULLFLY = 1<<22, // Special handling: skull in flight.

View file

@ -276,7 +276,7 @@ static void P_NetArchivePlayers(savebuffer_t *save)
WRITEUINT16(save->p, players[i].spinouttimer);
WRITEUINT8(save->p, players[i].spinouttype);
WRITEUINT8(save->p, players[i].instashield);
WRITEINT32(save->p, players[i].invulnhitlag);
WRITEINT32(save->p, players[i].nullHitlag);
WRITEUINT8(save->p, players[i].wipeoutslow);
WRITEUINT8(save->p, players[i].justbumped);
WRITEUINT8(save->p, players[i].tumbleBounces);
@ -381,9 +381,7 @@ static void P_NetArchivePlayers(savebuffer_t *save)
WRITEUINT32(save->p, players[i].roundscore);
WRITEUINT8(save->p, players[i].emeralds);
WRITEUINT8(save->p, players[i].bumpers);
WRITEINT16(save->p, players[i].karmadelay);
WRITEUINT32(save->p, players[i].overtimekarma);
WRITEINT16(save->p, players[i].spheres);
WRITEUINT32(save->p, players[i].spheredigestion);
@ -653,7 +651,7 @@ static void P_NetUnArchivePlayers(savebuffer_t *save)
players[i].spinouttimer = READUINT16(save->p);
players[i].spinouttype = READUINT8(save->p);
players[i].instashield = READUINT8(save->p);
players[i].invulnhitlag = READINT32(save->p);
players[i].nullHitlag = READINT32(save->p);
players[i].wipeoutslow = READUINT8(save->p);
players[i].justbumped = READUINT8(save->p);
players[i].tumbleBounces = READUINT8(save->p);
@ -758,9 +756,7 @@ static void P_NetUnArchivePlayers(savebuffer_t *save)
players[i].roundscore = READUINT32(save->p);
players[i].emeralds = READUINT8(save->p);
players[i].bumpers = READUINT8(save->p);
players[i].karmadelay = READINT16(save->p);
players[i].overtimekarma = READUINT32(save->p);
players[i].spheres = READINT16(save->p);
players[i].spheredigestion = READUINT32(save->p);

View file

@ -122,6 +122,7 @@ unsigned char mapmd5[16];
boolean udmf;
size_t numvertexes, numsegs, numsectors, numsubsectors, numnodes, numlines, numsides, nummapthings;
size_t num_orig_vertexes;
vertex_t *vertexes;
seg_t *segs;
sector_t *sectors;
@ -1921,6 +1922,7 @@ static void P_WriteTextmap(void)
side_t *wsides;
mtag_t freetag;
sectorspecialthings_t *specialthings;
boolean *wusedvertexes;
f = fopen(filepath, "w");
if (!f)
@ -1930,14 +1932,15 @@ static void P_WriteTextmap(void)
}
wmapthings = Z_Calloc(nummapthings * sizeof(*mapthings), PU_LEVEL, NULL);
wvertexes = Z_Calloc(numvertexes * sizeof(*vertexes), PU_LEVEL, NULL);
wvertexes = Z_Calloc(num_orig_vertexes * sizeof(*vertexes), PU_LEVEL, NULL);
wsectors = Z_Calloc(numsectors * sizeof(*sectors), PU_LEVEL, NULL);
wlines = Z_Calloc(numlines * sizeof(*lines), PU_LEVEL, NULL);
wsides = Z_Calloc(numsides * sizeof(*sides), PU_LEVEL, NULL);
specialthings = Z_Calloc(numsectors * sizeof(*sectors), PU_LEVEL, NULL);
wusedvertexes = Z_Calloc(num_orig_vertexes * sizeof(boolean), PU_LEVEL, NULL);
memcpy(wmapthings, mapthings, nummapthings * sizeof(*mapthings));
memcpy(wvertexes, vertexes, numvertexes * sizeof(*vertexes));
memcpy(wvertexes, vertexes, num_orig_vertexes * sizeof(*vertexes));
memcpy(wsectors, sectors, numsectors * sizeof(*sectors));
memcpy(wlines, lines, numlines * sizeof(*lines));
memcpy(wsides, sides, numsides * sizeof(*sides));
@ -1951,9 +1954,19 @@ static void P_WriteTextmap(void)
wsectors[i].tags.tags = memcpy(Z_Malloc(sectors[i].tags.count*sizeof(mtag_t), PU_LEVEL, NULL), sectors[i].tags.tags, sectors[i].tags.count*sizeof(mtag_t));
for (i = 0; i < numlines; i++)
{
size_t v;
if (lines[i].tags.count)
wlines[i].tags.tags = memcpy(Z_Malloc(lines[i].tags.count * sizeof(mtag_t), PU_LEVEL, NULL), lines[i].tags.tags, lines[i].tags.count * sizeof(mtag_t));
v = lines[i].v1 - vertexes;
wusedvertexes[v] = true;
v = lines[i].v2 - vertexes;
wusedvertexes[v] = true;
}
freetag = Tag_NextUnused(0);
for (i = 0; i < nummapthings; i++)
@ -1961,6 +1974,13 @@ static void P_WriteTextmap(void)
subsector_t *ss;
INT32 s;
if (wmapthings[i].type == mobjinfo[MT_WAYPOINT].doomednum
|| wmapthings[i].type == mobjinfo[MT_WAYPOINT_ANCHOR].doomednum
|| wmapthings[i].type == mobjinfo[MT_WAYPOINT_RISER].doomednum)
{
CONS_Alert(CONS_WARNING, M_GetText("Thing %s is a waypoint or waypoint parameter, which cannot be converted fully.\n"), sizeu1(i));
}
if (wmapthings[i].type != 751 && wmapthings[i].type != 752 && wmapthings[i].type != 758)
continue;
@ -2232,18 +2252,26 @@ static void P_WriteTextmap(void)
fprintf(f, "\n");
}
for (i = 0; i < numvertexes; i++)
j = 0;
for (i = 0; i < num_orig_vertexes; i++)
{
fprintf(f, "vertex // %s\n", sizeu1(i));
if (wusedvertexes[i] == false)
{
continue;
}
fprintf(f, "vertex // %s\n", sizeu1(j));
fprintf(f, "{\n");
fprintf(f, "x = %f;\n", FIXED_TO_FLOAT(wvertexes[i].x));
fprintf(f, "y = %f;\n", FIXED_TO_FLOAT(wvertexes[i].y));
if (wvertexes[i].floorzset)
fprintf(f, "zfloor = %f;\n", FIXED_TO_FLOAT(wvertexes[i].floorz));
if (wvertexes[i].ceilingzset)
fprintf(f, "zceiling = %f;\n", FIXED_TO_FLOAT(wvertexes[i].ceilingz));
fprintf(f, "x = %f;\n", FIXED_TO_FLOAT(wvertexes[j].x));
fprintf(f, "y = %f;\n", FIXED_TO_FLOAT(wvertexes[j].y));
if (wvertexes[j].floorzset)
fprintf(f, "zfloor = %f;\n", FIXED_TO_FLOAT(wvertexes[j].floorz));
if (wvertexes[j].ceilingzset)
fprintf(f, "zceiling = %f;\n", FIXED_TO_FLOAT(wvertexes[j].ceilingz));
fprintf(f, "}\n");
fprintf(f, "\n");
j++;
}
for (i = 0; i < numlines; i++)
@ -2563,6 +2591,7 @@ static void P_WriteTextmap(void)
Z_Free(wlines);
Z_Free(wsides);
Z_Free(specialthings);
Z_Free(wusedvertexes);
}
/** Loads the textmap data, after obtaining the elements count and allocating their respective space.
@ -2939,6 +2968,10 @@ static boolean P_LoadMapData(const virtres_t *virt)
if (numlines <= 0)
I_Error("Level has no linedefs");
// Copy original vertex count before BSP modifications,
// as it can alter how -writetextmap works.
num_orig_vertexes = numvertexes;
vertexes = Z_Calloc(numvertexes * sizeof (*vertexes), PU_LEVEL, NULL);
sectors = Z_Calloc(numsectors * sizeof (*sectors), PU_LEVEL, NULL);
sides = Z_Calloc(numsides * sizeof (*sides), PU_LEVEL, NULL);
@ -4400,7 +4433,7 @@ static void P_ConvertBinaryLinedefTypes(void)
break;
case 80: //Raise tagged things by type to this FOF
lines[i].args[0] = sides[lines[i].sidenum[0]].textureoffset >> FRACBITS;
// angle will be converted to tags elsewhere, because they aren't ready yet...
lines[i].args[1] = tag;
break;
case 81: //Block enemies
lines[i].flags |= ML_BLOCKMONSTERS;
@ -6880,10 +6913,8 @@ static void P_ConvertBinaryMap(void)
P_ConvertBinaryThingTypes();
P_ConvertBinaryLinedefFlags();
#if 0 // Don't do this yet...
if (M_CheckParm("-writetextmap"))
P_WriteTextmap();
#endif
}
/** Compute MD5 message digest for bytes read from memory source
@ -7588,6 +7619,8 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
sector_t *ss;
virtlump_t *encoreLump = NULL;
K_TimerReset();
levelloading = true;
// This is needed. Don't touch.
@ -7885,10 +7918,6 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
{
// Backwards compatibility for non-UDMF maps
K_AdjustWaypointsParameters();
// Moved over here...
if (M_CheckParm("-writetextmap"))
P_WriteTextmap();
}
if (!fromnetsave) // ugly hack for P_NetUnArchiveMisc (and P_LoadNetGame)
@ -8009,8 +8038,6 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate)
P_MapEnd(); // just in case MapLoad modifies tm.thing
}
K_TimerReset();
// No render mode or reloading gamestate, stop here.
if (rendermode == render_none || reloadinggamestate)
return true;

View file

@ -1112,13 +1112,18 @@ void P_HandleSlopeLanding(mobj_t *thing, pslope_t *slope)
// Handles sliding down slopes, like if they were made of butter :)
void P_ButteredSlope(mobj_t *mo)
{
fixed_t thrust;
const fixed_t gameSpeed = K_GetKartGameSpeedScalar(gamespeed);
fixed_t thrust = 0;
if (mo->flags & (MF_NOCLIPHEIGHT|MF_NOGRAVITY))
{
return; // don't slide down slopes if you can't touch them or you're not affected by gravity
}
if (P_CanApplySlopePhysics(mo, mo->standingslope) == false)
{
return; // No physics, no butter.
}
if (mo->player != NULL)
{
@ -1141,17 +1146,23 @@ void P_ButteredSlope(mobj_t *mo)
thrust = FINESINE(mo->standingslope->zangle>>ANGLETOFINESHIFT) * 5 / 4 * (mo->eflags & MFE_VERTICALFLIP ? 1 : -1);
if (mo->player) {
if (mo->momx || mo->momy)
{
fixed_t mult = FRACUNIT;
if (mo->momx || mo->momy) {
angle_t angle = R_PointToAngle2(0, 0, mo->momx, mo->momy) - mo->standingslope->xydirection;
angle_t angle = R_PointToAngle2(0, 0, mo->momx, mo->momy) - mo->standingslope->xydirection;
if (P_MobjFlip(mo) * mo->standingslope->zdelta < 0)
angle ^= ANGLE_180;
mult = FRACUNIT + (FRACUNIT + FINECOSINE(angle>>ANGLETOFINESHIFT))*4/3;
if (P_MobjFlip(mo) * mo->standingslope->zdelta < 0)
{
angle ^= ANGLE_180;
}
// Make uphill easier to climb, and downhill even faster.
mult = FINECOSINE(angle >> ANGLETOFINESHIFT);
// Make relative to game speed
mult = FixedMul(mult, gameSpeed);
mult = FRACUNIT + (FRACUNIT + mult)*4/3;
thrust = FixedMul(thrust, mult);
}

View file

@ -6185,7 +6185,7 @@ static ffloor_t *P_AddFakeFloor(sector_t *sec, sector_t *sec2, line_t *master, I
static void
P_RaiseTaggedThingsToFakeFloor (
UINT16 type,
const taglist_t *tags,
mtag_t tag,
sector_t *control
){
sector_t *target;
@ -6210,20 +6210,9 @@ P_RaiseTaggedThingsToFakeFloor (
continue;
}
if (!udmf)
{
// We have to convert these here, as mobjs, let alone
// sector thing lists, don't exist at the time of the rest
// of the binary map conversion.
const mtag_t convertTag = mthing->angle;
Tag_Add(&mthing->tags, convertTag);
Taggroup_Add(tags_mapthings, convertTag, (size_t)(mthing - mapthings));
}
if (
(type == 0 || mthing->type == type) &&
(tags->count == 0 || Tag_Share(&mthing->tags, tags))
(tag == 0 || udmf ? Tag_Find(&mthing->tags, tag) : mthing->angle == tag)
){
if (( mo->flags2 & MF2_OBJECTFLIP ))
{
@ -7771,7 +7760,7 @@ void P_SpawnSpecialsThatRequireObjects(boolean fromnetsave)
{
P_RaiseTaggedThingsToFakeFloor(
lines[i].args[0],
&lines[i].tags,
lines[i].args[1],
lines[i].frontsector
);
}

View file

@ -537,10 +537,6 @@ INT32 P_GivePlayerSpheres(player_t *player, INT32 num_spheres)
if (!(gametyperules & GTR_SPHERES)) // No spheres in Race mode)
return 0;
// Not alive
if ((gametyperules & GTR_BUMPERS) && (player->bumpers <= 0))
return 0;
if (num_spheres > 40) // Reached the cap, don't waste 'em!
num_spheres = 40;
else if (num_spheres < 0)
@ -2256,7 +2252,7 @@ static void P_UpdatePlayerAngle(player_t *player)
angle_t leniency = (2*ANG1/3) * min(player->cmd.latency, 6);
// Don't force another turning tic, just give them the desired angle!
if (targetDelta == angleChange || player->pflags & PF_DRIFTEND || (maxTurnRight == 0 && maxTurnLeft == 0))
if (targetDelta == angleChange || player->pflags & PF_DRIFTEND || K_Sliptiding(player) || (maxTurnRight == 0 && maxTurnLeft == 0))
{
// We are where we need to be.
// ...Or we aren't, but shouldn't be able to steer.
@ -4404,7 +4400,7 @@ void P_PlayerThink(player_t *player)
|| player->growshrinktimer > 0 // Grow doesn't flash either.
|| (player->respawn.state != RESPAWNST_NONE && player->respawn.truedeath == true) // Respawn timer (for drop dash effect)
|| (player->pflags & PF_NOCONTEST) // NO CONTEST explosion
|| ((gametyperules & GTR_BUMPERS) && player->bumpers <= 0 && player->karmadelay)))
|| player->karmadelay))
{
if (player->flashing > 1 && player->flashing < K_GetKartFlashing(player)
&& (leveltime & 1))

View file

@ -72,8 +72,8 @@ consvar_t stereoreverse = CVAR_INIT ("stereoreverse", "Off", CV_SAVE, CV_OnOff,
static consvar_t precachesound = CVAR_INIT ("precachesound", "Off", CV_SAVE, CV_OnOff, NULL);
// actual general (maximum) sound & music volume, saved into the config
consvar_t cv_soundvolume = CVAR_INIT ("soundvolume", "50", CV_SAVE, soundvolume_cons_t, NULL);
consvar_t cv_digmusicvolume = CVAR_INIT ("musicvolume", "50", CV_SAVE, soundvolume_cons_t, NULL);
consvar_t cv_soundvolume = CVAR_INIT ("soundvolume", "80", CV_SAVE, soundvolume_cons_t, NULL);
consvar_t cv_digmusicvolume = CVAR_INIT ("musicvolume", "80", CV_SAVE, soundvolume_cons_t, NULL);
// number of channels available
consvar_t cv_numChannels = CVAR_INIT ("snd_channels", "64", CV_SAVE|CV_CALL, CV_Unsigned, SetChannelsNum);
@ -791,9 +791,9 @@ void S_UpdateSounds(void)
mobj_t *listenmobj[MAXSPLITSCREENPLAYERS];
// Update sound/music volumes, if changed manually at console
if (actualsfxvolume != cv_soundvolume.value * USER_VOLUME_SCALE)
if (actualsfxvolume != cv_soundvolume.value)
S_SetSfxVolume (cv_soundvolume.value);
if (actualdigmusicvolume != cv_digmusicvolume.value * USER_VOLUME_SCALE)
if (actualdigmusicvolume != cv_digmusicvolume.value)
S_SetDigMusicVolume (cv_digmusicvolume.value);
// We're done now, if we're not in a level.
@ -990,7 +990,7 @@ void S_UpdateClosedCaptions(void)
void S_SetSfxVolume(INT32 volume)
{
//CV_SetValue(&cv_soundvolume, volume);
actualsfxvolume = volume * USER_VOLUME_SCALE;
actualsfxvolume = volume;
#ifdef HW3SOUND
hws_mode == HWS_DEFAULT_MODE ? I_SetSfxVolume(volume&0x1F) : HW3S_SetSfxVolume(volume&0x1F);
@ -1360,7 +1360,6 @@ static tic_t pause_starttic;
musicdef_t *musicdefstart = NULL;
struct cursongcredit cursongcredit; // Currently displayed song credit info
int musicdef_volume;
//
// S_FindMusicDef
@ -2249,14 +2248,12 @@ void S_ChangeMusicEx(const char *mmusic, UINT16 mflags, boolean looping, UINT32
music_flags = mflags;
music_looping = looping;
musicdef_volume = DEFAULT_MUSICDEF_VOLUME;
{
musicdef_t *def = S_FindMusicDef(music_name);
if (def)
{
musicdef_volume = def->volume;
I_SetCurrentSongVolume(def->volume);
}
}
@ -2350,7 +2347,7 @@ void S_SetMusicVolume(INT32 digvolume)
digvolume = cv_digmusicvolume.value;
//CV_SetValue(&cv_digmusicvolume, digvolume);
actualdigmusicvolume = digvolume * USER_VOLUME_SCALE;
actualdigmusicvolume = digvolume;
I_SetMusicVolume(digvolume);
}

View file

@ -35,10 +35,10 @@ extern openmpt_module *openmpt_mhandle;
#define PICKUP_SOUND 0x8000
//
#define SOUND_VOLUME_RANGE 256
#define MAX_SOUND_VOLUME 255
#define SOUND_VOLUME_RANGE 100
#define MAX_SOUND_VOLUME 100
#define DEFAULT_MUSICDEF_VOLUME ( 100 / VOLUME_DIVIDER )
#define DEFAULT_MUSICDEF_VOLUME 100
extern consvar_t stereoreverse;
extern consvar_t cv_soundvolume, cv_closedcaptioning, cv_digmusicvolume;
@ -197,7 +197,6 @@ extern struct cursongcredit
} cursongcredit;
extern musicdef_t *musicdefstart;
extern int musicdef_volume;
void S_LoadMusicDefs(UINT16 wadnum);
void S_InitMusicDefs(void);

View file

@ -205,7 +205,8 @@ static void init_exchndl()
if (exchndl_module == NULL)
{
I_Error("exchndl.dll or mgwhelp.dll is missing");
I_Error("exchndl.dll or mgwhelp.dll is missing, RPT files cannot be generated.\n"
"If you NEED to run without them, use -noexchndl.");
}
using PFN_ExcHndlInit = void(*)(void);
@ -297,6 +298,7 @@ int main(int argc, char **argv)
//I_OutputMsg("I_StartupSystem() ...\n");
I_StartupSystem();
#if defined (_WIN32)
if (!M_CheckParm("-noexchndl"))
{
#if 0 // just load the DLL
p_IsDebuggerPresent pfnIsDebuggerPresent = (p_IsDebuggerPresent)GetProcAddress(GetModuleHandleA("kernel32.dll"), "IsDebuggerPresent");

View file

@ -194,74 +194,9 @@ static char returnWadPath[256];
#include "../byteptr.h"
#endif
void I_StoreExJoystick(SDL_GameController *dev)
{
// ExJoystick is a massive hack to avoid needing to completely
// rewrite pretty much all of the controller support from scratch...
// Used in favor of most instances of SDL_GameControllerClose.
// If a joystick would've been discarded, then save it in an array,
// because we want it have it for the joystick input screen.
int index = 0;
if (dev == NULL)
{
// No joystick?
return;
}
index = SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(dev));
if (index >= MAXGAMEPADS || index < 0)
{
// Not enough space to save this joystick, completely discard.
SDL_GameControllerClose(dev);
return;
}
if (ExJoystick[index] == dev)
{
// No need to do anything else.
return;
}
if (ExJoystick[index] != NULL)
{
// Discard joystick in the old slot.
SDL_GameControllerClose(ExJoystick[index]);
}
// Keep for safe-keeping.
ExJoystick[index] = dev;
}
/** \brief The JoyReset function
\param JoySet Joystick info to reset
\return void
*/
static void JoyReset(SDLJoyInfo_t *JoySet)
{
if (JoySet->dev)
{
I_StoreExJoystick(JoySet->dev);
}
JoySet->dev = NULL;
JoySet->oldjoy = -1;
JoySet->axises = JoySet->buttons = JoySet->hats = JoySet->balls = 0;
//JoySet->scale
}
/** \brief First joystick up and running
*/
static INT32 joystick_started[MAXSPLITSCREENPLAYERS] = {0,0,0,0};
/** \brief SDL info about joystick 1
/** \brief SDL info about joysticks
*/
SDLJoyInfo_t JoyInfo[MAXSPLITSCREENPLAYERS];
SDL_GameController *ExJoystick[MAXGAMEPADS];
SDL_bool consolevent = SDL_FALSE;
SDL_bool framebuffer = SDL_FALSE;
@ -983,316 +918,78 @@ void I_JoyScale4(void)
JoyInfo[3].scale = Joystick[3].bGamepadStyle?1:cv_joyscale[1].value;
}
// Cheat to get the device index for a game controller handle
INT32 I_GetJoystickDeviceIndex(SDL_GameController *dev)
void I_SetGamepadPlayerIndex(INT32 device_id, INT32 player)
{
SDL_Joystick *joystick = NULL;
#if !(SDL_VERSION_ATLEAST(2,0,12))
(void)device_id;
(void)player;
#else
I_Assert(device_id > 0); // Gamepad devices are always ID 1 or higher
I_Assert(player >= 0 && player < MAXSPLITSCREENPLAYERS);
joystick = SDL_GameControllerGetJoystick(dev);
if (joystick)
SDL_GameController *controller = SDL_GameControllerFromInstanceID(device_id - 1);
if (controller == NULL)
{
return SDL_JoystickInstanceID(joystick);
return;
}
return -1;
SDL_GameControllerSetPlayerIndex(controller, player);
#endif
}
void I_UpdateJoystickDeviceIndex(UINT8 player)
void I_SetGamepadIndicatorColor(INT32 device_id, UINT8 red, UINT8 green, UINT8 blue)
{
///////////////////////////////////////////////
// update this joystick's device index (wow) //
///////////////////////////////////////////////
#if !(SDL_VERSION_ATLEAST(2,0,14))
(void)device_id;
(void)player;
#else
I_Assert(device_id > 0); // Gamepad devices are always ID 1 or higher
if (JoyInfo[player].dev)
SDL_GameController *controller = SDL_GameControllerFromInstanceID(device_id - 1);
if (controller == NULL)
{
cv_usejoystick[player].value = I_GetJoystickDeviceIndex(JoyInfo[player].dev) + 1;
CONS_Printf("I_UpdateJoystickDeviceIndex: Device for %d set to %d\n", player, cv_usejoystick[player].value);
}
else
{
UINT8 joystickID, compareJoystick;
for (joystickID = 0; joystickID < MAXSPLITSCREENPLAYERS; joystickID++)
{
// is this cv_usejoystick used?
const INT32 value = atoi(cv_usejoystick[joystickID].string);
for (compareJoystick = 0; compareJoystick < MAXSPLITSCREENPLAYERS; compareJoystick++)
{
if (compareJoystick == player)
continue;
if (value == JoyInfo[compareJoystick].oldjoy || value == cv_usejoystick[compareJoystick].value)
break;
}
if (compareJoystick == MAXSPLITSCREENPLAYERS)
{
// We DID make it through the whole loop, so we can use this one!
cv_usejoystick[player].value = value;
CONS_Printf("I_UpdateJoystickDeviceIndex: Device for %d set to %d\n", player, cv_usejoystick[player].value);
break;
}
}
if (joystickID == MAXSPLITSCREENPLAYERS)
{
// We DID NOT make it through the whole loop, so we can't assign this joystick to anything.
// When you try your best, but you don't succeed...
cv_usejoystick[player].value = 0;
CONS_Printf("I_UpdateJoystickDeviceIndex: Device for %d set to %d\n", player, 0);
}
}
}
// Misleading function: updates device indices for all players BUT the one specified.
// Necessary for SDL_JOYDEVICEADDED events
void I_UpdateJoystickDeviceIndices(UINT8 excludePlayer)
{
UINT8 player;
for (player = 0; player < MAXSPLITSCREENPLAYERS; player++)
{
if (player == excludePlayer)
continue;
I_UpdateJoystickDeviceIndex(player);
}
}
/** \brief Shuts down joystick
\return void
*/
void I_ShutdownJoystick(UINT8 index)
{
INT32 i;
event_t event;
event.device = I_GetJoystickDeviceIndex(JoyInfo[index].dev);
event.type = ev_keyup;
event.data2 = 0;
event.data3 = 0;
// emulate the up of all joystick buttons
for (i = 0; i < JOYBUTTONS; i++)
{
event.data1 = KEY_JOY1+i;
D_PostEvent(&event);
return;
}
// reset joystick position
event.type = ev_joystick;
for (i = 0; i < JOYAXES; i++)
{
event.data1 = i;
D_PostEvent(&event);
}
joystick_started[index] = 0;
JoyReset(&JoyInfo[index]);
// don't shut down the subsystem here, because hotplugging
}
/** \brief Open joystick handle
\param fname name of joystick
\return axises
*/
static int joy_open(int playerIndex, int joyIndex)
{
SDL_GameController *newdev = NULL;
int num_joy = 0;
if (SDL_WasInit(SDL_INIT_JOYSTICK) == 0)
{
CONS_Printf(M_GetText("Joystick subsystem not started\n"));
return -1;
}
if (SDL_WasInit(SDL_INIT_GAMECONTROLLER) == 0)
{
CONS_Printf(M_GetText("Game Controller subsystem not started\n"));
return -1;
}
if (joyIndex <= 0)
return -1;
num_joy = SDL_NumJoysticks();
if (num_joy == 0)
{
CONS_Printf("%s", M_GetText("Found no joysticks on this system\n"));
return -1;
}
newdev = SDL_GameControllerOpen(joyIndex-1);
// Handle the edge case where the device <-> joystick index assignment can change due to hotplugging
// This indexing is SDL's responsibility and there's not much we can do about it.
//
// Example:
// 1. Plug Controller A -> Index 0 opened
// 2. Plug Controller B -> Index 1 opened
// 3. Unplug Controller A -> Index 0 closed, Index 1 active
// 4. Unplug Controller B -> Index 0 inactive, Index 1 closed
// 5. Plug Controller B -> Index 0 opened
// 6. Plug Controller A -> Index 0 REPLACED, opened as Controller A; Index 1 is now Controller B
if (JoyInfo[playerIndex].dev)
{
if (JoyInfo[playerIndex].dev == newdev // same device, nothing to do
|| (newdev == NULL && SDL_GameControllerGetAttached(JoyInfo[playerIndex].dev))) // we failed, but already have a working device
{
return SDL_CONTROLLER_AXIS_MAX;
}
// Else, we're changing devices, so send neutral joy events
CONS_Debug(DBG_GAMELOGIC, "Joystick%d device is changing; resetting events...\n", playerIndex+1);
I_ShutdownJoystick(playerIndex);
}
JoyInfo[playerIndex].dev = newdev;
if (JoyInfo[playerIndex].dev == NULL)
{
CONS_Debug(DBG_GAMELOGIC, M_GetText("Joystick%d: Couldn't open device - %s\n"), playerIndex+1, SDL_GetError());
return -1;
}
else
{
CONS_Debug(DBG_GAMELOGIC, M_GetText("Joystick%d: %s\n"), playerIndex+1, SDL_GameControllerName(JoyInfo[playerIndex].dev));
JoyInfo[playerIndex].axises = SDL_CONTROLLER_AXIS_MAX;
JoyInfo[playerIndex].buttons = SDL_CONTROLLER_BUTTON_MAX;
JoyInfo[playerIndex].hats = 1;
JoyInfo[playerIndex].balls = 0;
//JoyInfo[playerIndex].bGamepadStyle = !stricmp(SDL_JoystickName(JoyInfo[playerIndex].dev), "pad");
return JoyInfo[playerIndex].axises;
}
SDL_GameControllerSetLED(controller, red, green, blue);
#endif
}
//
// I_InitJoystick
// I_StartupInput
//
void I_InitJoystick(UINT8 index)
void I_StartupInput(void)
{
SDL_GameController *newcontroller = NULL;
UINT8 i;
//I_ShutdownJoystick();
//SDL_SetHintWithPriority("SDL_XINPUT_ENABLED", "0", SDL_HINT_OVERRIDE);
if (M_CheckParm("-nojoy"))
return;
if (SDL_WasInit(SDL_INIT_GAMECONTROLLER))
{
return;
}
if (M_CheckParm("-noxinput"))
SDL_SetHintWithPriority("SDL_XINPUT_ENABLED", "0", SDL_HINT_OVERRIDE);
if (M_CheckParm("-nohidapi"))
SDL_SetHintWithPriority("SDL_JOYSTICK_HIDAPI", "0", SDL_HINT_OVERRIDE);
if (SDL_WasInit(SDL_INIT_JOYSTICK) == 0)
{
CONS_Printf("I_InitJoystick()...\n");
CONS_Printf("I_StartupInput()...\n");
if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) == -1)
{
CONS_Printf(M_GetText("Couldn't initialize joystick: %s\n"), SDL_GetError());
return;
}
if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) == -1)
{
CONS_Printf(M_GetText("Couldn't initialize game controllers: %s\n"), SDL_GetError());
return;
}
if (SDL_WasInit(SDL_INIT_GAMECONTROLLER) == 0)
{
if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) == -1)
{
CONS_Printf(M_GetText("Couldn't initialize gamepads: %s\n"), SDL_GetError());
return;
}
}
if (cv_usejoystick[index].value)
newcontroller = SDL_GameControllerOpen(cv_usejoystick[index].value-1);
for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
{
if (i == index)
continue;
if (JoyInfo[i].dev == newcontroller)
break;
}
if (newcontroller && i < MAXSPLITSCREENPLAYERS) // don't override an active device
{
cv_usejoystick[index].value = I_GetJoystickDeviceIndex(JoyInfo[index].dev) + 1;
CONS_Printf("I_InitJoystick: Device for %d set to %d\n", index, cv_usejoystick[index].value);
}
else if (newcontroller && joy_open(index, cv_usejoystick[index].value) != -1)
{
// SDL's device indexes are unstable, so cv_usejoystick may not match
// the actual device index. So let's cheat a bit and find the device's current index.
JoyInfo[index].oldjoy = I_GetJoystickDeviceIndex(JoyInfo[index].dev) + 1;
joystick_started[index] = 1;
}
else
{
if (JoyInfo[index].oldjoy)
I_ShutdownJoystick(index);
cv_usejoystick[index].value = 0;
CONS_Printf("I_InitJoystick: Device for %d set to %d\n", index, cv_usejoystick[index].value);
joystick_started[index] = 0;
}
for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
{
if (JoyInfo[i].dev == newcontroller)
break;
}
if (i == MAXSPLITSCREENPLAYERS)
{
// Joystick didn't end up being used
I_StoreExJoystick(newcontroller);
}
}
void I_InitJoystick1(void)
{
I_InitJoystick(0);
}
void I_InitJoystick2(void)
{
I_InitJoystick(1);
}
void I_InitJoystick3(void)
{
I_InitJoystick(2);
}
void I_InitJoystick4(void)
{
I_InitJoystick(3);
// Upon initialization, the gamecontroller subsystem will automatically dispatch controller device added events
// for controllers connected before initialization.
}
static void I_ShutdownInput(void)
{
UINT8 i;
// Yes, the name is misleading: these send neutral events to
// clean up the unplugged joystick's input
// Note these methods are internal to this file, not called elsewhere.
for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
I_ShutdownJoystick(i);
// The game code is now responsible for resetting its internal state based on ev_gamepad_device_removed events.
// In practice, Input should never be shutdown and restarted during runtime.
if (SDL_WasInit(SDL_INIT_GAMECONTROLLER) == SDL_INIT_GAMECONTROLLER)
{
@ -1322,14 +1019,28 @@ static char joyname[255]; // joystick name is straight from the driver
const char *I_GetJoyName(INT32 joyindex)
{
const char *tempname = NULL;
SDL_Joystick* joystick;
joyname[0] = 0;
joyindex--; //SDL's Joystick System starts at 0, not 1
if (SDL_WasInit(SDL_INIT_JOYSTICK) == SDL_INIT_JOYSTICK)
if (SDL_WasInit(SDL_INIT_JOYSTICK) != SDL_INIT_JOYSTICK)
{
tempname = SDL_JoystickNameForIndex(joyindex);
if (tempname)
strncpy(joyname, tempname, 255);
return joyname;
}
// joyindex corresponds to the open joystick *instance* ID, not the joystick number
joystick = SDL_JoystickFromInstanceID(joyindex);
if (joystick == NULL)
{
return joyname;
}
tempname = SDL_JoystickNameForIndex(joyindex);
if (tempname)
{
strncpy(joyname, tempname, 255);
}
return joyname;
}

View file

@ -542,7 +542,7 @@ static void Impl_HandleWindowEvent(SDL_WindowEvent evt)
SDLforceUngrabMouse();
}
memset(gamekeydown, 0, sizeof(gamekeydown)); // TODO this is a scary memset
memset(deviceResponding, false, sizeof (deviceResponding));
G_ResetAllDeviceResponding();
if (MOUSE_MENU)
{
@ -701,7 +701,7 @@ static void Impl_HandleControllerAxisEvent(SDL_ControllerAxisEvent evt)
event_t event;
INT32 value;
event.type = ev_joystick;
event.type = ev_gamepad_axis;
event.device = 1 + evt.which;
if (event.device == INT32_MAX)
@ -777,6 +777,40 @@ static void Impl_HandleControllerButtonEvent(SDL_ControllerButtonEvent evt, Uint
}
}
static void Impl_HandleControllerDeviceAddedEvent(SDL_ControllerDeviceEvent event)
{
// The game is always interested in controller events, even if they aren't internally assigned to a player.
// Thus, we *always* open SDL controllers as they become available, to begin receiving their events.
SDL_GameController* controller = SDL_GameControllerOpen(event.which);
if (controller == NULL)
{
return;
}
SDL_Joystick* joystick = SDL_GameControllerGetJoystick(controller);
SDL_JoystickID joystick_instance_id = SDL_JoystickInstanceID(joystick);
event_t engine_event {};
engine_event.type = ev_gamepad_device_added;
engine_event.device = 1 + joystick_instance_id;
D_PostEvent(&engine_event);
}
static void Impl_HandleControllerDeviceRemovedEvent(SDL_ControllerDeviceEvent event)
{
// SDL only posts Device Removed events for controllers that have actually been opened.
// Thus, we don't need to filter out controllers that may not have opened successfully prior to this event.
event_t engine_event {};
engine_event.type = ev_gamepad_device_removed;
engine_event.device = 1 + event.which;
D_PostEvent(&engine_event);
}
static ImGuiKey ImGui_ImplSDL2_KeycodeToImGuiKey(int keycode)
{
switch (keycode)
@ -983,8 +1017,6 @@ void I_GetEvent(void)
// otherwise we'll end up catching the warp back to center.
//int mouseMotionOnce = 0;
UINT8 i;
if (!graphics_started)
{
return;
@ -1031,147 +1063,14 @@ void I_GetEvent(void)
Impl_HandleControllerButtonEvent(evt.cbutton, evt.type);
break;
////////////////////////////////////////////////////////////
case SDL_CONTROLLERDEVICEADDED:
{
// OH BOY are you in for a good time! #abominationstation
SDL_GameController *newcontroller = SDL_GameControllerOpen(evt.cdevice.which);
CONS_Debug(DBG_GAMELOGIC, "Controller device index %d added\n", evt.cdevice.which + 1);
////////////////////////////////////////////////////////////
// Because SDL's device index is unstable, we're going to cheat here a bit:
// For the first joystick setting that is NOT active:
//
// 1. Set cv_usejoystickX.value to the new device index (this does not change what is written to config.cfg)
//
// 2. Set OTHERS' cv_usejoystickX.value to THEIR new device index, because it likely changed
// * If device doesn't exist, switch cv_usejoystick back to default value (.string)
// * BUT: If that default index is being occupied, use ANOTHER cv_usejoystick's default value!
////////////////////////////////////////////////////////////
for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
{
if (newcontroller && (!JoyInfo[i].dev || !SDL_GameControllerGetAttached(JoyInfo[i].dev)))
{
UINT8 j;
for (j = 0; j < MAXSPLITSCREENPLAYERS; j++)
{
if (i == j)
continue;
if (JoyInfo[j].dev == newcontroller)
break;
}
if (j == MAXSPLITSCREENPLAYERS)
{
// ensures we aren't overriding a currently active device
cv_usejoystick[i].value = evt.cdevice.which + 1;
I_UpdateJoystickDeviceIndices(0);
}
}
}
////////////////////////////////////////////////////////////
// Was cv_usejoystick disabled in settings?
////////////////////////////////////////////////////////////
for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
{
if (!strcmp(cv_usejoystick[i].string, "0") || !cv_usejoystick[i].value)
cv_usejoystick[i].value = 0;
else if (atoi(cv_usejoystick[i].string) <= I_NumJoys() // don't mess if we intentionally set higher than NumJoys
&& cv_usejoystick[i].value) // update the cvar ONLY if a device exists
CV_SetValue(&cv_usejoystick[i], cv_usejoystick[i].value);
}
////////////////////////////////////////////////////////////
// Update all joysticks' init states
// This is a little wasteful since cv_usejoystick already calls this, but
// we need to do this in case CV_SetValue did nothing because the string was already same.
// if the device is already active, this should do nothing, effectively.
////////////////////////////////////////////////////////////
for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
I_InitJoystick(i);
////////////////////////////////////////////////////////////
for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
CONS_Debug(DBG_GAMELOGIC, "Joystick%d device index: %d\n", i+1, JoyInfo[i].oldjoy);
#if 0
// update the menu
if (currentMenu == &OP_JoystickSetDef)
M_SetupJoystickMenu(0);
#endif
for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
{
if (JoyInfo[i].dev == newcontroller)
break;
}
if (i == MAXSPLITSCREENPLAYERS)
I_StoreExJoystick(newcontroller);
}
Impl_HandleControllerDeviceAddedEvent(evt.cdevice);
break;
////////////////////////////////////////////////////////////
case SDL_CONTROLLERDEVICEREMOVED:
for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
{
if (JoyInfo[i].dev && !SDL_GameControllerGetAttached(JoyInfo[i].dev))
{
CONS_Debug(DBG_GAMELOGIC, "Joystick%d removed, device index: %d\n", i+1, JoyInfo[i].oldjoy);
I_ShutdownJoystick(i);
}
}
////////////////////////////////////////////////////////////
// Update the device indexes, because they likely changed
// * If device doesn't exist, switch cv_usejoystick back to default value (.string)
// * BUT: If that default index is being occupied, use ANOTHER cv_usejoystick's default value!
////////////////////////////////////////////////////////////
for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
{
I_UpdateJoystickDeviceIndex(i);
}
////////////////////////////////////////////////////////////
// Was cv_usejoystick disabled in settings?
////////////////////////////////////////////////////////////
for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
{
if (!strcmp(cv_usejoystick[i].string, "0"))
{
cv_usejoystick[i].value = 0;
}
else if (atoi(cv_usejoystick[i].string) <= I_NumJoys() // don't mess if we intentionally set higher than NumJoys
&& cv_usejoystick[i].value) // update the cvar ONLY if a device exists
{
CV_SetValue(&cv_usejoystick[i], cv_usejoystick[i].value);
}
}
////////////////////////////////////////////////////////////
for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
CONS_Debug(DBG_GAMELOGIC, "Joystick%d device index: %d\n", i+1, JoyInfo[i].oldjoy);
#if 0
// update the menu
if (currentMenu == &OP_JoystickSetDef)
M_SetupJoystickMenu(0);
#endif
Impl_HandleControllerDeviceRemovedEvent(evt.cdevice);
break;
case SDL_QUIT:
LUA_HookBool(true, HOOK(GameQuit));
I_Quit();
@ -1195,10 +1094,7 @@ void I_GetEvent(void)
// In order to make wheels act like buttons, we have to set their state to Up.
// This is because wheel messages don't have an up/down state.
for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
{
gamekeydown[i][KEY_MOUSEWHEELDOWN] = gamekeydown[i][KEY_MOUSEWHEELUP] = 0;
}
G_GetDeviceGameKeyDownArray(0)[KEY_MOUSEWHEELDOWN] = G_GetDeviceGameKeyDownArray(0)[KEY_MOUSEWHEELUP] = 0;
}
static void half_warp_mouse(uint16_t x, uint16_t y) {

View file

@ -57,7 +57,8 @@ static shared_ptr<Mixer<2>> mixer_sound_effects;
static shared_ptr<Mixer<2>> mixer_music;
static shared_ptr<MusicPlayer> music_player;
static shared_ptr<Gain<2>> gain_sound_effects;
static shared_ptr<Gain<2>> gain_music;
static shared_ptr<Gain<2>> gain_music_player;
static shared_ptr<Gain<2>> gain_music_channel;
static vector<shared_ptr<SoundEffectPlayer>> sound_effect_channels;
@ -187,12 +188,14 @@ void initialize_sound()
mixer_music = make_shared<Mixer<2>>();
music_player = make_shared<MusicPlayer>();
gain_sound_effects = make_shared<Gain<2>>();
gain_music = make_shared<Gain<2>>();
gain_music_player = make_shared<Gain<2>>();
gain_music_channel = make_shared<Gain<2>>();
gain_sound_effects->bind(mixer_sound_effects);
gain_music->bind(mixer_music);
gain_music_player->bind(music_player);
gain_music_channel->bind(mixer_music);
master->add_source(gain_sound_effects);
master->add_source(gain_music);
mixer_music->add_source(music_player);
master->add_source(gain_music_channel);
mixer_music->add_source(gain_music_player);
for (size_t i = 0; i < static_cast<size_t>(cv_numChannels.value); i++)
{
shared_ptr<SoundEffectPlayer> player = make_shared<SoundEffectPlayer>();
@ -356,7 +359,7 @@ void I_SetSfxVolume(int volume)
if (gain_sound_effects)
{
gain_sound_effects->gain(vol * vol * vol);
gain_sound_effects->gain(std::clamp(vol * vol * vol, 0.f, 1.f));
}
}
@ -597,6 +600,12 @@ boolean I_LoadSong(char* data, size_t len)
return false;
}
if (gain_music_player)
{
// Reset song volume to 1.0 for newly loaded songs.
gain_music_player->gain(1.0);
}
return true;
}
@ -663,9 +672,22 @@ void I_SetMusicVolume(int volume)
{
float vol = static_cast<float>(volume) / 100.f;
if (gain_music)
if (gain_music_channel)
{
gain_music->gain(vol * vol * vol);
// Music channel volume is interpreted as logarithmic rather than linear.
// We approximate by cubing the gain level so vol 50 roughly sounds half as loud.
gain_music_channel->gain(std::clamp(vol * vol * vol, 0.f, 1.f));
}
}
void I_SetCurrentSongVolume(int volume)
{
float vol = static_cast<float>(volume) / 100.f;
if (gain_music_player)
{
// However, different from music channel volume, musicdef volumes are explicitly linear.
gain_music_player->gain(std::max(vol, 0.f));
}
}

View file

@ -36,9 +36,6 @@ extern "C" {
#define SDL2STUB() CONS_Printf("SDL2: stubbed: %s:%d\n", __func__, __LINE__)
#endif
// So m_menu knows whether to store cv_usejoystick value or string
#define JOYSTICK_HOTPLUG
/** \brief The JoyInfo_s struct
info about joystick
@ -65,9 +62,6 @@ typedef struct SDLJoyInfo_s
/** \brief SDL info about controllers
*/
extern SDLJoyInfo_t JoyInfo[MAXSPLITSCREENPLAYERS];
extern SDL_GameController *ExJoystick[MAXGAMEPADS];
void I_StoreExJoystick(SDL_GameController *dev);
/** \brief joystick axis deadzone
*/
@ -76,18 +70,6 @@ void I_StoreExJoystick(SDL_GameController *dev);
void I_GetConsoleEvents(void);
// So we can call this from i_video event loop
void I_ShutdownJoystick(UINT8 index);
// Cheat to get the device index for a game controller handle
INT32 I_GetJoystickDeviceIndex(SDL_GameController *dev);
// Quick thing to make SDL_JOYDEVICEADDED events less of an abomination
void I_UpdateJoystickDeviceIndex(UINT8 player);
void I_UpdateJoystickDeviceIndices(UINT8 excludePlayer);
void I_GetConsoleEvents(void);
void SDLforceUngrabMouse(void);
// Needed for some WIN32 functions

View file

@ -1135,6 +1135,7 @@ sfxinfo_t S_sfx[NUMSFX] =
{"waved2", false, 32, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},
{"waved3", false, 32, 64, -1, NULL, 0, -1, -1, LUMPERROR, ""},
{"waved4", false, 32, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},
{"waved5", false, 32, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},
// Passing sounds
{"pass01", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},

View file

@ -1204,6 +1204,7 @@ typedef enum
sfx_waved2,
sfx_waved3,
sfx_waved4,
sfx_waved5,
// Passing sounds
sfx_pass01,