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

This commit is contained in:
toaster 2023-07-19 22:46:49 +01:00
commit ba7307b425
16 changed files with 196 additions and 122 deletions

View file

@ -917,7 +917,7 @@ static void COM_Help_f(void)
boolean follower = (cvar->PossibleValue == Followercolor_cons_t); boolean follower = (cvar->PossibleValue == Followercolor_cons_t);
for (i = SKINCOLOR_NONE; i < numskincolors; ++i) for (i = SKINCOLOR_NONE; i < numskincolors; ++i)
{ {
if (K_ColorUsable(i, follower) == true) if (K_ColorUsable(i, follower, true) == true)
{ {
CONS_Printf(" %-3d : %s\n", i, skincolors[i].name); CONS_Printf(" %-3d : %s\n", i, skincolors[i].name);
if (i == cvar->value) if (i == cvar->value)

View file

@ -118,7 +118,7 @@ UINT32 playerpingtable[MAXPLAYERS]; //table of player latency values.
#define GENTLEMANSMOOTHING (TICRATE) #define GENTLEMANSMOOTHING (TICRATE)
static tic_t reference_lag; static tic_t reference_lag;
static UINT8 spike_time; static UINT8 spike_time;
static tic_t lowest_lag; static tic_t lowest_lag;
boolean server_lagless; boolean server_lagless;
static CV_PossibleValue_t mindelay_cons_t[] = {{0, "MIN"}, {30, "MAX"}, {0, NULL}}; static CV_PossibleValue_t mindelay_cons_t[] = {{0, "MIN"}, {30, "MAX"}, {0, NULL}};
@ -266,7 +266,7 @@ shouldsign_t ShouldSignChallenge(uint8_t *message)
#ifndef SRB2_LITTLE_ENDIAN #ifndef SRB2_LITTLE_ENDIAN
#error "FIXME: 64-bit timestamp field is not supported on Big Endian" #error "FIXME: 64-bit timestamp field is not supported on Big Endian"
#endif #endif
UINT64 then, now; UINT64 then, now;
UINT32 claimedIP, realIP; UINT32 claimedIP, realIP;
@ -2280,7 +2280,7 @@ static void CL_ConnectToServer(void)
{ {
PR_ApplyProfile(cv_lastprofile[i].value, i); PR_ApplyProfile(cv_lastprofile[i].value, i);
} }
// Slightly sucks that we have to duplicate these from d_main.c, but // Slightly sucks that we have to duplicate these from d_main.c, but
// the change to cv_lastprofile doesn't take in time for this codepath. // the change to cv_lastprofile doesn't take in time for this codepath.
if (M_CheckParm("-profile")) if (M_CheckParm("-profile"))
@ -2775,20 +2775,36 @@ void CL_ClearPlayer(INT32 playernum)
K_RemoveFollower(&players[playernum]); K_RemoveFollower(&players[playernum]);
} }
if (players[playernum].mo) #define PlayerPointerRemove(field) \
{ if (field) \
P_RemoveMobj(players[playernum].mo); { \
P_SetTarget(&players[playernum].mo, NULL); P_RemoveMobj(field); \
P_SetTarget(&field, NULL); \
} }
// These are mostly subservient to the player, and may not clean themselves up.
PlayerPointerRemove(players[playernum].mo);
PlayerPointerRemove(players[playernum].followmobj);
PlayerPointerRemove(players[playernum].stumbleIndicator);
PlayerPointerRemove(players[playernum].sliptideZipIndicator);
#undef PlayerPointerRemove
// These have thinkers of their own.
P_SetTarget(&players[playernum].whip, NULL);
P_SetTarget(&players[playernum].hand, NULL);
P_SetTarget(&players[playernum].hoverhyudoro, NULL);
P_SetTarget(&players[playernum].ringShooter, NULL);
// TODO: Any better handling in store?
P_SetTarget(&players[playernum].flickyAttacker, NULL);
P_SetTarget(&players[playernum].powerup.flickyController, NULL);
// These are camera items and possibly belong to multiple players.
P_SetTarget(&players[playernum].skybox.viewpoint, NULL); P_SetTarget(&players[playernum].skybox.viewpoint, NULL);
P_SetTarget(&players[playernum].skybox.centerpoint, NULL); P_SetTarget(&players[playernum].skybox.centerpoint, NULL);
P_SetTarget(&players[playernum].awayview.mobj, NULL); P_SetTarget(&players[playernum].awayview.mobj, NULL);
P_SetTarget(&players[playernum].followmobj, NULL);
P_SetTarget(&players[playernum].hoverhyudoro, NULL);
P_SetTarget(&players[playernum].stumbleIndicator, NULL);
P_SetTarget(&players[playernum].sliptideZipIndicator, NULL);
P_SetTarget(&players[playernum].ringShooter, NULL);
} }
// Handle parties. // Handle parties.
@ -4029,7 +4045,7 @@ static void Got_AddBot(UINT8 **p, INT32 playernum)
K_SetBot(newplayernum, skinnum, difficulty, style); K_SetBot(newplayernum, skinnum, difficulty, style);
} }
static boolean SV_AddWaitingPlayers(SINT8 node, UINT8 *availabilities, static boolean SV_AddWaitingPlayers(SINT8 node, UINT8 *availabilities,
const char *name, uint8_t *key, UINT16 *pwr, const char *name, uint8_t *key, UINT16 *pwr,
const char *name2, uint8_t *key2, UINT16 *pwr2, const char *name2, uint8_t *key2, UINT16 *pwr2,
const char *name3, uint8_t *key3, UINT16 *pwr3, const char *name3, uint8_t *key3, UINT16 *pwr3,
@ -4192,9 +4208,15 @@ boolean SV_SpawnServer(void)
serverrunning = true; serverrunning = true;
SV_ResetServer(); SV_ResetServer();
SV_GenContext(); SV_GenContext();
if (netgame && I_NetOpenSocket) if (netgame)
{ {
I_NetOpenSocket(); if (I_NetOpenSocket)
{
I_NetOpenSocket();
}
ourIP = 0;
STUN_bind(GotOurIP);
} }
// non dedicated server just connect to itself // non dedicated server just connect to itself
@ -4203,9 +4225,7 @@ boolean SV_SpawnServer(void)
else doomcom->numslots = 1; else doomcom->numslots = 1;
} }
ourIP = 0;
if (netgame && server)
STUN_bind(GotOurIP);
// strictly speaking, i'm not convinced the following is necessary // strictly speaking, i'm not convinced the following is necessary
// but I'm not confident enough to remove it entirely in case it breaks something // but I'm not confident enough to remove it entirely in case it breaks something
@ -4438,19 +4458,19 @@ static void HandleConnect(SINT8 node)
memcpy(lastReceivedKey[node][i], PR_GetLocalPlayerProfile(i)->public_key, sizeof(lastReceivedKey[node][i])); memcpy(lastReceivedKey[node][i], PR_GetLocalPlayerProfile(i)->public_key, sizeof(lastReceivedKey[node][i]));
} }
else // Remote player, gotta check their signature. else // Remote player, gotta check their signature.
{ {
if (PR_IsKeyGuest(lastReceivedKey[node][i])) // IsSplitPlayerOnNodeGuest isn't appropriate here, they're not in-game yet! if (PR_IsKeyGuest(lastReceivedKey[node][i])) // IsSplitPlayerOnNodeGuest isn't appropriate here, they're not in-game yet!
{ {
if (!cv_allowguests.value) if (!cv_allowguests.value)
{ {
SV_SendRefuse(node, M_GetText("The server doesn't allow GUESTs.\nCreate a profile to join!")); SV_SendRefuse(node, M_GetText("The server doesn't allow GUESTs.\nCreate a profile to join!"));
return; return;
} }
sigcheck = 0; // Always succeeds. Yes, this is a success response. C R Y P T O sigcheck = 0; // Always succeeds. Yes, this is a success response. C R Y P T O
} }
else else
{ {
sigcheck = crypto_eddsa_check(netbuffer->u.clientcfg.challengeResponse[i], lastReceivedKey[node][i], lastSentChallenge[node], CHALLENGELENGTH); sigcheck = crypto_eddsa_check(netbuffer->u.clientcfg.challengeResponse[i], lastReceivedKey[node][i], lastSentChallenge[node], CHALLENGELENGTH);
} }
@ -4473,7 +4493,7 @@ static void HandleConnect(SINT8 node)
CONS_Alert(CONS_WARNING, "Joining player's pubkey matches existing player, stat updates will be nonsense!\n"); CONS_Alert(CONS_WARNING, "Joining player's pubkey matches existing player, stat updates will be nonsense!\n");
#else #else
SV_SendRefuse(node, M_GetText("Duplicate pubkey already on server.\n(Did you share your profile?)")); SV_SendRefuse(node, M_GetText("Duplicate pubkey already on server.\n(Did you share your profile?)"));
return; return;
#endif #endif
} }
} }
@ -4491,7 +4511,7 @@ static void HandleConnect(SINT8 node)
CONS_Alert(CONS_WARNING, "Players with same pubkey in the joning party, stat updates will be nonsense!\n"); CONS_Alert(CONS_WARNING, "Players with same pubkey in the joning party, stat updates will be nonsense!\n");
#else #else
SV_SendRefuse(node, M_GetText("Duplicate pubkey in local party.\n(How did you even do this?)")); SV_SendRefuse(node, M_GetText("Duplicate pubkey in local party.\n(How did you even do this?)"));
return; return;
#endif #endif
} }
} }
@ -5013,7 +5033,7 @@ static void PT_Say(int node)
return; return;
} }
stop_spamming[say.source] = 4; stop_spamming[say.source] = 4;
serverplayer_t *stats = SV_GetStatsByPlayerIndex(say.source); serverplayer_t *stats = SV_GetStatsByPlayerIndex(say.source);
@ -5044,7 +5064,7 @@ static char NodeToSplitPlayer(int node, int split)
else if (split == 3) else if (split == 3)
return nodetoplayer4[node]; return nodetoplayer4[node];
return -1; return -1;
} }
/** Handles a packet received from a node that is in game /** Handles a packet received from a node that is in game
* *
@ -5073,7 +5093,7 @@ static void HandlePacketFromPlayer(SINT8 node)
if (netconsole >= MAXPLAYERS) if (netconsole >= MAXPLAYERS)
I_Error("bad table nodetoplayer: node %d player %d", doomcom->remotenode, netconsole); I_Error("bad table nodetoplayer: node %d player %d", doomcom->remotenode, netconsole);
#endif #endif
#ifdef SIGNGAMETRAFFIC #ifdef SIGNGAMETRAFFIC
if (server) if (server)
@ -5097,10 +5117,10 @@ static void HandlePacketFromPlayer(SINT8 node)
{ {
if (crypto_eddsa_check(netbuffer->signature[splitnodes], players[targetplayer].public_key, message, doomcom->datalength - BASEPACKETSIZE)) if (crypto_eddsa_check(netbuffer->signature[splitnodes], players[targetplayer].public_key, message, doomcom->datalength - BASEPACKETSIZE))
{ {
CONS_Alert(CONS_ERROR, "SIGFAIL! Packet type %d from node %d player %d\nkey %s size %d netconsole %d\n", CONS_Alert(CONS_ERROR, "SIGFAIL! Packet type %d from node %d player %d\nkey %s size %d netconsole %d\n",
netbuffer->packettype, node, splitnodes, netbuffer->packettype, node, splitnodes,
GetPrettyRRID(players[targetplayer].public_key, true), doomcom->datalength - BASEPACKETSIZE, netconsole); GetPrettyRRID(players[targetplayer].public_key, true), doomcom->datalength - BASEPACKETSIZE, netconsole);
// Something scary can happen when multiple kicks that resolve to the same node are processed in quick succession. // Something scary can happen when multiple kicks that resolve to the same node are processed in quick succession.
// Sometimes, a kick will still be left to process after the player's been disposed, and that causes the kick to resolve on the server instead! // Sometimes, a kick will still be left to process after the player's been disposed, and that causes the kick to resolve on the server instead!
// This sucks, so we check for a stale/misfiring kick beforehand. // This sucks, so we check for a stale/misfiring kick beforehand.
@ -5111,7 +5131,7 @@ static void HandlePacketFromPlayer(SINT8 node)
return; return;
} }
} }
} }
} }
} }
@ -5635,7 +5655,7 @@ static void HandlePacketFromPlayer(SINT8 node)
SendKick(targetplayer, KICK_MSG_SIGFAIL); SendKick(targetplayer, KICK_MSG_SIGFAIL);
break; break;
} }
else else
{ {
memcpy(lastReceivedSignature[targetplayer], netbuffer->u.responseall.signature[responseplayer], sizeof(lastReceivedSignature[targetplayer])); memcpy(lastReceivedSignature[targetplayer], netbuffer->u.responseall.signature[responseplayer], sizeof(lastReceivedSignature[targetplayer]));
} }
@ -5987,7 +6007,7 @@ static void CL_SendClientCmd(void)
lagDelay = reference_lag; lagDelay = reference_lag;
} }
} }
else else
{ {
reference_lag = lagDelay; // Adjust quickly if the connection improves. reference_lag = lagDelay; // Adjust quickly if the connection improves.
spike_time = 0; spike_time = 0;
@ -6601,10 +6621,10 @@ static void KickUnverifiedPlayers(void)
SendKick(i, KICK_MSG_SIGFAIL); SendKick(i, KICK_MSG_SIGFAIL);
} }
} }
} }
} }
// //
static void SendChallengeResults(void) static void SendChallengeResults(void)
{ {
int i; int i;
@ -7130,5 +7150,5 @@ void SendServerNotice(SINT8 target, char *message)
{ {
if (client) if (client)
return; return;
DoSayCommand(message, target + 1, HU_PRIVNOTICE, servernode); DoSayCommand(message, target + 1, HU_PRIVNOTICE, servernode);
} }

View file

@ -1494,9 +1494,9 @@ static void SendNameAndColor(const UINT8 n)
UINT16 sendFollowerColor = cv_followercolor[n].value; UINT16 sendFollowerColor = cv_followercolor[n].value;
// don't allow inaccessible colors // don't allow inaccessible colors
if (sendColor != SKINCOLOR_NONE && K_ColorUsable(sendColor, false) == false) if (sendColor != SKINCOLOR_NONE && K_ColorUsable(sendColor, false, true) == false)
{ {
if (player->skincolor && K_ColorUsable(player->skincolor, false) == true) if (player->skincolor && K_ColorUsable(player->skincolor, false, true) == true)
{ {
// Use our previous color // Use our previous color
CV_StealthSetValue(&cv_playercolor[n], player->skincolor); CV_StealthSetValue(&cv_playercolor[n], player->skincolor);
@ -1511,7 +1511,7 @@ static void SendNameAndColor(const UINT8 n)
} }
// ditto for follower colour: // ditto for follower colour:
if (sendFollowerColor != SKINCOLOR_NONE && K_ColorUsable(sendFollowerColor, true) == false) if (sendFollowerColor != SKINCOLOR_NONE && K_ColorUsable(sendFollowerColor, true, true) == false)
{ {
CV_StealthSet(&cv_followercolor[n], "Default"); // set it to "Default". I don't care about your stupidity! CV_StealthSet(&cv_followercolor[n], "Default"); // set it to "Default". I don't care about your stupidity!
sendFollowerColor = cv_followercolor[n].value; sendFollowerColor = cv_followercolor[n].value;
@ -1721,7 +1721,7 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum)
boolean kick = false; boolean kick = false;
// don't allow inaccessible colors // don't allow inaccessible colors
if (K_ColorUsable(p->skincolor, false) == false) if (K_ColorUsable(p->skincolor, false, false) == false)
{ {
kick = true; kick = true;
} }
@ -6826,7 +6826,7 @@ static void Color_OnChange(const UINT8 p)
I_Assert(p < MAXSPLITSCREENPLAYERS); I_Assert(p < MAXSPLITSCREENPLAYERS);
UINT16 color = cv_playercolor[p].value; UINT16 color = cv_playercolor[p].value;
boolean colorisgood = (color == SKINCOLOR_NONE || K_ColorUsable(color, false) == true); boolean colorisgood = (color == SKINCOLOR_NONE || K_ColorUsable(color, false, true) == true);
if (Playing() && splitscreen < p) if (Playing() && splitscreen < p)
{ {

View file

@ -2455,10 +2455,8 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
INT32 followerskin; INT32 followerskin;
UINT16 followercolor; UINT16 followercolor;
mobj_t *follower; // old follower, will probably be removed by the time we're dead but you never know. mobj_t *ringShooter, *hoverhyudoro;
mobj_t *hoverhyudoro; mobj_t *skyboxviewpoint, *skyboxcenterpoint;
mobj_t *skyboxviewpoint;
mobj_t *skyboxcenterpoint;
INT32 charflags; INT32 charflags;
UINT32 followitem; UINT32 followitem;
@ -2677,22 +2675,40 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
if (!betweenmaps) if (!betweenmaps)
{ {
follower = players[player].follower; K_RemoveFollower(&players[player]);
P_SetTarget(&players[player].follower, NULL);
P_SetTarget(&players[player].awayview.mobj, NULL); #define PlayerPointerRemove(field) \
P_SetTarget(&players[player].stumbleIndicator, NULL); if (field) \
{ \
P_RemoveMobj(field); \
P_SetTarget(&field, NULL); \
}
// These are mostly subservient to the player, and may not clean themselves up.
PlayerPointerRemove(players[player].followmobj);
PlayerPointerRemove(players[player].stumbleIndicator);
PlayerPointerRemove(players[player].sliptideZipIndicator);
#undef PlayerPointerRemove
// These will erase themselves.
P_SetTarget(&players[player].whip, NULL); P_SetTarget(&players[player].whip, NULL);
P_SetTarget(&players[player].hand, NULL); P_SetTarget(&players[player].hand, NULL);
P_SetTarget(&players[player].ringShooter, NULL);
P_SetTarget(&players[player].followmobj, NULL);
// TODO: Any better handling in store?
P_SetTarget(&players[player].awayview.mobj, NULL);
P_SetTarget(&players[player].flickyAttacker, NULL);
P_SetTarget(&players[player].powerup.flickyController, NULL);
// The following pointers are safe to set directly, because the end goal should be refcount consistency before and after remanifestation.
ringShooter = players[player].ringShooter;
hoverhyudoro = players[player].hoverhyudoro; hoverhyudoro = players[player].hoverhyudoro;
skyboxviewpoint = players[player].skybox.viewpoint; skyboxviewpoint = players[player].skybox.viewpoint;
skyboxcenterpoint = players[player].skybox.centerpoint; skyboxcenterpoint = players[player].skybox.centerpoint;
} }
else else
{ {
follower = hoverhyudoro = NULL; ringShooter = hoverhyudoro = NULL;
skyboxviewpoint = skyboxcenterpoint = NULL; skyboxviewpoint = skyboxcenterpoint = NULL;
} }
@ -2769,9 +2785,8 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
if (saveroundconditions) if (saveroundconditions)
memcpy(&p->roundconditions, &roundconditions, sizeof (p->roundconditions)); memcpy(&p->roundconditions, &roundconditions, sizeof (p->roundconditions));
if (follower) // See above comment about refcount consistency.
P_RemoveMobj(follower); p->ringShooter = ringShooter;
p->hoverhyudoro = hoverhyudoro; p->hoverhyudoro = hoverhyudoro;
p->skybox.viewpoint = skyboxviewpoint; p->skybox.viewpoint = skyboxviewpoint;
p->skybox.centerpoint = skyboxcenterpoint; p->skybox.centerpoint = skyboxcenterpoint;

View file

@ -217,11 +217,11 @@ void K_GenerateKartColormap(UINT8 *dest_colormap, INT32 skinnum, UINT8 color)
} }
/*-------------------------------------------------- /*--------------------------------------------------
boolean K_ColorUsable(skincolornum_t color, boolean follower) boolean K_ColorUsable(skincolornum_t color, boolean follower, boolean locked)
See header file for description. See header file for description.
--------------------------------------------------*/ --------------------------------------------------*/
boolean K_ColorUsable(skincolornum_t color, boolean follower) boolean K_ColorUsable(skincolornum_t color, boolean follower, boolean locked)
{ {
INT32 i = MAXUNLOCKABLES; INT32 i = MAXUNLOCKABLES;
@ -237,7 +237,7 @@ boolean K_ColorUsable(skincolornum_t color, boolean follower)
return false; return false;
} }
if (demo.playback) if (demo.playback || !locked)
{ {
// Simplifies things elsewhere... // Simplifies things elsewhere...
return true; return true;

View file

@ -117,7 +117,7 @@ void K_GenerateKartColormap(UINT8 *dest_colormap, INT32 skinnum, UINT8 color);
/*-------------------------------------------------- /*--------------------------------------------------
boolean K_ColorUsable(skincolornum_t color, skin_t *skin, follower_t *follower); boolean K_ColorUsable(skincolornum_t color, skin_t *skin, follower_t *follower, boolean locked);
Determines whenever or not we meet the unlockable conditions Determines whenever or not we meet the unlockable conditions
to use a certain color. to use a certain color.
@ -125,12 +125,13 @@ void K_GenerateKartColormap(UINT8 *dest_colormap, INT32 skinnum, UINT8 color);
Input Arguments:- Input Arguments:-
color - Color we want to use. color - Color we want to use.
follower - Set to include the special follower-only color options. follower - Set to include the special follower-only color options.
locked - use local player's unlocks.
Return:- Return:-
true if we can use it, otherwise false. true if we can use it, otherwise false.
--------------------------------------------------*/ --------------------------------------------------*/
boolean K_ColorUsable(skincolornum_t color, boolean follower); boolean K_ColorUsable(skincolornum_t color, boolean follower, boolean locked);
#ifdef __cplusplus #ifdef __cplusplus

View file

@ -722,7 +722,7 @@ void K_FollowerHornTaunt(player_t *taunter, player_t *victim)
|| victim == NULL || victim == NULL
|| taunter->followerskin < 0 || taunter->followerskin < 0
|| taunter->followerskin >= numfollowers || taunter->followerskin >= numfollowers
|| (P_IsLocalPlayer(victim) == false && cv_karthorns.value != 2) || (P_IsDisplayPlayer(victim) == false && cv_karthorns.value != 2)
|| P_MobjWasRemoved(taunter->mo) == true || P_MobjWasRemoved(taunter->mo) == true
|| P_MobjWasRemoved(taunter->follower) == true || P_MobjWasRemoved(taunter->follower) == true
) )

