diff --git a/src/d_clisrv.c b/src/d_clisrv.c index ebd58844e..1e5787583 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -114,6 +114,8 @@ UINT32 playerpingtable[MAXPLAYERS]; //table of player latency values. static tic_t lowest_lag; boolean server_lagless; +static CV_PossibleValue_t mindelay_cons_t[] = {{0, "MIN"}, {30, "MAX"}, {0, NULL}}; +consvar_t cv_mindelay = CVAR_INIT ("mindelay", "2", CV_SAVE, mindelay_cons_t, NULL); SINT8 nodetoplayer[MAXNETNODES]; SINT8 nodetoplayer2[MAXNETNODES]; // say the numplayer for this node if any (splitscreen) @@ -5648,7 +5650,7 @@ static inline void PingUpdate(void) if (nodeingame[i]) HSendPacket(i, true, 0, sizeof(INT32) * (MAXPLAYERS+1)); - pingmeasurecount = 1; //Reset count + pingmeasurecount = 0; //Reset count } static tic_t gametime = 0; @@ -5662,7 +5664,7 @@ static void UpdatePingTable(void) if (server) { - if (netgame && !(gametime % 35)) // update once per second. + if (Playing() && !(gametime % 35)) // update once per second. PingUpdate(); fastest = 0; @@ -5688,6 +5690,10 @@ static void UpdatePingTable(void) } } + // Don't gentleman below your mindelay + if (fastest < (tic_t)cv_mindelay.value) + fastest = (tic_t)cv_mindelay.value; + pingmeasurecount++; if (server_lagless) @@ -5719,6 +5725,11 @@ static void UpdatePingTable(void) } } } + else // We're a client, handle mindelay on the way out. + { + if ((neededtic - gametic) < (tic_t)cv_mindelay.value) + lowest_lag = cv_mindelay.value - (neededtic - gametic); + } } static void RenewHolePunch(void) diff --git a/src/d_clisrv.h b/src/d_clisrv.h index 72ce18a5c..ca36ba26c 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -445,6 +445,7 @@ extern UINT32 playerpingtable[MAXPLAYERS]; extern tic_t servermaxping; extern boolean server_lagless; +extern consvar_t cv_mindelay; extern consvar_t cv_netticbuffer, cv_allownewplayer, cv_maxconnections, cv_joindelay; extern consvar_t cv_resynchattempts, cv_blamecfail; diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 9dcc990a9..c0057896a 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -972,6 +972,7 @@ void D_RegisterClientCommands(void) CV_RegisterVar(&cv_rollingdemos); CV_RegisterVar(&cv_netstat); CV_RegisterVar(&cv_netticbuffer); + CV_RegisterVar(&cv_mindelay); #ifdef NETGAME_DEVMODE CV_RegisterVar(&cv_fishcake); diff --git a/src/d_player.h b/src/d_player.h index e467121a9..59b9aa921 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -591,6 +591,8 @@ typedef struct player_s UINT8 shrinkLaserDelay; + mobj_t *stumbleIndicator; + #ifdef HWRENDER fixed_t fovadd; // adjust FOV for hw rendering #endif diff --git a/src/deh_tables.c b/src/deh_tables.c index 99cbc8da0..6104df8c0 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -3840,6 +3840,8 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi "S_TRIPWIREBOOST_BLAST_TOP", "S_TRIPWIREBOOST_BLAST_BOTTOM", + "S_SMOOTHLANDING", + // DEZ respawn laser "S_DEZLASER", "S_DEZLASER_TRAIL1", @@ -5387,6 +5389,8 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t "MT_TRIPWIREBOOST", + "MT_SMOOTHLANDING", + "MT_DEZLASER", "MT_WAYPOINT", diff --git a/src/hu_stuff.c b/src/hu_stuff.c index fd57f4a36..1d7f23ac9 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -79,6 +79,7 @@ typedef enum patch_t *pinggfx[5]; // small ping graphic patch_t *mping[5]; // smaller ping graphic patch_t *pingmeasure[2]; // ping measurement graphic +patch_t *pinglocal[2]; // mindelay indecator patch_t *framecounter; patch_t *frameslash; // framerate stuff. Used in screen.c @@ -197,6 +198,9 @@ void HU_LoadGraphics(void) HU_UpdatePatch(&pingmeasure[0], "PINGD"); HU_UpdatePatch(&pingmeasure[1], "PINGMS"); + HU_UpdatePatch(&pinglocal[0], "PINGGFXL"); + HU_UpdatePatch(&pinglocal[1], "MPINGL"); + // fps stuff HU_UpdatePatch(&framecounter, "FRAMER"); HU_UpdatePatch(&frameslash, "FRAMESL"); @@ -2334,37 +2338,65 @@ void HU_Erase(void) static int Ping_gfx_num (int lag) { - if (lag < 2) + if (lag <= 2) return 0; - else if (lag < 4) + else if (lag <= 4) return 1; - else if (lag < 7) + else if (lag <= 7) return 2; - else if (lag < 10) + else if (lag <= 10) return 3; else return 4; } +static int +Ping_gfx_color (int lag) +{ + if (lag <= 2) + return SKINCOLOR_JAWZ; + else if (lag <= 4) + return SKINCOLOR_MINT; + else if (lag <= 7) + return SKINCOLOR_GOLD; + else if (lag <= 10) + return SKINCOLOR_RASPBERRY; + else + return SKINCOLOR_MAGENTA; +} + // // HU_drawPing // -void HU_drawPing(INT32 x, INT32 y, UINT32 lag, INT32 flags) +void HU_drawPing(INT32 x, INT32 y, UINT32 lag, INT32 flags, boolean offline) { UINT8 *colormap = NULL; INT32 measureid = cv_pingmeasurement.value ? 1 : 0; INT32 gfxnum; // gfx to draw + boolean drawlocal = (offline && cv_mindelay.value && lag <= (tic_t)cv_mindelay.value); + + if (!server && lag <= (tic_t)cv_mindelay.value) + { + lag = cv_mindelay.value; + drawlocal = true; + } gfxnum = Ping_gfx_num(lag); if (measureid == 1) V_DrawScaledPatch(x+11 - pingmeasure[measureid]->width, y+9, flags, pingmeasure[measureid]); - V_DrawScaledPatch(x+2, y, flags, pinggfx[gfxnum]); + + if (drawlocal) + V_DrawScaledPatch(x+2, y, flags, pinglocal[0]); + else + V_DrawScaledPatch(x+2, y, flags, pinggfx[gfxnum]); + + colormap = R_GetTranslationColormap(TC_RAINBOW, Ping_gfx_color(lag), GTC_CACHE); if (servermaxping && lag > servermaxping && hu_tick < 4) { // flash ping red if too high - colormap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_RASPBERRY, GTC_CACHE); + colormap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_WHITE, GTC_CACHE); } if (cv_pingmeasurement.value) @@ -2389,7 +2421,16 @@ HU_drawMiniPing (INT32 x, INT32 y, UINT32 lag, INT32 flags) w /= 2; } - patch = mping[Ping_gfx_num(lag)]; + // This looks kinda dumb, but basically: + // Servers with mindelay set modify the ping table. + // Clients with mindelay unset don't, because they can't. + // Both are affected by mindelay, but a client's lag value is pre-adjustment. + if (server && cv_mindelay.value && (tic_t)cv_mindelay.value <= lag) + patch = pinglocal[1]; + else if (!server && cv_mindelay.value && (tic_t)cv_mindelay.value >= lag) + patch = pinglocal[1]; + else + patch = mping[Ping_gfx_num(lag)]; if (( flags & V_SNAPTORIGHT )) x += ( w - SHORT (patch->width) ); diff --git a/src/hu_stuff.h b/src/hu_stuff.h index cc9959467..9bcf45e09 100644 --- a/src/hu_stuff.h +++ b/src/hu_stuff.h @@ -138,7 +138,7 @@ void HU_Drawer(void); char HU_dequeueChatChar(void); void HU_Erase(void); void HU_clearChatChars(void); -void HU_drawPing(INT32 x, INT32 y, UINT32 ping, INT32 flags); // Lat': Ping drawer for scoreboard. +void HU_drawPing(INT32 x, INT32 y, UINT32 ping, INT32 flags, boolean offline); // Lat': Ping drawer for scoreboard. void HU_drawMiniPing(INT32 x, INT32 y, UINT32 ping, INT32 flags); INT32 HU_CreateTeamScoresTbl(playersort_t *tab, UINT32 dmtotals[]); diff --git a/src/info.c b/src/info.c index e20340ee4..9b8e47626 100644 --- a/src/info.c +++ b/src/info.c @@ -590,6 +590,7 @@ char sprnames[NUMSPRITES + 1][5] = "BEXB", // Battle Bumper Explosion: Blast "TWBS", // Tripwire Boost "TWBT", // Tripwire BLASTER + "SMLD", // Smooth landing "DEZL", // DEZ Laser respawn // Additional Kart Objects @@ -4402,6 +4403,8 @@ state_t states[NUMSTATES] = {SPR_TWBT, FF_FULLBRIGHT|FF_ADD|FF_ANIMATE, -1, {NULL}, 6, 2, S_NULL}, // S_TRIPWIREBOOST_BLAST_TOP {SPR_TWBT, FF_FULLBRIGHT|FF_ADD|FF_ANIMATE|FF_VERTICALFLIP|FF_HORIZONTALFLIP, -1, {NULL}, 6, 2, S_NULL}, // S_TRIPWIREBOOST_BLAST_BOTTOM + {SPR_SMLD, FF_FULLBRIGHT|FF_ADD|FF_ANIMATE, -1, {NULL}, 7, 2, S_NULL}, // S_SMOOTHLANDING + {SPR_DEZL, FF_FULLBRIGHT|FF_PAPERSPRITE, 8, {NULL}, 0, 0, S_NULL}, // S_DEZLASER {SPR_DEZL, FF_FULLBRIGHT|1, 2, {NULL}, 0, 0, S_DEZLASER_TRAIL2}, // S_DEZLASER_TRAIL1 {SPR_DEZL, FF_FULLBRIGHT|2, 2, {NULL}, 0, 0, S_DEZLASER_TRAIL3}, // S_DEZLASER_TRAIL2 @@ -24502,6 +24505,33 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, + { // MT_SMOOTHLANDING + -1, // doomednum + S_SMOOTHLANDING, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 8*FRACUNIT, // radius + 16*FRACUNIT, // height + -1, // display offset + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_NOCLIPTHING|MF_DONTENCOREMAP, // flags + S_NULL // raisestate + }, + { // MT_DEZLASER -1, // doomednum S_DEZLASER, // spawnstate diff --git a/src/info.h b/src/info.h index ca3a4f298..426963709 100644 --- a/src/info.h +++ b/src/info.h @@ -1136,6 +1136,7 @@ typedef enum sprite SPR_BEXB, // Battle Bumper Explosion: Blast SPR_TWBS, // Tripwire Boost SPR_TWBT, // Tripwire BLASTER + SPR_SMLD, // Smooth landing SPR_DEZL, // DEZ Laser respawn // Additional Kart Objects @@ -4837,6 +4838,8 @@ typedef enum state S_TRIPWIREBOOST_BLAST_TOP, S_TRIPWIREBOOST_BLAST_BOTTOM, + S_SMOOTHLANDING, + // DEZ Laser respawn S_DEZLASER, S_DEZLASER_TRAIL1, @@ -6421,6 +6424,8 @@ typedef enum mobj_type MT_TRIPWIREBOOST, + MT_SMOOTHLANDING, + MT_DEZLASER, MT_WAYPOINT, diff --git a/src/k_bot.c b/src/k_bot.c index 968ba8632..1b5a183b0 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -952,6 +952,55 @@ static void K_BotTrick(player_t *player, ticcmd_t *cmd, line_t *botController) } } +/*-------------------------------------------------- + static angle_t K_BotSmoothLanding(player_t *player, angle_t destangle) + + Calculates a new destination angle while in the air, + to be able to successfully smooth land. + + Input Arguments:- + player - Bot player to check. + destangle - Previous destination angle. + + Return:- + New destination angle. +--------------------------------------------------*/ +static angle_t K_BotSmoothLanding(player_t *player, angle_t destangle) +{ + angle_t newAngle = destangle; + boolean air = !P_IsObjectOnGround(player->mo); + angle_t steepVal = air ? STUMBLE_STEEP_VAL_AIR : STUMBLE_STEEP_VAL; + angle_t slopeSteep = max(AngleDelta(player->mo->pitch, 0), AngleDelta(player->mo->roll, 0)); + + if (slopeSteep > steepVal) + { + fixed_t pitchMul = -FINESINE(destangle >> ANGLETOFINESHIFT); + fixed_t rollMul = FINECOSINE(destangle >> ANGLETOFINESHIFT); + angle_t testAngles[2]; + angle_t testDeltas[2]; + UINT8 i; + + testAngles[0] = R_PointToAngle2(0, 0, rollMul, pitchMul); + testAngles[1] = R_PointToAngle2(0, 0, -rollMul, -pitchMul); + + for (i = 0; i < 2; i++) + { + testDeltas[i] = AngleDelta(testAngles[i], destangle); + } + + if (testDeltas[1] < testDeltas[0]) + { + return testAngles[1]; + } + else + { + return testAngles[0]; + } + } + + return newAngle; +} + /*-------------------------------------------------- static INT32 K_HandleBotTrack(player_t *player, ticcmd_t *cmd, botprediction_t *predict) @@ -975,6 +1024,8 @@ static INT32 K_HandleBotTrack(player_t *player, ticcmd_t *cmd, botprediction_t * I_Assert(predict != NULL); + destangle = K_BotSmoothLanding(player, destangle); + moveangle = player->mo->angle; anglediff = AngleDeltaSigned(moveangle, destangle); @@ -1105,6 +1156,8 @@ static INT32 K_HandleBotReverse(player_t *player, ticcmd_t *cmd, botprediction_t } } + destangle = K_BotSmoothLanding(player, destangle); + // Calculate turn direction first. moveangle = player->mo->angle; angle = (moveangle - destangle); diff --git a/src/k_hud.c b/src/k_hud.c index e6ce9706b..27deb7211 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -2177,7 +2177,7 @@ void K_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, IN } else if (tab[i].num != serverplayer || !server_lagless) { - HU_drawPing(x + ((i < 8) ? -17 : rightoffset + 11), y-4, playerpingtable[tab[i].num], 0); + HU_drawPing(x + ((i < 8) ? -17 : rightoffset + 11), y-4, playerpingtable[tab[i].num], 0, false); } } @@ -4870,7 +4870,7 @@ void K_drawKartHUD(void) V_DrawCenteredString(BASEVIDWIDTH>>1, 176, V_REDMAP|V_SNAPTOBOTTOM, "WRONG WAY"); } - if (netgame && r_splitscreen) + if ((netgame || cv_mindelay.value) && r_splitscreen && Playing()) { K_drawMiniPing(); } diff --git a/src/k_kart.c b/src/k_kart.c index 472da6130..4602f1b21 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -19,6 +19,7 @@ #include "p_setup.h" #include "r_draw.h" #include "r_local.h" +#include "r_things.c" #include "s_sound.h" #include "st_stuff.h" #include "v_video.h" @@ -3804,6 +3805,234 @@ void K_TumblePlayer(player_t *player, mobj_t *inflictor, mobj_t *source) P_StartQuake(64<> ANGLETOFINESHIFT); + fixed_t rollMul = FINECOSINE(angle >> ANGLETOFINESHIFT); + + angle_t slope = FixedMul(pitch, pitchMul) + FixedMul(roll, rollMul); + + if (slope > ANGLE_180) + { + slope = InvAngle(slope); + } + + return slope; +} + +boolean K_CheckStumble(player_t *player, angle_t oldPitch, angle_t oldRoll, boolean fromAir) +{ + angle_t steepVal = ANGLE_MAX; + fixed_t gravityadjust; + angle_t oldSlope, newSlope; + angle_t slopeDelta; + + // If you don't land upright on a slope, then you tumble, + // kinda like Kirby Air Ride + + if (player->tumbleBounces) + { + // Already tumbling. + return false; + } + + if ((player->mo->pitch == oldPitch) + && (player->mo->roll == oldRoll)) + { + // No change. + return false; + } + + if (fromAir == true) + { + steepVal = STUMBLE_STEEP_VAL_AIR; + } + else + { + steepVal = STUMBLE_STEEP_VAL; + } + + oldSlope = K_StumbleSlope(player->mo->angle, oldPitch, oldRoll); + + if (oldSlope <= steepVal) + { + // Transferring from flat ground to a steep slope + // is a free action. (The other way around isn't, though.) + return false; + } + + newSlope = K_StumbleSlope(player->mo->angle, player->mo->pitch, player->mo->roll); + slopeDelta = AngleDelta(oldSlope, newSlope); + + if (slopeDelta <= steepVal) + { + // Needs to be VERY steep before we'll punish this. + return false; + } + + // Oh jeez, you landed on your side. + // You get to tumble. + + P_ResetPlayer(player); + +#if 0 + // Single, medium bounce + player->tumbleBounces = TUMBLEBOUNCES; + player->tumbleHeight = 30; +#else + // Two small bounces + player->tumbleBounces = TUMBLEBOUNCES-1; + player->tumbleHeight = 20; +#endif + + player->pflags &= ~PF_TUMBLESOUND; + S_StartSound(player->mo, sfx_s3k9b); + + gravityadjust = P_GetMobjGravity(player->mo)/2; // so we'll halve it for our calculations. + + if (player->mo->eflags & MFE_UNDERWATER) + gravityadjust /= 2; // halve "gravity" underwater + + // and then modulate momz like that... + player->mo->momz = -gravityadjust * player->tumbleHeight; + + P_SetPlayerMobjState(player->mo, S_KART_SPINOUT); + + if (P_IsDisplayPlayer(player)) + P_StartQuake(64<mo->pitch = player->mo->roll = 0; + return true; +} + +void K_InitStumbleIndicator(player_t *player) +{ + mobj_t *new = NULL; + + if (player == NULL) + { + return; + } + + if (player->mo == NULL || P_MobjWasRemoved(player->mo) == true) + { + return; + } + + if (player->stumbleIndicator != NULL && P_MobjWasRemoved(player->stumbleIndicator) == false) + { + P_RemoveMobj(player->stumbleIndicator); + } + + new = P_SpawnMobjFromMobj(player->mo, 0, 0, 0, MT_SMOOTHLANDING); + + P_SetTarget(&player->stumbleIndicator, new); + P_SetTarget(&new->target, player->mo); +} + +void K_UpdateStumbleIndicator(player_t *player) +{ + const angle_t fudge = ANG15; + mobj_t *mobj = NULL; + + boolean air = false; + angle_t steepVal = STUMBLE_STEEP_VAL; + angle_t slopeSteep = 0; + angle_t steepRange = ANGLE_90; + + INT32 delta = 0; + INT32 trans = 0; + + if (player == NULL) + { + return; + } + + if (player->mo == NULL || P_MobjWasRemoved(player->mo) == true) + { + return; + } + + if (player->stumbleIndicator == NULL || P_MobjWasRemoved(player->stumbleIndicator) == true) + { + K_InitStumbleIndicator(player); + return; + } + + mobj = player->stumbleIndicator; + + P_MoveOrigin(mobj, player->mo->x, player->mo->y, player->mo->z + (player->mo->height / 2)); + + air = !P_IsObjectOnGround(player->mo); + steepVal = (air ? STUMBLE_STEEP_VAL_AIR : STUMBLE_STEEP_VAL) - fudge; + slopeSteep = max(AngleDelta(player->mo->pitch, 0), AngleDelta(player->mo->roll, 0)); + + delta = 0; + + if (slopeSteep > steepVal) + { + angle_t testAngles[2]; + INT32 testDeltas[2]; + UINT8 i; + + testAngles[0] = R_PointToAngle2(0, 0, player->mo->pitch, player->mo->roll); + testAngles[1] = R_PointToAngle2(0, 0, -player->mo->pitch, -player->mo->roll); + + for (i = 0; i < 2; i++) + { + testDeltas[i] = AngleDeltaSigned(player->mo->angle, testAngles[i]); + } + + if (abs(testDeltas[1]) < abs(testDeltas[0])) + { + delta = testDeltas[1]; + } + else + { + delta = testDeltas[0]; + } + } + + if (delta < 0) + { + mobj->renderflags |= RF_HORIZONTALFLIP; + } + else + { + mobj->renderflags &= ~RF_HORIZONTALFLIP; + } + + steepRange = ANGLE_90 - steepVal; + delta = max(0, abs(delta) - ((signed)steepVal)); + trans = ((FixedDiv(AngleFixed(delta), AngleFixed(steepRange)) * (NUMTRANSMAPS - 2)) + (FRACUNIT/2)) / FRACUNIT; + + if (trans < 0) + { + trans = 0; + } + + if (trans > (NUMTRANSMAPS - 2)) + { + trans = (NUMTRANSMAPS - 2); + } + + // invert + trans = (NUMTRANSMAPS - 2) - trans; + + mobj->renderflags |= RF_DONTDRAW; + + if (trans < (NUMTRANSMAPS - 2)) + { + mobj->renderflags &= ~(RF_TRANSMASK | K_GetPlayerDontDrawFlag(player)); + + if (trans != 0) + { + mobj->renderflags |= (trans << RF_TRANSSHIFT); + } + } +} + static boolean K_LastTumbleBounceCondition(player_t *player) { return (player->tumbleBounces > TUMBLEBOUNCES && player->tumbleHeight < 60); @@ -3838,6 +4067,7 @@ static void K_HandleTumbleBounce(player_t *player) player->tumbleHeight = 10; player->pflags |= PF_TUMBLELASTBOUNCE; player->mo->rollangle = 0; // p_user.c will stop rotating the player automatically + player->mo->pitch = player->mo->roll = 0; // Prevent Kodachrome Void infinite } } @@ -7909,6 +8139,8 @@ void K_KartPlayerAfterThink(player_t *player) { K_KartResetPlayerColor(player); + K_UpdateStumbleIndicator(player); + // Move held objects (Bananas, Orbinaut, etc) K_MoveHeldObjects(player); @@ -9350,31 +9582,7 @@ static void K_KartSpindash(player_t *player) } else if (player->fastfall != 0) { - // Handle fastfall bounce. - const fixed_t maxBounce = player->mo->scale * 10; - const fixed_t minBounce = player->mo->scale; - fixed_t bounce = 2 * abs(player->fastfall) / 3; - - if (bounce > maxBounce) - { - bounce = maxBounce; - } - else - { - // Lose speed on bad bounce. - player->mo->momx /= 2; - player->mo->momy /= 2; - - if (bounce < minBounce) - { - bounce = minBounce; - } - } - - S_StartSound(player->mo, sfx_ffbonc); - player->mo->momz = bounce * P_MobjFlip(player->mo); - - player->fastfall = 0; + // Still handling fast-fall bounce. return; } @@ -9441,6 +9649,41 @@ static void K_KartSpindash(player_t *player) #undef SPINDASHTHRUSTTIME +boolean K_FastFallBounce(player_t *player) +{ + // Handle fastfall bounce. + if (player->fastfall != 0) + { + const fixed_t maxBounce = player->mo->scale * 10; + const fixed_t minBounce = player->mo->scale; + fixed_t bounce = 2 * abs(player->fastfall) / 3; + + if (bounce > maxBounce) + { + bounce = maxBounce; + } + else + { + // Lose speed on bad bounce. + player->mo->momx /= 2; + player->mo->momy /= 2; + + if (bounce < minBounce) + { + bounce = minBounce; + } + } + + S_StartSound(player->mo, sfx_ffbonc); + player->mo->momz = bounce * P_MobjFlip(player->mo); + + player->fastfall = 0; + return true; + } + + return false; +} + static void K_AirFailsafe(player_t *player) { const fixed_t maxSpeed = 6*player->mo->scale; diff --git a/src/k_kart.h b/src/k_kart.h index 6fc559929..9138997c5 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -28,6 +28,9 @@ Make sure this matches the actual number of states #define GROW_PHYSICS_SCALE (3*FRACUNIT/2) #define SHRINK_PHYSICS_SCALE (3*FRACUNIT/4) +#define STUMBLE_STEEP_VAL ANG60 +#define STUMBLE_STEEP_VAL_AIR (ANG30 + ANG10) + player_t *K_GetItemBoxPlayer(mobj_t *mobj); angle_t K_ReflectAngle(angle_t angle, angle_t against, fixed_t maxspeed, fixed_t yourspeed); @@ -75,6 +78,10 @@ void K_RemoveGrowShrink(player_t *player); void K_SpinPlayer(player_t *player, mobj_t *inflictor, mobj_t *source, INT32 type); void K_TumblePlayer(player_t *player, mobj_t *inflictor, mobj_t *source); void K_TumbleInterrupt(player_t *player); +angle_t K_StumbleSlope(angle_t angle, angle_t pitch, angle_t roll); +boolean K_CheckStumble(player_t *player, angle_t oldPitch, angle_t oldRoll, boolean fromAir); +void K_InitStumbleIndicator(player_t *player); +void K_UpdateStumbleIndicator(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); @@ -142,6 +149,7 @@ fixed_t K_GetNewSpeed(player_t *player); fixed_t K_3dKartMovement(player_t *player); boolean K_PlayerEBrake(player_t *player); SINT8 K_Sliptiding(player_t *player); +boolean K_FastFallBounce(player_t *player); void K_AdjustPlayerFriction(player_t *player); void K_MoveKartPlayer(player_t *player, boolean onground); void K_CheckSpectateStatus(void); diff --git a/src/k_menudef.c b/src/k_menudef.c index d97083509..97f77abd2 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -1035,6 +1035,12 @@ menuitem_t OPTIONS_Gameplay[] = {IT_STRING | IT_CVAR, "Karma Comeback", "Enable Karma Comeback in Battle mode.", NULL, {.cvar = &cv_kartcomeback}, 0, 0}, + {IT_SPACE | IT_NOTHING, NULL, NULL, + NULL, {NULL}, 0, 0}, + + {IT_STRING | IT_CVAR, "Minimum Input Delay", "Practice for online play! Higher = more delay.", + NULL, {.cvar = &cv_mindelay}, 0, 0}, + {IT_SPACE | IT_NOTHING, NULL, NULL, NULL, {NULL}, 0, 0}, diff --git a/src/p_local.h b/src/p_local.h index 6c885191c..5fc73f1a2 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -172,7 +172,7 @@ boolean P_IsObjectOnGroundIn(mobj_t *mo, sector_t *sec); boolean P_IsObjectOnRealGround(mobj_t *mo, sector_t *sec); // SRB2Kart #define P_IsObjectFlipped(o) ((o)->eflags & MFE_VERTICALFLIP) boolean P_InQuicksand(mobj_t *mo); -boolean P_PlayerHitFloor(player_t *player, boolean fromAir); +boolean P_PlayerHitFloor(player_t *player, boolean fromAir, angle_t oldPitch, angle_t oldRoll); void P_SetObjectMomZ(mobj_t *mo, fixed_t value, boolean relative); void P_RestoreMusic(player_t *player); diff --git a/src/p_map.c b/src/p_map.c index e00cbea1c..5b1827988 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -2801,12 +2801,15 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff) if (thing->momz <= 0) { + angle_t oldPitch = thing->pitch; + angle_t oldRoll = thing->roll; + thing->standingslope = tmfloorslope; P_SetPitchRollFromSlope(thing, thing->standingslope); - if (thing->momz == 0 && thing->player && !startingonground) + if (thing->player) { - P_PlayerHitFloor(thing->player, true); + P_PlayerHitFloor(thing->player, !startingonground, oldPitch, oldRoll); } } } @@ -2821,12 +2824,15 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff) if (thing->momz >= 0) { + angle_t oldPitch = thing->pitch; + angle_t oldRoll = thing->roll; + thing->standingslope = tmceilingslope; P_SetPitchRollFromSlope(thing, thing->standingslope); - if (thing->momz == 0 && thing->player && !startingonground) + if (thing->player) { - P_PlayerHitFloor(thing->player, true); + P_PlayerHitFloor(thing->player, !startingonground, oldPitch, oldRoll); } } } @@ -3117,7 +3123,7 @@ static boolean P_ThingHeightClip(mobj_t *thing) } if ((P_MobjFlip(thing)*(thing->z - oldz) > 0 || hitfloor) && thing->player) - P_PlayerHitFloor(thing->player, !onfloor); + P_PlayerHitFloor(thing->player, !onfloor, thing->pitch, thing->roll); // debug: be sure it falls to the floor thing->eflags &= ~MFE_ONGROUND; diff --git a/src/p_mobj.c b/src/p_mobj.c index 30c1ec0f2..b9dd4a4cc 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -1534,12 +1534,18 @@ void P_XYMovement(mobj_t *mo) } // adjust various things based on slope - if (mo->standingslope && abs(mo->standingslope->zdelta) > FRACUNIT>>8) { - if (!P_IsObjectOnGround(mo)) { // We fell off at some point? Do the twisty thing! + if (mo->standingslope && abs(mo->standingslope->zdelta) > FRACUNIT>>8) + { + if (!P_IsObjectOnGround(mo)) + { + // We fell off at some point? Do the twisty thing! P_SlopeLaunch(mo); xmove = mo->momx; ymove = mo->momy; - } else { // Still on the ground. + } + else + { + // Still on the ground. slopemom.x = xmove; slopemom.y = ymove; slopemom.z = 0; @@ -1771,7 +1777,9 @@ void P_XYMovement(mobj_t *mo) if (P_MobjWasRemoved(mo)) // MF_SPECIAL touched a player! O_o;; return; - if (moved && oldslope && !(mo->flags & MF_NOCLIPHEIGHT)) { // Check to see if we ran off + if (moved && oldslope && !(mo->flags & MF_NOCLIPHEIGHT)) + { + // Check to see if we ran off if (oldslope != mo->standingslope) { @@ -2727,6 +2735,7 @@ static boolean P_PlayerPolyObjectZMovement(mobj_t *mo) void P_PlayerZMovement(mobj_t *mo) { boolean onground; + angle_t oldPitch, oldRoll; I_Assert(mo != NULL); I_Assert(!P_MobjWasRemoved(mo)); @@ -2734,6 +2743,9 @@ void P_PlayerZMovement(mobj_t *mo) if (!mo->player) return; + oldPitch = mo->pitch; + oldRoll = mo->roll; + // Intercept the stupid 'fall through 3dfloors' bug if (mo->subsector->sector->ffloors) P_AdjustMobjFloorZ_FFloors(mo, mo->subsector->sector, 0); @@ -2810,7 +2822,7 @@ void P_PlayerZMovement(mobj_t *mo) mo->pmomz = 0; // We're on a new floor, don't keep doing platform movement. mo->eflags |= MFE_JUSTHITFLOOR; // Spin Attack - clipmomz = P_PlayerHitFloor(mo->player, true); + clipmomz = P_PlayerHitFloor(mo->player, true, oldPitch, oldRoll); P_PlayerPolyObjectZMovement(mo); if (clipmomz) @@ -2846,6 +2858,47 @@ void P_PlayerZMovement(mobj_t *mo) mo->eflags &= ~MFE_JUSTHITFLOOR; P_CheckGravity(mo, true); } + + // Even out pitch & roll slowly over time when respawning. + if (mo->player->respawn.state != RESPAWNST_NONE) + { + const angle_t speed = ANG2; //FixedMul(ANG2, abs(mo->momz) / 8); + angle_t dest = 0; + INT32 pitchDelta = AngleDeltaSigned(mo->pitch, 0); + INT32 rollDelta = AngleDeltaSigned(mo->roll, 0); + + if (abs(pitchDelta) <= speed && dest == 0) + { + mo->pitch = 0; + } + else if (abs(pitchDelta) > dest) + { + if (pitchDelta > 0) + { + mo->pitch -= speed; + } + else + { + mo->pitch += speed; + } + } + + if (abs(rollDelta) <= speed && dest == 0) + { + mo->roll = 0; + } + else if (abs(rollDelta) > dest) + { + if (rollDelta > 0) + { + mo->roll -= speed; + } + else + { + mo->roll += speed; + } + } + } } if (((mo->eflags & MFE_VERTICALFLIP && mo->z < mo->floorz) || (!(mo->eflags & MFE_VERTICALFLIP) && mo->z + mo->height > mo->ceilingz)) @@ -11340,6 +11393,8 @@ void P_SpawnPlayer(INT32 playernum) P_SetScale(mobj, mobj->destscale); P_FlashPal(p, 0, 0); // Resets + K_InitStumbleIndicator(p); + if (gametyperules & GTR_BUMPERS) { mobj_t *overheadarrow = P_SpawnMobj(mobj->x, mobj->y, mobj->z + mobj->height + 16*FRACUNIT, MT_PLAYERARROW); diff --git a/src/p_saveg.c b/src/p_saveg.c index 6640d7b51..fe11f0ee9 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -64,6 +64,7 @@ typedef enum SKYBOXVIEW = 0x08, SKYBOXCENTER = 0x10, HOVERHYUDORO = 0x20, + STUMBLE = 0x40, } player_saveflags; static inline void P_ArchivePlayer(void) @@ -202,6 +203,9 @@ static void P_NetArchivePlayers(void) if (players[i].hoverhyudoro) flags |= HOVERHYUDORO; + if (players[i].stumbleIndicator) + flags |= STUMBLE; + WRITEUINT16(save_p, flags); if (flags & SKYBOXVIEW) @@ -219,6 +223,9 @@ static void P_NetArchivePlayers(void) if (flags & HOVERHYUDORO) WRITEUINT32(save_p, players[i].hoverhyudoro->mobjnum); + if (flags & STUMBLE) + WRITEUINT32(save_p, players[i].stumbleIndicator->mobjnum); + WRITEUINT32(save_p, (UINT32)players[i].followitem); WRITEUINT32(save_p, players[i].charflags); @@ -510,6 +517,9 @@ static void P_NetUnArchivePlayers(void) if (flags & HOVERHYUDORO) players[i].hoverhyudoro = (mobj_t *)(size_t)READUINT32(save_p); + if (flags & STUMBLE) + players[i].stumbleIndicator = (mobj_t *)(size_t)READUINT32(save_p); + players[i].followitem = (mobjtype_t)READUINT32(save_p); //SetPlayerSkinByNum(i, players[i].skin); @@ -4291,6 +4301,13 @@ static void P_RelinkPointers(void) if (!P_SetTarget(&mobj->player->hoverhyudoro, P_FindNewPosition(temp))) CONS_Debug(DBG_GAMELOGIC, "hoverhyudoro not found on %d\n", mobj->type); } + if (mobj->player->stumbleIndicator) + { + temp = (UINT32)(size_t)mobj->player->stumbleIndicator; + mobj->player->stumbleIndicator = NULL; + if (!P_SetTarget(&mobj->player->stumbleIndicator, P_FindNewPosition(temp))) + CONS_Debug(DBG_GAMELOGIC, "stumbleIndicator not found on %d\n", mobj->type); + } } } } diff --git a/src/p_user.c b/src/p_user.c index 1e1cfcbcb..f733bad28 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -1339,7 +1339,7 @@ void P_DoPlayerExit(player_t *player) // // Handles player hitting floor surface. // Returns whether to clip momz. -boolean P_PlayerHitFloor(player_t *player, boolean fromAir) +boolean P_PlayerHitFloor(player_t *player, boolean fromAir, angle_t oldPitch, angle_t oldRoll) { boolean clipmomz; @@ -1347,9 +1347,32 @@ boolean P_PlayerHitFloor(player_t *player, boolean fromAir) clipmomz = !(P_CheckDeathPitCollide(player->mo)); - if (fromAir == true && clipmomz == true) + if (clipmomz == true) { - K_SpawnSplashForMobj(player->mo, abs(player->mo->momz)); + if (fromAir == true) + { + K_SpawnSplashForMobj(player->mo, abs(player->mo->momz)); + } + + if (player->mo->health > 0) + { + boolean air = fromAir; + + if (P_IsObjectOnGround(player->mo) && (player->mo->eflags & MFE_JUSTHITFLOOR)) + { + air = true; + } + + if (K_CheckStumble(player, oldPitch, oldRoll, air) == true) + { + return false; + } + + if (air == false && K_FastFallBounce(player) == true) + { + return false; + } + } } return clipmomz; @@ -1662,7 +1685,7 @@ static void P_CheckQuicksand(player_t *player) player->mo->z = ceilingheight - player->mo->height; if (player->mo->momz <= 0) - P_PlayerHitFloor(player, false); + P_PlayerHitFloor(player, false, player->mo->roll, player->mo->pitch); } else { @@ -1674,7 +1697,7 @@ static void P_CheckQuicksand(player_t *player) player->mo->z = floorheight; if (player->mo->momz >= 0) - P_PlayerHitFloor(player, false); + P_PlayerHitFloor(player, false, player->mo->roll, player->mo->pitch); } friction = abs(rover->master->v1->y - rover->master->v2->y)>>6; diff --git a/src/screen.c b/src/screen.c index 5d6b59ee8..f1b53a7f2 100644 --- a/src/screen.c +++ b/src/screen.c @@ -628,11 +628,14 @@ void SCR_DisplayTicRate(void) void SCR_DisplayLocalPing(void) { + boolean offline; + UINT32 ping = playerpingtable[consoleplayer]; // consoleplayer's ping is everyone's ping in a splitnetgame :P if (! r_splitscreen && ( cv_showping.value == 1 || (cv_showping.value == 2 && ping > servermaxping) )) // only show 2 (warning) if our ping is at a bad level { INT32 dispy = cv_ticrate.value ? 160 : 181; - HU_drawPing(307, dispy, ping, V_SNAPTORIGHT | V_SNAPTOBOTTOM | V_HUDTRANS); + offline = (consoleplayer == serverplayer); + HU_drawPing(307, dispy, ping, V_SNAPTORIGHT | V_SNAPTOBOTTOM | V_HUDTRANS, offline); } } diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index 2361a5349..a1948f2f1 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -1219,6 +1219,8 @@ void I_FinishUpdate(void) } } } + if (cv_mindelay.value && consoleplayer == serverplayer && Playing()) + SCR_DisplayLocalPing(); } if (marathonmode)