diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 814320fce..e63cafe33 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -617,6 +617,7 @@ static inline void resynch_write_player(resynch_pak *rsp, const size_t i) rsp->airtime = (tic_t)LONG(players[i].airtime); rsp->driftInput = players[i].driftInput; + rsp->airFailsafe = players[i].airFailsafe; rsp->trickpanel = (UINT8)players[i].trickpanel; rsp->trickdelay = (boolean)players[i].trickdelay; @@ -779,6 +780,7 @@ static void resynch_read_player(resynch_pak *rsp) players[i].airtime = (tic_t)LONG(rsp->airtime); players[i].driftInput = (boolean)rsp->driftInput; + players[i].airFailsafe = (boolean)rsp->airFailsafe; players[i].trickpanel = (UINT8)rsp->trickpanel; players[i].trickdelay = (boolean)rsp->trickdelay; diff --git a/src/d_clisrv.h b/src/d_clisrv.h index f5e21d2e3..82baf6581 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -281,6 +281,7 @@ typedef struct INT32 kartstuff[NUMKARTSTUFF]; tic_t airtime; boolean driftInput; + boolean airFailsafe; UINT8 trickpanel; boolean trickdelay; fixed_t trickmomx; diff --git a/src/d_player.h b/src/d_player.h index fc98e93bd..95fd8adea 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -525,6 +525,7 @@ typedef struct player_s respawnvars_t respawn; // Respawn info tic_t airtime; // Keep track of how long you've been in the air boolean driftInput; // Whenever or not try drifting. + boolean airFailsafe; // Whenever or not try the air boost UINT8 trickpanel; // Trick panel state boolean trickdelay; // Prevent tricks until control stick is neutral diff --git a/src/g_game.c b/src/g_game.c index 155633606..3802e47d2 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1309,9 +1309,11 @@ void G_PreLevelTitleCard(void) // boolean G_IsTitleCardAvailable(void) { +#if 0 // The current level has no name. if (!mapheaderinfo[gamemap-1]->lvlttl[0]) return false; +#endif // The title card is available. return true; diff --git a/src/hardware/r_opengl/r_opengl.c b/src/hardware/r_opengl/r_opengl.c index 1ded58204..9e9bbee81 100644 --- a/src/hardware/r_opengl/r_opengl.c +++ b/src/hardware/r_opengl/r_opengl.c @@ -150,8 +150,8 @@ FUNCPRINTF void GL_DBG_Printf(const char *format, ...) char str[4096] = ""; va_list arglist; - if (gllogstream) - { + if (gllogstream) + { va_start(arglist, format); vsnprintf(str, 4096, format, arglist); va_end(arglist); @@ -698,7 +698,38 @@ static INT32 shader_leveltime = 0; "float fd = fe - fs;\n" \ "darkness = clamp((darkness - fs) * (1.0 / fd), 0.0, 1.0);\n" \ "}\n" \ - "final_color = mix(final_color, fade_color, darkness);\n" + "float colorBrightness = sqrt((final_color.r * final_color.r) + (final_color.g * final_color.g) + (final_color.b * final_color.b));\n" \ + "float fogBrightness = sqrt((fade_color.r * fade_color.r) + (fade_color.g * fade_color.g) + (fade_color.b * fade_color.b));\n" \ + "float colorIntensity = 0.0;\n" \ + "if (fogBrightness > colorBrightness) {\n" \ + "colorIntensity = 1.0 - min(final_color.r, min(final_color.g, final_color.b));\n" \ + "colorIntensity = abs(colorIntensity - (1.0 - fogBrightness));\n" \ + "} else {\n" \ + "colorIntensity = max(final_color.r, max(final_color.g, final_color.b));\n" \ + "colorIntensity = abs(colorIntensity - (fogBrightness));\n" \ + "}\n" \ + "colorIntensity *= darkness;\n" \ + "if (abs(final_color.r - fade_color.r) <= colorIntensity) {\n" \ + "final_color.r = fade_color.r;\n" \ + "} else if (final_color.r < fade_color.r) {\n" \ + "final_color.r += colorIntensity;\n" \ + "} else {\n" \ + "final_color.r -= colorIntensity;\n" \ + "}\n" \ + "if (abs(final_color.g - fade_color.g) <= colorIntensity) {\n" \ + "final_color.g = fade_color.g;\n" \ + "} else if (final_color.g < fade_color.g) {\n" \ + "final_color.g += colorIntensity;\n" \ + "} else {\n" \ + "final_color.g -= colorIntensity;\n" \ + "}\n" \ + "if (abs(final_color.b - fade_color.b) <= colorIntensity) {\n" \ + "final_color.b = fade_color.b;\n" \ + "} else if (final_color.b < fade_color.b) {\n" \ + "final_color.b += colorIntensity;\n" \ + "} else {\n" \ + "final_color.b -= colorIntensity;\n" \ + "}\n" \ #define GLSL_SOFTWARE_FRAGMENT_SHADER \ "uniform sampler2D tex;\n" \ diff --git a/src/k_bot.c b/src/k_bot.c index 21cd4f18e..9fcee08ce 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -26,6 +26,7 @@ #include "d_ticcmd.h" #include "m_random.h" #include "r_things.h" // numskins +#include "k_race.h" // finishBeamLine /*-------------------------------------------------- @@ -296,7 +297,7 @@ boolean K_BotCanTakeCut(player_t *player) --------------------------------------------------*/ static UINT32 K_BotRubberbandDistance(player_t *player) { - const UINT32 spacing = FixedDiv(512 * FRACUNIT, K_GetKartGameSpeedScalar(gamespeed)) / FRACUNIT; + const UINT32 spacing = FixedDiv(640 * FRACUNIT, K_GetKartGameSpeedScalar(gamespeed)) / FRACUNIT; const UINT8 portpriority = player - players; UINT8 pos = 0; UINT8 i; @@ -429,8 +430,8 @@ fixed_t K_BotTopSpeedRubberband(player_t *player) } else { - // Max at +25% for level 9 bots - rubberband = FRACUNIT + ((rubberband - FRACUNIT) / 4); + // Max at +10% for level 9 bots + rubberband = FRACUNIT + ((rubberband - FRACUNIT) / 10); } // Only allow you to go faster than your regular top speed if you're facing the right direction @@ -519,28 +520,35 @@ fixed_t K_BotFrictionRubberband(player_t *player, fixed_t frict) See header file for description. --------------------------------------------------*/ -fixed_t K_DistanceOfLineFromPoint(fixed_t v1x, fixed_t v1y, fixed_t v2x, fixed_t v2y, fixed_t cx, fixed_t cy) +fixed_t K_DistanceOfLineFromPoint(fixed_t v1x, fixed_t v1y, fixed_t v2x, fixed_t v2y, fixed_t px, fixed_t py) { - fixed_t v1toc[2] = {cx - v1x, cy - v1y}; - fixed_t v1tov2[2] = {v2x - v1x, v2y - v1y}; - - fixed_t mag = FixedMul(v1tov2[0], v1tov2[0]) + FixedMul(v1tov2[1], v1tov2[1]); - fixed_t dot = FixedMul(v1toc[0], v1tov2[0]) + FixedMul(v1toc[1], v1tov2[1]); + // Copy+paste from P_ClosestPointOnLine :pensive: + fixed_t startx = v1x; + fixed_t starty = v1y; + fixed_t dx = v2x - v1x; + fixed_t dy = v2y - v1y; + fixed_t cx, cy; + fixed_t vx, vy; + fixed_t magnitude; fixed_t t; - fixed_t px, py; - if (mag == 0) - { - return 0; - } + cx = px - startx; + cy = py - starty; - t = FixedDiv(dot, mag); + vx = dx; + vy = dy; - px = v1x + FixedMul(v1tov2[0], t); - py = v1y + FixedMul(v1tov2[1], t); + magnitude = R_PointToDist2(v2x, v2y, startx, starty); + vx = FixedDiv(vx, magnitude); + vy = FixedDiv(vy, magnitude); - return P_AproxDistance(cx - px, cy - py); + t = (FixedMul(vx, cx) + FixedMul(vy, cy)); + + vx = FixedMul(vx, t); + vy = FixedMul(vy, t); + + return R_PointToDist2(px, py, startx + vx, starty + vy); } /*-------------------------------------------------- @@ -686,6 +694,145 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) return predict; } +/*-------------------------------------------------- + static UINT8 K_TrySpindash(player_t *player) + + Determines conditions where the bot should attempt to spindash. + + Input Arguments:- + player - Bot player to check. + + Return:- + 0 to make the bot drive normally, 1 to e-brake, 2 to e-brake & charge spindash. + (TODO: make this an enum) +--------------------------------------------------*/ +static UINT8 K_TrySpindash(player_t *player) +{ + const tic_t difficultyModifier = (TICRATE/6); + + if (player->kartstuff[k_spindashboost] || player->kartstuff[k_tiregrease]) + { + // You just released a spindash, you don't need to try again yet, jeez. + return 0; + } + + // Try "start boosts" first + if (leveltime == starttime) + { + // Forces them to release, even if they haven't fully charged. + // Don't want them to keep charging if they didn't have time to. + return 0; + } + + if (leveltime < starttime) + { + INT32 boosthold = starttime - K_GetSpindashChargeTime(player); + + boosthold -= (MAXBOTDIFFICULTY - player->botvars.difficulty) * difficultyModifier; + + if (leveltime >= (unsigned)boosthold) + { + // Start charging... + return 2; + } + else + { + // Just hold your ground and e-brake. + return 1; + } + } + + // Logic for normal racing. + if (player->powers[pw_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 (player->speed < 10*mapobjectscale // Below the speed threshold + && player->kartstuff[k_speedboost] < (FRACUNIT/8)) // If you have other boosts, you can probably trust it. + { + 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; + + if (player->kartstuff[k_spindash] > chargingPoint) + { + // Time to release. + return 0; + } + + return 2; + } + + // We're doing just fine, we don't need to spindash, thanks. + return 0; +} + +/*-------------------------------------------------- + static INT16 K_FindBotController(mobj_t *mo) + + Finds if any bot controller linedefs are tagged to the bot's sector. + + Input Arguments:- + mo - The bot player's mobj. + + Return:- + Line number of the bot controller. -1 if it doesn't exist. +--------------------------------------------------*/ +static INT16 K_FindBotController(mobj_t *mo) +{ + msecnode_t *node; + ffloor_t *rover; + INT16 lineNum = -1; + + I_Assert(mo != NULL); + I_Assert(!P_MobjWasRemoved(mo)); + + for (node = mo->touching_sectorlist; node; node = node->m_sectorlist_next) + { + if (!node->m_sector) + { + continue; + } + + lineNum = P_FindSpecialLineFromTag(2004, node->m_sector->tag, -1); + + if (lineNum != -1) + { + return lineNum; + } + + for (rover = node->m_sector->ffloors; rover; rover = rover->next) + { + sector_t *rs = NULL; + + if (!(rover->flags & FF_EXISTS)) + { + continue; + } + + if (mo->z > *rover->topheight || mo->z + mo->height < *rover->bottomheight) + { + continue; + } + + rs = §ors[rover->secnum]; + lineNum = P_FindSpecialLineFromTag(2004, rs->tag, -1); + + if (lineNum != -1) + { + return lineNum; + } + } + } + + return -1; +} + /*-------------------------------------------------- void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) @@ -694,7 +841,10 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) { botprediction_t *predict = NULL; + boolean trySpindash = true; + UINT8 spindash = 0; INT32 turnamt = 0; + INT16 botController = -1; // Can't build a ticcmd if we aren't spawned... if (!player->mo) @@ -705,53 +855,87 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) // Remove any existing controls memset(cmd, 0, sizeof(ticcmd_t)); - if (gamestate != GS_LEVEL - || player->mo->scale <= 1) // funny post-finish death + if ( + gamestate != GS_LEVEL + || player->mo->scale <= 1 + || player->playerstate == PST_DEAD + ) { // No need to do anything else. return; } - if (player->playerstate == PST_DEAD) - { - cmd->buttons |= BT_ACCELERATE; - return; - } - // Complete override of all ticcmd functionality if (LUAh_BotTiccmd(player, cmd)) - return; - - // Start boost handler - if (leveltime <= starttime) { - tic_t length = (TICRATE/6); - tic_t boosthold = starttime - K_GetSpindashChargeTime(player); - - cmd->buttons |= BT_EBRAKEMASK; - - boosthold -= (MAXBOTDIFFICULTY - player->botvars.difficulty) * length; - - if (leveltime >= boosthold) - { - cmd->buttons |= BT_DRIFT; - } - return; } - // Handle steering towards waypoints! - if (player->nextwaypoint != NULL && player->nextwaypoint->mobj != NULL && !P_MobjWasRemoved(player->nextwaypoint->mobj)) + botController = K_FindBotController(player->mo); + + if (player->trickpanel != 0) { + // Trick panel state -- do nothing until a controller line is found, in which case do a trick. + + if (player->trickpanel == 1 && botController != -1) + { + line_t *controllerLine = &lines[botController]; + INT32 type = (sides[controllerLine->sidenum[0]].rowoffset / FRACUNIT); + + // Y Offset: Trick type + switch (type) + { + case 1: + cmd->turning = KART_FULLTURN; + break; + case 2: + cmd->turning = -KART_FULLTURN; + break; + case 3: + cmd->buttons |= BT_FORWARD; + break; + case 4: + cmd->buttons |= BT_BACKWARD; + break; + } + } + + // Don't do anything else. + return; + } + + if ((player->nextwaypoint != NULL + && player->nextwaypoint->mobj != NULL + && !P_MobjWasRemoved(player->nextwaypoint->mobj)) + || (botController != -1)) + { + // Handle steering towards waypoints! SINT8 turnsign = 0; angle_t destangle, moveangle, angle; INT16 anglediff; - predict = K_CreateBotPrediction(player); + if (botController != -1) + { + const fixed_t dist = (player->mo->radius * 4); + line_t *controllerLine = &lines[botController]; + + // X Offset: Movement direction + destangle = FixedAngle(sides[controllerLine->sidenum[0]].textureoffset); + + // Overwritten prediction + predict = Z_Calloc(sizeof(botprediction_t), PU_LEVEL, NULL); + + predict->x = player->mo->x + FixedMul(dist, FINECOSINE(destangle >> ANGLETOFINESHIFT)); + predict->y = player->mo->y + FixedMul(dist, FINESINE(destangle >> ANGLETOFINESHIFT)); + predict->radius = (DEFAULT_WAYPOINT_RADIUS / 4) * mapobjectscale; + } + else + { + predict = K_CreateBotPrediction(player); + destangle = R_PointToAngle2(player->mo->x, player->mo->y, predict->x, predict->y); + } - destangle = R_PointToAngle2(player->mo->x, player->mo->y, predict->x, predict->y); moveangle = player->mo->angle; - angle = (moveangle - destangle); if (angle < ANGLE_180) @@ -777,7 +961,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) else { const fixed_t playerwidth = (player->mo->radius * 2); - const fixed_t realrad = predict->radius - (playerwidth * 4); // Remove a "safe" distance away from the edges of the road + fixed_t realrad = predict->radius - (playerwidth * 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, @@ -853,8 +1037,77 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) } } - // Handle item usage - K_BotItemUsage(player, cmd, turnamt); + if (leveltime <= starttime && finishBeamLine != NULL) + { + const fixed_t distBase = 384*mapobjectscale; + const fixed_t distAdjust = 64*mapobjectscale; + + const fixed_t closeDist = distBase + (distAdjust * (9 - player->kartweight)); + const fixed_t farDist = closeDist + (distAdjust * 2); + + fixed_t distToFinish = K_DistanceOfLineFromPoint( + finishBeamLine->v1->x, finishBeamLine->v1->y, + finishBeamLine->v2->x, finishBeamLine->v2->y, + player->mo->x, player->mo->y + ); + + // Don't run the spindash code at all until we're in the right place + trySpindash = false; + + // If you're too far, enable spindash & stay still. + // If you're too close, start backing up. + + if (distToFinish < closeDist) + { + // Silly way of getting us to reverse, but it respects the above code + // where we figure out what the shape of the track looks like. + UINT16 oldButtons = cmd->buttons; + + cmd->buttons &= ~(BT_ACCELERATE|BT_BRAKE); + + if (oldButtons & BT_ACCELERATE) + { + cmd->buttons |= BT_BRAKE; + } + + if (oldButtons & BT_BRAKE) + { + cmd->buttons |= BT_ACCELERATE; + } + + cmd->forwardmove = -cmd->forwardmove; + } + else if (distToFinish < farDist) + { + // We're in about the right place, spindash now. + cmd->forwardmove = 0; + trySpindash = true; + } + } + + if (trySpindash == true) + { + // Spindashing + spindash = K_TrySpindash(player); + + if (spindash > 0) + { + cmd->buttons |= BT_EBRAKEMASK; + cmd->forwardmove = 0; + + if (spindash == 2 && player->speed < 6*mapobjectscale) + { + cmd->buttons |= BT_DRIFT; + } + } + } + + if (spindash == 0) + { + // 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? + K_BotItemUsage(player, cmd, turnamt); + } if (turnamt != 0) { @@ -869,6 +1122,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) if (turnamt > 0) { + // Count up if (player->botvars.turnconfirm < BOTTURNCONFIRM) { player->botvars.turnconfirm++; @@ -876,11 +1130,24 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) } else if (turnamt < 0) { + // Count down if (player->botvars.turnconfirm > -BOTTURNCONFIRM) { player->botvars.turnconfirm--; } } + else + { + // Back to neutral + if (player->botvars.turnconfirm < 0) + { + player->botvars.turnconfirm++; + } + else if (player->botvars.turnconfirm > 0) + { + player->botvars.turnconfirm--; + } + } if (abs(player->botvars.turnconfirm) >= BOTTURNCONFIRM) { diff --git a/src/k_grandprix.c b/src/k_grandprix.c index 57e31a885..e46a6a7be 100644 --- a/src/k_grandprix.c +++ b/src/k_grandprix.c @@ -167,9 +167,9 @@ void K_InitGrandPrixBots(void) difficultylevels[10] = max(1, startingdifficulty-5); difficultylevels[11] = max(1, startingdifficulty-6); difficultylevels[12] = max(1, startingdifficulty-6); - difficultylevels[13] = max(1, startingdifficulty-6); + difficultylevels[13] = max(1, startingdifficulty-7); difficultylevels[14] = max(1, startingdifficulty-7); - difficultylevels[15] = max(1, startingdifficulty-7); + difficultylevels[15] = max(1, startingdifficulty-8); } for (i = 0; i < MAXPLAYERS; i++) diff --git a/src/k_hud.c b/src/k_hud.c index 93d1ec46a..8736eb8e2 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -2066,7 +2066,7 @@ static void K_drawKartSpeedometer(void) { case 1: // Sonic Drift 2 style percentage default: - convSpeed = (((25*stplyr->speed)/24) * 100) / K_GetKartSpeed(stplyr, false); // Based on top speed! (cheats with the numbers due to some weird discrepancy) + convSpeed = (stplyr->speed * 100) / K_GetKartSpeed(stplyr, false); // Based on top speed! labeln = 0; break; case 2: // Kilometers @@ -2085,7 +2085,8 @@ static void K_drawKartSpeedometer(void) } // Don't overflow - if (convSpeed > 999) + // (negative speed IS really high speed :V) + if (convSpeed > 999 || convSpeed < 0) convSpeed = 999; numbers[0] = ((convSpeed / 100) % 10); diff --git a/src/k_kart.c b/src/k_kart.c index 5a808a7b4..cf1dfb05f 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -2527,10 +2527,17 @@ fixed_t K_GetKartSpeed(player_t *player, boolean doboostpower) finalspeed = FixedMul(finalspeed, FRACUNIT + (sphereAdd * player->spheres)); } - if (K_PlayerUsesBotMovement(player) && player->botvars.rival == true) + if (K_PlayerUsesBotMovement(player)) { - // +10% top speed for the rival - finalspeed = FixedMul(finalspeed, 11*FRACUNIT/10); + // Increase bot speed by 1-10% depending on difficulty + fixed_t add = (player->botvars.difficulty * (FRACUNIT/10)) / MAXBOTDIFFICULTY; + finalspeed = FixedMul(finalspeed, FRACUNIT + add); + + if (player->botvars.rival == true) + { + // +10% top speed for the rival + finalspeed = FixedMul(finalspeed, 11*FRACUNIT/10); + } } if (player->mo && !P_MobjWasRemoved(player->mo)) @@ -6895,7 +6902,15 @@ void K_UpdateDistanceFromFinishLine(player_t *const player) INT32 K_GetKartRingPower(player_t *player) { - return (((9 - player->kartspeed) + (9 - player->kartweight)) / 2); + INT32 ringPower = ((9 - player->kartspeed) + (9 - player->kartweight)) / 2; + + if (K_PlayerUsesBotMovement(player)) + { + // Double for Lv. 9 + ringPower += (player->botvars.difficulty * ringPower) / MAXBOTDIFFICULTY; + } + + return ringPower; } // Returns false if this player being placed here causes them to collide with any other player @@ -7041,6 +7056,7 @@ INT32 K_GetKartDriftSparkValue(player_t *player) Stage 1: red sparks Stage 2: blue sparks Stage 3: big large rainbow sparks +Stage 0: air failsafe */ void K_SpawnDriftBoostExplosion(player_t *player, int stage) { @@ -7071,6 +7087,11 @@ void K_SpawnDriftBoostExplosion(player_t *player, int stage) S_StartSound(player->mo, sfx_kc5b); S_StartSound(player->mo, sfx_s3kc4l); break; + + case 0: + overlay->color = SKINCOLOR_SILVER; + overlay->fuse = 16; + break; } overlay->extravalue1 = stage; @@ -7583,11 +7604,14 @@ static void K_KartSpindash(player_t *player) return; } - if (player->speed < 6*mapobjectscale && player->powers[pw_flashing] == 0) + if (player->speed == 0 && cmd->turning != 0 && leveltime % 8 == 0) { - if (cmd->turning != 0 && leveltime % 8 == 0) - S_StartSound(player->mo, sfx_ruburn); + // Rubber burn turn sfx + S_StartSound(player->mo, sfx_ruburn); + } + if (player->speed < 6*player->mo->scale) + { if ((cmd->buttons & (BT_DRIFT|BT_BRAKE)) == (BT_DRIFT|BT_BRAKE)) { INT16 chargetime = MAXCHARGETIME - ++player->kartstuff[k_spindash]; @@ -7604,6 +7628,15 @@ static void K_KartSpindash(player_t *player) K_KartSpindashWind(player->mo); } + if (player->powers[pw_flashing] > 0 && (leveltime & 1) && player->kartstuff[k_hyudorotimer] == 0) + { + // Every frame that you're invisible from flashing, spill a ring. + // Intentionally a lop-sided trade-off, so the game doesn't become + // Funky Kong's Ring Racers. + + P_PlayerRingBurst(player, 1); + } + if (chargetime > 0) { UINT16 soundcharge = 0; @@ -7631,6 +7664,39 @@ static void K_KartSpindash(player_t *player) } } +static void K_AirFailsafe(player_t *player) +{ + const fixed_t maxSpeed = 6*player->mo->scale; + const fixed_t thrustSpeed = 6*player->mo->scale; // 10*player->mo->scale + + ticcmd_t *cmd = &player->cmd; + + if (player->speed > maxSpeed // Above the max speed that you're allowed to use this technique. + || player->respawn.state != RESPAWNST_NONE) // Respawning, you don't need this AND drop dash :V + { + player->airFailsafe = false; + return; + } + + if ((cmd->buttons & BT_ACCELERATE) || K_GetForwardMove(player) != 0) + { + // Queue up later + player->airFailsafe = true; + return; + } + + if (player->airFailsafe == true) + { + // Push the player forward + P_Thrust(player->mo, K_MomentumAngle(player->mo), thrustSpeed); + + S_StartSound(player->mo, sfx_s23c); + K_SpawnDriftBoostExplosion(player, 0); + + player->airFailsafe = false; + } +} + // // K_AdjustPlayerFriction // @@ -8504,9 +8570,18 @@ void K_MoveKartPlayer(player_t *player, boolean onground) } } - K_KartDrift(player, P_IsObjectOnGround(player->mo)); // Not using onground, since we don't want this affected by spring pads + K_KartDrift(player, onground); K_KartSpindash(player); + if (onground == false) + { + K_AirFailsafe(player); + } + else + { + player->airFailsafe = false; + } + // Play the starting countdown sounds if (player == &players[g_localplayers[0]]) // Don't play louder in splitscreen { diff --git a/src/k_race.c b/src/k_race.c index 7d65ee2e6..0ac9cb679 100644 --- a/src/k_race.c +++ b/src/k_race.c @@ -41,7 +41,7 @@ #include "k_bot.h" #include "k_hud.h" -static line_t *finishBeamLine = NULL; +line_t *finishBeamLine = NULL; static mobj_t *beamPoints[2]; static UINT8 numBeamPoints = 0; diff --git a/src/k_race.h b/src/k_race.h index 13d870f8e..b9021893e 100644 --- a/src/k_race.h +++ b/src/k_race.h @@ -14,6 +14,8 @@ #include "r_defs.h" +extern line_t *finishBeamLine; + #define FINISHLINEBEAM_SPACING (48*mapobjectscale) /*-------------------------------------------------- diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index a8e0e5810..61c6e85b1 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -220,6 +220,8 @@ static int player_get(lua_State *L) lua_pushinteger(L, plr->airtime); else if (fastcmp(field,"driftInput")) lua_pushboolean(L, plr->driftInput); + else if (fastcmp(field,"airFailsafe")) + lua_pushboolean(L, plr->airFailsafe); else if (fastcmp(field,"tumbleBounces")) lua_pushinteger(L, plr->tumbleBounces); else if (fastcmp(field,"tumbleHeight")) @@ -533,6 +535,8 @@ static int player_set(lua_State *L) plr->airtime = (tic_t)luaL_checkinteger(L, 3); else if (fastcmp(field,"driftInput")) plr->driftInput = luaL_checkboolean(L, 3); + else if (fastcmp(field,"airFailsafe")) + plr->airFailsafe = luaL_checkboolean(L, 3); else if (fastcmp(field,"tumbleBounces")) plr->tumbleBounces = (UINT8)luaL_checkinteger(L, 3); else if (fastcmp(field,"tumbleHeight")) diff --git a/src/p_enemy.c b/src/p_enemy.c index 01f8f6bb7..a2c719609 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -4229,7 +4229,7 @@ void A_AttractChase(mobj_t *actor) else actor->drawflags &= ~MFD_DONTDRAW; - // spilled rings have ghost trails and get capped to a certain speed + // spilled rings get capped to a certain speed if (actor->type == (mobjtype_t)actor->info->reactiontime) { const fixed_t maxspeed = 4<momx = FixedMul(FixedDiv(actor->momx, oldspeed), newspeed); actor->momy = FixedMul(FixedDiv(actor->momy, oldspeed), newspeed); } - - if (!P_IsObjectOnGround(actor)) - P_SpawnGhostMobj(actor)->tics = 3; } if (actor->tracer && actor->tracer->player && actor->tracer->health diff --git a/src/p_inter.c b/src/p_inter.c index e9fae2943..4371752c8 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -2182,10 +2182,7 @@ static void P_FlingBurst mo->momy = (mo->target->momy/2) + FixedMul(FINESINE(fa>>ANGLETOFINESHIFT), ns); ns = FixedMul(momz, player->mo->scale); - P_SetObjectMomZ(mo, (mo->target->momz/2) + ns, false); - - if (player->mo->eflags & MFE_VERTICALFLIP) - mo->momz *= -1; + mo->momz = (mo->target->momz/2) + ((ns) * P_MobjFlip(mo)); } /** Spills an injured player's rings. @@ -2240,6 +2237,6 @@ void P_PlayerRingBurst(player_t *player, INT32 num_rings) while (i < num_rings) { P_FlingBurst(player, fa, z, - MT_DEBTSPIKE, 90, 3 * player->mo->scale / 2, i++); + MT_DEBTSPIKE, 0, 3 * player->mo->scale / 2, i++); } } diff --git a/src/p_mobj.c b/src/p_mobj.c index 2fd5375f5..96a10458f 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -2296,6 +2296,11 @@ boolean P_ZMovement(mobj_t *mo) { mom.x = mom.y = 0; mom.z = -mom.z/2; + + if (mo->fuse == 0) + { + mo->fuse = 90; + } } else if (mo->flags & MF_MISSILE) { @@ -6743,6 +6748,10 @@ static boolean P_MobjRegularThink(mobj_t *mobj) if (mobj->fuse == 16)/* to red*/ K_SpawnDriftBoostClip(mobj->target->player); break; + + case 0:/* air failsafe boost */ + mobj->color = SKINCOLOR_SILVER; // force white + break; } { @@ -9197,6 +9206,7 @@ static void P_DefaultMobjShadowScale(mobj_t *thing) thing->shadowscale = FRACUNIT; break; case MT_RING: + case MT_FLINGRING: case MT_DEBTSPIKE: case MT_FLOATINGITEM: case MT_BLUESPHERE: diff --git a/src/p_saveg.c b/src/p_saveg.c index 7ea1decdf..6ed11bb3b 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -258,6 +258,7 @@ static void P_NetArchivePlayers(void) WRITEUINT32(save_p, players[i].airtime); WRITEUINT8(save_p, players[i].driftInput); + WRITEUINT8(save_p, players[i].airFailsafe); WRITEUINT8(save_p, players[i].trickpanel); WRITEUINT8(save_p, players[i].trickdelay); @@ -463,6 +464,7 @@ static void P_NetUnArchivePlayers(void) players[i].airtime = READUINT32(save_p); players[i].driftInput = (boolean)READUINT8(save_p); + players[i].airFailsafe = (boolean)READUINT8(save_p); players[i].trickpanel = READUINT8(save_p); players[i].trickdelay = READUINT8(save_p); diff --git a/src/p_spec.c b/src/p_spec.c index 96caeacc9..97fb66c06 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -6955,6 +6955,8 @@ void P_SpawnSpecials(boolean fromnetsave) break; case 2003: // Respawn Line break; + case 2004: // Bot controller + break; default: break; diff --git a/src/p_user.c b/src/p_user.c index 51427fd29..e4785b249 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -2632,9 +2632,6 @@ static void P_DeathThink(player_t *player) if (player->deadtimer < INT32_MAX) player->deadtimer++; - if (player->bot) // don't allow bots to do any of the below, B_CheckRespawn does all they need for respawning already - goto notrealplayer; - if ((player->pflags & PF_GAMETYPEOVER) && (gametyperules & GTR_CIRCUIT)) { player->karthud[khud_timeovercam]++; @@ -2677,8 +2674,6 @@ static void P_DeathThink(player_t *player) } } -notrealplayer: - if (!player->mo) return; diff --git a/src/r_things.c b/src/r_things.c index e1e38041b..c4e76303d 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -1236,28 +1236,24 @@ static void R_ProjectDropShadow(mobj_t *thing, vissprite_t *vis, fixed_t scale, patch_t *patch; fixed_t xscale, yscale, shadowxscale, shadowyscale, shadowskew, x1, x2; INT32 light = 0; - fixed_t scalemul; UINT8 trans; - fixed_t floordiff; + UINT8 trans = tr_transsub; fixed_t groundz; pslope_t *groundslope; - boolean isflipped = thing->eflags & MFE_VERTICALFLIP; groundz = R_GetShadowZ(thing, &groundslope); if (abs(groundz-viewz)/tz > 4) return; // Prevent stretchy shadows and possible crashes - floordiff = abs((isflipped ? thing->height : 0) + thing->z - groundz); + if (thing->whiteshadow == true) + { + trans = tr_transadd; + } - trans = floordiff / (100*FRACUNIT) + 3; - if (trans >= 9) return; - - scalemul = FixedMul(FRACUNIT - floordiff/640, scale); - - patch = W_CachePatchName((thing->whiteshadow == true ? "LSHADOW" : "DSHADOW"), PU_CACHE); + patch = W_CachePatchName("DSHADOW", PU_CACHE); xscale = FixedDiv(projection[viewssnum], tz); yscale = FixedDiv(projectiony[viewssnum], tz); - shadowxscale = FixedMul(thing->radius*2, scalemul); - shadowyscale = FixedMul(FixedMul(thing->radius*2, scalemul), FixedDiv(abs(groundz - viewz), tz)); + shadowxscale = FixedMul(thing->radius*2, scale); + shadowyscale = FixedMul(FixedMul(thing->radius*2, scale), FixedDiv(abs(groundz - viewz), tz)); shadowyscale = min(shadowyscale, shadowxscale) / SHORT(patch->height); shadowxscale /= SHORT(patch->width); shadowskew = 0; @@ -1274,9 +1270,9 @@ static void R_ProjectDropShadow(mobj_t *thing, vissprite_t *vis, fixed_t scale, //CONS_Printf("Shadow is sloped by %d %d\n", xslope, zslope); if (viewz < groundz) - shadowyscale += FixedMul(FixedMul(thing->radius*2 / SHORT(patch->height), scalemul), zslope); + shadowyscale += FixedMul(FixedMul(thing->radius*2 / SHORT(patch->height), scale), zslope); else - shadowyscale -= FixedMul(FixedMul(thing->radius*2 / SHORT(patch->height), scalemul), zslope); + shadowyscale -= FixedMul(FixedMul(thing->radius*2 / SHORT(patch->height), scale), zslope); shadowyscale = abs(shadowyscale); @@ -1354,16 +1350,9 @@ static void R_ProjectDropShadow(mobj_t *thing, vissprite_t *vis, fixed_t scale, else shadow->extra_colormap = thing->subsector->sector->extra_colormap; - shadow->transmap = transtables + (trans<transmap = transtables + ((trans-1) << FF_TRANSSHIFT); - if (thing->whiteshadow == true) - { - shadow->colormap = scalelight[LIGHTLEVELS - 1][0]; // full bright! - } - else - { - shadow->colormap = scalelight[0][0]; // full dark! - } + shadow->colormap = colormaps; objectsdrawn++; }