View file

@ -3994,8 +3994,6 @@ static void K_drawKartMinimap(void)
if (doprogressionbar == false) if (doprogressionbar == false)
{ {
angle_t ang = R_InterpolateAngle(mobj->old_angle, mobj->angle);
interpx = R_InterpolateFixed(mobj->old_x, mobj->x); interpx = R_InterpolateFixed(mobj->old_x, mobj->x);
interpy = R_InterpolateFixed(mobj->old_y, mobj->y); interpy = R_InterpolateFixed(mobj->old_y, mobj->y);
@ -4010,6 +4008,10 @@ static void K_drawKartMinimap(void)
if (!nocontest) if (!nocontest)
{ {
angle_t ang = R_InterpolateAngle(mobj->old_angle, mobj->angle);
if (encoremode)
ang = ANGLE_180 - ang;
K_drawKartMinimapIcon( K_drawKartMinimapIcon(
interpx, interpx,
interpy, interpy,
@ -4198,8 +4200,6 @@ static void K_drawKartMinimap(void)
if (doprogressionbar == false) if (doprogressionbar == false)
{ {
angle_t ang = R_InterpolateAngle(mobj->old_angle, mobj->angle);
interpx = R_InterpolateFixed(mobj->old_x, mobj->x); interpx = R_InterpolateFixed(mobj->old_x, mobj->x);
interpy = R_InterpolateFixed(mobj->old_y, mobj->y); interpy = R_InterpolateFixed(mobj->old_y, mobj->y);
@ -4214,6 +4214,10 @@ static void K_drawKartMinimap(void)
if (!nocontest) if (!nocontest)
{ {
angle_t ang = R_InterpolateAngle(mobj->old_angle, mobj->angle);
if (encoremode)
ang = ANGLE_180 - ang;
K_drawKartMinimapIcon( K_drawKartMinimapIcon(
interpx, interpx,
interpy, interpy,

View file

@ -8065,9 +8065,6 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
if (player->guardCooldown) if (player->guardCooldown)
player->guardCooldown--; player->guardCooldown--;
if (player->whip && P_MobjWasRemoved(player->whip))
P_SetTarget(&player->whip, NULL);
if (player->startboost > 0 && onground == true) if (player->startboost > 0 && onground == true)
{ {
@ -8326,20 +8323,11 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
player->tripwireState = TRIPSTATE_NONE; player->tripwireState = TRIPSTATE_NONE;
} }
if (player->hand && P_MobjWasRemoved(player->hand))
P_SetTarget(&player->hand, NULL);
if (player->flickyAttacker && P_MobjWasRemoved(player->flickyAttacker))
P_SetTarget(&player->flickyAttacker, NULL);
if (player->powerup.flickyController && P_MobjWasRemoved(player->powerup.flickyController))
P_SetTarget(&player->powerup.flickyController, NULL);
if (player->spectator == false) if (player->spectator == false)
{ {
K_KartEbrakeVisuals(player); K_KartEbrakeVisuals(player);
Obj_ServantHandHandling(player); Obj_ServantHandSpawning(player);
} }
if (K_GetKartButtons(player) & BT_BRAKE && if (K_GetKartButtons(player) & BT_BRAKE &&

View file

@ -144,7 +144,8 @@ void Obj_GachaBomReboundThink(mobj_t *mobj);
void Obj_SpawnGachaBomRebound(mobj_t *source, mobj_t *target); void Obj_SpawnGachaBomRebound(mobj_t *source, mobj_t *target);
/* Servant Hand */ /* Servant Hand */
void Obj_ServantHandHandling(player_t *player); void Obj_ServantHandSpawning(player_t *player);
void Obj_ServantHandThink(mobj_t *hand);
void Obj_PointPlayersToXY(fixed_t x, fixed_t y); void Obj_PointPlayersToXY(fixed_t x, fixed_t y);
/* Super Flicky Controller */ /* Super Flicky Controller */

View file

@ -373,7 +373,7 @@ void PR_LoadProfiles(void)
; // Valid, even outside the bounds ; // Valid, even outside the bounds
} }
else if (profilesList[i]->color >= numskincolors else if (profilesList[i]->color >= numskincolors
|| K_ColorUsable(profilesList[i]->color, false) == false) || K_ColorUsable(profilesList[i]->color, false, false) == false)
{ {
profilesList[i]->color = PROFILEDEFAULTCOLOR; profilesList[i]->color = PROFILEDEFAULTCOLOR;
} }
@ -383,13 +383,13 @@ void PR_LoadProfiles(void)
profilesList[i]->followercolor = READUINT16(save.p); profilesList[i]->followercolor = READUINT16(save.p);
if (profilesList[i]->followercolor == FOLLOWERCOLOR_MATCH if (profilesList[i]->followercolor == FOLLOWERCOLOR_MATCH
|| profilesList[i]->followercolor == FOLLOWERCOLOR_OPPOSITE) || profilesList[i]->followercolor == FOLLOWERCOLOR_OPPOSITE
|| profilesList[i]->followercolor == SKINCOLOR_NONE)
{ {
; // Valid, even outside the bounds ; // Valid, even outside the bounds
} }
else if (profilesList[i]->followercolor >= numskincolors else if (profilesList[i]->followercolor >= numskincolors
|| profilesList[i]->followercolor == SKINCOLOR_NONE || K_ColorUsable(profilesList[i]->followercolor, true, false) == false)
|| K_ColorUsable(profilesList[i]->followercolor, true) == false)
{ {
profilesList[i]->followercolor = PROFILEDEFAULTFOLLOWERCOLOR; profilesList[i]->followercolor = PROFILEDEFAULTFOLLOWERCOLOR;
} }

View file

@ -150,7 +150,7 @@ static void M_NewPlayerColors(setup_player_t *p)
// Add all unlocked colors // Add all unlocked colors
for (i = SKINCOLOR_NONE+1; i < numskincolors; i++) for (i = SKINCOLOR_NONE+1; i < numskincolors; i++)
{ {
if (K_ColorUsable(i, follower) == true) if (K_ColorUsable(i, follower, true) == true)
{ {
M_PushMenuColor(&p->colors, i); M_PushMenuColor(&p->colors, i);
} }

View file

@ -63,8 +63,11 @@ static void RemoveRingShooterPointer(mobj_t *base)
} }
// NULL the player's pointer. // NULL the player's pointer.
player = &players[ rs_base_playerid(base) ]; if (playeringame[ rs_base_playerid(base) ])
P_SetTarget(&player->ringShooter, NULL); {
player = &players[ rs_base_playerid(base) ];
P_SetTarget(&player->ringShooter, NULL);
}
// Remove our player ID // Remove our player ID
rs_base_playerid(base) = -1; rs_base_playerid(base) = -1;

View file

@ -8,7 +8,7 @@
#include "../r_main.h" #include "../r_main.h"
#include "../g_game.h" #include "../g_game.h"
void Obj_ServantHandHandling(player_t *player) void Obj_ServantHandSpawning(player_t *player)
{ {
if (player->pflags & PF_WRONGWAY || player->pflags & PF_POINTME) if (player->pflags & PF_WRONGWAY || player->pflags & PF_POINTME)
{ {
@ -36,71 +36,86 @@ void Obj_ServantHandHandling(player_t *player)
} }
} }
} }
if (player->hand)
{
player->hand->destscale = mapobjectscale;
}
} }
else if (player->handtimer != 0) else if (player->handtimer != 0)
{ {
player->handtimer--; player->handtimer--;
} }
}
void Obj_ServantHandThink(mobj_t *hand)
{
UINT8 handtimer = 0;
player_t *player = NULL;
if (P_MobjWasRemoved(hand->target))
{
P_RemoveMobj(hand);
return;
}
if (hand->target->health && hand->target->player && hand->target->player->hand == hand)
{
player = hand->target->player;
handtimer = hand->target->player->handtimer;
}
if (player->hand)
{ {
const fixed_t handpokespeed = 4; const fixed_t handpokespeed = 4;
const fixed_t looping = handpokespeed - abs((player->hand->threshold % (handpokespeed*2)) - handpokespeed); const fixed_t looping = handpokespeed - abs((hand->threshold % (handpokespeed*2)) - handpokespeed);
fixed_t xoffs = 0, yoffs = 0; fixed_t xoffs = 0, yoffs = 0;
player->hand->color = player->skincolor; if (hand->fuse != 0)
player->hand->angle = player->besthanddirection;
if (player->hand->fuse != 0)
{ {
; ;
} }
else if (looping != 0) else if (looping != 0)
{ {
xoffs = FixedMul(2 * looping * mapobjectscale, FINECOSINE(player->hand->angle >> ANGLETOFINESHIFT)), xoffs = FixedMul(2 * looping * mapobjectscale, FINECOSINE(hand->angle >> ANGLETOFINESHIFT)),
yoffs = FixedMul(2 * looping * mapobjectscale, FINESINE(player->hand->angle >> ANGLETOFINESHIFT)), yoffs = FixedMul(2 * looping * mapobjectscale, FINESINE(hand->angle >> ANGLETOFINESHIFT)),
player->hand->threshold++; hand->threshold++;
} }
else if (player->handtimer == 0) else if (handtimer == 0)
{ {
player->hand->fuse = 8; hand->fuse = 8;
} }
else else
{ {
player->hand->threshold++; hand->threshold++;
} }
if (player->hand->fuse != 0) if (hand->fuse != 0)
{ {
if ((player->hand->fuse > 4) ^ (player->handtimer < TICRATE/2)) if ((hand->fuse > 4) ^ (handtimer < TICRATE/2))
{ {
player->hand->spritexscale = FRACUNIT/3; hand->spritexscale = FRACUNIT/3;
player->hand->spriteyscale = 3*FRACUNIT; hand->spriteyscale = 3*FRACUNIT;
} }
else else
{ {
player->hand->spritexscale = 2*FRACUNIT; hand->spritexscale = 2*FRACUNIT;
player->hand->spriteyscale = FRACUNIT/2; hand->spriteyscale = FRACUNIT/2;
} }
} }
P_MoveOrigin(player->hand, if (player != NULL)
player->mo->x + xoffs, {
player->mo->y + yoffs, hand->color = player->skincolor;
player->mo->z + player->mo->height + 30*mapobjectscale hand->angle = player->besthanddirection;
);
K_FlipFromObject(player->hand, player->mo);
player->hand->sprzoff = player->mo->sprzoff; P_MoveOrigin(hand,
player->mo->x + xoffs,
player->mo->y + yoffs,
player->mo->z + player->mo->height + 30*mapobjectscale
);
K_FlipFromObject(hand, player->mo);
player->hand->renderflags &= ~RF_DONTDRAW; hand->sprzoff = player->mo->sprzoff;
player->hand->renderflags |= (RF_DONTDRAW & ~K_GetPlayerDontDrawFlag(player));
hand->renderflags &= ~RF_DONTDRAW;
hand->renderflags |= (RF_DONTDRAW & ~K_GetPlayerDontDrawFlag(player));
}
} }
} }

View file

@ -7380,6 +7380,13 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
Obj_MantaRingThink(mobj); Obj_MantaRingThink(mobj);
break; break;
} }
case MT_SERVANTHAND:
{
Obj_ServantHandThink(mobj);
if (P_MobjWasRemoved(mobj))
return false;
break;
}
case MT_BALLHOG: case MT_BALLHOG:
{ {
mobj_t *ghost = P_SpawnGhostMobj(mobj); mobj_t *ghost = P_SpawnGhostMobj(mobj);
@ -9831,10 +9838,11 @@ static boolean P_FuseThink(mobj_t *mobj)
} }
case MT_SERVANTHAND: case MT_SERVANTHAND:
{ {
if (!mobj->target if (P_MobjWasRemoved(mobj->target)
|| P_MobjWasRemoved(mobj->target) || !mobj->target->health
|| !mobj->target->player || !mobj->target->player
|| mobj->target->player->handtimer == 0) || mobj->target->player->handtimer == 0
|| mobj->target->player->hand != mobj)
{ {
P_RemoveMobj(mobj); P_RemoveMobj(mobj);
return false; return false;

View file

@ -4249,6 +4249,25 @@ void P_PlayerThink(player_t *player)
player->playerstate = PST_DEAD; player->playerstate = PST_DEAD;
} }
// Erasing invalid player pointers
{
#define PlayerPointerErase(field) \
if (field && P_MobjWasRemoved(field)) \
P_SetTarget(&field, NULL); \
PlayerPointerErase(player->followmobj);
PlayerPointerErase(player->stumbleIndicator);
PlayerPointerErase(player->sliptideZipIndicator);
PlayerPointerErase(player->whip);
PlayerPointerErase(player->hand);
PlayerPointerErase(player->ringShooter);
PlayerPointerErase(player->hoverhyudoro);
PlayerPointerErase(player->flickyAttacker);
PlayerPointerErase(player->powerup.flickyController);
#undef PlayerPointerErase
}
player->old_drawangle = player->drawangle; player->old_drawangle = player->drawangle;
P_TickAltView(&player->awayview); P_TickAltView(&player->awayview);
@ -4302,7 +4321,7 @@ void P_PlayerThink(player_t *player)
else if (player->kickstartaccel < ACCEL_KICKSTART) else if (player->kickstartaccel < ACCEL_KICKSTART)
{ {
player->kickstartaccel++; player->kickstartaccel++;
if ((player->kickstartaccel == ACCEL_KICKSTART) && P_IsLocalPlayer(player)) if ((player->kickstartaccel == ACCEL_KICKSTART) && !K_PlayerUsesBotMovement(player) && P_IsDisplayPlayer(player))
{ {
S_StartSound(NULL, sfx_ding); S_StartSound(NULL, sfx_ding);
} }