diff --git a/src/command.c b/src/command.c index 24d58a28c..6cacd3bcd 100644 --- a/src/command.c +++ b/src/command.c @@ -479,6 +479,7 @@ static void COM_TokenizeString(char *ptext) Z_Free(com_argv[i]); com_argc = 0; + Z_Free(com_args); com_args = NULL; com_flags = 0; @@ -502,7 +503,7 @@ static void COM_TokenizeString(char *ptext) break; if (com_argc == 1) - com_args = ptext; + com_args = COM_Purge(Z_StrDup(ptext), NULL); ptext = COM_Parse(ptext); if (ptext == NULL) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 0a6757f35..76470b865 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -1955,6 +1955,9 @@ static boolean CL_ServerConnectionTicker(const char *tmpsave, tic_t *oldtic, tic { I_OsPolling(); + I_NewTwodeeFrame(); + I_NewImguiFrame(); + // Needs to be updated here for M_DrawEggaChannel renderdeltatics = FRACUNIT; rendertimefrac = FRACUNIT; diff --git a/src/d_main.c b/src/d_main.c index d9eee1b0d..ca62f29c9 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -803,8 +803,11 @@ void D_SRB2Loop(void) HW3S_BeginFrameUpdate(); #endif - I_NewTwodeeFrame(); - I_NewImguiFrame(); + if (rendermode != render_none) + { + I_NewTwodeeFrame(); + I_NewImguiFrame(); + } if (realtics > 0 || singletics) { @@ -1500,8 +1503,11 @@ void D_SRB2Main(void) CONS_Printf("I_StartupGraphics()...\n"); I_StartupGraphics(); - I_NewTwodeeFrame(); - I_NewImguiFrame(); + if (rendermode != render_none) + { + I_NewTwodeeFrame(); + I_NewImguiFrame(); + } #ifdef HWRENDER // Lactozilla: Add every hardware mode CVAR and CCMD. @@ -1642,6 +1648,10 @@ void D_SRB2Main(void) I_Error("Bad '%s' level warp.\n" #if defined (_WIN32) "Are you using MSDOS 8.3 filenames in Zone Builder?\n" + "\n" + "To check: edit the Ring Racers game configuration in Zone Builder.\n" + "Go to the Testing tab and make sure \"Use short paths and file names\" is turned off.\n" + "(The option is hidden by default. Check \"Customize parameters\" to show it.)\n" #endif , word); } @@ -1652,7 +1662,7 @@ void D_SRB2Main(void) } { - if (!M_CheckParm("-server")) + if (!M_CheckParm("-server") && !M_CheckParm("-dedicated")) { G_SetUsedCheats(); diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 4b02e962f..71483bc61 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -226,6 +226,8 @@ static void Command_Schedule_List(void); static void Command_Automate_Set(void); +static void Command_Eval(void); + // ========================================================================= // CLIENT VARIABLES // ========================================================================= @@ -492,7 +494,7 @@ consvar_t cv_rollingdemos = CVAR_INIT ("rollingdemos", "On", CV_SAVE, CV_OnOff, static CV_PossibleValue_t pointlimit_cons_t[] = {{1, "MIN"}, {MAXSCORE, "MAX"}, {0, "None"}, {-1, "Default"}, {0, NULL}}; consvar_t cv_pointlimit = CVAR_INIT ("pointlimit", "Default", CV_NETVAR|CV_CALL|CV_NOINIT, pointlimit_cons_t, PointLimit_OnChange); -static CV_PossibleValue_t timelimit_cons_t[] = {{1, "MIN"}, {30, "MAX"}, {0, "None"}, {-1, "Default"}, {0, NULL}}; +static CV_PossibleValue_t timelimit_cons_t[] = {{1, "MIN"}, {30*60, "MAX"}, {0, "None"}, {-1, "Default"}, {0, NULL}}; consvar_t cv_timelimit = CVAR_INIT ("timelimit", "Default", CV_NETVAR|CV_CALL|CV_NOINIT, timelimit_cons_t, TimeLimit_OnChange); static CV_PossibleValue_t numlaps_cons_t[] = {{1, "MIN"}, {MAX_LAPS, "MAX"}, {0, "Map default"}, {0, NULL}}; @@ -745,6 +747,8 @@ void D_RegisterServerCommands(void) COM_AddCommand("automate_set", Command_Automate_Set); + COM_AddCommand("eval", Command_Eval); + // for master server connection AddMServCommands(); @@ -1083,6 +1087,7 @@ void D_RegisterClientCommands(void) COM_AddCommand("god", Command_CheatGod_f); COM_AddCommand("setrings", Command_Setrings_f); COM_AddCommand("setlives", Command_Setlives_f); + COM_AddCommand("setscore", Command_Setscore_f); COM_AddCommand("devmode", Command_Devmode_f); COM_AddCommand("savecheckpoint", Command_Savecheckpoint_f); COM_AddCommand("scale", Command_Scale_f); @@ -2051,6 +2056,10 @@ void D_Cheat(INT32 playernum, INT32 cheat, ...) COPY(WRITESINT8, int); COPY(WRITEUINT8, unsigned int); break; + + case CHEAT_SCORE: + COPY(WRITEUINT32, UINT32); + break; } #undef COPY @@ -5029,7 +5038,7 @@ static void TimeLimit_OnChange(void) break; default: - CONS_Printf(M_GetText("Time limit has been set to %d minute%s.\n"), cv_timelimit.value,cv_timelimit.value == 1 ? "" : "s"); + CONS_Printf(M_GetText("Time limit has been set to %d second%s.\n"), cv_timelimit.value,cv_timelimit.value == 1 ? "" : "s"); } timelimitintics = K_TimeLimitForGametype(); @@ -5051,7 +5060,7 @@ static void TimeLimit_OnChange(void) break; default: - CONS_Printf(M_GetText("Time limit will be %d minute%s next round.\n"), cv_timelimit.value,cv_timelimit.value == 1 ? "" : "s"); + CONS_Printf(M_GetText("Time limit will be %d second%s next round.\n"), cv_timelimit.value,cv_timelimit.value == 1 ? "" : "s"); } } } @@ -5739,6 +5748,15 @@ static void Got_Cheat(UINT8 **cp, INT32 playernum) break; } + case CHEAT_SCORE: { + UINT32 score = READUINT32(*cp); + + player->roundscore = score; + + CV_CheaterWarning(targetPlayer, va("score = %u", score)); + break; + } + case NUMBER_OF_CHEATS: break; } @@ -6114,6 +6132,18 @@ static void Command_Automate_Set(void) SendNetXCmd(XD_AUTOMATE, buf, buf_p - buf); } +static void Command_Eval(void) +{ + const char *args = COM_Args(); + + if (args) + { + const fixed_t n = LUA_EvalMath(args); + + CONS_Printf("%f (%d)\n", FixedToFloat(n), n); + } +} + /** Makes a change to ::cv_forceskin take effect immediately. * * \sa Command_SetForcedSkin_f, cv_forceskin, forcedskin diff --git a/src/g_game.c b/src/g_game.c index 597624021..73e60a491 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1088,6 +1088,13 @@ static void G_DoAnglePrediction(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer, p { angle_t angleChange = 0; + // Chasecam stops in these situations, so local cam should stop too. + // Otherwise it'll jerk when it resumes. + if (player->playerstate == PST_DEAD) + return; + if (player->mo != NULL && !P_MobjWasRemoved(player->mo) && player->mo->hitlag > 0) + return; + while (realtics > 0) { localsteering[ssplayer - 1] = K_UpdateSteeringValue(localsteering[ssplayer - 1], cmd->turning); diff --git a/src/k_kart.c b/src/k_kart.c index 4284c286d..ce2827cf7 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -11347,7 +11347,7 @@ tic_t K_TimeLimitForGametype(void) if (cv_timelimit.value != -1) { - return cv_timelimit.value * (60*TICRATE); + return cv_timelimit.value * TICRATE; } // No time limit for Break the Capsules FREE PLAY diff --git a/src/lua_script.c b/src/lua_script.c index ed35d9e69..17abc7384 100644 --- a/src/lua_script.c +++ b/src/lua_script.c @@ -699,6 +699,10 @@ fixed_t LUA_EvalMath(const char *word) lua_pushboolean(L, true); lua_call(L, 1, 0); + lua_pushcfunction(L, LUA_MathLib); + lua_pushboolean(L, true); + lua_call(L, 1, 0); + // change ^ into ^^ for Lua. strcpy(buf, "return "); b = buf+strlen(buf); diff --git a/src/m_cheat.c b/src/m_cheat.c index 582b339f6..62a5a2b53 100644 --- a/src/m_cheat.c +++ b/src/m_cheat.c @@ -763,6 +763,14 @@ void Command_Setlives_f(void) D_Cheat(consoleplayer, CHEAT_LIVES, atoi(COM_Argv(1))); } +void Command_Setscore_f(void) +{ + REQUIRE_CHEATS; + REQUIRE_INLEVEL; + + D_Cheat(consoleplayer, CHEAT_SCORE, atoi(COM_Argv(1))); +} + void Command_Grayscale_f(void) { REQUIRE_CHEATS; diff --git a/src/m_cheat.h b/src/m_cheat.h index b8b85db00..331f70563 100644 --- a/src/m_cheat.h +++ b/src/m_cheat.h @@ -35,6 +35,7 @@ typedef enum { CHEAT_RELATIVE_TELEPORT, CHEAT_DEVMODE, CHEAT_GIVEITEM, + CHEAT_SCORE, NUMBER_OF_CHEATS } cheat_t; @@ -71,6 +72,7 @@ void Command_CheatGod_f(void); void Command_Savecheckpoint_f(void); void Command_Setrings_f(void); void Command_Setlives_f(void); +void Command_Setscore_f(void); void Command_Devmode_f(void); void Command_Scale_f(void); void Command_Gravflip_f(void); diff --git a/src/p_spec.c b/src/p_spec.c index cc6e3cf0e..92c005e6e 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -1973,7 +1973,7 @@ static void K_HandleLapIncrement(player_t *player) rainbowstartavailable = false; } - if (netgame && player->laps >= numlaps) + if (netgame && player->laps > numlaps) CON_LogMessage(va(M_GetText("%s has finished the race.\n"), player_names[player-players])); player->starpostnum = 0; diff --git a/src/p_user.c b/src/p_user.c index ba3481130..94e96a45b 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -2117,6 +2117,78 @@ static void P_3dMovement(player_t *player) } } +// For turning correction in P_UpdatePlayerAngle. +// Given a range of possible steering inputs, finds a steering input that corresponds to the desired angle change. +static INT16 P_FindClosestTurningForAngle(player_t *player, INT32 targetAngle, INT16 lowBound, INT16 highBound) +{ + INT16 newBound; + INT16 preferred = lowBound; + int attempts = 0; + + // Only works if our low bound is actually our low bound. + if (highBound < lowBound) + { + INT16 tmp = lowBound; + lowBound = highBound; + highBound = tmp; + } + + // Slightly frumpy binary search for the ideal turning input. + // We do this instead of reversing K_GetKartTurnValue so that future handling changes are automatically accounted for. + + while (attempts++ < 20) // Practical calls of this function search maximum 10 times, this is solely for safety. + { + // These need to be treated as signed, or situations where boundaries straddle 0 are a mess. + INT32 lowAngle = K_GetKartTurnValue(player, lowBound) << TICCMD_REDUCE; + INT32 highAngle = K_GetKartTurnValue(player, highBound) << TICCMD_REDUCE; + + // EXIT CONDITION 1: Hopeless search, target angle isn't between boundaries at all. + if (lowAngle >= targetAngle) + return lowBound; + if (highAngle <= targetAngle) + return highBound; + + // Test the middle of our steering range, so we can see which side is more promising. + newBound = (lowBound + highBound) / 2; + + // EXIT CONDITION 2: Boundaries converged and we're all out of precision. + if (newBound == lowBound || newBound == highBound) + break; + + INT32 newAngle = K_GetKartTurnValue(player, newBound) << TICCMD_REDUCE; + + angle_t lowError = abs(targetAngle - lowAngle); + angle_t highError = abs(targetAngle - highAngle); + angle_t newError = abs(targetAngle - newAngle); + + // CONS_Printf("steering %d / %d / %d - angle %d / %d / %d - TA %d - error %d / %d / %d\n", lowBound, newBound, highBound, lowAngle, newAngle, highAngle, targetAngle, lowError, newError, highError); + + // EXIT CONDITION 3: We got lucky! + if (lowError == 0) + return lowBound; + if (newError == 0) + return newBound; + if (highError == 0) + return highBound; + + // If not, store the best estimate... + if (lowError <= newError && lowError <= highError) + preferred = lowBound; + if (highError <= newError && highError <= lowError) + preferred = highBound; + if (newError <= lowError && newError <= highError) + preferred = newBound; + + // ....and adjust the bounds for another run. + if (lowAngle <= targetAngle && targetAngle <= newAngle) + highBound = newBound; + else + lowBound = newBound; + } + + return preferred; +} + // // P_UpdatePlayerAngle // @@ -2153,6 +2225,13 @@ static void P_UpdatePlayerAngle(player_t *player) angle_t targetAngle = (player->cmd.angle) << TICCMD_REDUCE; angle_t targetDelta = targetAngle - (player->mo->angle); + // Corrections via fake turn go through easing. + // That means undoing them takes the same amount of time as doing them. + // This can lead to oscillating death spiral states on a multi-tic correction, as we swing past the target angle. + // So before we go into death-spirals, if our predicton is _almost_ right... + 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)) { // We are where we need to be. @@ -2163,25 +2242,15 @@ static void P_UpdatePlayerAngle(player_t *player) // so we momentarily ignore the camera angle and let the server trust our inputs instead. // That way, even if you're steering blind, you get the intended "kick-out" effect. } - else if (targetDelta >= ANGLE_180 && maxTurnLeft >= targetDelta) // Overshot, so just fudge it. + else { - angleChange = targetDelta; - player->steering = targetsteering; - } - else if (targetDelta <= ANGLE_180 && maxTurnRight <= targetDelta) // Overshot, so just fudge it. - { - angleChange = targetDelta; - player->steering = targetsteering; - } - else if (targetDelta >= ANGLE_180 && maxTurnLeft < targetDelta) // Undershot, slam the stick. - { - angleChange = maxTurnLeft; - player->steering = steeringLeft; - } - else if (targetDelta <= ANGLE_180 && maxTurnRight > targetDelta) // Undershot, slam the stick. - { - angleChange = maxTurnRight; - player->steering = steeringRight; + // We're off. Try to legally steer the player towards their camera. + player->steering = P_FindClosestTurningForAngle(player, targetDelta, steeringLeft, steeringRight); + angleChange = K_GetKartTurnValue(player, player->steering) << TICCMD_REDUCE; + + // And if the resulting steering input is close enough, snap them exactly. + if (min(targetDelta - angleChange, angleChange - targetDelta) <= leniency) + angleChange = targetDelta; } } else @@ -2218,6 +2287,7 @@ static void P_UpdatePlayerAngle(player_t *player) } } + // // P_SpectatorMovement // diff --git a/src/r_things.c b/src/r_things.c index dc73628d8..92f9148ee 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -1065,6 +1065,25 @@ static void R_DrawVisSprite(vissprite_t *vis) pwidth = patch->width; #endif + if (vis->x1test && vis->x2test) + { + INT32 x1test = vis->x1test; + INT32 x2test = vis->x2test; + + if (x1test < 0) + x1test = 0; + + if (x2test >= vid.width) + x2test = vid.width-1; + + const INT32 t = (vis->startfrac + (vis->xiscale * (x2test - x1test))) >> FRACBITS; + + if (x1test <= x2test && (t < 0 || t >= pwidth)) + { + CONS_Printf("THE GAME WOULD HAVE CRASHED, %d (old) vs %d (new)\n", (x2test - x1test), (vis->x2 - vis->x1)); + } + } + // Non-paper drawing loop for (dc_x = vis->x1; dc_x <= vis->x2; dc_x++, frac += vis->xiscale, sprtopscreen += vis->shear.tan) { @@ -1452,6 +1471,9 @@ static void R_ProjectDropShadow( shadow->x1 = x1 < portalclipstart ? portalclipstart : x1; shadow->x2 = x2 >= portalclipend ? portalclipend-1 : x2; + shadow->x1test = 0; + shadow->x2test = 0; + shadow->xscale = FixedMul(xscale, shadowxscale); //SoM: 4/17/2000 shadow->scale = FixedMul(yscale, shadowyscale); shadow->thingscale = interp.scale; @@ -1598,6 +1620,9 @@ static void R_ProjectBoundingBox(mobj_t *thing, vissprite_t *vis) box->sortscale = yscale; box->dispoffset = 0; } + + box->x1test = 0; + box->x2test = 0; } // @@ -1616,6 +1641,7 @@ static void R_ProjectSprite(mobj_t *thing) fixed_t sort_x = 0, sort_y = 0, sort_z; INT32 x1, x2; + INT32 x1test = 0, x2test = 0; spritedef_t *sprdef; spriteframe_t *sprframe; @@ -2001,18 +2027,35 @@ static void R_ProjectSprite(mobj_t *thing) scalestep = 0; yscale = sortscale; tx += offset; - x1 = (centerxfrac + FixedMul(tx,xscale))>>FRACBITS; + //x1 = (centerxfrac + FixedMul(tx,xscale))>>FRACBITS; + x1 = centerx + (FixedMul(tx,xscale) / FRACUNIT); + + x1test = (centerxfrac + FixedMul(tx,xscale))>>FRACBITS; + + if (x1test > viewwidth) + x1test = 0; // off the right side? if (x1 > viewwidth) return; tx += offset2; - x2 = ((centerxfrac + FixedMul(tx,xscale))>>FRACBITS); x2--; + //x2 = ((centerxfrac + FixedMul(tx,xscale))>>FRACBITS); x2--; + x2 = (centerx + (FixedMul(tx,xscale) / FRACUNIT)) - 1; + + x2test = ((centerxfrac + FixedMul(tx,xscale))>>FRACBITS) - 1; + + if (x2test < 0) + x2test = 0; // off the left side if (x2 < 0) return; + +#if 0 + if ((x2 - x1) != (x2test - x1test)) + CONS_Printf("[%d] %d != %d\n", objectsdrawn, x2 - x1, x2test - x1test); +#endif } // Adjust the sort scale if needed @@ -2101,6 +2144,12 @@ static void R_ProjectSprite(mobj_t *thing) if (x2 < portalclipstart || x1 >= portalclipend) return; + if (x2test < portalclipstart || x1test >= portalclipend) + { + x1test = 0; + x2test = 0; + } + if (P_PointOnLineSide(interp.x, interp.y, portalclipline) != 0) return; } @@ -2269,6 +2318,9 @@ static void R_ProjectSprite(mobj_t *thing) vis->x1 = x1 < portalclipstart ? portalclipstart : x1; vis->x2 = x2 >= portalclipend ? portalclipend-1 : x2; + vis->x1test = x1test < portalclipstart ? portalclipstart : x1test; + vis->x2test = x2test >= portalclipend ? portalclipend-1 : x2test; + vis->sector = thing->subsector->sector; vis->szt = (INT16)((centeryfrac - FixedMul(vis->gzt - viewz, sortscale))>>FRACBITS); vis->sz = (INT16)((centeryfrac - FixedMul(vis->gz - viewz, sortscale))>>FRACBITS); @@ -2290,6 +2342,12 @@ static void R_ProjectSprite(mobj_t *thing) if (shadowdraw || shadoweffects) { + if (x1test && x2test) + { + vis->xiscaletest = (patch->width<width<shear.offset = vis->x1-x1; @@ -2303,6 +2361,7 @@ static void R_ProjectSprite(mobj_t *thing) { vis->startfrac = spr_width-1; vis->xiscale = -iscale; + vis->xiscaletest = -vis->xiscaletest; } else { @@ -2310,10 +2369,13 @@ static void R_ProjectSprite(mobj_t *thing) vis->xiscale = iscale; } + vis->startfractest = vis->startfrac; + if (vis->x1 > x1) { vis->startfrac += FixedDiv(vis->xiscale, this_scale) * (vis->x1 - x1); vis->scale += FixedMul(scalestep, spriteyscale) * (vis->x1 - x1); + vis->startfractest += FixedDiv(vis->xiscaletest, this_scale) * (vis->x1test - x1test); } vis->transmap = R_GetBlendTable(blendmode, trans); @@ -2525,6 +2587,9 @@ static void R_ProjectPrecipitationSprite(precipmobj_t *thing) vis->x1 = x1 < portalclipstart ? portalclipstart : x1; vis->x2 = x2 >= portalclipend ? portalclipend-1 : x2; + vis->x1test = 0; + vis->x2test = 0; + vis->xscale = xscale; //SoM: 4/17/2000 vis->sector = thing->subsector->sector; vis->szt = (INT16)((centeryfrac - FixedMul(vis->gzt - viewz, yscale))>>FRACBITS); diff --git a/src/r_things.h b/src/r_things.h index 4ea4b8342..2d82b4381 100644 --- a/src/r_things.h +++ b/src/r_things.h @@ -161,6 +161,9 @@ struct vissprite_t mobj_t *mobj; // for easy access INT32 x1, x2; + INT32 x1test, x2test; + fixed_t xiscaletest; + fixed_t startfractest; fixed_t gx, gy; // for line side calculation fixed_t gz, gzt; // global bottom/top for silhouette clipping and sorting with 3D floors diff --git a/src/st_stuff.c b/src/st_stuff.c index 289a6c3a7..24aa174ee 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -237,7 +237,9 @@ void ST_Start(void) ST_Stop(); ST_InitData(); - st_stopped = false; + + if (!dedicated) + st_stopped = false; } //