diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 19c45d82b..00207d1bb 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -5114,7 +5114,7 @@ static void SV_Maketic(void) { INT32 i; - ps_botticcmd_time = 0; + PS_ResetBotInfo(); for (i = 0; i < MAXPLAYERS; i++) { @@ -5123,9 +5123,13 @@ static void SV_Maketic(void) if (K_PlayerUsesBotMovement(&players[i])) { - precise_t t = I_GetPreciseTime(); + const precise_t t = I_GetPreciseTime(); + K_BuildBotTiccmd(&players[i], &netcmds[maketic%BACKUPTICS][i]); - ps_botticcmd_time += I_GetPreciseTime() - t; + + ps_bots[i].isBot = true; + ps_bots[i].total = I_GetPreciseTime() - t; + ps_botticcmd_time += ps_bots[i].total; continue; } diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 9b310eb66..c90129081 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -61,6 +61,7 @@ #include "k_follower.h" #include "doomstat.h" #include "deh_tables.h" +#include "m_perfstats.h" #ifdef NETGAME_DEVMODE #define CV_RESTRICT CV_NETVAR @@ -511,7 +512,13 @@ consvar_t cv_mute = CVAR_INIT ("mute", "Off", CV_NETVAR|CV_CALL, CV_OnOff, Mute_ consvar_t cv_sleep = CVAR_INIT ("cpusleep", "1", CV_SAVE, sleeping_cons_t, NULL); static CV_PossibleValue_t perfstats_cons_t[] = { - {0, "Off"}, {1, "Rendering"}, {2, "Logic"}, {3, "ThinkFrame"}, {0, NULL}}; + {PS_OFF, "Off"}, + {PS_RENDER, "Rendering"}, + {PS_LOGIC, "Logic"}, + {PS_BOT, "Bots"}, + {PS_THINKFRAME, "ThinkFrame"}, + {0, NULL} +}; consvar_t cv_perfstats = CVAR_INIT ("perfstats", "Off", 0, perfstats_cons_t, NULL); consvar_t cv_director = CVAR_INIT ("director", "Off", 0, CV_OnOff, NULL); diff --git a/src/d_player.h b/src/d_player.h index 33e7026e3..85eec2e3e 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -204,11 +204,19 @@ typedef enum typedef enum { - TRIP_NONE, - TRIP_PASSED, - TRIP_BLOCKED, + TRIPSTATE_NONE, + TRIPSTATE_PASSED, + TRIPSTATE_BLOCKED, } tripwirestate_t; +typedef enum +{ + TRIPWIRE_NONE, + TRIPWIRE_IGNORE, + TRIPWIRE_BOOST, + TRIPWIRE_BLASTER, +} tripwirepass_t; + typedef enum { // Unsynced, HUD or clientsided effects @@ -260,6 +268,8 @@ typedef enum #define TUMBLEBOUNCES 3 #define TUMBLEGRAVITY (4*FRACUNIT) +#define TRIPWIRETIME (TICRATE) + //} // for kickstartaccel @@ -444,6 +454,8 @@ typedef struct player_s UINT16 draftleeway; // Leniency timer before removing draft power SINT8 lastdraft; // (-1 to 15) - Last player being drafted + UINT8 tripwireState; // see tripwirestate_t + UINT8 tripwirePass; // see tripwirepass_t UINT16 tripwireLeniency; // When reaching a state that lets you go thru tripwire, you get an extra second leniency after it ends to still go through it. UINT16 itemroulette; // Used for the roulette when deciding what item to give you (was "pw_kartitem") @@ -513,8 +525,6 @@ typedef struct player_s SINT8 glanceDir; // Direction the player is trying to look backwards in - UINT8 tripWireState; // see tripwirestate_t - // SINT8 lives; diff --git a/src/deh_tables.c b/src/deh_tables.c index 2796b23dc..47a6ca515 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -3805,6 +3805,12 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi "S_BATTLEBUMPER_EXBLAST9", "S_BATTLEBUMPER_EXBLAST10", + // Tripwire + "S_TRIPWIREBOOST_TOP", + "S_TRIPWIREBOOST_BOTTOM", + "S_TRIPWIREBOOST_BLAST_TOP", + "S_TRIPWIREBOOST_BLAST_BOTTOM", + // DEZ respawn laser "S_DEZLASER", "S_DEZLASER_TRAIL1", @@ -5338,6 +5344,8 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t "MT_BATTLEBUMPER_DEBRIS", "MT_BATTLEBUMPER_BLAST", + "MT_TRIPWIREBOOST", + "MT_DEZLASER", "MT_WAYPOINT", diff --git a/src/g_game.c b/src/g_game.c index 60261e368..bbc2f0293 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2410,7 +2410,6 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) p->karthud[khud_fault] = khudfault; p->nocontrol = nocontrol; p->kickstartaccel = kickstartaccel; - p->tripWireState = TRIP_NONE; p->botvars.rubberband = FRACUNIT; p->botvars.controller = UINT16_MAX; diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index 871ecf6ad..01e900c38 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -5129,7 +5129,7 @@ static void HWR_ProjectSprite(mobj_t *thing) R_InterpolateMobjState(thing, FRACUNIT, &interp); } - dispoffset = thing->info->dispoffset; + dispoffset = thing->dispoffset; // hitlag vibrating (todo: interp somehow?) if (thing->hitlag > 0 && (thing->eflags & MFE_DAMAGEHITLAG)) diff --git a/src/info.c b/src/info.c index 7d329a35b..72665927e 100644 --- a/src/info.c +++ b/src/info.c @@ -580,7 +580,8 @@ char sprnames[NUMSPRITES + 1][5] = "BEXS", // Battle Bumper Explosion: Shell "BDEB", // Battle Bumper Explosion: Debris "BEXB", // Battle Bumper Explosion: Blast - + "TWBS", // Tripwire Boost + "TWBT", // Tripwire BLASTER "DEZL", // DEZ Laser respawn // Additional Kart Objects @@ -4361,6 +4362,12 @@ state_t states[NUMSTATES] = {SPR_BEXB, FF_FULLBRIGHT|FF_PAPERSPRITE|FF_TRANS80, 2, {NULL}, 0, 0, S_BATTLEBUMPER_EXBLAST10}, // S_BATTLEBUMPER_EXBLAST9 {SPR_BEXB, FF_FULLBRIGHT|FF_PAPERSPRITE|FF_TRANS90, 2, {NULL}, 0, 0, S_NULL}, // S_BATTLEBUMPER_EXBLAST10 + {SPR_TWBS, FF_FULLBRIGHT|FF_ADD|FF_ANIMATE, -1, {NULL}, 6, 2, S_NULL}, // S_TRIPWIREBOOST_TOP + {SPR_TWBS, FF_FULLBRIGHT|FF_ADD|FF_ANIMATE|FF_VERTICALFLIP|FF_HORIZONTALFLIP, -1, {NULL}, 6, 2, S_NULL}, // S_TRIPWIREBOOST_BOTTOM + + {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_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 @@ -24164,6 +24171,33 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, + { // MT_TRIPWIREBOOST + -1, // doomednum + S_TRIPWIREBOOST_TOP, // spawnstate + 1000, // spawnhealth + S_TRIPWIREBOOST_BOTTOM, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_TRIPWIREBOOST_BLAST_TOP, // meleestate + S_TRIPWIREBOOST_BLAST_BOTTOM, // 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 9a571f518..ab1867973 100644 --- a/src/info.h +++ b/src/info.h @@ -1126,7 +1126,8 @@ typedef enum sprite SPR_BEXS, // Battle Bumper Explosion: Shell SPR_BDEB, // Battle Bumper Explosion: Debris SPR_BEXB, // Battle Bumper Explosion: Blast - + SPR_TWBS, // Tripwire Boost + SPR_TWBT, // Tripwire BLASTER SPR_DEZL, // DEZ Laser respawn // Additional Kart Objects @@ -4794,6 +4795,11 @@ typedef enum state S_BATTLEBUMPER_EXBLAST9, S_BATTLEBUMPER_EXBLAST10, + S_TRIPWIREBOOST_TOP, + S_TRIPWIREBOOST_BOTTOM, + S_TRIPWIREBOOST_BLAST_TOP, + S_TRIPWIREBOOST_BLAST_BOTTOM, + // DEZ Laser respawn S_DEZLASER, S_DEZLASER_TRAIL1, @@ -6364,6 +6370,8 @@ typedef enum mobj_type MT_BATTLEBUMPER_DEBRIS, MT_BATTLEBUMPER_BLAST, + MT_TRIPWIREBOOST, + MT_DEZLASER, MT_WAYPOINT, diff --git a/src/k_bot.c b/src/k_bot.c index 0ae14c50e..e64089aef 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -28,6 +28,7 @@ #include "r_things.h" // numskins #include "k_race.h" // finishBeamLine #include "k_boss.h" +#include "m_perfstats.h" /*-------------------------------------------------- @@ -279,7 +280,7 @@ boolean K_BotCanTakeCut(player_t *player) { if ( #if 1 - K_TripwirePassConditions(player) == true + K_TripwirePassConditions(player) != TRIPWIRE_NONE #else K_ApplyOffroad(player) == false #endif @@ -347,8 +348,8 @@ static fixed_t K_BotSpeedScaled(player_t *player, fixed_t speed) // Going downhill: FRACUNIT*2 slopeMul = FRACUNIT + FINECOSINE(angle >> ANGLETOFINESHIFT); - // Range: 0.9 to 1.1 - result = FixedMul(result, (FRACUNIT*9/10) + (slopeMul/10)); + // Range: 0.5 to 1.5 + result = FixedMul(result, (FRACUNIT>>1) + (slopeMul >> 1)); } } @@ -638,133 +639,119 @@ fixed_t K_DistanceOfLineFromPoint(fixed_t v1x, fixed_t v1y, fixed_t v2x, fixed_t --------------------------------------------------*/ static botprediction_t *K_CreateBotPrediction(player_t *player) { + const precise_t time = I_GetPreciseTime(); + // Stair janking makes it harder to steer, so attempt to steer harder. - const UINT8 jankDiv = (player->stairjank > 0 ? 2 : 1); + const UINT8 jankDiv = (player->stairjank > 0) ? 2 : 1; const INT16 handling = K_GetKartTurnValue(player, KART_FULLTURN) / jankDiv; // Reduce prediction based on how fast you can turn - const INT16 normal = KART_FULLTURN; // "Standard" handling to compare to - const tic_t futuresight = (TICRATE * normal) / max(1, handling); // How far ahead into the future to try and predict + const tic_t futuresight = (TICRATE * KART_FULLTURN) / max(1, handling); // How far ahead into the future to try and predict const fixed_t speed = K_BotSpeedScaled(player, P_AproxDistance(player->mo->momx, player->mo->momy)); - const INT32 startDist = (768 * mapobjectscale) / FRACUNIT; + const INT32 startDist = (DEFAULT_WAYPOINT_RADIUS * 2 * mapobjectscale) / FRACUNIT; const INT32 distance = ((speed / FRACUNIT) * futuresight) + startDist; - botprediction_t *predict = Z_Calloc(sizeof(botprediction_t), PU_STATIC, NULL); - waypoint_t *wp = player->nextwaypoint; - - INT32 distanceleft = distance; - fixed_t smallestradius = INT32_MAX; - angle_t angletonext = ANGLE_MAX; - // Halves radius when encountering a wall on your way to your destination. fixed_t radreduce = FRACUNIT; - size_t nwp; + INT32 distanceleft = distance; + fixed_t smallestradius = INT32_MAX; + angle_t angletonext = ANGLE_MAX; + INT32 disttonext = INT32_MAX; + + waypoint_t *finishLine = K_GetFinishLineWaypoint(); + waypoint_t *wp = player->nextwaypoint; + mobj_t *prevwpmobj = player->mo; + + const boolean useshortcuts = K_BotCanTakeCut(player); + const boolean huntbackwards = false; + boolean pathfindsuccess = false; + path_t pathtofinish = {0}; + + botprediction_t *predict = Z_Calloc(sizeof(botprediction_t), PU_STATIC, NULL); size_t i; - // Reduce distance left by your distance to the starting waypoint. - // This prevents looking too far ahead if the closest waypoint is really far away. - distanceleft -= P_AproxDistance(player->mo->x - wp->mobj->x, player->mo->y - wp->mobj->y) / FRACUNIT; + // Init defaults in case of pathfind failure + angletonext = R_PointToAngle2(prevwpmobj->x, prevwpmobj->y, wp->mobj->x, wp->mobj->y); + disttonext = P_AproxDistance(prevwpmobj->x - wp->mobj->x, prevwpmobj->y - wp->mobj->y) / FRACUNIT; - // We don't want to look ahead at all, just go to the first waypoint. - if (distanceleft <= 0) - { - predict->x = wp->mobj->x; - predict->y = wp->mobj->y; - predict->radius = wp->mobj->radius; - return predict; - } - - angletonext = R_PointToAngle2( - player->mo->x, player->mo->y, - wp->mobj->x, wp->mobj->y + pathfindsuccess = K_PathfindToWaypoint( + player->nextwaypoint, finishLine, + &pathtofinish, + useshortcuts, huntbackwards ); - // Go through waypoints until we've traveled the distance we wanted to predict ahead! - while (distanceleft > 0) + // Go through the waypoints until we've traveled the distance we wanted to predict ahead! + if (pathfindsuccess == true) { - INT32 disttonext = INT32_MAX; - - if (wp->mobj->radius < smallestradius) + for (i = 0; i < pathtofinish.numnodes; i++) { - smallestradius = wp->mobj->radius; - } + wp = (waypoint_t *)pathtofinish.array[i].nodedata; - if (wp->numnextwaypoints == 0) - { - // Well, this is where I get off. - distanceleft = 0; - break; - } - - // Calculate nextwaypoints index to use - // nextwaypoints[0] by default - nwp = 0; - - // There are multiple nextwaypoints, - // so we need to find the most convenient one to us. - // Let's compare the angle to the player's! - if (wp->numnextwaypoints > 1) - { - angle_t delta = ANGLE_MAX; - angle_t a = ANGLE_MAX; - - for (i = 0; i < wp->numnextwaypoints; i++) + if (i == 0) { - if (K_GetWaypointIsEnabled(wp->nextwaypoints[i]) == false) + prevwpmobj = player->mo; + } + else + { + prevwpmobj = ((waypoint_t *)pathtofinish.array[ i - 1 ].nodedata)->mobj; + } + + angletonext = R_PointToAngle2(prevwpmobj->x, prevwpmobj->y, wp->mobj->x, wp->mobj->y); + disttonext = P_AproxDistance(prevwpmobj->x - wp->mobj->x, prevwpmobj->y - wp->mobj->y) / FRACUNIT; + + if (P_TraceBotTraversal(player->mo, wp->mobj) == false) + { + // If we can't get a direct path to this waypoint, predict less. + distanceleft -= disttonext; + radreduce = FRACUNIT >> 1; + } + + if (wp->mobj->radius < smallestradius) + { + smallestradius = wp->mobj->radius; + } + + distanceleft -= disttonext; + + if (distanceleft <= 0) + { + // We're done!! + break; + } + + if (i == pathtofinish.numnodes-1 && disttonext > 0) + { + // We were pathfinding to the finish, but we want to go past it. + // Set up a new pathfind. + + waypoint_t *next = NULL; + + if (finishLine->numnextwaypoints == 0) { - continue; + distanceleft = 0; + break; } - if (K_GetWaypointIsShortcut(wp->nextwaypoints[i]) == true && K_BotCanTakeCut(player) == false) - { - continue; - } + // default to first one + next = wp->nextwaypoints[0]; - // Unlike the other parts of this function, we're comparing the player's physical position, NOT the position of the waypoint!! - // This should roughly correspond with how players will think about path splits. - a = R_PointToAngle2( - player->mo->x, player->mo->y, - wp->nextwaypoints[i]->mobj->x, wp->nextwaypoints[i]->mobj->y + pathfindsuccess = K_PathfindToWaypoint( + next, finishLine, + &pathtofinish, + useshortcuts, huntbackwards ); - if (a > ANGLE_180) - { - a = InvAngle(a); - } - a = player->mo->angle - a; - - if (a < delta) + if (pathfindsuccess == false) { - nwp = i; - delta = a; + distanceleft = 0; + break; } } } - - angletonext = R_PointToAngle2( - wp->mobj->x, wp->mobj->y, - wp->nextwaypoints[nwp]->mobj->x, wp->nextwaypoints[nwp]->mobj->y - ); - disttonext = (INT32)wp->nextwaypointdistances[nwp]; - - if (P_TraceBotTraversal(player->mo, wp->nextwaypoints[nwp]->mobj) == false) - { - // If we can't get a direct path to this waypoint, we don't want to check much further... - disttonext *= 2; - radreduce = FRACUNIT/2; - } - - if (disttonext > distanceleft) - { - break; - } - - distanceleft -= disttonext; - - wp = wp->nextwaypoints[nwp]; + Z_Free(pathtofinish.array); } // Set our predicted point's coordinates, @@ -777,10 +764,11 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) if (distanceleft > 0) { // Scaled with the leftover anglemul! - predict->x += P_ReturnThrustX(NULL, angletonext, distanceleft * FRACUNIT); - predict->y += P_ReturnThrustY(NULL, angletonext, distanceleft * FRACUNIT); + predict->x += P_ReturnThrustX(NULL, angletonext, min(disttonext, distanceleft) * FRACUNIT); + predict->y += P_ReturnThrustY(NULL, angletonext, min(disttonext, distanceleft) * FRACUNIT); } + ps_bots[player - players].prediction += I_GetPreciseTime() - time; return predict; } @@ -804,9 +792,11 @@ static UINT8 K_TrySpindash(player_t *player) const fixed_t baseAccel = K_GetNewSpeed(player) - oldSpeed; const fixed_t speedDiff = player->speed - player->lastspeed; - if (player->spindashboost || player->tiregrease) + const INT32 angleDiff = AngleDelta(player->mo->angle, K_MomentumAngle(player->mo)); + + if (player->spindashboost || player->tiregrease // You just released a spindash, you don't need to try again yet, jeez. + || P_PlayerInPain(player) || !P_IsObjectOnGround(player->mo)) // Not in a state where we want 'em to spindash. { - // You just released a spindash, you don't need to try again yet, jeez. player->botvars.spindashconfirm = 0; return 0; } @@ -837,36 +827,13 @@ static UINT8 K_TrySpindash(player_t *player) } } - // Logic for normal racing. - if (player->flashing > 0) - { - // Don't bother trying to spindash. - // Trying to spindash while flashing is fine during POSITION, but not during the actual race. - return 0; - } - - if (speedDiff < (baseAccel / 4)) - { - if (player->botvars.spindashconfirm < BOTSPINDASHCONFIRM) - { - player->botvars.spindashconfirm++; - } - } - else - { - if (player->botvars.spindashconfirm > 0) - { - player->botvars.spindashconfirm--; - } - } - if (player->botvars.spindashconfirm >= BOTSPINDASHCONFIRM) { INT32 chargingPoint = (K_GetSpindashChargeTime(player) + difficultyModifier); // Release quicker the higher the difficulty is. // Sounds counter-productive, but that's actually the best strategy after the race has started. - chargingPoint -= player->botvars.difficulty * difficultyModifier; + chargingPoint -= min(DIFFICULTBOT, player->botvars.difficulty) * difficultyModifier; if (player->spindash > chargingPoint) { @@ -876,6 +843,25 @@ static UINT8 K_TrySpindash(player_t *player) return 2; } + else + { + // Logic for normal racing. + if (speedDiff < (baseAccel / 8) // Moving too slowly + || angleDiff > ANG60) // Being pushed backwards + { + if (player->botvars.spindashconfirm < BOTSPINDASHCONFIRM) + { + player->botvars.spindashconfirm++; + } + } + else if (player->botvars.spindashconfirm >= BOTSPINDASHCONFIRM) + { + if (player->botvars.spindashconfirm > 0) + { + player->botvars.spindashconfirm--; + } + } + } // We're doing just fine, we don't need to spindash, thanks. return 0; @@ -1007,29 +993,27 @@ static INT32 K_HandleBotTrack(player_t *player, ticcmd_t *cmd, botprediction_t * // Handle steering towards waypoints! INT32 turnamt = 0; SINT8 turnsign = 0; - angle_t moveangle, angle; - INT16 anglediff; + angle_t moveangle; + INT32 anglediff; I_Assert(predict != NULL); moveangle = player->mo->angle; - angle = (moveangle - destangle); + anglediff = AngleDeltaSigned(moveangle, destangle); - if (angle < ANGLE_180) + if (anglediff < 0) { - turnsign = -1; // Turn right - anglediff = AngleFixed(angle)>>FRACBITS; + turnsign = 1; } else { - turnsign = 1; // Turn left - anglediff = 360-(AngleFixed(angle)>>FRACBITS); + turnsign = -1; } anglediff = abs(anglediff); turnamt = KART_FULLTURN * turnsign; - if (anglediff > 90) + if (anglediff > ANGLE_90) { // Wrong way! cmd->forwardmove = -MAXPLMOVE; @@ -1038,7 +1022,7 @@ static INT32 K_HandleBotTrack(player_t *player, ticcmd_t *cmd, botprediction_t * else { const fixed_t playerwidth = (player->mo->radius * 2); - fixed_t realrad = predict->radius - (playerwidth * 4); // Remove a "safe" distance away from the edges of the road + fixed_t realrad = predict->radius*3/4; // Remove a "safe" distance away from the edges of the road fixed_t rad = realrad; fixed_t dirdist = K_DistanceOfLineFromPoint( player->mo->x, player->mo->y, @@ -1046,19 +1030,26 @@ static INT32 K_HandleBotTrack(player_t *player, ticcmd_t *cmd, botprediction_t * predict->x, predict->y ); - if (realrad < player->mo->radius) + if (realrad < playerwidth) { - realrad = player->mo->radius; + realrad = playerwidth; } - if (anglediff > 0) - { - // Become more precise based on how hard you need to turn - // This makes predictions into turns a little nicer - // Facing 90 degrees away from the predicted point gives you a 1/3 radius - rad = FixedMul(rad, ((135 - anglediff) * FRACUNIT) / 135); - } + // Become more precise based on how hard you need to turn + // This makes predictions into turns a little nicer + // Facing 90 degrees away from the predicted point gives you 0 radius + rad = FixedMul(rad, + FixedDiv(max(0, ANGLE_90 - anglediff), ANGLE_90) + ); + // Become more precise the slower you're moving + // Also helps with turns + // Full speed uses full radius + rad = FixedMul(rad, + FixedDiv(K_BotSpeedScaled(player, player->speed), K_GetKartSpeed(player, false, false)) + ); + + // Cap the radius to reasonable bounds if (rad > realrad) { rad = realrad; @@ -1068,36 +1059,14 @@ static INT32 K_HandleBotTrack(player_t *player, ticcmd_t *cmd, botprediction_t * rad = playerwidth; } - cmd->buttons |= BT_ACCELERATE; - // Full speed ahead! + cmd->buttons |= BT_ACCELERATE; cmd->forwardmove = MAXPLMOVE; if (dirdist <= rad) { - fixed_t speedmul = FixedDiv(K_BotSpeedScaled(player, player->speed), K_GetKartSpeed(player, false, false)); - fixed_t speedrad = rad/4; - - if (speedmul > FRACUNIT) - { - speedmul = FRACUNIT; - } - - // Increase radius with speed - // At low speed, the CPU will try to be more accurate - // At high speed, they're more likely to lawnmower - speedrad += FixedMul(speedmul, rad - speedrad); - - if (speedrad < playerwidth) - { - speedrad = playerwidth; - } - - if (dirdist <= speedrad) - { - // Don't turn at all - turnamt = 0; - } + // Going the right way, don't turn at all. + turnamt = 0; } } @@ -1254,6 +1223,7 @@ static INT32 K_HandleBotReverse(player_t *player, ticcmd_t *cmd, botprediction_t --------------------------------------------------*/ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) { + precise_t t = 0; botprediction_t *predict = NULL; boolean trySpindash = true; angle_t destangle = 0; @@ -1328,7 +1298,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) if (botController != NULL && (botController->flags & ML_EFFECT1)) { - const fixed_t dist = (player->mo->radius * 4); + const fixed_t dist = DEFAULT_WAYPOINT_RADIUS * player->mo->scale; // X Offset: Movement direction destangle = FixedAngle(sides[botController->sidenum[0]].textureoffset); @@ -1474,7 +1444,9 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) { // Don't pointlessly try to use rings/sneakers while charging a spindash. // TODO: Allowing projectile items like orbinaut while e-braking would be nice, maybe just pass in the spindash variable? + t = I_GetPreciseTime(); K_BotItemUsage(player, cmd, turnamt); + ps_bots[player - players].item = I_GetPreciseTime() - t; } if (turnamt != 0) diff --git a/src/k_bot.h b/src/k_bot.h index f03187205..f476848f0 100644 --- a/src/k_bot.h +++ b/src/k_bot.h @@ -28,7 +28,7 @@ #define BOTTURNCONFIRM 4 // How many tics without being able to accelerate before we'll let you spindash. -#define BOTSPINDASHCONFIRM (TICRATE/4) +#define BOTSPINDASHCONFIRM (2*TICRATE) // Point for bots to aim for typedef struct botprediction_s { diff --git a/src/k_botsearch.c b/src/k_botsearch.c index b62b55852..af7464c30 100644 --- a/src/k_botsearch.c +++ b/src/k_botsearch.c @@ -27,6 +27,7 @@ #include "m_random.h" #include "r_things.h" // numskins #include "p_slopes.h" // P_GetZAt +#include "m_perfstats.h" struct globalsmuggle { @@ -172,26 +173,24 @@ static boolean K_BotHatesThisSectorsSpecial(player_t *player, sector_t *sec) boolean K_BotHatesThisSector(player_t *player, sector_t *sec, fixed_t x, fixed_t y) { const boolean flip = (player->mo->eflags & MFE_VERTICALFLIP); - INT32 specialflag = 0; fixed_t highestfloor = INT32_MAX; sector_t *bestsector = NULL; ffloor_t *rover; + // TODO: Properly support SF_FLIPSPECIAL_FLOOR / SF_FLIPSPECIAL_CEILING. + // An earlier attempt at it caused lots of false positives and other weird + // quirks with intangible FOFs. + if (flip == true) { - specialflag = SF_FLIPSPECIAL_CEILING; highestfloor = P_GetZAt(sec->c_slope, x, y, sec->ceilingheight); } else { - specialflag = SF_FLIPSPECIAL_FLOOR; highestfloor = P_GetZAt(sec->f_slope, x, y, sec->floorheight); } - if (sec->flags & specialflag) - { - bestsector = sec; - } + bestsector = sec; for (rover = sec->ffloors; rover; rover = rover->next) { @@ -209,15 +208,13 @@ boolean K_BotHatesThisSector(player_t *player, sector_t *sec, fixed_t x, fixed_t if (!(rover->flags & FF_BLOCKPLAYER)) { if ((top >= player->mo->z) && (bottom <= player->mo->z + player->mo->height) - && K_BotHatesThisSectorsSpecial(player, rover->master->frontsector)) + && K_BotHatesThisSectorsSpecial(player, rover->master->frontsector)) { // Bad intangible sector at our height, so we DEFINITELY want to avoid return true; } - } - if ((rover->flags & FF_BLOCKPLAYER) && !(rover->master->frontsector->flags & specialflag)) - { + // Ignore them, we want the one below it. continue; } @@ -225,7 +222,7 @@ boolean K_BotHatesThisSector(player_t *player, sector_t *sec, fixed_t x, fixed_t if (flip == true) { if (bottom < highestfloor - && bottom >= player->mo->z + player->mo->height) + && bottom >= player->mo->z + player->mo->height) { bestsector = rover->master->frontsector; highestfloor = bottom; @@ -234,7 +231,7 @@ boolean K_BotHatesThisSector(player_t *player, sector_t *sec, fixed_t x, fixed_t else { if (top > highestfloor - && top <= player->mo->z) + && top <= player->mo->z) { bestsector = rover->master->frontsector; highestfloor = top; @@ -617,6 +614,8 @@ static BlockItReturn_t K_FindObjectsForNudging(mobj_t *thing) --------------------------------------------------*/ void K_NudgePredictionTowardsObjects(botprediction_t *predict, player_t *player) { + const precise_t time = I_GetPreciseTime(); + INT32 xl, xh, yl, yh, bx, by; fixed_t distToPredict = R_PointToDist2(player->mo->x, player->mo->y, predict->x, predict->y); @@ -731,7 +730,7 @@ void K_NudgePredictionTowardsObjects(botprediction_t *predict, player_t *player) // Check if our side is invalid, if so, don't do the code below. if (gotoSide != -1 && globalsmuggle.gotoObjs[gotoSide] == 0) { - // Do not use a side + // Do not use a side gotoSide = -1; } @@ -773,6 +772,8 @@ void K_NudgePredictionTowardsObjects(botprediction_t *predict, player_t *player) //distToPredict = R_PointToDist2(player->mo->x, player->mo->y, predict->x, predict->y); } } + + ps_bots[player - players].nudge += I_GetPreciseTime() - time; } /*-------------------------------------------------- diff --git a/src/k_grandprix.c b/src/k_grandprix.c index 740730c6f..077532fa3 100644 --- a/src/k_grandprix.c +++ b/src/k_grandprix.c @@ -639,7 +639,7 @@ void K_RetireBots(void) --------------------------------------------------*/ void K_FakeBotResults(player_t *bot) { - const UINT32 distfactor = FixedMul(32 * bot->mo->scale, K_GetKartGameSpeedScalar(gamespeed)) / FRACUNIT; + const UINT32 distfactor = FixedMul(32 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed)) / FRACUNIT; UINT32 worstdist = 0; tic_t besttime = UINT32_MAX; UINT8 numplayers = 0; diff --git a/src/k_kart.c b/src/k_kart.c index f47bf3b17..189b946cd 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -2944,23 +2944,32 @@ boolean K_SlopeResistance(player_t *player) return false; } -boolean K_TripwirePassConditions(player_t *player) +tripwirepass_t K_TripwirePassConditions(player_t *player) { if ( player->invincibilitytimer || - player->sneakertimer || - player->growshrinktimer > 0 || + player->sneakertimer + ) + return TRIPWIRE_BLASTER; + + if ( player->flamedash || - player->hyudorotimer || player->speed > 2 * K_GetKartSpeed(player, false, true) ) - return true; - return false; + return TRIPWIRE_BOOST; + + if ( + player->growshrinktimer > 0 || + player->hyudorotimer + ) + return TRIPWIRE_IGNORE; + + return TRIPWIRE_NONE; } boolean K_TripwirePass(player_t *player) { - return (K_TripwirePassConditions(player) || (player->tripwireLeniency > 0)); + return (player->tripwirePass != TRIPWIRE_NONE); } boolean K_WaterRun(player_t *player) @@ -3667,7 +3676,7 @@ void K_TumblePlayer(player_t *player, mobj_t *inflictor, mobj_t *source) player->tumbleBounces = 1; - if (player->tripWireState == TRIP_PASSED) + if (player->tripwireState == TRIPSTATE_PASSED) { player->tumbleHeight = 50; } @@ -3781,15 +3790,15 @@ void K_TumbleInterrupt(player_t *player) void K_ApplyTripWire(player_t *player, tripwirestate_t state) { - if (state == TRIP_PASSED) + if (state == TRIPSTATE_PASSED) S_StartSound(player->mo, sfx_ssa015); - else if (state == TRIP_BLOCKED) + else if (state == TRIPSTATE_BLOCKED) S_StartSound(player->mo, sfx_kc40); - player->tripWireState = state; + player->tripwireState = state; K_AddHitLag(player->mo, 10, false); - if (state == TRIP_PASSED && player->spinouttimer && + if (state == TRIPSTATE_PASSED && player->spinouttimer && player->speed > 2 * K_GetKartSpeed(player, false, true)) { K_TumblePlayer(player, NULL, NULL); @@ -4939,90 +4948,6 @@ void K_DriftDustHandling(mobj_t *spawner) } } -static void K_SpawnTripwireVFX(mobj_t *mo) -{ - tic_t t = leveltime; - angle_t ang, aoff; - SINT8 sign = 1; - boolean altColor = false; - mobj_t *dust; - boolean drifting = false; - UINT8 i; - - I_Assert(mo != NULL); - I_Assert(!P_MobjWasRemoved(mo)); - - if (!P_IsObjectOnGround(mo)) - return; - - if (mo->player) - { - ang = mo->player->drawangle; - - if (mo->player->drift != 0) - { - drifting = true; - ang += (mo->player->drift * ((ANGLE_270 + ANGLE_22h) / 5)); // -112.5 doesn't work. I fucking HATE SRB2 angles - if (mo->player->drift < 0) - sign = 1; - else - sign = -1; - } - } - else - ang = mo->angle; - - if (drifting == false) - { - i = (t & 1); - - if (i & 1) - sign = -1; - else - sign = 1; - } - else - { - if (t & 1) - { - return; - } - - t /= 2; - i = (t & 1); - } - - aoff = (ang + ANGLE_180) + (ANGLE_45 * sign); - - dust = P_SpawnMobj(mo->x + FixedMul(24*mo->scale, FINECOSINE(aoff>>ANGLETOFINESHIFT)), - mo->y + FixedMul(24*mo->scale, FINESINE(aoff>>ANGLETOFINESHIFT)), - mo->z, MT_DRIFTDUST); - - P_SetTarget(&dust->target, mo); - P_InitAngle(dust, ang - (ANGLE_90 * sign)); // point completely perpendicular from the player - P_SetScale(dust, mo->scale); - dust->destscale = mo->scale * 6; - dust->scalespeed = mo->scale/12; - K_FlipFromObject(dust, mo); - - altColor = (sign > 0); - - if ((t / 2) & 1) - { - dust->tics++; // "randomize" animation - altColor = !altColor; - } - - dust->colorized = true; - dust->color = altColor ? SKINCOLOR_BLOSSOM : SKINCOLOR_JAWZ; - - dust->momx = (4*mo->momx)/5; - dust->momy = (4*mo->momy)/5; - dust->momz = (4*P_GetMobjZMovement(mo))/5; - - P_Thrust(dust, dust->angle, 4*mo->scale); -} - void K_Squish(mobj_t *mo) { const fixed_t maxstretch = 4*FRACUNIT; @@ -7289,6 +7214,52 @@ static void K_LookForRings(mobj_t *pmo) P_BlockThingsIterator(bx, by, PIT_AttractingRings); } +static void K_UpdateTripwire(player_t *player) +{ + fixed_t speedThreshold = (3*K_GetKartSpeed(player, false, true))/4; + boolean goodSpeed = (player->speed >= speedThreshold); + boolean boostExists = (player->tripwireLeniency > 0); // can't be checked later because of subtractions... + tripwirepass_t triplevel = K_TripwirePassConditions(player); + + if (triplevel != TRIPWIRE_NONE) + { + if (!boostExists) + { + mobj_t *front = P_SpawnMobjFromMobj(player->mo, 0, 0, 0, MT_TRIPWIREBOOST); + mobj_t *back = P_SpawnMobjFromMobj(player->mo, 0, 0, 0, MT_TRIPWIREBOOST); + + P_SetTarget(&front->target, player->mo); + P_SetTarget(&back->target, player->mo); + + front->dispoffset = 1; + front->old_angle = back->old_angle = K_MomentumAngle(player->mo); + back->dispoffset = -1; + back->extravalue1 = 1; + P_SetMobjState(back, S_TRIPWIREBOOST_BOTTOM); + } + + player->tripwirePass = triplevel; + player->tripwireLeniency = max(player->tripwireLeniency, TRIPWIRETIME); + } + else + { + if (boostExists) + { + player->tripwireLeniency--; + if (goodSpeed == false && player->tripwireLeniency > 0) + { + // Decrease at double speed when your speed is bad. + player->tripwireLeniency--; + } + } + + if (player->tripwireLeniency <= 0) + { + player->tripwirePass = TRIPWIRE_NONE; + } + } +} + /** \brief Decreases various kart timers and powers per frame. Called in P_PlayerThink in p_user.c \param player player object passed from P_PlayerThink @@ -7644,16 +7615,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) K_HandleTumbleBounce(player); } - if (player->tripwireLeniency > 0) - { - player->tripwireLeniency--; - K_SpawnTripwireVFX(player->mo); - } - - if (K_TripwirePassConditions(player) == true) - { - player->tripwireLeniency = max(player->tripwireLeniency, TICRATE); - } + K_UpdateTripwire(player); K_KartPlayerHUDUpdate(player); @@ -7765,14 +7727,14 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) // Handle invincibility sfx K_UpdateInvincibilitySounds(player); // Also thanks, VAda! - if (player->tripWireState != TRIP_NONE) + if (player->tripwireState != TRIPSTATE_NONE) { - if (player->tripWireState == TRIP_PASSED) + if (player->tripwireState == TRIPSTATE_PASSED) S_StartSound(player->mo, sfx_cdfm63); - else if (player->tripWireState == TRIP_BLOCKED) + else if (player->tripwireState == TRIPSTATE_BLOCKED) S_StartSound(player->mo, sfx_kc4c); - player->tripWireState = TRIP_NONE; + player->tripwireState = TRIPSTATE_NONE; } K_KartEbrakeVisuals(player); diff --git a/src/k_kart.h b/src/k_kart.h index 797899cea..00c36ea76 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -121,7 +121,7 @@ void K_StripOther(player_t *player); void K_MomentumToFacing(player_t *player); boolean K_ApplyOffroad(player_t *player); boolean K_SlopeResistance(player_t *player); -boolean K_TripwirePassConditions(player_t *player); +tripwirepass_t K_TripwirePassConditions(player_t *player); boolean K_TripwirePass(player_t *player); boolean K_WaterRun(player_t *player); void K_ApplyTripWire(player_t *player, tripwirestate_t state); diff --git a/src/lua_hooklib.c b/src/lua_hooklib.c index 0a46674c2..771182a59 100644 --- a/src/lua_hooklib.c +++ b/src/lua_hooklib.c @@ -482,7 +482,7 @@ void LUAh_ThinkFrame(void) if (hookp->type != hook_ThinkFrame) continue; - if (cv_perfstats.value == 3) + if (cv_perfstats.value == PS_THINKFRAME) time_taken = I_GetPreciseTime(); PushHook(gL, hookp); if (lua_pcall(gL, 0, 0, 1)) { @@ -491,7 +491,7 @@ void LUAh_ThinkFrame(void) lua_pop(gL, 1); hookp->error = true; } - if (cv_perfstats.value == 3) + if (cv_perfstats.value == PS_THINKFRAME) { lua_Debug ar; time_taken = I_GetPreciseTime() - time_taken; diff --git a/src/lua_mobjlib.c b/src/lua_mobjlib.c index 4fd02d9a1..418a43bf9 100644 --- a/src/lua_mobjlib.c +++ b/src/lua_mobjlib.c @@ -99,7 +99,8 @@ enum mobj_e { mobj_sprxoff, mobj_spryoff, mobj_sprzoff, - mobj_hitlag + mobj_hitlag, + mobj_dispoffset }; static const char *const mobj_opt[] = { @@ -180,6 +181,7 @@ static const char *const mobj_opt[] = { "spryoff", "sprzoff", "hitlag", + "dispoffset", NULL}; #define UNIMPLEMENTED luaL_error(L, LUA_QL("mobj_t") " field " LUA_QS " is not implemented for Lua and cannot be accessed.", mobj_opt[field]) @@ -458,6 +460,9 @@ static int mobj_get(lua_State *L) case mobj_hitlag: lua_pushinteger(L, mo->hitlag); break; + case mobj_dispoffset: + lua_pushinteger(L, mo->dispoffset); + break; default: // extra custom variables in Lua memory lua_getfield(L, LUA_REGISTRYINDEX, LREG_EXTVARS); I_Assert(lua_istable(L, -1)); @@ -830,6 +835,9 @@ static int mobj_set(lua_State *L) case mobj_hitlag: mo->hitlag = luaL_checkinteger(L, 3); break; + case mobj_dispoffset: + mo->dispoffset = luaL_checkinteger(L, 3); + break; default: lua_getfield(L, LUA_REGISTRYINDEX, LREG_EXTVARS); I_Assert(lua_istable(L, -1)); diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index c448a287a..1d5f6bee7 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -292,6 +292,10 @@ static int player_get(lua_State *L) lua_pushinteger(L, plr->draftleeway); else if (fastcmp(field,"lastdraft")) lua_pushinteger(L, plr->lastdraft); + else if (fastcmp(field,"tripwireState")) + lua_pushinteger(L, plr->tripwireState); + else if (fastcmp(field,"tripwirePass")) + lua_pushinteger(L, plr->tripwirePass); else if (fastcmp(field,"tripwireLeniency")) lua_pushinteger(L, plr->tripwireLeniency); else if (fastcmp(field,"itemroulette")) @@ -646,6 +650,10 @@ static int player_set(lua_State *L) plr->draftleeway = luaL_checkinteger(L, 3); else if (fastcmp(field,"lastdraft")) plr->lastdraft = luaL_checkinteger(L, 3); + else if (fastcmp(field,"tripwireState")) + plr->tripwireState = luaL_checkinteger(L, 3); + else if (fastcmp(field,"tripwirePass")) + plr->tripwirePass = luaL_checkinteger(L, 3); else if (fastcmp(field,"tripwireLeniency")) plr->tripwireLeniency = luaL_checkinteger(L, 3); else if (fastcmp(field,"itemroulette")) diff --git a/src/m_perfstats.c b/src/m_perfstats.c index a1d4d0fba..0be2eb805 100644 --- a/src/m_perfstats.c +++ b/src/m_perfstats.c @@ -18,6 +18,7 @@ #include "i_time.h" #include "z_zone.h" #include "p_local.h" +#include "g_game.h" #ifdef HWRENDER #include "hardware/hw_main.h" @@ -62,6 +63,8 @@ ps_hookinfo_t *thinkframe_hooks = NULL; int thinkframe_hooks_length = 0; int thinkframe_hooks_capacity = 16; +ps_botinfo_t ps_bots[MAXPLAYERS]; + static INT32 draw_row; void PS_SetThinkFrameHookInfo(int index, precise_t time_taken, char* short_src) @@ -85,6 +88,12 @@ void PS_SetThinkFrameHookInfo(int index, precise_t time_taken, char* short_src) thinkframe_hooks_length = index + 1; } +void PS_ResetBotInfo(void) +{ + memset(ps_bots, 0, sizeof(ps_bots)); + ps_botticcmd_time = 0; +} + static void PS_SetFrameTime(void) { precise_t currenttime = I_GetPreciseTime(); @@ -486,15 +495,126 @@ void M_DrawPerfStats(void) PS_SetFrameTime(); - if (cv_perfstats.value == 1) // rendering + if (cv_perfstats.value == PS_RENDER) // rendering { M_DrawRenderStats(); } - else if (cv_perfstats.value == 2) // logic + else if (cv_perfstats.value == PS_LOGIC) // logic { M_DrawTickStats(); } - else if (cv_perfstats.value == 3) // lua thinkframe + else if (cv_perfstats.value == PS_BOT) // bot ticcmd + { + if (vid.width < 640 || vid.height < 400) // low resolution + { + // it's not gonna fit very well.. + V_DrawThinString(30, 30, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, "Not available for resolutions below 640x400"); + } + else // high resolution + { + precise_t otherTime = 0; + int i; + + // text writing position + int x = 2; + int y = 4; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (ps_bots[i].isBot == false) + { + continue; + } + + snprintf(s, sizeof s - 1, "Bot %d (%s):", i + 1, player_names[i]); + V_DrawSmallString(x, y, V_MONOSPACE | V_ALLOWLOWERCASE | V_GRAYMAP, s); + + snprintf(s, sizeof s - 1, "%ld", (long)((ps_bots[i].total) / (I_GetPrecisePrecision() / 1000000))); + V_DrawRightAlignedSmallString(x + 98, y, V_MONOSPACE | V_ALLOWLOWERCASE | V_GRAYMAP, s); + + y += 4; // repeated code! + if (y > 192) + { + y = 4; + x += 106; + if (x > 214) + break; + } + + snprintf(s, sizeof s - 1, "Prediction:"); + V_DrawSmallString(x, y, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, s); + + snprintf(s, sizeof s - 1, "%ld", (long)((ps_bots[i].prediction) / (I_GetPrecisePrecision() / 1000000))); + V_DrawRightAlignedSmallString(x + 98, y, V_MONOSPACE | V_ALLOWLOWERCASE, s); + + y += 4; // repeated code! + if (y > 192) + { + y = 4; + x += 106; + if (x > 214) + break; + } + + snprintf(s, sizeof s - 1, "Nudge:"); + V_DrawSmallString(x, y, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, s); + + snprintf(s, sizeof s - 1, "%ld", (long)((ps_bots[i].nudge) / (I_GetPrecisePrecision() / 1000000))); + V_DrawRightAlignedSmallString(x + 98, y, V_MONOSPACE | V_ALLOWLOWERCASE, s); + + y += 4; // repeated code! + if (y > 192) + { + y = 4; + x += 106; + if (x > 214) + break; + } + + snprintf(s, sizeof s - 1, "Item:"); + V_DrawSmallString(x, y, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, s); + + snprintf(s, sizeof s - 1, "%ld", (long)((ps_bots[i].item) / (I_GetPrecisePrecision() / 1000000))); + V_DrawRightAlignedSmallString(x + 98, y, V_MONOSPACE | V_ALLOWLOWERCASE, s); + + y += 4; // repeated code! + if (y > 192) + { + y = 4; + x += 106; + if (x > 214) + break; + } + + snprintf(s, sizeof s - 1, "Other:"); + V_DrawSmallString(x, y, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, s); + + otherTime = ps_bots[i].total - ps_bots[i].prediction - ps_bots[i].nudge - ps_bots[i].item; + snprintf(s, sizeof s - 1, "%ld", (long)(otherTime / (I_GetPrecisePrecision() / 1000000))); + V_DrawRightAlignedSmallString(x + 98, y, V_MONOSPACE | V_ALLOWLOWERCASE, s); + + y += 4; // repeated code! + if (y > 192) + { + y = 4; + x += 106; + if (x > 214) + break; + } + + // add an extra space + y += 4; // repeated code! + if (y > 192) + { + y = 4; + x += 106; + if (x > 214) + break; + } + } + } + } + else if (cv_perfstats.value == PS_THINKFRAME) // lua thinkframe { if (!(gamestate == GS_LEVEL || (gamestate == GS_TITLESCREEN && titlemapinaction))) return; diff --git a/src/m_perfstats.h b/src/m_perfstats.h index dae2f2030..2c448031c 100644 --- a/src/m_perfstats.h +++ b/src/m_perfstats.h @@ -16,6 +16,15 @@ #include "lua_script.h" #include "p_local.h" +typedef enum +{ + PS_OFF = 0, + PS_RENDER, + PS_LOGIC, + PS_BOT, + PS_THINKFRAME, +} ps_types_t; + extern precise_t ps_tictime; extern precise_t ps_playerthink_time; @@ -37,6 +46,19 @@ typedef struct void PS_SetThinkFrameHookInfo(int index, precise_t time_taken, char* short_src); +typedef struct +{ + boolean isBot; + precise_t total; + precise_t prediction; // K_CreateBotPrediction + precise_t nudge; // K_NudgePredictionTowardsObjects + precise_t item; // K_BotItemUsage +} ps_botinfo_t; + +extern ps_botinfo_t ps_bots[MAXPLAYERS]; + +void PS_ResetBotInfo(void); + void M_DrawPerfStats(void); #endif diff --git a/src/p_map.c b/src/p_map.c index 3d6938ed2..d667b0346 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -3801,7 +3801,7 @@ void P_BouncePlayerMove(mobj_t *mo) if (P_IsLineTripWire(bestslideline)) { // TRIPWIRE CANNOT BE MADE NONBOUNCY - K_ApplyTripWire(mo->player, TRIP_BLOCKED); + K_ApplyTripWire(mo->player, TRIPSTATE_BLOCKED); } else { diff --git a/src/p_mobj.c b/src/p_mobj.c index 6cf5e1a29..a5a2eb801 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -7163,6 +7163,89 @@ static boolean P_MobjRegularThink(mobj_t *mobj) } } break; + case MT_TRIPWIREBOOST: + if (!mobj->target || !mobj->target->health + || !mobj->target->player || !mobj->target->player->tripwireLeniency) + { + P_RemoveMobj(mobj); + return false; + } + + mobj->angle = K_MomentumAngle(mobj->target); + P_MoveOrigin(mobj, mobj->target->x, mobj->target->y, mobj->target->z + (mobj->target->height >> 1)); + mobj->destscale = mobj->target->scale; + P_SetScale(mobj, mobj->target->scale); + + if (mobj->extravalue1) + { + mobj->angle += ANGLE_180; + } + + { + fixed_t convSpeed = (mobj->target->player->speed * 100) / K_GetKartSpeed(mobj->target->player, false, true); + UINT8 trans = ((mobj->target->player->tripwireLeniency + 1) * (NUMTRANSMAPS+1)) / TRIPWIRETIME; + + if (trans > NUMTRANSMAPS) + trans = NUMTRANSMAPS; + + trans = NUMTRANSMAPS - trans; + + if ((trans >= NUMTRANSMAPS) // not a valid visibility + || (convSpeed < 150 && (leveltime & 1)) // < 150% flickering + || (mobj->target->player->tripwirePass < TRIPWIRE_BOOST) // Not strong enough to make an aura + || mobj->target->player->flamedash) // Flameshield dash + { + mobj->renderflags |= RF_DONTDRAW; + } + else + { + boolean blastermode = (convSpeed >= 200) && (mobj->target->player->tripwirePass >= TRIPWIRE_BLASTER); + + mobj->renderflags &= ~(RF_TRANSMASK|RF_DONTDRAW); + if (trans != 0) + { + mobj->renderflags |= (trans << RF_TRANSSHIFT); + } + mobj->renderflags |= (mobj->target->renderflags & RF_DONTDRAW); + + if (mobj->target->player->invincibilitytimer > 0) + { + if (mobj->target->player->invincibilitytimer > itemtime+(2*TICRATE)) + { + mobj->color = K_RainbowColor(leveltime / 2); + } + else + { + mobj->color = SKINCOLOR_INVINCFLASH; + } + mobj->colorized = true; + } + else if (mobj->target->player->curshield == KSHIELD_FLAME) + { + mobj->color = SKINCOLOR_KETCHUP; + mobj->colorized = true; + } + else + { + mobj->color = SKINCOLOR_NONE; + mobj->colorized = false; + } + + if (blastermode == !(mobj->flags2 & MF2_AMBUSH)) + { + mobj->flags2 ^= MF2_AMBUSH; + if (blastermode) + { + P_SetMobjState(mobj, (mobj->extravalue1) ? S_TRIPWIREBOOST_BLAST_BOTTOM : S_TRIPWIREBOOST_BLAST_TOP); + } + else + { + P_SetMobjState(mobj, (mobj->extravalue1) ? S_TRIPWIREBOOST_BOTTOM : S_TRIPWIREBOOST_TOP); + } + } + } + } + break; case MT_BOOSTFLAME: if (!mobj->target || !mobj->target->health) { @@ -9840,6 +9923,7 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) // Sprite rendering mobj->spritexscale = mobj->spriteyscale = mobj->scale; mobj->spritexoffset = mobj->spriteyoffset = 0; + mobj->dispoffset = info->dispoffset; mobj->floorspriteslope = NULL; // set subsector and/or block links diff --git a/src/p_mobj.h b/src/p_mobj.h index 0f05d7c90..3b947bec6 100644 --- a/src/p_mobj.h +++ b/src/p_mobj.h @@ -408,6 +408,8 @@ typedef struct mobj_s struct terrain_s *terrain; // Terrain definition of the floor this object last hit. NULL when in the air. INT32 hitlag; // Sal-style hit lag, straight from Captain Fetch's jowls + INT32 dispoffset; + // WARNING: New fields must be added separately to savegame and Lua. } mobj_t; diff --git a/src/p_saveg.c b/src/p_saveg.c index 81adeca60..5569f24d5 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -291,6 +291,8 @@ static void P_NetArchivePlayers(void) WRITEUINT16(save_p, players[i].draftleeway); WRITESINT8(save_p, players[i].lastdraft); + WRITEUINT8(save_p, players[i].tripwireState); + WRITEUINT8(save_p, players[i].tripwirePass); WRITEUINT16(save_p, players[i].tripwireLeniency); WRITEUINT16(save_p, players[i].itemroulette); @@ -355,7 +357,6 @@ static void P_NetArchivePlayers(void) WRITEUINT32(save_p, players[i].spheredigestion); WRITESINT8(save_p, players[i].glanceDir); - WRITEUINT8(save_p, players[i].tripWireState); WRITEUINT8(save_p, players[i].typing_timer); WRITEUINT8(save_p, players[i].typing_duration); @@ -575,6 +576,8 @@ static void P_NetUnArchivePlayers(void) players[i].draftleeway = READUINT16(save_p); players[i].lastdraft = READSINT8(save_p); + players[i].tripwireState = READUINT8(save_p); + players[i].tripwirePass = READUINT8(save_p); players[i].tripwireLeniency = READUINT16(save_p); players[i].itemroulette = READUINT16(save_p); @@ -639,7 +642,6 @@ static void P_NetUnArchivePlayers(void) players[i].spheredigestion = READUINT32(save_p); players[i].glanceDir = READSINT8(save_p); - players[i].tripWireState = READUINT8(save_p); players[i].typing_timer = READUINT8(save_p); players[i].typing_duration = READUINT8(save_p); @@ -1600,7 +1602,7 @@ typedef enum MD2_SPRITEXOFFSET = 1<<20, MD2_SPRITEYOFFSET = 1<<21, MD2_FLOORSPRITESLOPE = 1<<22, - // 1<<23 was taken out, maybe reuse later + MD2_DISPOFFSET = 1<<23, MD2_HITLAG = 1<<24, MD2_WAYPOINTCAP = 1<<25, MD2_KITEMCAP = 1<<26, @@ -1841,6 +1843,8 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type) } if (mobj->hitlag) diff2 |= MD2_HITLAG; + if (mobj->dispoffset) + diff2 |= MD2_DISPOFFSET; if (mobj == waypointcap) diff2 |= MD2_WAYPOINTCAP; if (mobj == kitemcap) @@ -2049,6 +2053,10 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type) { WRITEINT32(save_p, mobj->hitlag); } + if (diff2 & MD2_DISPOFFSET) + { + WRITEINT32(save_p, mobj->dispoffset); + } if (diff2 & MD2_LASTMOMZ) { WRITEINT32(save_p, mobj->lastmomz); @@ -3154,6 +3162,10 @@ static thinker_t* LoadMobjThinker(actionf_p1 thinker) { mobj->hitlag = READINT32(save_p); } + if (diff2 & MD2_DISPOFFSET) + { + mobj->dispoffset = READINT32(save_p); + } if (diff2 & MD2_LASTMOMZ) { mobj->lastmomz = READINT32(save_p); diff --git a/src/p_sight.c b/src/p_sight.c index 4faffcf32..10d670ede 100644 --- a/src/p_sight.c +++ b/src/p_sight.c @@ -516,6 +516,7 @@ typedef struct { divline_t strace; // from t1 to t2 fixed_t bbox[4]; mobj_t *compareThing; + boolean alreadyHates; } traceblocking_t; static boolean P_CrossBlockingSubsector(size_t num, register traceblocking_t *tb) @@ -664,6 +665,7 @@ boolean P_TraceBlockingLines(mobj_t *t1, mobj_t *t2) tb.bbox[BOXTOP] = t2->y, tb.bbox[BOXBOTTOM] = t1->y; tb.compareThing = t1; + tb.alreadyHates = false; // the head node is the last node output return P_CrossBSPNodeBlocking((INT32)numnodes - 1, &tb); @@ -760,31 +762,17 @@ static boolean P_CrossBotTraversalSubsector(size_t num, register traceblocking_t return false; } - if (tb->compareThing->player != NULL) + if (tb->compareThing->player != NULL && tb->alreadyHates == false) { - // Treat damage sectors like walls - boolean alreadyHates = K_BotHatesThisSector( - tb->compareThing->player, tb->compareThing->subsector->sector, - tb->compareThing->x, tb->compareThing->y - ); + // Treat damage sectors like walls, if you're not already in a bad sector. + vertex_t pos; + P_ClosestPointOnLine(tb->compareThing->x, tb->compareThing->y, line, &pos); - if (alreadyHates == false) + if (K_BotHatesThisSector(tb->compareThing->player, line->frontsector, pos.x, pos.y) + || K_BotHatesThisSector(tb->compareThing->player, line->backsector, pos.x, pos.y)) { - INT32 lineside = 0; - vertex_t pos; - - P_ClosestPointOnLine(tb->compareThing->x, tb->compareThing->y, line, &pos); - lineside = P_PointOnLineSide(tb->compareThing->x, tb->compareThing->y, line); - - if (K_BotHatesThisSector( - tb->compareThing->player, - ((lineside == 1) ? line->frontsector : line->backsector), - pos.x, pos.y - )) - { - // This line does not block us, but we don't want to be in it. - return false; - } + // This line does not block us, but we don't want to be in it. + return false; } } } @@ -864,6 +852,17 @@ boolean P_TraceBotTraversal(mobj_t *t1, mobj_t *t2) tb.bbox[BOXTOP] = t2->y, tb.bbox[BOXBOTTOM] = t1->y; tb.compareThing = t1; + if (t1->player != NULL) + { + tb.alreadyHates = K_BotHatesThisSector( + t1->player, t1->subsector->sector, + t1->x, t1->y + ); + } + else + { + tb.alreadyHates = false; + } // the head node is the last node output return P_CrossBSPNodeBotTraversal((INT32)numnodes - 1, &tb); diff --git a/src/p_spec.c b/src/p_spec.c index 6d437fa08..942e72fb6 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -2070,7 +2070,7 @@ void P_CrossSpecialLine(line_t *line, INT32 side, mobj_t *thing) if (P_IsLineTripWire(line)) { - K_ApplyTripWire(player, TRIP_PASSED); + K_ApplyTripWire(player, TRIPSTATE_PASSED); } switch (line->special) diff --git a/src/p_user.c b/src/p_user.c index ec4d540c1..4030232f7 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -2727,17 +2727,17 @@ static CV_PossibleValue_t CV_CamSpeed[] = {{0, "MIN"}, {1*FRACUNIT, "MAX"}, {0, static CV_PossibleValue_t CV_CamRotate[] = {{-720, "MIN"}, {720, "MAX"}, {0, NULL}}; consvar_t cv_cam_dist[MAXSPLITSCREENPLAYERS] = { - CVAR_INIT ("cam_dist", "160", CV_FLOAT|CV_SAVE, NULL, NULL), - CVAR_INIT ("cam2_dist", "160", CV_FLOAT|CV_SAVE, NULL, NULL), - CVAR_INIT ("cam3_dist", "160", CV_FLOAT|CV_SAVE, NULL, NULL), - CVAR_INIT ("cam4_dist", "160", CV_FLOAT|CV_SAVE, NULL, NULL) + CVAR_INIT ("cam_dist", "190", CV_FLOAT|CV_SAVE, NULL, NULL), + CVAR_INIT ("cam2_dist", "190", CV_FLOAT|CV_SAVE, NULL, NULL), + CVAR_INIT ("cam3_dist", "190", CV_FLOAT|CV_SAVE, NULL, NULL), + CVAR_INIT ("cam4_dist", "190", CV_FLOAT|CV_SAVE, NULL, NULL) }; consvar_t cv_cam_height[MAXSPLITSCREENPLAYERS] = { - CVAR_INIT ("cam_height", "50", CV_FLOAT|CV_SAVE, NULL, NULL), - CVAR_INIT ("cam2_height", "50", CV_FLOAT|CV_SAVE, NULL, NULL), - CVAR_INIT ("cam3_height", "50", CV_FLOAT|CV_SAVE, NULL, NULL), - CVAR_INIT ("cam4_height", "50", CV_FLOAT|CV_SAVE, NULL, NULL) + CVAR_INIT ("cam_height", "75", CV_FLOAT|CV_SAVE, NULL, NULL), + CVAR_INIT ("cam2_height", "75", CV_FLOAT|CV_SAVE, NULL, NULL), + CVAR_INIT ("cam3_height", "75", CV_FLOAT|CV_SAVE, NULL, NULL), + CVAR_INIT ("cam4_height", "75", CV_FLOAT|CV_SAVE, NULL, NULL) }; consvar_t cv_cam_still[MAXSPLITSCREENPLAYERS] = { diff --git a/src/r_things.c b/src/r_things.c index 4d5df9484..cc5694dff 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -1503,7 +1503,7 @@ static void R_ProjectSprite(mobj_t *thing) fixed_t paperoffset = 0, paperdistance = 0; angle_t centerangle = 0; - INT32 dispoffset = thing->info->dispoffset; + INT32 dispoffset = thing->dispoffset; //SoM: 3/17/2000 fixed_t gz = 0, gzt = 0;